main.go 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607
  1. package main
  2. import (
  3. "bytes"
  4. "crypto/tls"
  5. "database/sql"
  6. "database/sql/driver"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "html/template"
  11. "io"
  12. "io/ioutil"
  13. "log"
  14. "net"
  15. "net/http"
  16. "os"
  17. "regexp"
  18. "sort"
  19. "strings"
  20. "sync"
  21. "time"
  22. "unicode"
  23. "github.com/lib/pq"
  24. "github.com/oschwald/geoip2-golang"
  25. )
  26. var (
  27. useHTTP = os.Getenv("UR_USE_HTTP") != ""
  28. debug = os.Getenv("UR_DEBUG") != ""
  29. keyFile = getEnvDefault("UR_KEY_FILE", "key.pem")
  30. certFile = getEnvDefault("UR_CRT_FILE", "crt.pem")
  31. dbConn = getEnvDefault("UR_DB_URL", "postgres://user:password@localhost/ur?sslmode=disable")
  32. listenAddr = getEnvDefault("UR_LISTEN", "0.0.0.0:8443")
  33. geoIPPath = getEnvDefault("UR_GEOIP", "GeoLite2-City.mmdb")
  34. tpl *template.Template
  35. compilerRe = regexp.MustCompile(`\(([A-Za-z0-9()., -]+) \w+-\w+(?:| android| default)\) ([\[email protected]]+)`)
  36. progressBarClass = []string{"", "progress-bar-success", "progress-bar-info", "progress-bar-warning", "progress-bar-danger"}
  37. featureOrder = []string{"Various", "Folder", "Device", "Connection", "GUI"}
  38. knownVersions = []string{"v2", "v3"}
  39. )
  40. var funcs = map[string]interface{}{
  41. "commatize": commatize,
  42. "number": number,
  43. "proportion": proportion,
  44. "counter": func() *counter {
  45. return &counter{}
  46. },
  47. "progressBarClassByIndex": func(a int) string {
  48. return progressBarClass[a%len(progressBarClass)]
  49. },
  50. "slice": func(numParts, whichPart int, input []feature) []feature {
  51. var part []feature
  52. perPart := (len(input) / numParts) + len(input)%2
  53. parts := make([][]feature, 0, numParts)
  54. for len(input) >= perPart {
  55. part, input = input[:perPart], input[perPart:]
  56. parts = append(parts, part)
  57. }
  58. if len(input) > 0 {
  59. parts = append(parts, input[:len(input)])
  60. }
  61. return parts[whichPart-1]
  62. },
  63. }
  64. func getEnvDefault(key, def string) string {
  65. if val := os.Getenv(key); val != "" {
  66. return val
  67. }
  68. return def
  69. }
  70. type IntMap map[string]int
  71. func (p IntMap) Value() (driver.Value, error) {
  72. return json.Marshal(p)
  73. }
  74. func (p *IntMap) Scan(src interface{}) error {
  75. source, ok := src.([]byte)
  76. if !ok {
  77. return errors.New("Type assertion .([]byte) failed.")
  78. }
  79. var i map[string]int
  80. err := json.Unmarshal(source, &i)
  81. if err != nil {
  82. return err
  83. }
  84. *p = i
  85. return nil
  86. }
  87. type report struct {
  88. Received time.Time // Only from DB
  89. UniqueID string
  90. Version string
  91. LongVersion string
  92. Platform string
  93. NumFolders int
  94. NumDevices int
  95. TotFiles int
  96. FolderMaxFiles int
  97. TotMiB int
  98. FolderMaxMiB int
  99. MemoryUsageMiB int
  100. SHA256Perf float64
  101. MemorySize int
  102. // v2 fields
  103. URVersion int
  104. NumCPU int
  105. FolderUses struct {
  106. ReadOnly int
  107. IgnorePerms int
  108. IgnoreDelete int
  109. AutoNormalize int
  110. SimpleVersioning int
  111. ExternalVersioning int
  112. StaggeredVersioning int
  113. TrashcanVersioning int
  114. }
  115. DeviceUses struct {
  116. Introducer int
  117. CustomCertName int
  118. CompressAlways int
  119. CompressMetadata int
  120. CompressNever int
  121. DynamicAddr int
  122. StaticAddr int
  123. }
  124. Announce struct {
  125. GlobalEnabled bool
  126. LocalEnabled bool
  127. DefaultServersDNS int
  128. DefaultServersIP int
  129. OtherServers int
  130. }
  131. Relays struct {
  132. Enabled bool
  133. DefaultServers int
  134. OtherServers int
  135. }
  136. UsesRateLimit bool
  137. UpgradeAllowedManual bool
  138. UpgradeAllowedAuto bool
  139. // V2.5 fields (fields that were in v2 but never added to the database
  140. UpgradeAllowedPre bool
  141. RescanIntvs pq.Int64Array
  142. // v3 fields
  143. Uptime int
  144. NATType string
  145. AlwaysLocalNets bool
  146. CacheIgnoredFiles bool
  147. OverwriteRemoteDeviceNames bool
  148. ProgressEmitterEnabled bool
  149. CustomDefaultFolderPath bool
  150. WeakHashSelection string
  151. CustomTrafficClass bool
  152. CustomTempIndexMinBlocks bool
  153. TemporariesDisabled bool
  154. TemporariesCustom bool
  155. LimitBandwidthInLan bool
  156. CustomReleaseURL bool
  157. RestartOnWakeup bool
  158. CustomStunServers bool
  159. FolderUsesV3 struct {
  160. ScanProgressDisabled int
  161. ConflictsDisabled int
  162. ConflictsUnlimited int
  163. ConflictsOther int
  164. DisableSparseFiles int
  165. DisableTempIndexes int
  166. AlwaysWeakHash int
  167. CustomWeakHashThreshold int
  168. FsWatcherEnabled int
  169. PullOrder IntMap
  170. FilesystemType IntMap
  171. FsWatcherDelays pq.Int64Array
  172. }
  173. GUIStats struct {
  174. Enabled int
  175. UseTLS int
  176. UseAuth int
  177. InsecureAdminAccess int
  178. Debugging int
  179. InsecureSkipHostCheck int
  180. InsecureAllowFrameLoading int
  181. ListenLocal int
  182. ListenUnspecified int
  183. Theme IntMap
  184. }
  185. BlockStats struct {
  186. Total int
  187. Renamed int
  188. Reused int
  189. Pulled int
  190. CopyOrigin int
  191. CopyOriginShifted int
  192. CopyElsewhere int
  193. }
  194. TransportStats IntMap
  195. IgnoreStats struct {
  196. Lines int
  197. Inverts int
  198. Folded int
  199. Deletable int
  200. Rooted int
  201. Includes int
  202. EscapedIncludes int
  203. DoubleStars int
  204. Stars int
  205. }
  206. // V3 fields added late in the RC
  207. WeakHashEnabled bool
  208. // Generated
  209. Date string
  210. Address string
  211. }
  212. func (r *report) Validate() error {
  213. if r.UniqueID == "" || r.Version == "" || r.Platform == "" {
  214. return fmt.Errorf("missing required field")
  215. }
  216. if len(r.Date) != 8 {
  217. return fmt.Errorf("date not initialized")
  218. }
  219. // Some fields may not be null.
  220. if r.RescanIntvs == nil {
  221. r.RescanIntvs = []int64{}
  222. }
  223. if r.FolderUsesV3.FsWatcherDelays == nil {
  224. r.FolderUsesV3.FsWatcherDelays = []int64{}
  225. }
  226. return nil
  227. }
  228. func (r *report) FieldPointers() []interface{} {
  229. // All the fields of the report, in the same order as the database fields.
  230. return []interface{}{
  231. &r.Received, &r.UniqueID, &r.Version, &r.LongVersion, &r.Platform,
  232. &r.NumFolders, &r.NumDevices, &r.TotFiles, &r.FolderMaxFiles,
  233. &r.TotMiB, &r.FolderMaxMiB, &r.MemoryUsageMiB, &r.SHA256Perf,
  234. &r.MemorySize, &r.Date,
  235. // V2
  236. &r.URVersion, &r.NumCPU, &r.FolderUses.ReadOnly, &r.FolderUses.IgnorePerms,
  237. &r.FolderUses.IgnoreDelete, &r.FolderUses.AutoNormalize, &r.DeviceUses.Introducer,
  238. &r.DeviceUses.CustomCertName, &r.DeviceUses.CompressAlways,
  239. &r.DeviceUses.CompressMetadata, &r.DeviceUses.CompressNever,
  240. &r.DeviceUses.DynamicAddr, &r.DeviceUses.StaticAddr,
  241. &r.Announce.GlobalEnabled, &r.Announce.LocalEnabled,
  242. &r.Announce.DefaultServersDNS, &r.Announce.DefaultServersIP,
  243. &r.Announce.OtherServers, &r.Relays.Enabled, &r.Relays.DefaultServers,
  244. &r.Relays.OtherServers, &r.UsesRateLimit, &r.UpgradeAllowedManual,
  245. &r.UpgradeAllowedAuto, &r.FolderUses.SimpleVersioning,
  246. &r.FolderUses.ExternalVersioning, &r.FolderUses.StaggeredVersioning,
  247. &r.FolderUses.TrashcanVersioning,
  248. // V2.5
  249. &r.UpgradeAllowedPre, &r.RescanIntvs,
  250. // V3
  251. &r.Uptime, &r.NATType, &r.AlwaysLocalNets, &r.CacheIgnoredFiles,
  252. &r.OverwriteRemoteDeviceNames, &r.ProgressEmitterEnabled, &r.CustomDefaultFolderPath,
  253. &r.WeakHashSelection, &r.CustomTrafficClass, &r.CustomTempIndexMinBlocks,
  254. &r.TemporariesDisabled, &r.TemporariesCustom, &r.LimitBandwidthInLan,
  255. &r.CustomReleaseURL, &r.RestartOnWakeup, &r.CustomStunServers,
  256. &r.FolderUsesV3.ScanProgressDisabled, &r.FolderUsesV3.ConflictsDisabled,
  257. &r.FolderUsesV3.ConflictsUnlimited, &r.FolderUsesV3.ConflictsOther,
  258. &r.FolderUsesV3.DisableSparseFiles, &r.FolderUsesV3.DisableTempIndexes,
  259. &r.FolderUsesV3.AlwaysWeakHash, &r.FolderUsesV3.CustomWeakHashThreshold,
  260. &r.FolderUsesV3.FsWatcherEnabled,
  261. &r.FolderUsesV3.PullOrder, &r.FolderUsesV3.FilesystemType,
  262. &r.FolderUsesV3.FsWatcherDelays,
  263. &r.GUIStats.Enabled, &r.GUIStats.UseTLS, &r.GUIStats.UseAuth,
  264. &r.GUIStats.InsecureAdminAccess,
  265. &r.GUIStats.Debugging, &r.GUIStats.InsecureSkipHostCheck,
  266. &r.GUIStats.InsecureAllowFrameLoading, &r.GUIStats.ListenLocal,
  267. &r.GUIStats.ListenUnspecified, &r.GUIStats.Theme,
  268. &r.BlockStats.Total, &r.BlockStats.Renamed,
  269. &r.BlockStats.Reused, &r.BlockStats.Pulled, &r.BlockStats.CopyOrigin,
  270. &r.BlockStats.CopyOriginShifted, &r.BlockStats.CopyElsewhere,
  271. &r.TransportStats,
  272. &r.IgnoreStats.Lines, &r.IgnoreStats.Inverts, &r.IgnoreStats.Folded,
  273. &r.IgnoreStats.Deletable, &r.IgnoreStats.Rooted, &r.IgnoreStats.Includes,
  274. &r.IgnoreStats.EscapedIncludes, &r.IgnoreStats.DoubleStars, &r.IgnoreStats.Stars,
  275. // V3 added late in the RC
  276. &r.WeakHashEnabled,
  277. &r.Address,
  278. }
  279. }
  280. func (r *report) FieldNames() []string {
  281. // The database fields that back this struct in PostgreSQL
  282. return []string{
  283. // V1
  284. "Received",
  285. "UniqueID",
  286. "Version",
  287. "LongVersion",
  288. "Platform",
  289. "NumFolders",
  290. "NumDevices",
  291. "TotFiles",
  292. "FolderMaxFiles",
  293. "TotMiB",
  294. "FolderMaxMiB",
  295. "MemoryUsageMiB",
  296. "SHA256Perf",
  297. "MemorySize",
  298. "Date",
  299. // V2
  300. "ReportVersion",
  301. "NumCPU",
  302. "FolderRO",
  303. "FolderIgnorePerms",
  304. "FolderIgnoreDelete",
  305. "FolderAutoNormalize",
  306. "DeviceIntroducer",
  307. "DeviceCustomCertName",
  308. "DeviceCompressAlways",
  309. "DeviceCompressMetadata",
  310. "DeviceCompressNever",
  311. "DeviceDynamicAddr",
  312. "DeviceStaticAddr",
  313. "AnnounceGlobalEnabled",
  314. "AnnounceLocalEnabled",
  315. "AnnounceDefaultServersDNS",
  316. "AnnounceDefaultServersIP",
  317. "AnnounceOtherServers",
  318. "RelayEnabled",
  319. "RelayDefaultServers",
  320. "RelayOtherServers",
  321. "RateLimitEnabled",
  322. "UpgradeAllowedManual",
  323. "UpgradeAllowedAuto",
  324. // v0.12.19+
  325. "FolderSimpleVersioning",
  326. "FolderExternalVersioning",
  327. "FolderStaggeredVersioning",
  328. "FolderTrashcanVersioning",
  329. // V2.5
  330. "UpgradeAllowedPre",
  331. "RescanIntvs",
  332. // V3
  333. "Uptime",
  334. "NATType",
  335. "AlwaysLocalNets",
  336. "CacheIgnoredFiles",
  337. "OverwriteRemoteDeviceNames",
  338. "ProgressEmitterEnabled",
  339. "CustomDefaultFolderPath",
  340. "WeakHashSelection",
  341. "CustomTrafficClass",
  342. "CustomTempIndexMinBlocks",
  343. "TemporariesDisabled",
  344. "TemporariesCustom",
  345. "LimitBandwidthInLan",
  346. "CustomReleaseURL",
  347. "RestartOnWakeup",
  348. "CustomStunServers",
  349. "FolderScanProgressDisabled",
  350. "FolderConflictsDisabled",
  351. "FolderConflictsUnlimited",
  352. "FolderConflictsOther",
  353. "FolderDisableSparseFiles",
  354. "FolderDisableTempIndexes",
  355. "FolderAlwaysWeakHash",
  356. "FolderCustomWeakHashThreshold",
  357. "FolderFsWatcherEnabled",
  358. "FolderPullOrder",
  359. "FolderFilesystemType",
  360. "FolderFsWatcherDelays",
  361. "GUIEnabled",
  362. "GUIUseTLS",
  363. "GUIUseAuth",
  364. "GUIInsecureAdminAccess",
  365. "GUIDebugging",
  366. "GUIInsecureSkipHostCheck",
  367. "GUIInsecureAllowFrameLoading",
  368. "GUIListenLocal",
  369. "GUIListenUnspecified",
  370. "GUITheme",
  371. "BlocksTotal",
  372. "BlocksRenamed",
  373. "BlocksReused",
  374. "BlocksPulled",
  375. "BlocksCopyOrigin",
  376. "BlocksCopyOriginShifted",
  377. "BlocksCopyElsewhere",
  378. "Transport",
  379. "IgnoreLines",
  380. "IgnoreInverts",
  381. "IgnoreFolded",
  382. "IgnoreDeletable",
  383. "IgnoreRooted",
  384. "IgnoreIncludes",
  385. "IgnoreEscapedIncludes",
  386. "IgnoreDoubleStars",
  387. "IgnoreStars",
  388. // V3 added late in the RC
  389. "WeakHashEnabled",
  390. "Address",
  391. }
  392. }
  393. func setupDB(db *sql.DB) error {
  394. _, err := db.Exec(`CREATE TABLE IF NOT EXISTS Reports (
  395. Received TIMESTAMP NOT NULL,
  396. UniqueID VARCHAR(32) NOT NULL,
  397. Version VARCHAR(32) NOT NULL,
  398. LongVersion VARCHAR(256) NOT NULL,
  399. Platform VARCHAR(32) NOT NULL,
  400. NumFolders INTEGER NOT NULL,
  401. NumDevices INTEGER NOT NULL,
  402. TotFiles INTEGER NOT NULL,
  403. FolderMaxFiles INTEGER NOT NULL,
  404. TotMiB INTEGER NOT NULL,
  405. FolderMaxMiB INTEGER NOT NULL,
  406. MemoryUsageMiB INTEGER NOT NULL,
  407. SHA256Perf DOUBLE PRECISION NOT NULL,
  408. MemorySize INTEGER NOT NULL,
  409. Date VARCHAR(8) NOT NULL
  410. )`)
  411. if err != nil {
  412. return err
  413. }
  414. var t string
  415. row := db.QueryRow(`SELECT 'UniqueIDIndex'::regclass`)
  416. if err := row.Scan(&t); err != nil {
  417. if _, err = db.Exec(`CREATE UNIQUE INDEX UniqueIDIndex ON Reports (Date, UniqueID)`); err != nil {
  418. return err
  419. }
  420. }
  421. row = db.QueryRow(`SELECT 'ReceivedIndex'::regclass`)
  422. if err := row.Scan(&t); err != nil {
  423. if _, err = db.Exec(`CREATE INDEX ReceivedIndex ON Reports (Received)`); err != nil {
  424. return err
  425. }
  426. }
  427. // V2
  428. row = db.QueryRow(`SELECT attname FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'reports') AND attname = 'reportversion'`)
  429. if err := row.Scan(&t); err != nil {
  430. // The ReportVersion column doesn't exist; add the new columns.
  431. _, err = db.Exec(`ALTER TABLE Reports
  432. ADD COLUMN ReportVersion INTEGER NOT NULL DEFAULT 0,
  433. ADD COLUMN NumCPU INTEGER NOT NULL DEFAULT 0,
  434. ADD COLUMN FolderRO INTEGER NOT NULL DEFAULT 0,
  435. ADD COLUMN FolderIgnorePerms INTEGER NOT NULL DEFAULT 0,
  436. ADD COLUMN FolderIgnoreDelete INTEGER NOT NULL DEFAULT 0,
  437. ADD COLUMN FolderAutoNormalize INTEGER NOT NULL DEFAULT 0,
  438. ADD COLUMN DeviceIntroducer INTEGER NOT NULL DEFAULT 0,
  439. ADD COLUMN DeviceCustomCertName INTEGER NOT NULL DEFAULT 0,
  440. ADD COLUMN DeviceCompressAlways INTEGER NOT NULL DEFAULT 0,
  441. ADD COLUMN DeviceCompressMetadata INTEGER NOT NULL DEFAULT 0,
  442. ADD COLUMN DeviceCompressNever INTEGER NOT NULL DEFAULT 0,
  443. ADD COLUMN DeviceDynamicAddr INTEGER NOT NULL DEFAULT 0,
  444. ADD COLUMN DeviceStaticAddr INTEGER NOT NULL DEFAULT 0,
  445. ADD COLUMN AnnounceGlobalEnabled BOOLEAN NOT NULL DEFAULT FALSE,
  446. ADD COLUMN AnnounceLocalEnabled BOOLEAN NOT NULL DEFAULT FALSE,
  447. ADD COLUMN AnnounceDefaultServersDNS INTEGER NOT NULL DEFAULT 0,
  448. ADD COLUMN AnnounceDefaultServersIP INTEGER NOT NULL DEFAULT 0,
  449. ADD COLUMN AnnounceOtherServers INTEGER NOT NULL DEFAULT 0,
  450. ADD COLUMN RelayEnabled BOOLEAN NOT NULL DEFAULT FALSE,
  451. ADD COLUMN RelayDefaultServers INTEGER NOT NULL DEFAULT 0,
  452. ADD COLUMN RelayOtherServers INTEGER NOT NULL DEFAULT 0,
  453. ADD COLUMN RateLimitEnabled BOOLEAN NOT NULL DEFAULT FALSE,
  454. ADD COLUMN UpgradeAllowedManual BOOLEAN NOT NULL DEFAULT FALSE,
  455. ADD COLUMN UpgradeAllowedAuto BOOLEAN NOT NULL DEFAULT FALSE,
  456. ADD COLUMN FolderSimpleVersioning INTEGER NOT NULL DEFAULT 0,
  457. ADD COLUMN FolderExternalVersioning INTEGER NOT NULL DEFAULT 0,
  458. ADD COLUMN FolderStaggeredVersioning INTEGER NOT NULL DEFAULT 0,
  459. ADD COLUMN FolderTrashcanVersioning INTEGER NOT NULL DEFAULT 0
  460. `)
  461. if err != nil {
  462. return err
  463. }
  464. }
  465. row = db.QueryRow(`SELECT 'ReportVersionIndex'::regclass`)
  466. if err := row.Scan(&t); err != nil {
  467. if _, err = db.Exec(`CREATE INDEX ReportVersionIndex ON Reports (ReportVersion)`); err != nil {
  468. return err
  469. }
  470. }
  471. // V2.5
  472. row = db.QueryRow(`SELECT attname FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'reports') AND attname = 'upgradeallowedpre'`)
  473. if err := row.Scan(&t); err != nil {
  474. // The ReportVersion column doesn't exist; add the new columns.
  475. _, err = db.Exec(`ALTER TABLE Reports
  476. ADD COLUMN UpgradeAllowedPre BOOLEAN NOT NULL DEFAULT FALSE,
  477. ADD COLUMN RescanIntvs INT[] NOT NULL DEFAULT '{}'
  478. `)
  479. if err != nil {
  480. return err
  481. }
  482. }
  483. // V3
  484. row = db.QueryRow(`SELECT attname FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'reports') AND attname = 'uptime'`)
  485. if err := row.Scan(&t); err != nil {
  486. // The Uptime column doesn't exist; add the new columns.
  487. _, err = db.Exec(`ALTER TABLE Reports
  488. ADD COLUMN Uptime INTEGER NOT NULL DEFAULT 0,
  489. ADD COLUMN NATType VARCHAR(32) NOT NULL DEFAULT '',
  490. ADD COLUMN AlwaysLocalNets BOOLEAN NOT NULL DEFAULT FALSE,
  491. ADD COLUMN CacheIgnoredFiles BOOLEAN NOT NULL DEFAULT FALSE,
  492. ADD COLUMN OverwriteRemoteDeviceNames BOOLEAN NOT NULL DEFAULT FALSE,
  493. ADD COLUMN ProgressEmitterEnabled BOOLEAN NOT NULL DEFAULT FALSE,
  494. ADD COLUMN CustomDefaultFolderPath BOOLEAN NOT NULL DEFAULT FALSE,
  495. ADD COLUMN WeakHashSelection VARCHAR(32) NOT NULL DEFAULT '',
  496. ADD COLUMN CustomTrafficClass BOOLEAN NOT NULL DEFAULT FALSE,
  497. ADD COLUMN CustomTempIndexMinBlocks BOOLEAN NOT NULL DEFAULT FALSE,
  498. ADD COLUMN TemporariesDisabled BOOLEAN NOT NULL DEFAULT FALSE,
  499. ADD COLUMN TemporariesCustom BOOLEAN NOT NULL DEFAULT FALSE,
  500. ADD COLUMN LimitBandwidthInLan BOOLEAN NOT NULL DEFAULT FALSE,
  501. ADD COLUMN CustomReleaseURL BOOLEAN NOT NULL DEFAULT FALSE,
  502. ADD COLUMN RestartOnWakeup BOOLEAN NOT NULL DEFAULT FALSE,
  503. ADD COLUMN CustomStunServers BOOLEAN NOT NULL DEFAULT FALSE,
  504. ADD COLUMN FolderScanProgressDisabled INTEGER NOT NULL DEFAULT 0,
  505. ADD COLUMN FolderConflictsDisabled INTEGER NOT NULL DEFAULT 0,
  506. ADD COLUMN FolderConflictsUnlimited INTEGER NOT NULL DEFAULT 0,
  507. ADD COLUMN FolderConflictsOther INTEGER NOT NULL DEFAULT 0,
  508. ADD COLUMN FolderDisableSparseFiles INTEGER NOT NULL DEFAULT 0,
  509. ADD COLUMN FolderDisableTempIndexes INTEGER NOT NULL DEFAULT 0,
  510. ADD COLUMN FolderAlwaysWeakHash INTEGER NOT NULL DEFAULT 0,
  511. ADD COLUMN FolderCustomWeakHashThreshold INTEGER NOT NULL DEFAULT 0,
  512. ADD COLUMN FolderFsWatcherEnabled INTEGER NOT NULL DEFAULT 0,
  513. ADD COLUMN FolderPullOrder JSONB NOT NULL DEFAULT '{}',
  514. ADD COLUMN FolderFilesystemType JSONB NOT NULL DEFAULT '{}',
  515. ADD COLUMN FolderFsWatcherDelays INT[] NOT NULL DEFAULT '{}',
  516. ADD COLUMN GUIEnabled INTEGER NOT NULL DEFAULT 0,
  517. ADD COLUMN GUIUseTLS INTEGER NOT NULL DEFAULT 0,
  518. ADD COLUMN GUIUseAuth INTEGER NOT NULL DEFAULT 0,
  519. ADD COLUMN GUIInsecureAdminAccess INTEGER NOT NULL DEFAULT 0,
  520. ADD COLUMN GUIDebugging INTEGER NOT NULL DEFAULT 0,
  521. ADD COLUMN GUIInsecureSkipHostCheck INTEGER NOT NULL DEFAULT 0,
  522. ADD COLUMN GUIInsecureAllowFrameLoading INTEGER NOT NULL DEFAULT 0,
  523. ADD COLUMN GUIListenLocal INTEGER NOT NULL DEFAULT 0,
  524. ADD COLUMN GUIListenUnspecified INTEGER NOT NULL DEFAULT 0,
  525. ADD COLUMN GUITheme JSONB NOT NULL DEFAULT '{}',
  526. ADD COLUMN BlocksTotal INTEGER NOT NULL DEFAULT 0,
  527. ADD COLUMN BlocksRenamed INTEGER NOT NULL DEFAULT 0,
  528. ADD COLUMN BlocksReused INTEGER NOT NULL DEFAULT 0,
  529. ADD COLUMN BlocksPulled INTEGER NOT NULL DEFAULT 0,
  530. ADD COLUMN BlocksCopyOrigin INTEGER NOT NULL DEFAULT 0,
  531. ADD COLUMN BlocksCopyOriginShifted INTEGER NOT NULL DEFAULT 0,
  532. ADD COLUMN BlocksCopyElsewhere INTEGER NOT NULL DEFAULT 0,
  533. ADD COLUMN Transport JSONB NOT NULL DEFAULT '{}',
  534. ADD COLUMN IgnoreLines INTEGER NOT NULL DEFAULT 0,
  535. ADD COLUMN IgnoreInverts INTEGER NOT NULL DEFAULT 0,
  536. ADD COLUMN IgnoreFolded INTEGER NOT NULL DEFAULT 0,
  537. ADD COLUMN IgnoreDeletable INTEGER NOT NULL DEFAULT 0,
  538. ADD COLUMN IgnoreRooted INTEGER NOT NULL DEFAULT 0,
  539. ADD COLUMN IgnoreIncludes INTEGER NOT NULL DEFAULT 0,
  540. ADD COLUMN IgnoreEscapedIncludes INTEGER NOT NULL DEFAULT 0,
  541. ADD COLUMN IgnoreDoubleStars INTEGER NOT NULL DEFAULT 0,
  542. ADD COLUMN IgnoreStars INTEGER NOT NULL DEFAULT 0
  543. `)
  544. if err != nil {
  545. return err
  546. }
  547. }
  548. // V3 added late in the RC
  549. row = db.QueryRow(`SELECT attname FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'reports') AND attname = 'weakhashenabled'`)
  550. if err := row.Scan(&t); err != nil {
  551. // The WeakHashEnabled column doesn't exist; add the new columns.
  552. _, err = db.Exec(`ALTER TABLE Reports
  553. ADD COLUMN WeakHashEnabled BOOLEAN NOT NULL DEFAULT FALSE
  554. ADD COLUMN Address VARCHAR(45) NOT NULL DEFAULT ''
  555. `)
  556. if err != nil {
  557. return err
  558. }
  559. }
  560. return nil
  561. }
  562. func insertReport(db *sql.DB, r report) error {
  563. r.Received = time.Now().UTC()
  564. fields := r.FieldPointers()
  565. params := make([]string, len(fields))
  566. for i := range params {
  567. params[i] = fmt.Sprintf("$%d", i+1)
  568. }
  569. query := "INSERT INTO Reports (" + strings.Join(r.FieldNames(), ", ") + ") VALUES (" + strings.Join(params, ", ") + ")"
  570. _, err := db.Exec(query, fields...)
  571. return err
  572. }
  573. type withDBFunc func(*sql.DB, http.ResponseWriter, *http.Request)
  574. func withDB(db *sql.DB, f withDBFunc) http.HandlerFunc {
  575. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  576. f(db, w, r)
  577. })
  578. }
  579. func main() {
  580. log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
  581. log.SetOutput(os.Stdout)
  582. // Template
  583. fd, err := os.Open("static/index.html")
  584. if err != nil {
  585. log.Fatalln("template:", err)
  586. }
  587. bs, err := ioutil.ReadAll(fd)
  588. if err != nil {
  589. log.Fatalln("template:", err)
  590. }
  591. fd.Close()
  592. tpl = template.Must(template.New("index.html").Funcs(funcs).Parse(string(bs)))
  593. // DB
  594. db, err := sql.Open("postgres", dbConn)
  595. if err != nil {
  596. log.Fatalln("database:", err)
  597. }
  598. err = setupDB(db)
  599. if err != nil {
  600. log.Fatalln("database:", err)
  601. }
  602. // TLS & Listening
  603. var listener net.Listener
  604. if useHTTP {
  605. listener, err = net.Listen("tcp", listenAddr)
  606. } else {
  607. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  608. if err != nil {
  609. log.Fatalln("tls:", err)
  610. }
  611. cfg := &tls.Config{
  612. Certificates: []tls.Certificate{cert},
  613. SessionTicketsDisabled: true,
  614. }
  615. listener, err = tls.Listen("tcp", listenAddr, cfg)
  616. }
  617. if err != nil {
  618. log.Fatalln("listen:", err)
  619. }
  620. srv := http.Server{
  621. ReadTimeout: 5 * time.Second,
  622. WriteTimeout: 15 * time.Second,
  623. }
  624. http.HandleFunc("/", withDB(db, rootHandler))
  625. http.HandleFunc("/newdata", withDB(db, newDataHandler))
  626. http.HandleFunc("/summary.json", withDB(db, summaryHandler))
  627. http.HandleFunc("/movement.json", withDB(db, movementHandler))
  628. http.HandleFunc("/performance.json", withDB(db, performanceHandler))
  629. http.HandleFunc("/blockstats.json", withDB(db, blockStatsHandler))
  630. http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
  631. err = srv.Serve(listener)
  632. if err != nil {
  633. log.Fatalln("https:", err)
  634. }
  635. }
  636. var (
  637. cacheData []byte
  638. cacheTime time.Time
  639. cacheMut sync.Mutex
  640. )
  641. const maxCacheTime = 5 * 60 * time.Second
  642. func rootHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  643. if r.URL.Path == "/" || r.URL.Path == "/index.html" {
  644. cacheMut.Lock()
  645. defer cacheMut.Unlock()
  646. if time.Since(cacheTime) > maxCacheTime {
  647. rep := getReport(db)
  648. buf := new(bytes.Buffer)
  649. err := tpl.Execute(buf, rep)
  650. if err != nil {
  651. log.Println(err)
  652. http.Error(w, "Template Error", http.StatusInternalServerError)
  653. return
  654. }
  655. cacheData = buf.Bytes()
  656. cacheTime = time.Now()
  657. }
  658. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  659. w.Write(cacheData)
  660. } else {
  661. http.Error(w, "Not found", 404)
  662. return
  663. }
  664. }
  665. func newDataHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  666. defer r.Body.Close()
  667. addr := r.Header.Get("X-Forwarded-For")
  668. if addr != "" {
  669. addr = strings.Split(addr, ", ")[0]
  670. } else {
  671. addr = r.RemoteAddr
  672. }
  673. if host, _, err := net.SplitHostPort(addr); err == nil {
  674. addr = host
  675. }
  676. if net.ParseIP(addr) == nil {
  677. addr = ""
  678. }
  679. var rep report
  680. rep.Date = time.Now().UTC().Format("20060102")
  681. rep.Address = addr
  682. lr := &io.LimitedReader{R: r.Body, N: 40 * 1024}
  683. bs, _ := ioutil.ReadAll(lr)
  684. if err := json.Unmarshal(bs, &rep); err != nil {
  685. log.Println("decode:", err)
  686. if debug {
  687. log.Printf("%s", bs)
  688. }
  689. http.Error(w, "JSON Decode Error", http.StatusInternalServerError)
  690. return
  691. }
  692. if err := rep.Validate(); err != nil {
  693. log.Println("validate:", err)
  694. if debug {
  695. log.Printf("%#v", rep)
  696. }
  697. http.Error(w, "Validation Error", http.StatusInternalServerError)
  698. return
  699. }
  700. if err := insertReport(db, rep); err != nil {
  701. log.Println("insert:", err)
  702. if debug {
  703. log.Printf("%#v", rep)
  704. }
  705. http.Error(w, "Database Error", http.StatusInternalServerError)
  706. return
  707. }
  708. }
  709. func summaryHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  710. s, err := getSummary(db)
  711. if err != nil {
  712. log.Println("summaryHandler:", err)
  713. http.Error(w, "Database Error", http.StatusInternalServerError)
  714. return
  715. }
  716. bs, err := s.MarshalJSON()
  717. if err != nil {
  718. log.Println("summaryHandler:", err)
  719. http.Error(w, "JSON Encode Error", http.StatusInternalServerError)
  720. return
  721. }
  722. w.Header().Set("Content-Type", "application/json")
  723. w.Write(bs)
  724. }
  725. func movementHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  726. s, err := getMovement(db)
  727. if err != nil {
  728. log.Println("movementHandler:", err)
  729. http.Error(w, "Database Error", http.StatusInternalServerError)
  730. return
  731. }
  732. bs, err := json.Marshal(s)
  733. if err != nil {
  734. log.Println("movementHandler:", err)
  735. http.Error(w, "JSON Encode Error", http.StatusInternalServerError)
  736. return
  737. }
  738. w.Header().Set("Content-Type", "application/json")
  739. w.Write(bs)
  740. }
  741. func performanceHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  742. s, err := getPerformance(db)
  743. if err != nil {
  744. log.Println("performanceHandler:", err)
  745. http.Error(w, "Database Error", http.StatusInternalServerError)
  746. return
  747. }
  748. bs, err := json.Marshal(s)
  749. if err != nil {
  750. log.Println("performanceHandler:", err)
  751. http.Error(w, "JSON Encode Error", http.StatusInternalServerError)
  752. return
  753. }
  754. w.Header().Set("Content-Type", "application/json")
  755. w.Write(bs)
  756. }
  757. func blockStatsHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
  758. s, err := getBlockStats(db)
  759. if err != nil {
  760. log.Println("blockStatsHandler:", err)
  761. http.Error(w, "Database Error", http.StatusInternalServerError)
  762. return
  763. }
  764. bs, err := json.Marshal(s)
  765. if err != nil {
  766. log.Println("blockStatsHandler:", err)
  767. http.Error(w, "JSON Encode Error", http.StatusInternalServerError)
  768. return
  769. }
  770. w.Header().Set("Content-Type", "application/json")
  771. w.Write(bs)
  772. }
  773. type category struct {
  774. Values [4]float64
  775. Key string
  776. Descr string
  777. Unit string
  778. Type NumberType
  779. }
  780. type feature struct {
  781. Key string
  782. Version string
  783. Count int
  784. Pct float64
  785. }
  786. type featureGroup struct {
  787. Key string
  788. Version string
  789. Counts map[string]int
  790. }
  791. // Used in the templates
  792. type counter struct {
  793. n int
  794. }
  795. func (c *counter) Current() int {
  796. return c.n
  797. }
  798. func (c *counter) Increment() string {
  799. c.n++
  800. return ""
  801. }
  802. func (c *counter) DrawTwoDivider() bool {
  803. return c.n != 0 && c.n%2 == 0
  804. }
  805. // add sets a key in a nested map, initializing things if needed as we go.
  806. func add(storage map[string]map[string]int, parent, child string, value int) {
  807. n, ok := storage[parent]
  808. if !ok {
  809. n = make(map[string]int)
  810. storage[parent] = n
  811. }
  812. n[child] += value
  813. }
  814. // inc makes sure that even for unused features, we initialize them in the
  815. // feature map. Furthermore, this acts as a helper that accepts booleans
  816. // to increment by one, or integers to increment by that integer.
  817. func inc(storage map[string]int, key string, i interface{}) {
  818. cv := storage[key]
  819. switch v := i.(type) {
  820. case bool:
  821. if v {
  822. cv++
  823. }
  824. case int:
  825. cv += v
  826. }
  827. storage[key] = cv
  828. }
  829. type location struct {
  830. Latitude float64
  831. Longitude float64
  832. }
  833. func getReport(db *sql.DB) map[string]interface{} {
  834. geoip, err := geoip2.Open(geoIPPath)
  835. if err != nil {
  836. log.Println("opening geoip db", err)
  837. geoip = nil
  838. } else {
  839. defer geoip.Close()
  840. }
  841. nodes := 0
  842. countriesTotal := 0
  843. var versions []string
  844. var platforms []string
  845. var numFolders []int
  846. var numDevices []int
  847. var totFiles []int
  848. var maxFiles []int
  849. var totMiB []int
  850. var maxMiB []int
  851. var memoryUsage []int
  852. var sha256Perf []float64
  853. var memorySize []int
  854. var uptime []int
  855. var compilers []string
  856. var builders []string
  857. locations := make(map[location]int)
  858. countries := make(map[string]int)
  859. reports := make(map[string]int)
  860. totals := make(map[string]int)
  861. // category -> version -> feature -> count
  862. features := make(map[string]map[string]map[string]int)
  863. // category -> version -> feature -> group -> count
  864. featureGroups := make(map[string]map[string]map[string]map[string]int)
  865. for _, category := range featureOrder {
  866. features[category] = make(map[string]map[string]int)
  867. featureGroups[category] = make(map[string]map[string]map[string]int)
  868. for _, version := range knownVersions {
  869. features[category][version] = make(map[string]int)
  870. featureGroups[category][version] = make(map[string]map[string]int)
  871. }
  872. }
  873. // Initialize some features that hide behind if conditions, and might not
  874. // be initialized.
  875. add(featureGroups["Various"]["v2"], "Upgrades", "Pre-release", 0)
  876. add(featureGroups["Various"]["v2"], "Upgrades", "Automatic", 0)
  877. add(featureGroups["Various"]["v2"], "Upgrades", "Manual", 0)
  878. add(featureGroups["Various"]["v2"], "Upgrades", "Disabled", 0)
  879. add(featureGroups["Various"]["v3"], "Temporary Retention", "Disabled", 0)
  880. add(featureGroups["Various"]["v3"], "Temporary Retention", "Custom", 0)
  881. add(featureGroups["Various"]["v3"], "Temporary Retention", "Default", 0)
  882. add(featureGroups["Connection"]["v3"], "IP version", "IPv4", 0)
  883. add(featureGroups["Connection"]["v3"], "IP version", "IPv6", 0)
  884. add(featureGroups["Connection"]["v3"], "IP version", "Unknown", 0)
  885. var numCPU []int
  886. var rep report
  887. rows, err := db.Query(`SELECT ` + strings.Join(rep.FieldNames(), ",") + ` FROM Reports WHERE Received > now() - '1 day'::INTERVAL`)
  888. if err != nil {
  889. log.Println("sql:", err)
  890. return nil
  891. }
  892. defer rows.Close()
  893. for rows.Next() {
  894. err := rows.Scan(rep.FieldPointers()...)
  895. if err != nil {
  896. log.Println("sql:", err)
  897. return nil
  898. }
  899. if geoip != nil && rep.Address != "" {
  900. if addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(rep.Address, "0")); err == nil {
  901. city, err := geoip.City(addr.IP)
  902. if err == nil {
  903. loc := location{
  904. Latitude: city.Location.Latitude,
  905. Longitude: city.Location.Longitude,
  906. }
  907. locations[loc]++
  908. countries[city.Country.Names["en"]]++
  909. countriesTotal++
  910. }
  911. }
  912. }
  913. nodes++
  914. versions = append(versions, transformVersion(rep.Version))
  915. platforms = append(platforms, rep.Platform)
  916. if m := compilerRe.FindStringSubmatch(rep.LongVersion); len(m) == 3 {
  917. compilers = append(compilers, m[1])
  918. builders = append(builders, m[2])
  919. }
  920. if rep.NumFolders > 0 {
  921. numFolders = append(numFolders, rep.NumFolders)
  922. }
  923. if rep.NumDevices > 0 {
  924. numDevices = append(numDevices, rep.NumDevices)
  925. }
  926. if rep.TotFiles > 0 {
  927. totFiles = append(totFiles, rep.TotFiles)
  928. }
  929. if rep.FolderMaxFiles > 0 {
  930. maxFiles = append(maxFiles, rep.FolderMaxFiles)
  931. }
  932. if rep.TotMiB > 0 {
  933. totMiB = append(totMiB, rep.TotMiB*(1<<20))
  934. }
  935. if rep.FolderMaxMiB > 0 {
  936. maxMiB = append(maxMiB, rep.FolderMaxMiB*(1<<20))
  937. }
  938. if rep.MemoryUsageMiB > 0 {
  939. memoryUsage = append(memoryUsage, rep.MemoryUsageMiB*(1<<20))
  940. }
  941. if rep.SHA256Perf > 0 {
  942. sha256Perf = append(sha256Perf, rep.SHA256Perf*(1<<20))
  943. }
  944. if rep.MemorySize > 0 {
  945. memorySize = append(memorySize, rep.MemorySize*(1<<20))
  946. }
  947. if rep.Uptime > 0 {
  948. uptime = append(uptime, rep.Uptime)
  949. }
  950. totals["Device"] += rep.NumDevices
  951. totals["Folder"] += rep.NumFolders
  952. if rep.URVersion >= 2 {
  953. reports["v2"]++
  954. numCPU = append(numCPU, rep.NumCPU)
  955. // Various
  956. inc(features["Various"]["v2"], "Rate limiting", rep.UsesRateLimit)
  957. if rep.UpgradeAllowedPre {
  958. add(featureGroups["Various"]["v2"], "Upgrades", "Pre-release", 1)
  959. } else if rep.UpgradeAllowedAuto {
  960. add(featureGroups["Various"]["v2"], "Upgrades", "Automatic", 1)
  961. } else if rep.UpgradeAllowedManual {
  962. add(featureGroups["Various"]["v2"], "Upgrades", "Manual", 1)
  963. } else {
  964. add(featureGroups["Various"]["v2"], "Upgrades", "Disabled", 1)
  965. }
  966. // Folders
  967. inc(features["Folder"]["v2"], "Automatic normalization", rep.FolderUses.AutoNormalize)
  968. inc(features["Folder"]["v2"], "Ignore deletes", rep.FolderUses.IgnoreDelete)
  969. inc(features["Folder"]["v2"], "Ignore permissions", rep.FolderUses.IgnorePerms)
  970. inc(features["Folder"]["v2"], "Mode, send-only", rep.FolderUses.ReadOnly)
  971. add(featureGroups["Folder"]["v2"], "Versioning", "Simple", rep.FolderUses.SimpleVersioning)
  972. add(featureGroups["Folder"]["v2"], "Versioning", "External", rep.FolderUses.ExternalVersioning)
  973. add(featureGroups["Folder"]["v2"], "Versioning", "Staggered", rep.FolderUses.StaggeredVersioning)
  974. add(featureGroups["Folder"]["v2"], "Versioning", "Trashcan", rep.FolderUses.TrashcanVersioning)
  975. add(featureGroups["Folder"]["v2"], "Versioning", "Disabled", rep.NumFolders-rep.FolderUses.SimpleVersioning-rep.FolderUses.ExternalVersioning-rep.FolderUses.StaggeredVersioning-rep.FolderUses.TrashcanVersioning)
  976. // Device
  977. inc(features["Device"]["v2"], "Custom certificate", rep.DeviceUses.CustomCertName)
  978. inc(features["Device"]["v2"], "Introducer", rep.DeviceUses.Introducer)
  979. add(featureGroups["Device"]["v2"], "Compress", "Always", rep.DeviceUses.CompressAlways)
  980. add(featureGroups["Device"]["v2"], "Compress", "Metadata", rep.DeviceUses.CompressMetadata)
  981. add(featureGroups["Device"]["v2"], "Compress", "Nothing", rep.DeviceUses.CompressNever)
  982. add(featureGroups["Device"]["v2"], "Addresses", "Dynamic", rep.DeviceUses.DynamicAddr)
  983. add(featureGroups["Device"]["v2"], "Addresses", "Static", rep.DeviceUses.StaticAddr)
  984. // Connections
  985. inc(features["Connection"]["v2"], "Relaying, enabled", rep.Relays.Enabled)
  986. inc(features["Connection"]["v2"], "Discovery, global enabled", rep.Announce.GlobalEnabled)
  987. inc(features["Connection"]["v2"], "Discovery, local enabled", rep.Announce.LocalEnabled)
  988. add(featureGroups["Connection"]["v2"], "Discovery", "Default servers (using DNS)", rep.Announce.DefaultServersDNS)
  989. add(featureGroups["Connection"]["v2"], "Discovery", "Default servers (using IP)", rep.Announce.DefaultServersIP)
  990. add(featureGroups["Connection"]["v2"], "Discovery", "Other servers", rep.Announce.DefaultServersIP)
  991. add(featureGroups["Connection"]["v2"], "Relaying", "Default relays", rep.Relays.DefaultServers)
  992. add(featureGroups["Connection"]["v2"], "Relaying", "Other relays", rep.Relays.OtherServers)
  993. }
  994. if rep.URVersion >= 3 {
  995. reports["v3"]++
  996. inc(features["Various"]["v3"], "Custom LAN classification", rep.AlwaysLocalNets)
  997. inc(features["Various"]["v3"], "Ignore caching", rep.CacheIgnoredFiles)
  998. inc(features["Various"]["v3"], "Overwrite device names", rep.OverwriteRemoteDeviceNames)
  999. inc(features["Various"]["v3"], "Download progress disabled", !rep.ProgressEmitterEnabled)
  1000. inc(features["Various"]["v3"], "Custom default path", rep.CustomDefaultFolderPath)
  1001. inc(features["Various"]["v3"], "Custom traffic class", rep.CustomTrafficClass)
  1002. inc(features["Various"]["v3"], "Custom temporary index threshold", rep.CustomTempIndexMinBlocks)
  1003. inc(features["Various"]["v3"], "Weak hash enabled", rep.WeakHashEnabled)
  1004. inc(features["Various"]["v3"], "LAN rate limiting", rep.LimitBandwidthInLan)
  1005. inc(features["Various"]["v3"], "Custom release server", rep.CustomReleaseURL)
  1006. inc(features["Various"]["v3"], "Restart after suspend", rep.RestartOnWakeup)
  1007. inc(features["Various"]["v3"], "Custom stun servers", rep.CustomStunServers)
  1008. inc(features["Various"]["v3"], "Ignore patterns", rep.IgnoreStats.Lines > 0)
  1009. if rep.NATType != "" {
  1010. natType := rep.NATType
  1011. natType = strings.Replace(natType, "unknown", "Unknown", -1)
  1012. natType = strings.Replace(natType, "Symetric", "Symmetric", -1)
  1013. add(featureGroups["Various"]["v3"], "NAT Type", natType, 1)
  1014. }
  1015. if rep.TemporariesDisabled {
  1016. add(featureGroups["Various"]["v3"], "Temporary Retention", "Disabled", 1)
  1017. } else if rep.TemporariesCustom {
  1018. add(featureGroups["Various"]["v3"], "Temporary Retention", "Custom", 1)
  1019. } else {
  1020. add(featureGroups["Various"]["v3"], "Temporary Retention", "Default", 1)
  1021. }
  1022. inc(features["Folder"]["v3"], "Scan progress disabled", rep.FolderUsesV3.ScanProgressDisabled)
  1023. inc(features["Folder"]["v3"], "Disable sharing of partial files", rep.FolderUsesV3.DisableTempIndexes)
  1024. inc(features["Folder"]["v3"], "Disable sparse files", rep.FolderUsesV3.DisableSparseFiles)
  1025. inc(features["Folder"]["v3"], "Weak hash, always", rep.FolderUsesV3.AlwaysWeakHash)
  1026. inc(features["Folder"]["v3"], "Weak hash, custom threshold", rep.FolderUsesV3.CustomWeakHashThreshold)
  1027. inc(features["Folder"]["v3"], "Filesystem watcher", rep.FolderUsesV3.FsWatcherEnabled)
  1028. add(featureGroups["Folder"]["v3"], "Conflicts", "Disabled", rep.FolderUsesV3.ConflictsDisabled)
  1029. add(featureGroups["Folder"]["v3"], "Conflicts", "Unlimited", rep.FolderUsesV3.ConflictsUnlimited)
  1030. add(featureGroups["Folder"]["v3"], "Conflicts", "Limited", rep.FolderUsesV3.ConflictsOther)
  1031. for key, value := range rep.FolderUsesV3.PullOrder {
  1032. add(featureGroups["Folder"]["v3"], "Pull Order", prettyCase(key), value)
  1033. }
  1034. totals["GUI"] += rep.GUIStats.Enabled
  1035. inc(features["GUI"]["v3"], "Auth Enabled", rep.GUIStats.UseAuth)
  1036. inc(features["GUI"]["v3"], "TLS Enabled", rep.GUIStats.UseTLS)
  1037. inc(features["GUI"]["v3"], "Insecure Admin Access", rep.GUIStats.InsecureAdminAccess)
  1038. inc(features["GUI"]["v3"], "Skip Host check", rep.GUIStats.InsecureSkipHostCheck)
  1039. inc(features["GUI"]["v3"], "Allow Frame loading", rep.GUIStats.InsecureAllowFrameLoading)
  1040. add(featureGroups["GUI"]["v3"], "Listen address", "Local", rep.GUIStats.ListenLocal)
  1041. add(featureGroups["GUI"]["v3"], "Listen address", "Unspecified", rep.GUIStats.ListenUnspecified)
  1042. add(featureGroups["GUI"]["v3"], "Listen address", "Other", rep.GUIStats.Enabled-rep.GUIStats.ListenLocal-rep.GUIStats.ListenUnspecified)
  1043. for theme, count := range rep.GUIStats.Theme {
  1044. add(featureGroups["GUI"]["v3"], "Theme", prettyCase(theme), count)
  1045. }
  1046. for transport, count := range rep.TransportStats {
  1047. add(featureGroups["Connection"]["v3"], "Transport", strings.Title(transport), count)
  1048. if strings.HasSuffix(transport, "4") {
  1049. add(featureGroups["Connection"]["v3"], "IP version", "IPv4", count)
  1050. } else if strings.HasSuffix(transport, "6") {
  1051. add(featureGroups["Connection"]["v3"], "IP version", "IPv6", count)
  1052. } else {
  1053. add(featureGroups["Connection"]["v3"], "IP version", "Unknown", count)
  1054. }
  1055. }
  1056. }
  1057. }
  1058. var categories []category
  1059. categories = append(categories, category{
  1060. Values: statsForInts(totFiles),
  1061. Descr: "Files Managed per Device",
  1062. })
  1063. categories = append(categories, category{
  1064. Values: statsForInts(maxFiles),
  1065. Descr: "Files in Largest Folder",
  1066. })
  1067. categories = append(categories, category{
  1068. Values: statsForInts(totMiB),
  1069. Descr: "Data Managed per Device",
  1070. Unit: "B",
  1071. Type: NumberBinary,
  1072. })
  1073. categories = append(categories, category{
  1074. Values: statsForInts(maxMiB),
  1075. Descr: "Data in Largest Folder",
  1076. Unit: "B",
  1077. Type: NumberBinary,
  1078. })
  1079. categories = append(categories, category{
  1080. Values: statsForInts(numDevices),
  1081. Descr: "Number of Devices in Cluster",
  1082. })
  1083. categories = append(categories, category{
  1084. Values: statsForInts(numFolders),
  1085. Descr: "Number of Folders Configured",
  1086. })
  1087. categories = append(categories, category{
  1088. Values: statsForInts(memoryUsage),
  1089. Descr: "Memory Usage",
  1090. Unit: "B",
  1091. Type: NumberBinary,
  1092. })
  1093. categories = append(categories, category{
  1094. Values: statsForInts(memorySize),
  1095. Descr: "System Memory",
  1096. Unit: "B",
  1097. Type: NumberBinary,
  1098. })
  1099. categories = append(categories, category{
  1100. Values: statsForFloats(sha256Perf),
  1101. Descr: "SHA-256 Hashing Performance",
  1102. Unit: "B/s",
  1103. Type: NumberBinary,
  1104. })
  1105. categories = append(categories, category{
  1106. Values: statsForInts(numCPU),
  1107. Descr: "Number of CPU cores",
  1108. })
  1109. categories = append(categories, category{
  1110. Values: statsForInts(uptime),
  1111. Descr: "Uptime (v3)",
  1112. Type: NumberDuration,
  1113. })
  1114. reportFeatures := make(map[string][]feature)
  1115. for featureType, versions := range features {
  1116. var featureList []feature
  1117. for version, featureMap := range versions {
  1118. // We count totals of the given feature type, for example number of
  1119. // folders or devices, if that doesn't exist, we work out percentage
  1120. // against the total of the version reports. Things like "Various"
  1121. // never have counts.
  1122. total, ok := totals[featureType]
  1123. if !ok {
  1124. total = reports[version]
  1125. }
  1126. for key, count := range featureMap {
  1127. featureList = append(featureList, feature{
  1128. Key: key,
  1129. Version: version,
  1130. Count: count,
  1131. Pct: (100 * float64(count)) / float64(total),
  1132. })
  1133. }
  1134. }
  1135. sort.Sort(sort.Reverse(sortableFeatureList(featureList)))
  1136. reportFeatures[featureType] = featureList
  1137. }
  1138. reportFeatureGroups := make(map[string][]featureGroup)
  1139. for featureType, versions := range featureGroups {
  1140. var featureList []featureGroup
  1141. for version, featureMap := range versions {
  1142. for key, counts := range featureMap {
  1143. featureList = append(featureList, featureGroup{
  1144. Key: key,
  1145. Version: version,
  1146. Counts: counts,
  1147. })
  1148. }
  1149. }
  1150. reportFeatureGroups[featureType] = featureList
  1151. }
  1152. var countryList []feature
  1153. for country, count := range countries {
  1154. countryList = append(countryList, feature{
  1155. Key: country,
  1156. Count: count,
  1157. Pct: (100 * float64(count)) / float64(countriesTotal),
  1158. })
  1159. sort.Sort(sort.Reverse(sortableFeatureList(countryList)))
  1160. }
  1161. r := make(map[string]interface{})
  1162. r["features"] = reportFeatures
  1163. r["featureGroups"] = reportFeatureGroups
  1164. r["nodes"] = nodes
  1165. r["versionNodes"] = reports
  1166. r["categories"] = categories
  1167. r["versions"] = group(byVersion, analyticsFor(versions, 2000), 5)
  1168. r["versionPenetrations"] = penetrationLevels(analyticsFor(versions, 2000), []float64{50, 75, 90, 95})
  1169. r["platforms"] = group(byPlatform, analyticsFor(platforms, 2000), 5)
  1170. r["compilers"] = group(byCompiler, analyticsFor(compilers, 2000), 3)
  1171. r["builders"] = analyticsFor(builders, 12)
  1172. r["featureOrder"] = featureOrder
  1173. r["locations"] = locations
  1174. r["contries"] = countryList
  1175. return r
  1176. }
  1177. func ensureDir(dir string, mode int) {
  1178. fi, err := os.Stat(dir)
  1179. if os.IsNotExist(err) {
  1180. os.MkdirAll(dir, 0700)
  1181. } else if mode >= 0 && err == nil && int(fi.Mode()&0777) != mode {
  1182. os.Chmod(dir, os.FileMode(mode))
  1183. }
  1184. }
  1185. var (
  1186. plusRe = regexp.MustCompile(`\+.*$`)
  1187. plusStr = "(+dev)"
  1188. )
  1189. // transformVersion returns a version number formatted correctly, with all
  1190. // development versions aggregated into one.
  1191. func transformVersion(v string) string {
  1192. if v == "unknown-dev" {
  1193. return v
  1194. }
  1195. if !strings.HasPrefix(v, "v") {
  1196. v = "v" + v
  1197. }
  1198. v = plusRe.ReplaceAllString(v, " "+plusStr)
  1199. return v
  1200. }
  1201. type summary struct {
  1202. versions map[string]int // version string to count index
  1203. max map[string]int // version string to max users per day
  1204. rows map[string][]int // date to list of counts
  1205. }
  1206. func newSummary() summary {
  1207. return summary{
  1208. versions: make(map[string]int),
  1209. max: make(map[string]int),
  1210. rows: make(map[string][]int),
  1211. }
  1212. }
  1213. func (s *summary) setCount(date, version string, count int) {
  1214. idx, ok := s.versions[version]
  1215. if !ok {
  1216. idx = len(s.versions)
  1217. s.versions[version] = idx
  1218. }
  1219. if s.max[version] < count {
  1220. s.max[version] = count
  1221. }
  1222. row := s.rows[date]
  1223. if len(row) <= idx {
  1224. old := row
  1225. row = make([]int, idx+1)
  1226. copy(row, old)
  1227. s.rows[date] = row
  1228. }
  1229. row[idx] = count
  1230. }
  1231. func (s *summary) MarshalJSON() ([]byte, error) {
  1232. var versions []string
  1233. for v := range s.versions {
  1234. versions = append(versions, v)
  1235. }
  1236. sort.Strings(versions)
  1237. var filtered []string
  1238. for _, v := range versions {
  1239. if s.max[v] > 50 {
  1240. filtered = append(filtered, v)
  1241. }
  1242. }
  1243. versions = filtered
  1244. headerRow := []interface{}{"Day"}
  1245. for _, v := range versions {
  1246. headerRow = append(headerRow, v)
  1247. }
  1248. var table [][]interface{}
  1249. table = append(table, headerRow)
  1250. var dates []string
  1251. for k := range s.rows {
  1252. dates = append(dates, k)
  1253. }
  1254. sort.Strings(dates)
  1255. for _, date := range dates {
  1256. row := []interface{}{date}
  1257. for _, ver := range versions {
  1258. idx := s.versions[ver]
  1259. if len(s.rows[date]) > idx && s.rows[date][idx] > 0 {
  1260. row = append(row, s.rows[date][idx])
  1261. } else {
  1262. row = append(row, nil)
  1263. }
  1264. }
  1265. table = append(table, row)
  1266. }
  1267. return json.Marshal(table)
  1268. }
  1269. func getSummary(db *sql.DB) (summary, error) {
  1270. s := newSummary()
  1271. rows, err := db.Query(`SELECT Day, Version, Count FROM VersionSummary WHERE Day > now() - '2 year'::INTERVAL;`)
  1272. if err != nil {
  1273. return summary{}, err
  1274. }
  1275. defer rows.Close()
  1276. for rows.Next() {
  1277. var day time.Time
  1278. var ver string
  1279. var num int
  1280. err := rows.Scan(&day, &ver, &num)
  1281. if err != nil {
  1282. return summary{}, err
  1283. }
  1284. if ver == "v0.0" {
  1285. // ?
  1286. continue
  1287. }
  1288. // SUPER UGLY HACK to avoid having to do sorting properly
  1289. if len(ver) == 4 { // v0.x
  1290. ver = ver[:3] + "0" + ver[3:] // now v0.0x
  1291. }
  1292. s.setCount(day.Format("2006-01-02"), ver, num)
  1293. }
  1294. return s, nil
  1295. }
  1296. func getMovement(db *sql.DB) ([][]interface{}, error) {
  1297. rows, err := db.Query(`SELECT Day, Added, Removed, Bounced FROM UserMovement WHERE Day > now() - '2 year'::INTERVAL ORDER BY Day`)
  1298. if err != nil {
  1299. return nil, err
  1300. }
  1301. defer rows.Close()
  1302. res := [][]interface{}{
  1303. {"Day", "Joined", "Left", "Bounced"},
  1304. }
  1305. for rows.Next() {
  1306. var day time.Time
  1307. var added, removed, bounced int
  1308. err := rows.Scan(&day, &added, &removed, &bounced)
  1309. if err != nil {
  1310. return nil, err
  1311. }
  1312. row := []interface{}{day.Format("2006-01-02"), added, -removed, bounced}
  1313. if removed == 0 {
  1314. row[2] = nil
  1315. }
  1316. if bounced == 0 {
  1317. row[3] = nil
  1318. }
  1319. res = append(res, row)
  1320. }
  1321. return res, nil
  1322. }
  1323. func getPerformance(db *sql.DB) ([][]interface{}, error) {
  1324. rows, err := db.Query(`SELECT Day, TotFiles, TotMiB, SHA256Perf, MemorySize, MemoryUsageMiB FROM Performance WHERE Day > '2014-06-20'::TIMESTAMP ORDER BY Day`)
  1325. if err != nil {
  1326. return nil, err
  1327. }
  1328. defer rows.Close()
  1329. res := [][]interface{}{
  1330. {"Day", "TotFiles", "TotMiB", "SHA256Perf", "MemorySize", "MemoryUsageMiB"},
  1331. }
  1332. for rows.Next() {
  1333. var day time.Time
  1334. var sha256Perf float64
  1335. var totFiles, totMiB, memorySize, memoryUsage int
  1336. err := rows.Scan(&day, &totFiles, &totMiB, &sha256Perf, &memorySize, &memoryUsage)
  1337. if err != nil {
  1338. return nil, err
  1339. }
  1340. row := []interface{}{day.Format("2006-01-02"), totFiles, totMiB, float64(int(sha256Perf*10)) / 10, memorySize, memoryUsage}
  1341. res = append(res, row)
  1342. }
  1343. return res, nil
  1344. }
  1345. func getBlockStats(db *sql.DB) ([][]interface{}, error) {
  1346. rows, err := db.Query(`SELECT Day, Reports, Pulled, Renamed, Reused, CopyOrigin, CopyOriginShifted, CopyElsewhere FROM BlockStats WHERE Day > '2017-10-23'::TIMESTAMP ORDER BY Day`)
  1347. if err != nil {
  1348. return nil, err
  1349. }
  1350. defer rows.Close()
  1351. res := [][]interface{}{
  1352. {"Day", "Number of Reports", "Transferred (GiB)", "Saved by renaming files (GiB)", "Saved by resuming transfer (GiB)", "Saved by reusing data from old file (GiB)", "Saved by reusing shifted data from old file (GiB)", "Saved by reusing data from other files (GiB)"},
  1353. }
  1354. blocksToGb := float64(8 * 1024)
  1355. for rows.Next() {
  1356. var day time.Time
  1357. var reports, pulled, renamed, reused, copyOrigin, copyOriginShifted, copyElsewhere float64
  1358. err := rows.Scan(&day, &reports, &pulled, &renamed, &reused, &copyOrigin, &copyOriginShifted, &copyElsewhere)
  1359. if err != nil {
  1360. return nil, err
  1361. }
  1362. row := []interface{}{
  1363. day.Format("2006-01-02"),
  1364. reports,
  1365. pulled / blocksToGb,
  1366. renamed / blocksToGb,
  1367. reused / blocksToGb,
  1368. copyOrigin / blocksToGb,
  1369. copyOriginShifted / blocksToGb,
  1370. copyElsewhere / blocksToGb,
  1371. }
  1372. res = append(res, row)
  1373. }
  1374. return res, nil
  1375. }
  1376. type sortableFeatureList []feature
  1377. func (l sortableFeatureList) Len() int {
  1378. return len(l)
  1379. }
  1380. func (l sortableFeatureList) Swap(a, b int) {
  1381. l[a], l[b] = l[b], l[a]
  1382. }
  1383. func (l sortableFeatureList) Less(a, b int) bool {
  1384. if l[a].Pct != l[b].Pct {
  1385. return l[a].Pct < l[b].Pct
  1386. }
  1387. return l[a].Key > l[b].Key
  1388. }
  1389. func prettyCase(input string) string {
  1390. output := ""
  1391. for i, runeValue := range input {
  1392. if i == 0 {
  1393. runeValue = unicode.ToUpper(runeValue)
  1394. } else if unicode.IsUpper(runeValue) {
  1395. output += " "
  1396. }
  1397. output += string(runeValue)
  1398. }
  1399. return output
  1400. }