From f40f3b3b7be8127b6adf497f68f9450304eea47f Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Wed, 11 Jun 2014 20:04:23 +0200 Subject: [PATCH] Anonymous Usage Reporting --- auto/gui.files.go | 4 +- cmd/syncthing/gui.go | 59 +++++++++++++++++- cmd/syncthing/main.go | 12 ++++ cmd/syncthing/usage_report.go | 109 ++++++++++++++++++++++++++++++++++ config/config.go | 4 ++ gui/app.js | 57 +++++++++++++++--- gui/index.html | 28 ++++++++- 7 files changed, 260 insertions(+), 13 deletions(-) create mode 100644 cmd/syncthing/usage_report.go diff --git a/auto/gui.files.go b/auto/gui.files.go index fc4256a50..be50cacf5 100644 --- a/auto/gui.files.go +++ b/auto/gui.files.go @@ -18,7 +18,7 @@ func init() { bs, _ = ioutil.ReadAll(gr) Assets["angular.min.js"] = bs - bs, _ = hex.DecodeString("1f8b080000096e8800ffec3c6b73db3892dffd2b30bacc929ac89493d99dbb8ae36c659cccae372f579c4c5d9dc757458990c531452a246859e5e8bf5f3700527852749cccee55adab6662038d7ea1d1e86e001c8fc971b15c97e9e59c91f078481e1f3cfa33f9477c554cc8cf457949e23c21059bd3924c8b9c95e9a466455945e47996113eaa2225ad68794d93680fb07dac28296684cdd38a54455d4e290c4c28813f2f8b6b5ae634219335a0256f4e3eec576c9d5192a5539ac338368f199942d78422aa595103f13487764a5e9f1cbf7c7bf692ccd28c467b7be31f7eafb234676452162b20ff84b0b2a623ce649ad7b4f97b99d515fe27fe263f8c61e465564ce28c3c784266715601509c5fd6595ccabf11682fa8e1970ae49db2e0706fef3a2e49b5cea720567e498e9a11d1a248ea8c8641db178cc8f9c5f0900fa8cb6c12039a2312808a389e162e024667e96538aba1212d72123e9833b63c2d8beb34a1e590dcee11f8d11aa384cee23a6355745395b3bfd318dadec60b4ee0bff78fcfdeffb2ffa1b8a23910da31f6b828ae52da8cd5466e86269bac2cb28c966170d6b41eb332034115deab69b1044572920defa8826549af5fc40cc91c1cb6ad9794bd7b054d3825db56d4515c32a1603e13c009178463475672ca09560070bb39343a419d76fb627df202650cb4d61c2c12919c5f38909ce428a8c69eec5f96052ba645763c87f9073b6ed954606859c2fab0715794e62fb1cfe6068c886636eb255d168da4bc1d96c41958647e59c1029915252593a2c82a92c16c420b63b4d42932c6818117de7e9b264f48f03aad18cd0111cc2068610a66cf67969c4ae9888020cf9304a6a4a21500b2f5129654c0e80d83bfe44c8935b51929c8dfc43767344f5e4d969582fe5dcd2e0b9cd7f7680aafd3450abee655faf3b81a6e71e7f56242cb4eecef015f9c9fe420e8759c9d2914440f69ba487867ccd2badcc865e797e33f8dcb181651f69e7eaa0148550ea88c808260649e701d6d41fae30724c2280dcd23f25fc06312d12b26a0bfea150a7fe34ef3799ebfcce34946138588e8222f52b03bf0f0eb2d62b4cf4ebe5f17530f52de732f9ca745c9fc0889eceeafe3336cff596c37eadac166b26d57d9d4c67f3ccd4f6d39450bc14ef7e00bdd0b5ed6e9997b61cbf5aa4eccc793fb2ce68fbaa088ed790d7b31ecb0d3983bfe8f9ac83b119ec655b52acaa41ba90225112fb72d9ddc7e78ad2e5b8c44fefee1c3e919041225014277b0a1e7a727afe85a41060d44b44814f132bd82bf8d496ab744d8e2ceeae994d2842661b323e24f3a23e1777c03545b95094ef39485c343bd2b0cfe23a70c7470c5f79060889b469c85c11c36f7c080b6b757fcd9682c6c37db3bb2b11dd8cd04c256f39a25c52aef8674edfb3ad31b5bb9bfc4e0d56ccdfac52a29abcbdca70ef784f8b47e3b89a75749592cc10e801e582ed80558c3a488cb4446911bcfa47409d86efc3390638ec06d84a5f2c6a3ac0810864d84f99004e36a0d0b7d013c56687755a5849649cc625338dd400f5d262030021b385e617928829cd0cd9d44de4c903a6efbfbbbc9efb09f46a0b32a54c39d61046bf5653c9d2bc8d3c49a18a7027818f5574473144003cd31eff8f8fee4b8582c8b1c1c0c62eaab1e450d1cef799a5c589a30a5527f77f3a8c4b0bd670a63e3bc5801758ca421765d85c39106c130140d1166bf8db887644c1e1d1c1ce89069221d55f3a304e8305e170ddd26e80c33309f8ab837c3ce681e57ef5639c4904b5ab23557b5031e7f9a14edd0eadd582d0c366a3716248a93028e0a421e60ff4dcce6d122be090f46e4bfc80f42971ce224ff79cd68f5a1601000ec3bd2090b0a95c792a1cda146b9a8d92ed210d8f5a1ad8179896f203766d33909a94fb596520efa8ae000d46763e3f2107a4a66ba895d8b41a44abdd7819960b9c96d0ef7cc240a223456579a3bc56673efc08dbd98e9ab1ee160dd1f41ce56e79042a739046feead05028efc2ac7dd4ef5ee1a051b354c14a413b0c6be43125ecc67ac582e81b21333af4d30b18603f47c0e32bc7f3bba01e7ff9e1f5c44acf80804ca63981a70e60f4507cccba46265f86878a8cb21871f9101268eb0d90ec8e7cf64db7a92647460ca22ba1f423709075b2eb1d4704acb29f8e7f892ca89794806df0f074e69a54a14811c337e9c41c4f8ed273ccd67c53798ed0453b5f2ab4db63663410a73e3a52c1762e00b93b6682a31f15e4ccb325dc490b6b93039d467cca16e14df7c22619ff449ec50af281f727fcdf11ff4c3aa4de172ca402a8080ddc26927b8b00489b10b40e1e1d0d42bdf8d665901111a9071bb452c8039dc22361fcfb4d819b945370f708ecd4b0e88dec2bf272f2e74a3434053374d7b8451594639595422a8c2b5a9b5ae754958c1e39fc008bf08c592ad7fa4f45024c4c56252865de8fba189b1c3eb04bc7cc0c56fbdb143b3275394eaff835e8babbbea5326277751da02a2bdcaaf2ddb59ffcbaacbf68fbd74663bc39d3af3fb469415ab397fb0c624635c69b298d4e5dbffda31df8acaff6922184ea0739ff28bf22bdd390dc88cce293734e578c2c3a684b8a66505b85dfc7d4bf56429ecbdbffa69ef9867d86e13246c694717172558604ec34f63d4c319c09031aad61890df866cbe552511580fb57c03459478a38ce6976c0e211779e491b88d0f3a0495d82056f61a833c4edb690d325a91bd3d4394c1c017a1b4f30ce43d8355900e19758b69a27fc8697f72071178c6fbd625b839c5b9b0046358684c98a297be4a096b9172f141c34e15edd24f1fe5f4d60c4d52a654e9dd9a198f2154bba224265861c41805c6aedb6eed4c329220ef964db2dd9c40e398508795406afeedc285a5f74e3c00a0e2c082ae94a96f2594d7d5dd2aaae26bd809e4a16da320cb72a6b840c83fcedebd8d2a7ef499ce0c2e150e7140b164fccc74ce4fc7ab27e436002a0c9cd9fe07b0af8057ee97993c6b18ff5e1579b0d998958a655159753ba005b20143234ec455b5e857a714b8c698e3f4ae7d5893d81e52f3e29bdab6bb3ce99889dec65ad525fcaf58507113622a4ec2f5396b4fc7bf6bad8b7eaae3ac72dbe9a8d3d487e4f3674b15e267077a305f0f6addb0d35928591eea4a07992b4ad1833407ff64956619c9294807e9c884b6b282bae4d1831166da3c45e2a08ad7007cdc4998a1d708ec9b08fa610f9fe63d539ae760f96b607f45dabb01199e8a66eb3dafa145bd5c8e3167871df876ba1de72479f98ac401a70c4989a9531dea8c955105ab9f85c1087d58bc54d6dd8d1260dc44e06d16b043f145e3e262ebbfb4338ebd6e7f39c003b081af54c90dc8bb04b573327dba3d0773773da7f2383f8939f06d28c68519d5679cad52ac58afe86489859c761dc19acaf9d1d39ebd540cdb1edaa7212d1a081290e5c098a28e65672f29139995dde9b8614a3fa40b5ad42cdcee5af63a5d41005cac225c5e081229549a5f0d32237e5ce3b1b6ce9b47824bfe7fc3b3cbf3d72f3229b72d2847ba3df63fd719f05d8d72d3d3ec1cf26318e6cc428ca8bc415b9725040a72c48388de80cb48c25b989a6690c50a9200f5bdbc01ffe254a3067646b39938aa3312422d1fb4e5dd3216b51739c09791a336666f9ba3df8b3407f7461ccb15815f022745193d003b3e2d39d35a208e53d6a8ed2b4479a99d056a8a076a69f249b9cf50cd8b55e0c615273b90b966f156d517b09facf37801fc6f7acca4b1c49c53e986f9e3159dd08c32ba53d91645eb46063fd175ab64f7950a971efa65f6ceb4ac591f6aa8a42c05d1dbc3537048930fc5d1f2836edc58c461b77621c17d08c9fbf809aa81dae8e821ec3d043685b627c08e521cc17fa7d534f9fb31263e4991d311490ff7ee68546dadc3219505d57a45b32001bacde2290dc7640cbc0440ae6dd96f5ab483cc2688b328b44ec1e541ef1b24a2926ccf202e53f093769292a7fa9210352ae878f8d055a95661cfd30bb58ea8aba8236d6c066f253eb44025e7762a813f9392c6573d2ad8e24a0860f22c1d21f1b2aee6a1bdab7a5c485415250b9be2715cd22f5de97d97057ff1d0e0f2c54e6a99d6ebda7a7b36dcfa77e6ea4dedac73b9a610af98deed0b2c2f37d408c673689966deb3a8ad0aef372287c4909d76cf43e3a09459dfce9eb9f8a5e1599548054ca9f755eea8122fa0603ed9d334c48515db367cd5481a6182419e592f07765ac734a371f9b2b91fd31925a9cf11343ecf75ae65f57c9f3cbae06ced4c10f8b831e7c413c7cdca14c2ea6c6d4f6bc54a95d5edb67c77e3dd160f3b4db8e24134fcbfdd4aa6ad331d99c57dec1b0e3b6ac480c793d92f8b3e06d3c0e95722bda9cd7be8be5b6a234768c5176f6aa38c0173c9f899773367b776fcac82cbd8c7baca69053fbb489d37fee5c2becf6c04ad0e54f2fc0aa3f93ffdc9456b0b106175983bb14195e2d1a0757fc9c52a87c4f7150a25d7fee91dfb8ad2258c78d8cd1bbe1e5954d11500bbac6f17f6eefecf9fc95fbe34ad4503ed99e720d9af90e740126899fd4e7bbfd56cea0958ef97a77f7fbcc818ac74ca2c1ecf2d8b1d21bac68e15a24b044e73b1a0da30ebfcc2eed317b01213d86bb8f5f0e21017932f27165750ec269737dee248d072c7218a102220b8155ee68964c4bcbc6fc628f823326e37bfc67dc916c6e12fec2040c06a1ec59621684eb304ce60644308af0130f668de8fee0431e0c5289d43f40b3642e33eb0ae21531b0e497b0c78a57b38d7bd1a639c0bbd9d4488acbc19232c51fee54b24d0ee2b5ce6293774dfd67cf7847b0ee94b65ace7c642acb816a20edf32f3eeb178146e86f288470b7ddb98c6b8b0112ae7dcc3a1a70c2bd0f19ccc1138f34e5e851c8c88e7ac43cc62b727efe5b6be4eb94a1a95662d8e5d53bb46f307990b65e22d9aa6a7a96e2a18b3b650659c27c5423c140e7f3c18911f1f7b6ab379ca7c67efee936b7917e9ae37f6e530e795fd1d14eff8a8a9519afbe19202219f85f3c3733bf1dec194bc0c7047a6da17ea5ea67c47963b0e3565e14a2bfc7b0b2f16aeb7db3cd705efaabcb8809bc7eaf8ef9b7819fa57871701bf576a3e18eb351577bb4be13e44eab847e13bdb505e48c2326dde8687ba40e2580fcff5b8382d6b8a4ac37844260d97ca4da898df49128f05b677a13095910013278079df4ca2792ac187ae9ac3fe23cb8fcb61cfe430a9823d05ab88d9385eabfa68e015e3b698c5d067ed50d08ea21bbec5797413472fd21222ada25c73d2ed5ffda96f113c5311d83ca02167eae72c164aea9b39f65ead7cb1382f9b5043b2218da5b9526813e45bc642a59829bbbf714cb150896562775fe0d1c350953a132b5851a9ce45a67391d069bac0bb3460c92392d71a33497a99b20a1f454f9b1017a7043f88603d8490e80f1a5ef83f0241f3624ebc51e0bf66c5a5f8259e70d2437c04d7f63c3a682211a46c3cb8031ec9bec4ac8b26d8d474acee8d19cdc1b670d49e607b2278e3ff4056fce34f87ade445cd84231e404c93d3d55953e959cdf1eb0aa1e86faa654f09fc22d6d956216254c3bce0048f280ac90ee724aaf05b38e163c50b4ad20f8f24060902920b2d80d3971d82fa88a064a0128da73696d3d68204d10e4db8c6b61f809165cb208f6144ac7ff6a5996f894d79b29b2f6b3622fcb193c328783750fd25bda149d89a9c366aeb738d2fd2340c4dd23ce6df85e8cb8fe917792337dcd677fa5e331d10ef932881e51978f9c77f263fa8ff58492b871c1f39400f5d54bbb5f47888cfe402f2b7f42eacf5e1e91eccbce9c74c2717f720ffca4d5e7d1755e237a51a7bc031c12e3b5b50fc0ad4bf909df14763ca3f1dda3441ef636777e1ac0f4bf731b35ebc7432710fea57dfc0c8aab9f81acd97d898c6bcf36aba87689cade275f5b6f9f2cdb7b76fe72b4b95fd5d1c4fe7757e75f2e22b33eb62557d6fc1af1cc6250f0e8496f96b90701cdd3e1afdb4195f9af5080ebc13ad6ce2c03279db0f764e1ab79453b0b27fba12c41d89f1f96fe3df7ebb183b7580619490af898c8ec88f1ee3500cc0692083288ac6f8705c201451908a7cfff150969dc6839d6ac403cafc7e0bcef581110ebabd96f2bdbc96e2e326e1b9477a4dc3a0e039bd3859e8e24b65437c9ff009098e95faaf242e3f80d836338838ab695627560f4f57cd2ab1bcb90cb88fe4af0a898d82962e801cc32f223d156685b9e9d100bf053520f9e53e7fb57c34d02b17e7126704d9cae0d9d3311ff94c52e854539da79f6a5e385494d4a5a34f350cc60f7c5dbe414e1429b234bf7ab2c5c1d53022345b8c48cc580979ce94959969add8163d00bbab685945755ecdd3997215f83aa5ab5fe3cc7df0c09fd0f72955363fe3315951024bbece923c602234066d3b810567a0d95f112a656b435bfc38e4d01a2aabec5bde44f9b315e4a28339fc302782138a825424ce4a1a27eb2f628f1fb7f9f9dbcd435a1181f0ab6ac76a9136d6ea471fb3b1ee07749a339f502c81a4c9bfed79f78ce9eaea36683707fc5e08fa7e7e89be95ee2bdc2d341523a934b1c2ff9e3fdfff9fc7fbff7971fb97c79b0763efd791ee23f94ee97b21f72d448efddb2d90ff030000ffff010000ffffeb001f9c8a580000") + bs, _ = hex.DecodeString("1f8b080000096e8800ffec3c7f73dbb692fffb53a0baf4897264ca49fa7a37519c4eeaa4eff9a5493c71d2bb39d79da144c8624d910a015ad6d8feeeb70b80144000141d2779ef669ea68d2562b1d85d2c16fb03e068440ef3e5ba48cee79c048703f278ffd10fe41fd1453e213fe7c53989b298e47c4e0b32cd335e249392e7050bc98b3425a217230565b4b8a471b803d83e324af219e1f384119697c59442c79812f8799e5fd222a33199ac012d7973f4618ff1754a499a4c6906fdf83ce2640a4d138aa86679098327193ca7e4d7a3c3576f4f5e915992d2706767b4fb274b938c934991af60f8a78417251d0a2293aca4d5ef655a32fc5ffe26bb23e8799ee69328250f9e92599432008ab2f3328d0af51b8176fa257c61c0ef94f7c73b3b975141d83a9b025bd93939a87a848b3c2e531af4ebb6fe909c9e0dc6a24359a49308d01c903e8848e0a9e1422074969c07b3121e247946820773ce97c7457e99c4b41890eb1d021fe36118d35954a69c8557ac98fd9d46f0ec6db41003fccfdee1c9fb5ff63ee417348381b6f43dccf38b84567d8d9eb7832699bcc8d3941641ffa47a7ac88b1418d56867d37c0982144356b4a3089605bd7c19711c667f5c3f3da7fcdd6b788453b2798a328a0a2e052c660228118c08ec484a46c5800c00ae6fc78d4610a7fd7cb13e7a893cf68da719682422393d732039ca9051833cd5be2c729e4ff3f4700ef30f7a5c93a9c1d0a280f561e3669466afb0cda6069488a636e9055de60e4ef171c141a891afed18849ed055438ab0a04e409fb37306cb6b9617944cf23c6524055d80279cd3c2a49773010c9c88e7d749fc94f47f4d18a7192082f907194e61d108bd20c74a3644429017710c13ca280340be5ec282ec737ac5fbb7430ddb9be8ea8466f1ebc99269f8de95fc3c4735788f9af36bb248c034bd4e7e1eb1c10659562e26b430d1bd0704517694012b97517aa2a1942da46a22c176544adddcd854e31d101e474504cb287d4f3f95a0e83abf2005023c83f667b1607b03d282107a493d6c480fb1fd024692c85629c416f1e9939b4fa3f445961d83126918c563f23201c50003be26aab985b60ad1ab2c9aa434f6e3daa041653491fc4d98692716d9d40dcd091a959fe52ea12b2d3e269be7defe1f8fb3639b02f98460636be7f776d717599ead1779c960af8c7082c4a2957b8703d1996905cfcbe4c4bd34d58ad3c5f4f168db721c5666576e9026f1a6c410db8b12f662d861a79130fc1f0dd96d45781c31b6ca8bb81da906a5102f374f5aa9fdf0abbe4ad113f9fb870fc727e0481404066ac8b70dd98be3a3d774ad4fdaf111914f148a68995cc0efc624d55b226c7127e5744a694ce3a0da11f193cc48f09dd800f5a7da042759c283c1d86c0afaff91510e32b8107b487f809b469406fd396ceefd06b4bdbde2e7d62061b3d9de918c4dc776221096cd4b1ee7abac1dd2b5ef9b44dfdac2fd250213674bd6cf56417959643e71b827c427f5eb4934bd888b7c097a00e381e6825e80364cf2a888951779eb99943606eb5d7c067ccc11b8f6b074da84971502c2a0f2301f92fe88ad61a12f8046867ac798e65ac6e02c3499331574ec52018911c8c0fe1ac903e9e4046eea14f26a82f47e9befef267fc2f61982cc58a0bb3b8310d6eaab683ad79027b135314e010837ea274473d0870734c3b8e3e3fba3c37cb1cc33303088a9ab78343108bca7497c6649a2c995fedd4da3e6c3769e29f48db31cdd39f4a4c1775d0583a101c1d1150d1066aff6b80764441eedefef9b9049ac0c55f5d11c74e86fb2866613648611984f44c29a6163388fd8bb55065ee092167c2d44ed80c74f15a28dadd65beb090777c38d0507c549014305fe0f90ff26e2f370115d05fb43f25f6457ca52401c653faf39651f720e3ec39e239cb0a050783c1ed8141a23e725df3634b8755dc636c0bc83df426ccca77312509f682da1ec7765c10168cec6adcb42982159d34c6c5b0c3254eabc0e9a01967bb8dbf14e332202578f97cc30a7f8b8b977e0c69ecfcc558f70b0ee0f20662b3308a1930c5c39f7d6020e477691e16ea75b7763041b354c14440fb0c6bec321bc984f78be5cc2c84ecc2237c1e51aeea3e5730c23da37bd2b70f1f774ff2ce4f94718a03884a90163fe5036c0bc4c182f824783b1c987ea7e407a18fac166db23373764f3f4284e69afc98b6c7e08cd24e86da8c454c3312da6609fc1195613f390f4be1ff49cdc2a91680c3966fc30058ff1eb4f7892cdf2af30db31c66dc5179b6c63c6fa09cc8d7764b510fb3e37698386c989f7625a16c92282c0cc85c921bec61c9a4af1d52712f6491fc70ef1caf4a1b0d702ff7e37acc6142ea71cb80208d82d9c7a820b4b0e31720168348c9b7215bbd12ccdc1438361dc661113600eb3888f0f6786ef8cd4a2990738c7e6a53a846fe1efd1cb3353e910b0299bea79885e594ac5b0284410856b53ab4deb92f05cf83ffd86fb4528a66cfd3d958522012e96e6c8b00b7d3f68626cb13a7d917110ecd7d6d821d9a32972f5ff41aef9c55de5a98293bb086d01de1ef34bcb36d6ffb2e2b2ed632799d9c670abccfcb61179c56cce379698224c084d2593da6cfb4f2df3ad89fc9fc642c308b4ee537e567ea35ba701893129158aa695273c642a884b5a30c0eda2ef6b8a274d60effdcd3ff6967986ed36c6812de998ec22070b8c694435462fce008694533dc780f456c3661b511289756cc41bc8a2c21ba6343be77370b9c8230fc7b57fd0c2a8c206beb2571954396dab36286f45b57674517a3d9f8752cf330cefe9ac83b4f0686a4ce5fd434cfba3db89c01aef5b17e3cd29cea42634ba058d09d3e4d255284129432ed169d02aa26df2e9229cce92a171c2b52cbd5b32a311b86a17944404338ce8a340df75dd6cd4244305f26e5905db55051afb0426ac02d2e36f172e4cbdb7e201001d072674154f5d33a122afee16118b2e61275045db4a4096e64c7181907f9cbc7b1b3251bc4c660d2a350ab143bee4a2663a17d571f6945cf761140ec66cef03e8575f64ee97a9aa358cfe6479d6bfbd6d662a9639b3f2763016f006040dc520aeac45b73ca5c435c218a773eec39ac4ba482d926ffab3ede949c74c7456565616f04fbea0f224c45456c2cd39ababe3dfd5da453f9551cadc7a3a6c55f501b9b9b144213f5bd083fa7a509b8a9dcc0245f2c0143af0cc28450b5215fec92a49539251e00ec29109ad790571a9d243c3cdb4690a65a14ae4007cd42998815709ec930866b1474cf34e939b17a0f96b207f45eaea7e8af5d874bde355b4b093c969ccd9b805df56b3e39c242f5da12c702a979434656a429df02264b0fa79d01fa20d8b96dababbd21c8cab10accd027628b1685c546cec9751e3d869b7973d2c80f57ca94aa140de2568d4c9cce9f614e6ee5aa7f2183f85b9efdb501a0766749b71b24a3063bda293252672ea75046b2a13a5a71d7ba934747b6057436a34e02420c9fdc614b52c3b7b49359159d19d891ba6f443b2a079c983cdae65afd31538c0f92ac4e58520a1364af5b531cc50946b3cdad67af2485229fe6d5876557ffd2c9572eb8256d2edb0ffb96ac07755cadb8e6ae7e01fdd306714d2f0ca2bb4655180a3a07a3c08e915988c38b886a9a93a59a4e01020be5757605f9c6234c04e683a93a5ba464068c48336bf1bc2c2fa2007d8327250fbecf5e3f0cf3cc9c0bc11c77245e05740495e840f408f8f0b41b4e188e3945562fb025e5e62478186e061b424fea49d6760f37cd577e38ae22dc85cb378adcb0bc88fd759b400fa6f3bcc64638939a7d20df3ed051dd39472ba55d8d688d6890c51d1758b64fb910a971cba45f6ceb0ac5a1fbaaba42d05d9dac15208c8261d9aa115856edc5864b1db3890e02e428a3651416da06e347460f61e0c3799b627c0f6521cce7fabd654f1fb21063e719ed12149c63b7754aa3ad7e1e0ca82aaad62332101b24da3290d466404b4f461b8fac95ef5c42864564e9c35426d145c16f4be4e220ac9b60cf23085a8b493843c339784cc5141c3c387ae4cb50e7b9a9ce9794453442d6163d579c3f1d8025594dba1047e26058d2e3a64b0e59110c0e4593a92e365c9e681bdab7a4c48c8f2820755f2382ae8e7aef4aecb42dc78a870f97c273d4deb356d9d2d1b6efd5b63f52a77d6ba5c13f0579ad6ed33342f6b881194676ca966d631a9ad33ef572207c7109db6cf4365a0b459dfcc5e73f12bc5b332911a9896ef636eaf120fa0603cd95135e481155b377cd9481a6280419e5b3707b66ac734a551f1aa3a1fd3ea25e9d7110c3a4f4daa55f67c8f3c3a13646d0d1044bf91a0c4e3c7cd8a04dcea746d4f2be3854eea665bbebbf26e9287ad2acc84130dffd65bc9b436a6c366721fdb0683961c31e0f144f6cbbc8bc25470e691486f688307c7ef16daa81e46f2c51bda687d405d5251f3aee6ecdaf69f7570e5fb5847392de767db50a7957d39b3cf33379c56072a55bf426ffe2f7f718db50108313b2c8c588f25581ab4ce2fb948159078d9421bc9b57f7afbbea674093d1eb6d3867747162cbc006097f66dc3dede7e7343fefab9612d2a68c7380787fd02710e048196da6fd5f76b43a79e82f67e7ef8f7ed594667a5956779796e996f71d10d722c175d2170aa8b0555bb59a767769bb980359fc05ec3b58597455c0cbe9c585c4eb17bb8acb21607722cb71fa231211d826b69659e2a429a87f79b3e0a7e64c4eda6b7715eb28671d80bdb0990b08645b179e857d52c89b33fb421a4d50018bbb76847738218f060944921da051b61e33cb029a1a6341c9c76e8f0dab470ae73358d7e2ef4761021a3f2aa8fd444f5cb1748a0de335ce6895074dfd67cf7807b0ee10b6bace74a432cbf16bc0edf32f3eeb1580a6fbaf288c7707d6b9fa6716023d0eadc8381270d2bd18998cce1388b469185ec0d89a7d62167b1dd9277325b5f265da594cad016c7ae691ca3f946ea42b9bc8b66c8696aaa0afaac3554116571be90577d8327fb43f2e4b127379b25dc577b7757aed559a4bb9ed857dd9c47f6b78c78c74b4d95d0dc17973408752d5c14cfedc07b0b51ea30c01d89aa6fa87b89f2952cb7143555e2ca48fc7b132f16aeb79b38d705efcabcb880abcbeaf8f74db40cfcabc38b409c2b0d9acdfa026fb05f5fae45efde0bf3924e533c44e4f2044623723423a5b8865b54d770c93cc22bf2144c194dc49b2f2298e625ecede01d14602b243e7b83046c2b4a5651c6f17440c42ec48b2b4a0618e0f7429ef599cef3644a43f273c9111adcb53e177d5ce8f09041798e6816242e7145135cb54994621db25c0e09cb110bfc40d4e2ad0e6405443b9101311c330be2f51c94cc920242d2cb04262624ff3da7997aff86c49230bc1bc6a89b307c2b488d0f4017f83e01e89ee14b3b0a32877f1889cef32152a724e1c2232eba637d74c76ac5fd4f90f81b5288eb269f960ba02794146e92bfc14f4fe1bf3f6ec2ddf1ef6c77b0e904bf7e3f807f82d33fc667bb8370f7c1e0e60ff87704fbd283473dc7152ba16d1b04de7b562629405c6fd3e9006fb76caeeae16596f122bada0325134d4ff6771fffb0fbe4c77dd79d2bdf51e28ab8879a489ee9a3ec118973575491dd08f083db6a79bfabac35b1db5cc4bb1bd5bb9d8a7297835b4e4475a0439a81bb9260bc94a3d3c53469533ebedf1ac9fa6c9e2724f7da3f6f44eb3ec9d2d013c3f1b29c3931c2bd787193d6c68c93fb7bf3a25f7b07ab5abddf233077297956030f6b088daa99d6f6c9201a92492503ed786b240e9aca1b609b03aeb88329808913a0798858a179a6c0070e4f97ec3db29c73d5edb9eaa644b0a3619581b8c06b95941a7865bf0d66d9f579dd15a4a3c946c42d1ed944e1cba480f0392fd662e8fa57f7d137089eeb086c1ad03b49f577142db47c66ea08a88c9cf4e2b4a8e24745865296ea9cb83da0880316fa88a916d2356acf0b7db054866c0bac270f74ae53e996692235a9484d2a6081260b3c20099a3c245969101327e70967f8a68b6995b7c029c197da58b7db14fafd8a16f147222007fac533f135cdcfe5976822861ee0cde6bae5d17e155ee2c8c4bc450d34c28e26319bac49320d19eb014f4a33d02decb523c99e48dac49f9b1bf2e4c771cd795e72e95df72050cde8eaa44adfafe6f8fe9c40b65725906704bec875b61188ec55112f29c1ba73aec81194840c5f70163cd62c921afae181c2a04080732905f0e455831c7d489033108941531da01b6b418118957021b1cd5bbd542daa9f45d02332dfe555cdb7c2a6bd87215b967c48c40d568752886618f597e48ac641ad7246af8dcd6dbc66ac2268926491789d4f577a9a76513c148a5bdb4edf15d57de2bde72ab13c072bfff807b2abffb13291027274e0001dbb466d97d2e301ba8b7df2b7e42ea475a1e91ec4bce9464c2b15f718feb57b78fdb26b812f0aacf401fbf4b7e9d982e2abfdfe85f44cdc04d6feb448b3097a1f3dbb0b655d48ba8f9a75a2a595887b8c7ef115948ccde58bd23e47c70ce29df78d3c8346e92a5ab3b7f2a56cdf42bf9d57e775f2b7513c9d97d9c5d1cb2f4cac8b54fd129d38471e15c23990521657fc825178fd68f8e3ede8bc996416c05bd1aa47025865e4f6fa5b274d68ca3168d93f5d08f2e0dbe8f4f7d1efbf9f8d9c3240374af257794607e48947393405702a482f0cc31126452442e905e9c8f71e0f542d61d4db2a463c7592dd6fc1b9de1a254037e9a6efd559431f35b1883d924b1af47311b8ca72711b5d3a19f2a5b34f49ff502beaa9c1d55b6debc71c3c4e364dcbd86a11e16ab3f4a7aea300ee03f5551be256434b17301cc7d7dc3d936a85b1e9410f5ff0d723d9f99e7815c541cf0ccf4f15ce10a295def36723d1f3b91aa1554c65967c2a45354813529b8c3e95d019df3d79fe0629d1b84893ece2e9068710c390d074312411e705c439535ea44d6dc567e103d03b460b1696199b2733ed7e07be4df5b728755793c57b51bad49faa8f4c13c3922fd31893bfc23506693b81256520d9df102ae1eb86b4448ddb9b47dcd0266b5a3523672dc4615e17c10945461889d28246f1fab3c813691d3f7ddb6948189108bfa874ac274ac76af9987d6ead435fadea2c2614532049fc6f7dde3e63a6b8da15da4d8138ec87b65f24066beebec081f1a660d42895aff0c7e98bbdff7dbcf79f67d77f7d7cfb60e47de5dd7d38dfca7d27e4be8528b07fbd05f27f000000ffff010000ffff341b4f235f5e0000") gr, _ = gzip.NewReader(bytes.NewBuffer(bs)) bs, _ = ioutil.ReadAll(gr) Assets["app.js"] = bs @@ -63,7 +63,7 @@ func init() { bs, _ = ioutil.ReadAll(gr) Assets["favicon.png"] = bs - bs, _ = hex.DecodeString("1f8b080000096e8800ffec5d7b77dbb692ffdf9f02e1ed2676d7a4ec244dbb8eacb3ae9db6de368f1327dbedf674f7402424a2260986006deb3abe9ffdce007c3f245ab613dfb67f2416096030180c06bf193c387e70f4faf0dd2f6f5e105f85c16463fcc0b6370e45bc48f8dc5764f3708b3cded97d4afe8b9e8a29f956247342238f08e5b384b82252099fa64a24d22107414074294912265972c63c67e3bd6444cc88f2b92452a489cba094c7083ccec5194b22e691e902689297c7ef6ca916012301775904e5944f15712169ca366622856a79042f19f9e9f8f0c5ab931764c603e66cd836b08ddc938046f37d8b451689e6368de37d4b2e2217ea8ee6fa95e65704014bf6ad933ce550258145dc804ab96f61a640d0530b4932ea4d360819874c51e2fa34914ced5ba99ad9df586582af546cb30f293fdbb7fec77e7f601f8a30a68a4f03666909b1084a1dbfd867de9c55ca453464fbd61967e7b1485425eb39f794bfefb1331083ad1fb6a1e15c711ad8d2a501dbdf75765a843c26dd84c78a8ba842ab958da6ca17492b47c0a353e8b5002406c9ca4d15e12e52f21336dbb766f40c1f9d180439d9c0028aab804d0a21928fe4f2123bf91574ee2ba86973ebea6a3c32b98a0a0cb1a9104aaa84c62357ca51f1e4843c72e08d95f181aa207dc694e1d0a8865ac4d008c52e1416d629844c85b72097fa272131f53c60c89e0aa544b847bede892f9e67693368b43da3210f167bc4fa8105674c719792572c65d636295e6c938304a4bd4d248da40daacc6786c4d586fe9306ffee7f55d418d264ce235b89788fec3a5fb1b096d74166ed504442c614b4ffb28b97972c0ac436792922eac2df431149010ab94dac4318331c86da2b760e1c16641a55505036d46e0fc78d37515a75553241d569a7a2bcfa5367d021fda905656f29e5ced482b25748612a128f25467691881aed0ac45c34c4bc47769ed77bbaf24693b1bf2a3b3c1692e388d8439d823179d6ac804b6547c29ea660145451957ead15ce468533ac350a42c382348c8a321e977140a12f7904baceec6920dcd39c0f506d3392411f73fd281447db4c509d32614addd37982160f6b1109b03f9f6e3e7ef26c9b3c7eba83ffed6e3daf4b30a11e4fe51e7952697c2e9fddf8823c2ddfe7827c0caf1fe7afaf9aed02358b1c8f82e168e879c066c0ed4ea9e8b5e6edee94afb5e6d380cf51fed8c8e7ab65950b3837c46df9625f90073c44ab4923d53112608e288a9dfb5c315b8f99bd489c839da91983736684ff7467a79390d7b22ba6f98fc1aaf431f19f21f338259b21bdc8a4f2f5b3afe38bad8254363260928c61a083524ecc9beaf829321332fa12b417a64a452829ac2581b948a1e165cedcd92bf2922fa15d09d889294c8d24f64156128605a14120ce090a7b9a307a2a71260d04144f589c08281380128d24cc71301f9f73e557291a8d8019fecb51f1ba2a57e89224a4412ed8ab42a1c6233d88602e1d99c97463ac5ba86710001ae49d8841d913825338be8be8593113d3334c317fd040e43f3d36a369001326cce44ce7e373aaa73d331b8c3d5e10c1598e42a3932c0de711d0eb7a1df6340148634dc63c9ce72968792c22131727221b9fecddc7dfe8f98f98f9d97af2186647ad3ee6f768428ae9702c411e414ecce71e9840fb02e6ab7afd666a481583da3f82b02069d2398f6a72932c47de9234a8b423974ee5a71e7245c3f51c9c17f012117be23caaa4423acd26e8bf59cd7c20fff91cf10c1a84eca14ae585c7d5c3682ae3e7e369217bd025a87e3c9ac23f5aaba8e4bca82064515ae346f33b297832f80d90e1296018cf7bcb62b1b905d4abe29c078bd8479c428a5fb6ef61cf1ac13d64217078e079048bc3cc209205b2361e414d83abc69e19543534ff9c191134aa4712bd1517a2e167dc43cdbd067b6078d489310c72108fae9837f8cb8b5f53307cb85c3e24e80134abf5c13e1d1fdd8954a49f2a54b241dc89d9acc59a297e4d89807957345103556506d9fd46c56f0d85ae7ac7a334a88cec4a6a99321e81b034761e81453006b6c7369afc6891bfe38954605acfb7898882050177e03c227c4622e6322969b2784e32bec8394d229c8232eb9d9107097010c503a03fe3f3e3088d6261508070698a6bcc0476e8818daddaab4a3a08850544ff6f67d5d66d5733af8d138ece35f69fd453b453624df256bc62cc631ecc504f2685c4fac9e2fcd5b453f1e49d8f6e2db6374df45c447c2ac16d6580a3e819bab8e050450266701740280583ef94930509531479c60e4ed65926edeb46ecbc4eda198fe2ba2aac641a8177650eccb2014f0a38352e957928fa69aac0eb56912d43fd279b73490c38399b57ee46d30d1b0d46ab7a12309accf885d5d157f517b5c7ca43f6b3a5f16c9e06803740411bfa5cd3dc9c1e162c2711ed30904d0429080fb74a0a9d5afe6c9992db08fc638b700f059bd5c0995caeeed9d0b8bcc4228798b089bf9ce323c00fbaafe09151656822fac3bf3f01dbd86bbde26e0ca55a3ec8d933b21ad934b2a801079043406399e3891890028622fe566b6f0e46f02534ec0b0e7ee40534a64dbe81ea56c10062a4e41cf184b9bafb3e121df57843957f75b58a7c39064805d8199a278aaab42afa3a64ab2a2b6d49132d50bd279a2a9e754eae1a55a9d43b2217302924ddaa6d88656b666cfa2ddd7d615ca76a11d2080564cfe0c8f018b16f0715a4637c85ae344c4dfa9230d15f6d89146d429fca804624024496d4e0d5407c86b533cd3a3ed2c12faf97f9513ff7376f9871e66c11b3a8d1c0ef74ca4d5a568c991b3530c70821e0c0e0d74c64bf393c3aa301efd387c1edcff0812df9bc2980174922d66d7f37b39fb3a35d1182dbd474301057f88988f8df355cb8416f7758b3cfd5d47920a64d97e17b7847839a2f771b1d3bd764bfe301933031d0e09c2ee4ab349cb2e4ea4aaf38c86dd253e8db85d285a63ca23844befd7c02f345d894d74fc2bd03710548f59ad2d265ee91b0dc40a49e8d3e5e2068335ef01aa0bb9869b87e3b128bc0e1b8a6c0b0c83d9217866b1b527a4925b81837d5aedefc3906cb260e2d98b7004b5f838f6a4d7e61b20f68f51078d0a0f04aac22f0f9a49d823d67ee296b2ae6f13c1209236f58127229c1d4cbbb97b9a913ab94eb8bbd46e43e4b5ec7c36d1ab4a6581327ff992b7f4d93a0294b1dc54491ac3db7424a1f4a8624bda8d0e17bb4dc0afdbadbc9b130bad51d1588131e8229b27a029165eb06c40500acba3c68c235a062a25f5d1ad2e51ead1114a8baf418c3ac38f35a00b7e6cd47407c981b5f2c7154fc762c7d389ba3ebfe6bbe40b0b9f5dbfd71de4df372af1d9f6ce473488cbe8a5560f28bf2b58faccd7a74dcd05f2ef959e1298380ff7296dbce722b6277f092bc573cb899972117801a4207fedc0f60a3a83c958d961ebe797f7b2d75e314e63d17464c03fcc16304de564283bdddabab7fbba758f8287b4dde52c5d694045412311745297f7da484a2c123f4a6a731ea40c840cdddab2b7cdaecc97b1c6928fc0e1fab6ab3f5b98596c61d227b1fdf89c044aa864b0c5c98db17598ee932cd06960fa248005865af7f240ff6490af66bc6a35e933558b8b839c71749339693d7464e70abe17a519d95b8355f75c47d30baa04c5d5c07b396b5de9abcd6fb5a8682e356251e8de62c29ea78d053c96c36a496cf694d9b11d6ff66895cdf869e99d27f44acacd15c0e773e3b5c1eb4d2a4b9d52b4d254ceb43ac7abb30b651dea7e5a60ec47ad375a6e69e8f0e1a9df876dd4527a4908569ab60f98e169d9a22fa6bd1a996382472c6a366e4ecc0f3a071ebc56e8c0220855af7ff1117247a34fd4f0193b3063b6841cddad34aecd72cf26706cd4d590cc1cecd327702a1ef19bec23603851b8fafbf80d66dc425cb9f263639aa6f4dc2577a395b76efbf63986636f838018be6ca2713b2731f37e2bd128abbec461bf0aab0131a8e90b3d27eec66bd81faf212de3aef78c860040316647bd60f7b61b807fedcd5d55ebecd1a10da2ce12cf2828581ac58488b5a8f88bbdf7c57017bddfbf0aa4aad77c4194518b6bfb5893f5efff869b7ddd574bad8845a1c01c0d7dfeab35bc34e06e45b13b347609279d9e1af2a54de682b7aeb6840df86fafa1efa8ae9c9b2685097d91b3c0db83782264a57a489644e7102d189981a59939334c643236444be13491ab637f70eaa42421d73aefc74ea00e81ab93408fd5151d52861d06112571b7e022d978abc352fd6ac6d49835ca03f17c962e4093745f0971dc538aa3ede4d23b9942936f1dbb46bb7fa2dd4009d654eac1e76ecd75fbecf5a2f26812f289253638948283c000eb93a171e95c9a3c76f31fe4dd619f55887deea44dbe33410f34e0b6e3264e73b7bacb8c9839619461e0df4d922fc3f8f3cd506b8ffb45eaccbed5e6976d80524865a1bea1bb36a640e0b0466e6b69a9df19ff69899aeb6b5a689715cafaadc7e2d19d841dc773d650401fd3681eec280853ea54c499c08802ca13e164516a011e418649b40d79112313a35e26f01582e80f6439f05012f8e9c19cee3c1d6b26636cd9663bd4f5a6f1aefd4a7a4c8701fb48947337107bad4dc43de107d2e814fa23c5c9252e60e79a3cd2cf145e0dd76dfe7c7507a7a3e3fe4721ffa3d0f92df7ed7574fe7d47b24970e9ebe0f98629fa4f7cb531e50bd361ece6d75f7f1514f4773efc31a5385216607b7d3db77d0b38d5361b5c27a5bc6314039c567dc35076c1e861e95fef36a38b5dc16518fa8deb8ff2b59ce615013fc0f2179e368bf7ec4b55d14d0e565b8803efc485c3f8d4ecd8ef02674c603a7f95668c85cf4aaa1618e4513c864034209a7110547d49c49fd908cdaf4add1351ad9e1950c3a105478203a940ea810f79e6544071dff09c5597363ea6120109a369d90eb8e18ad25e8e3f7a2ad3c0070ef860fee56f7c5f9bef5005904cbf2e2023cd7d62c9e8facf20c6955b11ba40651c2484607a935c6c84c246176241b7f5ad97d2338385fe83e59b2ee8305f29d52dab3d56f2f1f8179b5357a7e8467cc73424ea46372ce17d9167cf2f06157aac713b5682fe38c033a65019e9237ccc1b09b18fb7204f01ed35a257814e3f1bd6c31b625d7b2a13884f37592ca70d6cdcbee9e69d80cab72a3896ebbdef80b16204d70910a19cb4290782dca8794e3be43dd6a1b2be11e8c24cd5ee7524b1e0aead18425b64c87505b3c2cb566bad638a70de0273606ac6b79a7ba27b4a3eb4cb77efcd8d5ad71a25b011ce2a1cbc8f41ca2776d338986ede6ce2052bb33c8d2aa3e21d94163306066600b936c2e3442720e394109487dcf11ce31f813880a7db70e988b4daef7af7a5b4e5780336f5acf789efcec035c307749806f81a73bb1d26d72ca588cbc861caad5371f8d5938d1f726457a7cc29339290a2d83f2a00735d625074928219cfed5bf9ef72b3a430f40a750be2583adde25d00978e215b89d06343aede76b50fd5a255e69955fcd825605e03567c5134ceae3b78110a766f838e458e1a9bf34f0b440c9578ff5154fd4452dc24542e81f17571c75bfe1090040959066f422d29bb6e4b6710a654b61a64c17ca54a6a7ed8d586277e4b8db4c2eb56a14b75322225a6ed0e200b8f6f591b07deb0711e63b69321b8654ba2cd8209345cd8ece6ed3d469244ecc41f3482a982251dc9951cec7af1ba4d2740dae01360f41df96eca8598cc5b0d241fef31a52f4162036ee6ab10036c2a50eafcb9893fd7d62101f8abaac745d7917bc9ea8e49a727fa1cd26aedc5222590c2340c1c8e9b8a6a49c1a78bc8721cc7cc36ec13ec64d9616ccc4931704a31db34403079a2a81d12197e8402368e1c25c1bc772f2037a7c3c425a9f0004176b4c953b26e819cb6ec0a8777ec5527d511c70bc7e94fe04e877c5e9ef1b682f39ea061f68bcf349f18405336b05e73a1e69ee1dc0a3f55591c328003f7ff0b52330b1a6cdbdbd479ac4cd5d0f3ceab0daf5c05c7f04d7a37ac8eb561c903e82b7e786e03194956e48754db42bbd6b6db42bdf1077a664c8c19f0d77a623b5c79dd175572630931d6f37a99d65ef9cc174d9ccb929a48493527d4ecb567264c3b0b5ba37bf96a0c7f359318fbd35671d2b6e4e1af10f29eefc82b105d9638af02bdab746fff72bb5ff7e60ffef8efd1ff6ff3bbf5dee6e3f7b7af5c5a877e2d3ed1ce699e8accd536f8d9e28bc938eb4d23b39c11b254026266805366c6656154871c9c5c2212f333c8fef257401427a737198013b7a87622f721ec2ac41cf4694dafe68809c54b5a3f02a4cae5ba8aeea2cf4a9721723435d86e19c644a735d460a3fcb5c0c52edc6cd674f4b4741439e00d0c956b7afb09d3b0ada37c05ec67f638c744e4075c723fdab462f0a164bdadd0241e665f7de999b9924bc0d65995132e9d7304b58a06698f0c5754c932650374eff181d95e629374026df9a26a8b8e1a2b4449fc0ae1861f65916935ada167c46f45c3727793c409fae47440f2cb3c4213f73b028a0cc6ec234b6e733c255e9133334e00ec141a03848b5e2061b45fd47a1a626ac92e2665a2af3c1817705a36d33998b934e9a168689f4ebb587b269fa4ab35251c6d6788e515ac34dcbf0213664a3d75a08e3d97080d1d9822a453c3a3e15177d3ad93bfa74aa198166d014947a064e7998be7a3396b917a0bbee2543bfdba2f50d37739b0206e9e2043c3a17b57c9688105519fc07494280be383cca281f9866bc93ad99218fb999709b8e1f9d67c347e2a1bd62d04995fba7d95cddf64f9734e4de7766ed883e69df34f0093a146303597564ca95e9df2cf44ace318a8ad13c9c6fd1fa641de9107de17c44be3b78676ed3d0d6a8237ab084abebf44e63354122f04346f16e001dc2ea0b1f352baaf548f508cd8af333754e6ea5f32578c5388674857a2774be0dda6a1dcfee3ebb729bba70a2b931772122433806f5f509e6a30215438fb1d86b7474f7cb3f8835961cb741e038caf684ebc507a20756f9e693d9650c2079d873b8eb1663b8618c0b5986117d1b35cd00842355fe3ac71de6423333e6a1bb11ff7908bc4d00487f3fa2dc94784b0819a1c84a790e81d2a6e08f8cc57d60ba9a63209c2e8b5813fc3fefd2a5c6a60aa72b04cceea1caf312e06cbc99e53a678814582de4e0b2efdeb56f5e9561178aaea6375610cdf17b98cc41cb4a8d0455c515b96d9c85cc7746d641b0d56a9762d8960e34992b182b5dd32c113dcbdb709cdbbc42d7ad66f317911a9ef40226550477c001388a58cef39a587b0db0dd5c002897c1fb56636bf4da4bb3e5e4d2b5408b53522dce86bb01b577843091579c10aea7ad39d3f073aacf88465970071daf85b6912eee1c0494899fac3863ba5fb56043aa5c1fdc34f0c98245511ac34486c246af443ef50248762b7b3d4e58519fbf164070d2b8e182c7e0abefef72c123bf25be6f336a967cef563bba96204879e5fd2d2f3e7cca4040d56dc8bf0d02d6aae8895e689a5f6461323aa89db83cfd0883648f701a6da59889e7d152106b70cae5655e186f4ad5f7e064cffa7355181e5a027933c4820ad520b40ca39459f139bb9320472bfa2275078f5c4086d7b139eb5992fe6d394ee945bf7d7e5ca760a74204cb85d7e701b4c4b74c444bfc841b49611d07e153f95f3d83609ef293bb1b079d6931b00602f6fef547c9f7ef8fffe423e43a1258d37d5ea68034e6a76cb1421eed866f165f255971844f8dce76ec6fecdda736d464435572f4e4c957d6e4bda4737dec6d6bb9fe0ddb1d9f1dcaef172b0e23cbb67a769666755dff9b15358ccad4c19be31fd962b3c50900aaef59c412da858e56f6e0dacb039f7bcbd2b53e16f42f8bc907a35bfc693ea369ce590018c7af82e84f53fe6eb0b44e9d3433fefe2165c9c27eecec384f56e72ebf80f97bf303984bcbd1386e6480b6ea9b1600baeacfb7fe130000ffff010000ffffa6321ba9cf750000") + bs, _ = hex.DecodeString("1f8b080000096e8800ffec5d7b77dbb692ff3f9f02e1ed26ce5d93721e4dbb8eac5dd74e5b6f9bc78993ed767bba7b20129250930443807654c7f7b3efcc806f91922cdb896fdb3f128b0430180c0683df0c1e1cde3d7c75f0f6e7d7cfd9cc44e1e8cef0aeebde3950c93c95d399615b070fd8a39d874fd87ff2133566dfa874ca781c3065662265be8a4d2ac79951a9f6d87e18322aa5592ab4484f45e0dd79a705531366665233adb2d417502a100c1ea7ea54a4b108d8780e34d98ba3b7ae36f350b050fa22867266c60df321692cee4c5406d5ca185e0af6e3d1c1f397c7cfd94486c2bbe3bac03672cf421e4ff71c113b2c9eba3c49f61c3d8f7da83b9ed22be25785a148f79ce322e5c0a4a1c3fc906bbde760a650f11307490a1e8cee30368c84e1cc9ff1540bb3e76466e27eed540933631257bccfe4e99ef3dfeebb7df740450937721c0a872424622875f47c4f0453512b17f348ec39a7529c252a35b5ac673230b3bd409c82185c7ad886864b2379e86a9f8762efa1b7b3402810da4f6562a48a6bb416b2f1cccc54ba902394f109f45a08128364e36786491f29cd5231d97326fc141fbd040439ba83058c34a1189542641fd9f93976f24be8dc9750d3d6838b8be1c0e62a2bb0c4c64a196d529e0c7cad07e59317c9d883374ece07aa829e09612c875635cc3c814618f1c160614a616cac82393ba79f8c253c08802177ac8c51d12efb6a27f9f02c4f9b40a3dd098f6438df65cef7223c1546fa9cbd149970b659f9629beda720ed6da679ac5d506539b1242eeed09f2cfcd7d997658d114fa732768d4a76d943ef4b1135f27ac8ac1ba958e98483f69f77f1f242c4a1da662f54cc7df87ba062ad4021b79973006346c2507b29ce80c3924cab0a0eca86da1de0b809468654d7a423549dc55494577fea043aa43fb5a41c2ca5dc995a520e4a298c551a88d4ca2e5671ab5da19aaa969877d9ceb3664fd7de1019f7cbaac313a5258e885dd4291893a7ed0aa4366eacdc710646c19455d16b52381715ceb2d62a080d0bb3282ecb0452272187be9431e8ba70c7a1f24f0a3e40b5ed48067d2cf4a3541cb299a03a55c298fb27d3142d1ed6a252607f3ade7af4f8e9367bf46407ff7bf8e0595382290f64a677d9e35ae30bf93c4c3eb027d5fb42908fe0f5a3e2f545bb5da066b11770301c2d3d0fc504b8dda914bdd1bc873bd56bd27c1eca29ca1f1bf96cb5ac0a0117867851bed817ecae8cd06af2d8748c049823ca62673369844b636637566760671ac6e04c58e13fd9d9e924142cd815dbfc476055fa98f88f480492b3ad887fc8a5f2d5d3af920f0f4a52f9c880493281810e4a39b26feae3a7ccccd8e0efa0bd30551ac659692d19cc45060daff0a6de6e9997fd1dda95829d18c3d4c89219c84ac3b0603c0cd51943618f53c14f34cea4a182e2a9485205654250a28186390ee6e3336966758a56236086fffba07c5d972b74491af1b010ec45a950c3010d22984b077632bd33a416d20c024083bd5509287bca700ac777313f2d67627e8a29f60f1a88e26720263c0b61c284995c503e39e534edd9d96018c89208ce721c1a9de669388f805e37eb70c729401a673494d1b44841cbe3309dfa3811b9f8e43e7cf435cd7fcccecfcee347303b92fad8df83112ba7c3a106798405b1990cc004ba1f60be6ad66fa786cc08a8fd23080b92469df328911be5398a966461ad1d85746a3f69c8950da739b82810a42a09d4595c4b85749e4fd07f73daf940fed329e2193408f9439dcaf3409a7bf15827cf86e352f6a04b50fd7030867fbc5151c579594124e2acc10df13b2a79b2f80d90e109609820782312b5f500a8d7c5390de7c90c710a2b7fb9b3007bd60aee9e8880c3fd2060581c660695ce91b5e1006a5abb6aec99b5aa86e69f092b8256f548a2b7e25234f25406a8b997600f0c8f39b68641afc5a3afa62dfe8ae297148c5c5f2eef53f400dad5cec03e1d1dde8854f42c33a8646b71a7269305d66cf14b4a04ccbbe1a959535526907dd6aaf88da5d055ef709085b5915d4bad528603101661e70158046b607b6ca3cd8f16f95b996a03a6f56c9ba9389c337007ce6226272c16bed09aa7f3672ce78b9df134c62928b7de397990800451dc05fa13393d8ad1289606050857a6b8c14ce84601d8d8babdaaa5835044c8e87f37afb669bbda795d9c7028d770f6b899424e89332a5af15288400430433d1e9512eb278bf357db4e25a3b733746bb1bd594a73119b710d6eab001cc54fd1c505872a563083fb004239187caf9a2c5894a1c8737670b2ce3391af1b8bb326696f38489aaab0926904deb53930cf063c19e0d4ba54f6a1eca7b101afdbc4ae8ee84f3ee7b20470723eafdc8ca65b365a8cd6f524143c9dc80f4e475f355f341e6b0ff9cf058d17d32c04bc010adad2e786e616f4b060358990c3c0b610a4203c7c5051e8d4f2a7cb94dc45e09f384c0628d8bc0629f47275cf87c6f9391639c0842dfce51d1d027ea0be8247c18da589e80ffffe086c63aff58abb35941af92067cfc86a652364d1000e20879027bac0130920050c45fcadd1de028ce04b68d81712fcc80fd09845f22d54b70a06302b25ef50a6c2a7eefbc828eaf19a9bd9c5c52af2d51860356067691e1b6eb2bae89b90adaeac7c419a68819a3dd156f1bc730ad5a84ba5d91185805929e985dad6b16ced8c6dbfa5bb2faceb542fc25aa180fc191c199920f6eda08274acafd09586a9695f1226ce565b22c3dbd0a736a0118900912535040d109f63ed5cb38e0e29f815f4323fe8e7feea0db3ce9cab1211b71af82da55ca565e598b952030b8c10010e0c7fc945f6ab27e3531eca3e7d58bbfd393e70b59cb605f03c4dd5a6edef66f67376b4af22709bda0e06e28a59aa62f93bc1852bf4768735fb5c4d9d866adc7619be83773c6cf872d7d1b15322fbad0c8586898187677cae5f66d158a41717b4e2a0b7594fa16fe6860a8d65cc71887cf3f9043653515b5e3f2aff06c41522d54b4a8bcadc2261f9a1ca02177dbc50f176bce01540773521b87e3d128bc1e1b8a4c0b0c82d9217866b5b527ac135b81857d5aedefc0506cb270e12cc1b80a5afc04775463f0bdd07b47a08dc6d5178a95611f87cd2cec09e0bff44b415f3681aab54b0d7228da4d660eaf5cdcbdcd68955eacdc5de20729b254ff17097870b53ac8d93ff24cd6c4393409435453151241bcfad90d28792218916153a7c8f05b7825e773b390e46b7baa302492a2330454e4f20b26add1a710100abbe0cdb700da8d8e857978674b9471b0405ea2e3dc6306bce3c09e0dabcf91888afe7c6974b1c35bf1d4b1f4ca6e8baff522c106c3df8f5f638efb67985d78e4f2ef2b94e8cbe8e5560f28b8bb58fbccd343aaee82f57fcacf09441c07f39cb8bcef242c46eff057b676478352f43cf0135441efcb91dc0c6707da25b2d3d78fdeefa5aea2719cc7b3e8c9816f883c718bcad9487bb0f2f2efee59662e1c3fc357bc38dd8501250492c7c14a5fee5be518687f7d19b1e27a803910035f72f2ef069ab27ef514c50f82d3ed6d5e6c1e7165a967488ec5d7223025399595f62e0c25cbfc80a4c976b36b0bc1fc70ac0aa78f503bbbbc732b05f1319f79aacb5858b9b73662a6dc7728adad8316e35dc2caab312b716ab8eb80f860aeaccc775306759eb9dd12bdad7b22e385ea824e0f154a4651d777b2a994cd6a9e5735ad37684f5bf44aa37b7a1a7b6f41f112b139a2be0ce6787cb6bad3411b7b4d254c1b43ec44adb85b18dfa362d377520d6abae33b5f77c74d0e8c4b79b2e3a21853c4c5b07cb37b4e8d416d15f8b4e8dc4752267326e47cef683001ab759ecc62a00526874ff1f7141a247d3ff1430396fb08716d4ae3dadc47eed227f66d0dc96c53ad8b95de64620f42dc357d866a070e5f1f517d0ba8eb864f5d3c62607cdad49f88a96b375f7fe3b816976838f178a786a666cc4766ee346bc97ca485f5c69035e1d7642c31172d6da8fdd4c1ba8cfcfe1adf756460246306041b1eb7cbf1b45bbe0cf5d5cec16dbac01a14d5229e2209c5bc88a8548d434226e7ef35d0dec75efc3ab2b35ed88b38ab0defed636fe78f5c3a7dd76d7d0e972136a7904005f7f4367b7d63b19506c4dcc1f814911e487bfea50f9cea2a22f1c0de8db50dfdc435f333d79160275b9bdc1d380bb0368a2f655966ae1952710bd589881333ace123c34c206ec5b9566d1e2e6deb5aad050c7549a5936f600740d7c1e46b34159d52015d0611a571b7e042dd786bdb12f36ac6d49837ca03f55e97c10283f43f0971fc538ac3fde4c23a5d61936f19bac6bb7fa35d4009d654fac1e74ecd75fbecf9a1693c01754e989b5442c52010087429d4b8fcae6a1f15b8e7f9b75c203d1a1b794e80692876ada69c16d86fc7c678f15b779d032c3c8e3219d2dc2ff8bc8536380cf9e348b75b9dd2bcd8ef8008911694373635683cc4189c0ecdcd6b033b3273d66a6ab6d0bd3c4306956556dbfd602ec20eebb1e0b86807e9b417761c0824e297396a40a204b44c7a2d81c34821d816c53e83a562146af41fc0d00cb39d0be37136128cb236796f3646d6bd9309b76cb31ed93a64de39dfa9496196e8336c978a26e4097da7bc85ba22f24f04994476a56c9dc63afc9ccb2990a83ebeefbe2184a4fcf17875c6e43bf1741f2ebeffafae99c668f14d2c1d3f7a130e293f47e75ca03aa27e3e15d57771f1df674b40cde6f305558626e783dbd7d033ddb3a15d6284cdb328e00ca193991be3d60732f0ab89e3dab8753ab6d11cd88ea95fbbf96e50c0635c3ff1092b78ef6d323aeeda280cecfa339f4e147e6cfb2f8c4ee086f43673c705a6c8586cc65af5a1af65834834c2e2094681c737044ed99d4f7e96091be33b844233bbc92b50e04951e0885d20115e2deb39ce85ac77f2275dade987a102a84a66d27e4b22386b4047dfc5eb45504006eddf0c1ddea3375b6e7dc4516c1b23cff009eebc22c5e8cacea0c695db15ba4d6a284918c0e521b8c91894aa3fc4836fe74f2fb4670703ea73e59b2ee83058a9d52e4d9d2dbf3fb605e5d42cff7f18c7941c88b2926e77d916fc167f7ee75a5063235f3c5659c61c8c722c453f296391876236b5f0e01de63da42091927787c2f5f8c5d906bd5501cc2c53a496d3853f3f2bb675a36c3a9dd68426da78dbf6001b21417a990b13c0489d7a2bccf24ee3ba456bb58890c6024117b9d4b2d4528a8471396d8320aa12ef0b0d49a51ad49411bc04f620d58d7f24e7d4f6847d7d96efdf8b1ab5b93945a011ce2a1cbd8f61ca277b2998c60bbbd338835ee0c7248d5472c3f680c06cc0e6c6593ed854648ce63c728014df71ce11c833f81a8a2bb75c05c6c49dabf1a3cf0ba029c45d37ac6f3e8a719c0057b9704f81678ba132bdd66274224c86b24a15abaf96828a211dd9b14d3f884277b52145a06e5410f1aac6b0992304a79fdab7f3def5774060d40af54be2583add925d00978e215b81d873c3ee9e76badfa49255e92caaf66815401782d580994d074fc3654eac40e1f8f1d193cf597850109947df988ae78e23e6a112e1242fff8b8e248fd8627000055429ad58b98366de96deb14ea0585190b2a94ab4c4fdb5bb1c4eec871b7995c6ad57824729b86b068b9554b42607d46e7c2f69cef55546ca7c90d1992ea32636bd92d6eb77576dba74e4b716c4f9bc7dac03c8932cf2d733188fd30d3b67f7021b07d12faba04c8ed8a2cc696f68b9f9790623007b1499fc4020009d73b822e8bcef6f698857d28eaaad24de55df27a6cd24bcafd39d94e5cbee54c8b04868181e1d3715749353fc86417e398c5aedd927d0c9e2c2d988ba72808963b1129a1079e198521229f51b411b4706eef8e1305f9357a7c38405a9f0009970b4db58b26f8a9c8afc168767ecd5c7d519e72bc7ca8fe18e87705eb6f1b72af38ea462068c18b99f15884136705e71494b4970fe0f9fabac8611480b3bff6dd2330bb66ed0dbe8744e2eafe079e7758ed7f60ae3f82ff513fe9752d5e481fc1ebf345f02cca4a5fa4be30da95deb540da956f1d9fa662c8c39f2d9fa623b5c7a7a1ba6b1398cd8e579c340eb477ce605436f7704a29e1a4d49cd3f2e51cdd326c0bdd5bdc4dd0e3feac98c7ded8038f355f278be5fb0cb77fc1d882ec09470c16ef3983fffd85bbbfefbbffb3e3fe9bfb7fdeafe70fb79f3eb9f862d03bf1513bd7734f286bfbe85bab274a17a523ad72518ef15a0990898d5c810d9bd8a50556de7431f7d88b1cd4e37b0d5d80b8dede1e66c10e6d53ec85cfeb306b21b41525d91f42c9695d3b4ad7c2e6ba86eaea1e439f2a7731b2aedfb03e27b9d25c9691d2d9b2b783d4bb71ebe993ca5b20c813023a79d0ed306c17de023908d8cbf86f88e1ce11a8ee7040bf1af4e270bea4dd0b20c8beecde407335938457a22c334a36fd1266090b340c13beb88c6922024de3f48fc161659e0a0364f36d6882ca6b2e2a4bf409ec8a15669f65b1a9956dc16744cf4d73520405e8883d227a6059a41efb4982450165f65341d85e4e983495632cd0807b0c07819120d59a2f6c15f51fa59adad84a863b6ab92e06075e188cb6cd662e8f3b112d8c15d1eb8d87b26dfa4ab35253c685f19ca0b4d6372deb0fb175767b6d84309eae0f303a5b50a788e7c7c7ea439f4ef68e3e4ab523d00e9a9252cfc0a94ed4d7afc7b2970374d7bd64e8775bb4bee166af54c0485d928247e7a3964f5215a12a83ffa05904d017874715ea03d38c17b3b5331481371b73a320d2593e7c349edc2b079d36857f9acfd58bfee99286dcface6c9cd3678bd70d7c820ec5d8405e1d1b4b63fb378fbfb2330ca562480fe75bb43e79477a8c6e9d8fd9b7fb6fed951a648d3aa2074bb8ba4cefb4961434023f64142f08a010565ff8a85d51a347eae768561ca26972722d9dafc12bc6314415d276e8622fb4b37046bbfb00cb75eac23171632f444486700cd21d0af6cb0235438f01d94b7474f7cb3f8835d612f742e038ca3786d30a04a38155bdf9647619034801f61c6ebdc5186e94e06a966584aea4e63980f0b4295e17b8c3de6a66c73c7437e2bf0081b70d00d14724aa9d89d78490118aac94e73a50da16fc4188a40f4cd773ac09a7ab22ce08ff2fba74a9b1a9c3e91a01bb85a8f6bc04385b6f66b9ce592225568b24b8ec0f6fda37afcbb00b45d7d35bcb88f60c3e4ce6a065954682aae2b2dc36ce42f663239b20d87ab54b31ec820eb4992b19ab5cd33c113dcbeb709c177985ae5bcde6cf2ab33cd12a26370cb7c101388a45c1f386587b03b0dd5e00a8d6c2fb96641bf416d767abc9a56b9516a7a4469c0db7049277843051d69c1049d3d65410fc1cd341d1380feea0e335271be9e3f6414099f8dd8a5341fd4a828db8f167e0a6814f16cecbd21826b214eef44ae4532f80e457b337e38435f5f96b0104278d2b2e78ac7dfffd4d2e781457c5f7ed48cd936fdd6a47d7124475edfd35af3d7cca3840dd6b28be0f02c6aaec885e645a5c6661337aa89cb83a7d1f6364f771165d48b1f3cefda518d6c294f3f3a230de964a77e1e4cff4c92a8c0e2d41bc3960417d6a115a0651aaacf89cdf4b508015ba4cddc3631790e15562cf7b56a47f5d0e537ac16f9f1bd729d8b152e172e1f539000be25b26a2256ec295a4b0897ff0a9dcaf9e4130cde4f1cd8d83ceb40458030107fffca3e4bb77477ff2117219096ce83d2f53409ec813315f218fc5866f955f2659718ccf0c4e77dcafdd874f5ca8c985aaf4e0f1e32f9dd13bcda774f4edc172fd5b6f877c7e30bf5fac388c1cd7e9d95d9ad775f9ef563420aa30fbaf8f7e10f3ad054e004f7d276291f22e70b4b207375e1df8dc3b962ef5c1a07f5a487e59704b9a4fce5b6a7a006ef6d9cfe16c7cea6a9fbe07b71fab781ea94ce7ad7d43ad055df8f7abc1e0fa8129bb1fd74fe70946e9b2ba54f193b0b89e127019ce69436edd754eb97f425b12a1cb93901b1c247a9bca322d7fcff7dbf22429c3224062522e87a6581d5da906231e09dbc581004f7096cb3949aa22e28bb6f1522839ef263ee5b27698ab7994abd53c3e9da6624acbaab831155d3c3f5f89cac630cec061e7a7d046ba95083776f3d611646473e1a0f8924434c87dbc5d6ad85541112bb2d7a9c00fd2363fee534b607bd02f192e3ee7cf75bde9187309c8a01664aa2aa86e64b0ef0fb1a33eb2dfb4bddeadf8de1d94ff0476b17ea75ff19d37789398776f36b3897431f6a626b1bafbaff4f87dbce76f4d663a6d225eb2bdb141c49ff6dbc2f6f01930889f4aa2eff5fe66630b943a6a67fced7d26d2b9fbc8dbf11eafce5d7d16f8b7f657819796030bd0ca006da5eb67c088d137adff1f0000ffff010000ffffe8fd6786e47a0000") gr, _ = gzip.NewReader(bytes.NewBuffer(bs)) bs, _ = ioutil.ReadAll(gr) Assets["index.html"] = bs diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 89cdc5fb3..48f43de3c 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -98,6 +98,7 @@ func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) erro router.Get("/rest/system", restGetSystem) router.Get("/rest/errors", restGetErrors) router.Get("/rest/discovery", restGetDiscovery) + router.Get("/rest/report", restGetReport) router.Get("/qr/:text", getQR) router.Post("/rest/config", restPostConfig) @@ -107,6 +108,8 @@ func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) erro router.Post("/rest/error", restPostError) router.Post("/rest/error/clear", restClearErrors) router.Post("/rest/discovery/hint", restPostDiscoveryHint) + router.Post("/rest/report/enable", restPostReportEnable) + router.Post("/rest/report/disable", restPostReportDisable) mr := martini.New() mr.Use(csrfMiddleware) @@ -195,7 +198,7 @@ func restGetConfig(w http.ResponseWriter) { json.NewEncoder(w).Encode(encCfg) } -func restPostConfig(req *http.Request) { +func restPostConfig(req *http.Request, m *model.Model) { var newCfg config.Configuration err := json.NewDecoder(req.Body).Decode(&newCfg) if err != nil { @@ -242,6 +245,29 @@ func restPostConfig(req *http.Request) { } } + if newCfg.Options.UREnabled && !cfg.Options.UREnabled { + // UR was enabled + cfg.Options.UREnabled = true + cfg.Options.URDeclined = false + cfg.Options.URAccepted = usageReportVersion + // Set the corresponding options in newCfg so we don't trigger the restart check if this was the only option change + newCfg.Options.URDeclined = false + newCfg.Options.URAccepted = usageReportVersion + sendUsageRport(m) + go usageReportingLoop(m) + } else if !newCfg.Options.UREnabled && cfg.Options.UREnabled { + // UR was disabled + cfg.Options.UREnabled = false + cfg.Options.URDeclined = true + cfg.Options.URAccepted = 0 + // Set the corresponding options in newCfg so we don't trigger the restart check if this was the only option change + newCfg.Options.URDeclined = true + newCfg.Options.URAccepted = 0 + stopUsageReporting() + } else { + cfg.Options.URDeclined = newCfg.Options.URDeclined + } + if !reflect.DeepEqual(cfg.Options, newCfg.Options) { configInSync = false } @@ -347,6 +373,10 @@ func restGetDiscovery(w http.ResponseWriter) { json.NewEncoder(w).Encode(discoverer.All()) } +func restGetReport(w http.ResponseWriter, m *model.Model) { + json.NewEncoder(w).Encode(reportData(m)) +} + func getQR(w http.ResponseWriter, params martini.Params) { code, err := qr.Encode(params["text"], qr.M) if err != nil { @@ -358,6 +388,33 @@ func getQR(w http.ResponseWriter, params martini.Params) { w.Write(code.PNG()) } +func restPostReportEnable(m *model.Model) { + if cfg.Options.UREnabled { + return + } + + cfg.Options.UREnabled = true + cfg.Options.URDeclined = false + cfg.Options.URAccepted = usageReportVersion + + go usageReportingLoop(m) + sendUsageRport(m) + saveConfig() +} + +func restPostReportDisable(m *model.Model) { + if !cfg.Options.UREnabled { + return + } + + cfg.Options.UREnabled = false + cfg.Options.URDeclined = true + cfg.Options.URAccepted = 0 + + stopUsageReporting() + saveConfig() +} + func basic(username string, passhash string) http.HandlerFunc { return func(res http.ResponseWriter, req *http.Request) { if validAPIKey(req.Header.Get("X-API-Key")) { diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index c4e22f70f..d02f5033c 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -393,6 +393,18 @@ func main() { } } + if cfg.Options.UREnabled && cfg.Options.URAccepted < usageReportVersion { + l.Infoln("Anonymous usage report has changed; revoking acceptance") + cfg.Options.UREnabled = false + } + if cfg.Options.UREnabled { + go usageReportingLoop(m) + go func() { + time.Sleep(10 * time.Minute) + sendUsageRport(m) + }() + } + <-stop l.Okln("Exiting") } diff --git a/cmd/syncthing/usage_report.go b/cmd/syncthing/usage_report.go new file mode 100644 index 000000000..f7ed2558c --- /dev/null +++ b/cmd/syncthing/usage_report.go @@ -0,0 +1,109 @@ +package main + +import ( + "bytes" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "net/http" + "runtime" + "strings" + "time" + + "github.com/calmh/syncthing/model" +) + +// Current version number of the usage report, for acceptance purposes. If +// fields are added or changed this integer must be incremented so that users +// are prompted for acceptance of the new report. +const usageReportVersion = 1 + +var stopUsageReportingCh = make(chan struct{}) + +func reportData(m *model.Model) map[string]interface{} { + res := make(map[string]interface{}) + res["uniqueID"] = strings.ToLower(certID([]byte(myID)))[:6] + res["version"] = Version + res["platform"] = runtime.GOOS + "-" + runtime.GOARCH + res["numRepos"] = len(cfg.Repositories) + res["numNodes"] = len(cfg.Nodes) + + var totFiles, maxFiles int + var totBytes, maxBytes int64 + for _, repo := range cfg.Repositories { + files, _, bytes := m.GlobalSize(repo.ID) + totFiles += files + totBytes += bytes + if files > maxFiles { + maxFiles = files + } + if bytes > maxBytes { + maxBytes = bytes + } + } + + res["totFiles"] = totFiles + res["repoMaxFiles"] = maxFiles + res["totMiB"] = totBytes / 1024 / 1024 + res["repoMaxMiB"] = maxBytes / 1024 / 1024 + + var mem runtime.MemStats + runtime.ReadMemStats(&mem) + res["memoryUsageMiB"] = mem.Sys / 1024 / 1024 + + var perf float64 + for i := 0; i < 5; i++ { + p := cpuBench() + if p > perf { + perf = p + } + } + res["sha256Perf"] = perf + + return res +} + +func sendUsageRport(m *model.Model) error { + d := reportData(m) + var b bytes.Buffer + json.NewEncoder(&b).Encode(d) + _, err := http.Post("https://data.syncthing.net/newdata", "application/json", &b) + return err +} + +func usageReportingLoop(m *model.Model) { + l.Infoln("Starting usage reporting") + t := time.NewTicker(86400 * time.Second) +loop: + for { + select { + case <-stopUsageReportingCh: + break loop + case <-t.C: + sendUsageRport(m) + } + } + l.Infoln("Stopping usage reporting") +} + +func stopUsageReporting() { + stopUsageReportingCh <- struct{}{} +} + +// Returns CPU performance as a measure of single threaded SHA-256 MiB/s +func cpuBench() float64 { + chunkSize := 100 * 1 << 10 + h := sha256.New() + bs := make([]byte, chunkSize) + rand.Reader.Read(bs) + + t0 := time.Now() + b := 0 + for time.Since(t0) < 125*time.Millisecond { + h.Write(bs) + b += chunkSize + } + h.Sum(nil) + d := time.Since(t0) + return float64(int(float64(b)/d.Seconds()/(1<<20)*100)) / 100 +} diff --git a/config/config.go b/config/config.go index 54d3512fd..286819269 100644 --- a/config/config.go +++ b/config/config.go @@ -157,6 +157,10 @@ type OptionsConfiguration struct { StartBrowser bool `xml:"startBrowser" default:"true"` UPnPEnabled bool `xml:"upnpEnabled" default:"true"` + UREnabled bool `xml:"urEnabled"` // If true, send usage reporting data + URDeclined bool `xml:"urDeclined"` // If true, don't ask again + URAccepted int `xml:"urAccepted"` // Accepted usage reporting version + Deprecated_ReadOnly bool `xml:"readOnly,omitempty" json:"-"` Deprecated_GUIEnabled bool `xml:"guiEnabled,omitempty" json:"-"` Deprecated_GUIAddress string `xml:"guiAddress,omitempty" json:"-"` diff --git a/gui/app.js b/gui/app.js index 29417876d..b5ae50015 100644 --- a/gui/app.js +++ b/gui/app.js @@ -30,21 +30,24 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) { $scope.seenError = ''; $scope.model = {}; $scope.repos = {}; + $scope.reportData = {}; + $scope.reportPreview = false; // Strings before bools look better $scope.settings = [ - {id: 'ListenStr', descr: 'Sync Protocol Listen Addresses', type: 'text', restart: true}, - {id: 'MaxSendKbps', descr: 'Outgoing Rate Limit (KiB/s)', type: 'number', restart: true}, - {id: 'RescanIntervalS', descr: 'Rescan Interval (s)', type: 'number', restart: true}, - {id: 'ReconnectIntervalS', descr: 'Reconnect Interval (s)', type: 'number', restart: true}, - {id: 'ParallelRequests', descr: 'Max Outstanding Requests', type: 'number', restart: true}, - {id: 'MaxChangeKbps', descr: 'Max File Change Rate (KiB/s)', type: 'number', restart: true}, + {id: 'ListenStr', descr: 'Sync Protocol Listen Addresses', type: 'text'}, + {id: 'MaxSendKbps', descr: 'Outgoing Rate Limit (KiB/s)', type: 'number'}, + {id: 'RescanIntervalS', descr: 'Rescan Interval (s)', type: 'number'}, + {id: 'ReconnectIntervalS', descr: 'Reconnect Interval (s)', type: 'number'}, + {id: 'ParallelRequests', descr: 'Max Outstanding Requests', type: 'number'}, + {id: 'MaxChangeKbps', descr: 'Max File Change Rate (KiB/s)', type: 'number'}, - {id: 'GlobalAnnEnabled', descr: 'Global Discovery', type: 'bool', restart: true}, - {id: 'LocalAnnEnabled', descr: 'Local Discovery', type: 'bool', restart: true}, - {id: 'LocalAnnPort', descr: 'Local Discovery Port', type: 'number', restart: true}, + {id: 'LocalAnnPort', descr: 'Local Discovery Port', type: 'number'}, + {id: 'LocalAnnEnabled', descr: 'Local Discovery', type: 'bool'}, + {id: 'GlobalAnnEnabled', descr: 'Global Discovery', type: 'bool'}, {id: 'StartBrowser', descr: 'Start Browser', type: 'bool'}, {id: 'UPnPEnabled', descr: 'Enable UPnP', type: 'bool'}, + {id: 'UREnabled', descr: 'Anonymous Usage Reporting', type: 'bool'}, ]; $scope.guiSettings = [ @@ -544,11 +547,47 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) { $scope.repos = repoMap($scope.config.Repositories); $scope.refresh(); + + if (!$scope.config.Options.UREnabled && !$scope.config.Options.URDeclined) { + // If usage reporting has been neither accepted nor declined, + // we want to ask the user to make a choice. But we don't want + // to bug them during initial setup, so we set a cookie with + // the time of the first visit. When that cookie is present + // and the time is more than four hours ago, we ask the + // question. + + var firstVisit = document.cookie.replace(/(?:(?:^|.*;\s*)firstVisit\s*\=\s*([^;]*).*$)|^.*$/, "$1"); + if (!firstVisit) { + document.cookie = "firstVisit=" + Date.now() + ";max-age=" + 30*24*3600; + } else { + if (+firstVisit < Date.now() - 4*3600*1000){ + $('#ur').modal({backdrop: 'static', keyboard: false}); + } + } + } }); $http.get(urlbase + '/config/sync').success(function (data) { $scope.configInSync = data.configInSync; }); + + $http.get(urlbase + '/report').success(function (data) { + $scope.reportData = data; + }); + }; + + $scope.acceptUR = function () { + $scope.config.Options.UREnabled = true; + $scope.config.Options.URDeclined = false; + $scope.saveConfig(); + $('#ur').modal('hide'); + }; + + $scope.declineUR = function () { + $scope.config.Options.UREnabled = false; + $scope.config.Options.URDeclined = true; + $scope.saveConfig(); + $('#ur').modal('hide'); }; $scope.init(); diff --git a/gui/index.html b/gui/index.html index d21d304e7..6f0a4fdd5 100644 --- a/gui/index.html +++ b/gui/index.html @@ -564,7 +564,7 @@ found in the LICENSE file.