gui.go 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "crypto/tls"
  9. "encoding/json"
  10. "fmt"
  11. "io/ioutil"
  12. "net"
  13. "net/http"
  14. "os"
  15. "path/filepath"
  16. "reflect"
  17. "runtime"
  18. "runtime/pprof"
  19. "sort"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "github.com/rcrowley/go-metrics"
  24. "github.com/syncthing/syncthing/lib/config"
  25. "github.com/syncthing/syncthing/lib/db"
  26. "github.com/syncthing/syncthing/lib/discover"
  27. "github.com/syncthing/syncthing/lib/events"
  28. "github.com/syncthing/syncthing/lib/logger"
  29. "github.com/syncthing/syncthing/lib/model"
  30. "github.com/syncthing/syncthing/lib/osutil"
  31. "github.com/syncthing/syncthing/lib/protocol"
  32. "github.com/syncthing/syncthing/lib/rand"
  33. "github.com/syncthing/syncthing/lib/stats"
  34. "github.com/syncthing/syncthing/lib/sync"
  35. "github.com/syncthing/syncthing/lib/tlsutil"
  36. "github.com/syncthing/syncthing/lib/upgrade"
  37. "github.com/vitrun/qart/qr"
  38. "golang.org/x/crypto/bcrypt"
  39. )
  40. var (
  41. startTime = time.Now()
  42. )
  43. type apiService struct {
  44. id protocol.DeviceID
  45. cfg configIntf
  46. httpsCertFile string
  47. httpsKeyFile string
  48. statics *staticsServer
  49. model modelIntf
  50. eventSub events.BufferedSubscription
  51. diskEventSub events.BufferedSubscription
  52. discoverer discover.CachingMux
  53. connectionsService connectionsIntf
  54. fss *folderSummaryService
  55. systemConfigMut sync.Mutex // serializes posts to /rest/system/config
  56. stop chan struct{} // signals intentional stop
  57. configChanged chan struct{} // signals intentional listener close due to config change
  58. started chan string // signals startup complete by sending the listener address, for testing only
  59. startedOnce chan struct{} // the service has started successfully at least once
  60. guiErrors logger.Recorder
  61. systemLog logger.Recorder
  62. }
  63. type modelIntf interface {
  64. GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}
  65. Completion(device protocol.DeviceID, folder string) model.FolderCompletion
  66. Override(folder string)
  67. NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)
  68. NeedSize(folder string) db.Counts
  69. ConnectionStats() map[string]interface{}
  70. DeviceStatistics() map[string]stats.DeviceStatistics
  71. FolderStatistics() map[string]stats.FolderStatistics
  72. CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool)
  73. CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool)
  74. ResetFolder(folder string)
  75. Availability(folder, file string, version protocol.Vector, block protocol.BlockInfo) []model.Availability
  76. GetIgnores(folder string) ([]string, []string, error)
  77. SetIgnores(folder string, content []string) error
  78. DelayScan(folder string, next time.Duration)
  79. ScanFolder(folder string) error
  80. ScanFolders() map[string]error
  81. ScanFolderSubdirs(folder string, subs []string) error
  82. BringToFront(folder, file string)
  83. ConnectedTo(deviceID protocol.DeviceID) bool
  84. GlobalSize(folder string) db.Counts
  85. LocalSize(folder string) db.Counts
  86. CurrentSequence(folder string) (int64, bool)
  87. RemoteSequence(folder string) (int64, bool)
  88. State(folder string) (string, time.Time, error)
  89. }
  90. type configIntf interface {
  91. GUI() config.GUIConfiguration
  92. RawCopy() config.Configuration
  93. Options() config.OptionsConfiguration
  94. Replace(cfg config.Configuration) error
  95. Subscribe(c config.Committer)
  96. Folders() map[string]config.FolderConfiguration
  97. Devices() map[protocol.DeviceID]config.DeviceConfiguration
  98. SetDevice(config.DeviceConfiguration) error
  99. Save() error
  100. ListenAddresses() []string
  101. RequiresRestart() bool
  102. }
  103. type connectionsIntf interface {
  104. Status() map[string]interface{}
  105. }
  106. func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, eventSub events.BufferedSubscription, diskEventSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connectionsIntf, errors, systemLog logger.Recorder) *apiService {
  107. service := &apiService{
  108. id: id,
  109. cfg: cfg,
  110. httpsCertFile: httpsCertFile,
  111. httpsKeyFile: httpsKeyFile,
  112. statics: newStaticsServer(cfg.GUI().Theme, assetDir),
  113. model: m,
  114. eventSub: eventSub,
  115. diskEventSub: diskEventSub,
  116. discoverer: discoverer,
  117. connectionsService: connectionsService,
  118. systemConfigMut: sync.NewMutex(),
  119. stop: make(chan struct{}),
  120. configChanged: make(chan struct{}),
  121. startedOnce: make(chan struct{}),
  122. guiErrors: errors,
  123. systemLog: systemLog,
  124. }
  125. return service
  126. }
  127. func (s *apiService) getListener(guiCfg config.GUIConfiguration) (net.Listener, error) {
  128. cert, err := tls.LoadX509KeyPair(s.httpsCertFile, s.httpsKeyFile)
  129. if err != nil {
  130. l.Infoln("Loading HTTPS certificate:", err)
  131. l.Infoln("Creating new HTTPS certificate")
  132. // When generating the HTTPS certificate, use the system host name per
  133. // default. If that isn't available, use the "syncthing" default.
  134. var name string
  135. name, err = os.Hostname()
  136. if err != nil {
  137. name = tlsDefaultCommonName
  138. }
  139. cert, err = tlsutil.NewCertificate(s.httpsCertFile, s.httpsKeyFile, name, httpsRSABits)
  140. }
  141. if err != nil {
  142. return nil, err
  143. }
  144. tlsCfg := &tls.Config{
  145. Certificates: []tls.Certificate{cert},
  146. MinVersion: tls.VersionTLS10, // No SSLv3
  147. CipherSuites: []uint16{
  148. // No RC4
  149. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  150. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  151. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
  152. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
  153. tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
  154. tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
  155. tls.TLS_RSA_WITH_AES_128_CBC_SHA,
  156. tls.TLS_RSA_WITH_AES_256_CBC_SHA,
  157. tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
  158. tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
  159. },
  160. }
  161. rawListener, err := net.Listen("tcp", guiCfg.Address())
  162. if err != nil {
  163. return nil, err
  164. }
  165. listener := &tlsutil.DowngradingListener{
  166. Listener: rawListener,
  167. TLSConfig: tlsCfg,
  168. }
  169. return listener, nil
  170. }
  171. func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
  172. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  173. // Marshalling might fail, in which case we should return a 500 with the
  174. // actual error.
  175. bs, err := json.Marshal(jsonObject)
  176. if err != nil {
  177. // This Marshal() can't fail though.
  178. bs, _ = json.Marshal(map[string]string{"error": err.Error()})
  179. http.Error(w, string(bs), http.StatusInternalServerError)
  180. return
  181. }
  182. w.Write(bs)
  183. }
  184. func (s *apiService) Serve() {
  185. listener, err := s.getListener(s.cfg.GUI())
  186. if err != nil {
  187. select {
  188. case <-s.startedOnce:
  189. // We let this be a loud user-visible warning as it may be the only
  190. // indication they get that the GUI won't be available.
  191. l.Warnln("Starting API/GUI:", err)
  192. return
  193. default:
  194. // This is during initialization. A failure here should be fatal
  195. // as there will be no way for the user to communicate with us
  196. // otherwise anyway.
  197. l.Fatalln("Starting API/GUI:", err)
  198. }
  199. }
  200. if listener == nil {
  201. // Not much we can do here other than exit quickly. The supervisor
  202. // will log an error at some point.
  203. return
  204. }
  205. defer listener.Close()
  206. // The GET handlers
  207. getRestMux := http.NewServeMux()
  208. getRestMux.HandleFunc("/rest/db/completion", s.getDBCompletion) // device folder
  209. getRestMux.HandleFunc("/rest/db/file", s.getDBFile) // folder file
  210. getRestMux.HandleFunc("/rest/db/ignores", s.getDBIgnores) // folder
  211. getRestMux.HandleFunc("/rest/db/need", s.getDBNeed) // folder [perpage] [page]
  212. getRestMux.HandleFunc("/rest/db/status", s.getDBStatus) // folder
  213. getRestMux.HandleFunc("/rest/db/browse", s.getDBBrowse) // folder [prefix] [dirsonly] [levels]
  214. getRestMux.HandleFunc("/rest/events", s.getIndexEvents) // since [limit] [timeout]
  215. getRestMux.HandleFunc("/rest/events/disk", s.getDiskEvents) // since [limit] [timeout]
  216. getRestMux.HandleFunc("/rest/stats/device", s.getDeviceStats) // -
  217. getRestMux.HandleFunc("/rest/stats/folder", s.getFolderStats) // -
  218. getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id
  219. getRestMux.HandleFunc("/rest/svc/lang", s.getLang) // -
  220. getRestMux.HandleFunc("/rest/svc/report", s.getReport) // -
  221. getRestMux.HandleFunc("/rest/svc/random/string", s.getRandomString) // [length]
  222. getRestMux.HandleFunc("/rest/system/browse", s.getSystemBrowse) // current
  223. getRestMux.HandleFunc("/rest/system/config", s.getSystemConfig) // -
  224. getRestMux.HandleFunc("/rest/system/config/insync", s.getSystemConfigInsync) // -
  225. getRestMux.HandleFunc("/rest/system/connections", s.getSystemConnections) // -
  226. getRestMux.HandleFunc("/rest/system/discovery", s.getSystemDiscovery) // -
  227. getRestMux.HandleFunc("/rest/system/error", s.getSystemError) // -
  228. getRestMux.HandleFunc("/rest/system/ping", s.restPing) // -
  229. getRestMux.HandleFunc("/rest/system/status", s.getSystemStatus) // -
  230. getRestMux.HandleFunc("/rest/system/upgrade", s.getSystemUpgrade) // -
  231. getRestMux.HandleFunc("/rest/system/version", s.getSystemVersion) // -
  232. getRestMux.HandleFunc("/rest/system/debug", s.getSystemDebug) // -
  233. getRestMux.HandleFunc("/rest/system/log", s.getSystemLog) // [since]
  234. getRestMux.HandleFunc("/rest/system/log.txt", s.getSystemLogTxt) // [since]
  235. // The POST handlers
  236. postRestMux := http.NewServeMux()
  237. postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio) // folder file [perpage] [page]
  238. postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores) // folder
  239. postRestMux.HandleFunc("/rest/db/override", s.postDBOverride) // folder
  240. postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] [delay]
  241. postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig) // <body>
  242. postRestMux.HandleFunc("/rest/system/error", s.postSystemError) // <body>
  243. postRestMux.HandleFunc("/rest/system/error/clear", s.postSystemErrorClear) // -
  244. postRestMux.HandleFunc("/rest/system/ping", s.restPing) // -
  245. postRestMux.HandleFunc("/rest/system/reset", s.postSystemReset) // [folder]
  246. postRestMux.HandleFunc("/rest/system/restart", s.postSystemRestart) // -
  247. postRestMux.HandleFunc("/rest/system/shutdown", s.postSystemShutdown) // -
  248. postRestMux.HandleFunc("/rest/system/upgrade", s.postSystemUpgrade) // -
  249. postRestMux.HandleFunc("/rest/system/pause", s.makeDevicePauseHandler(true)) // device
  250. postRestMux.HandleFunc("/rest/system/resume", s.makeDevicePauseHandler(false)) // device
  251. postRestMux.HandleFunc("/rest/system/debug", s.postSystemDebug) // [enable] [disable]
  252. // Debug endpoints, not for general use
  253. debugMux := http.NewServeMux()
  254. debugMux.HandleFunc("/rest/debug/peerCompletion", s.getPeerCompletion)
  255. debugMux.HandleFunc("/rest/debug/httpmetrics", s.getSystemHTTPMetrics)
  256. debugMux.HandleFunc("/rest/debug/cpuprof", s.getCPUProf) // duration
  257. debugMux.HandleFunc("/rest/debug/heapprof", s.getHeapProf)
  258. getRestMux.Handle("/rest/debug/", s.whenDebugging(debugMux))
  259. // A handler that splits requests between the two above and disables
  260. // caching
  261. restMux := noCacheMiddleware(metricsMiddleware(getPostHandler(getRestMux, postRestMux)))
  262. // The main routing handler
  263. mux := http.NewServeMux()
  264. mux.Handle("/rest/", restMux)
  265. mux.HandleFunc("/qr/", s.getQR)
  266. // Serve compiled in assets unless an asset directory was set (for development)
  267. mux.Handle("/", s.statics)
  268. // Handle the special meta.js path
  269. mux.HandleFunc("/meta.js", s.getJSMetadata)
  270. guiCfg := s.cfg.GUI()
  271. // Wrap everything in CSRF protection. The /rest prefix should be
  272. // protected, other requests will grant cookies.
  273. handler := csrfMiddleware(s.id.String()[:5], "/rest", guiCfg, mux)
  274. // Add our version and ID as a header to responses
  275. handler = withDetailsMiddleware(s.id, handler)
  276. // Wrap everything in basic auth, if user/password is set.
  277. if len(guiCfg.User) > 0 && len(guiCfg.Password) > 0 {
  278. handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, handler)
  279. }
  280. // Redirect to HTTPS if we are supposed to
  281. if guiCfg.UseTLS() {
  282. handler = redirectToHTTPSMiddleware(handler)
  283. }
  284. // Add the CORS handling
  285. handler = corsMiddleware(handler)
  286. if addressIsLocalhost(guiCfg.Address()) && !guiCfg.InsecureSkipHostCheck {
  287. // Verify source host
  288. handler = localhostMiddleware(handler)
  289. }
  290. handler = debugMiddleware(handler)
  291. srv := http.Server{
  292. Handler: handler,
  293. // ReadTimeout must be longer than SyncthingController $scope.refresh
  294. // interval to avoid HTTP keepalive/GUI refresh race.
  295. ReadTimeout: 15 * time.Second,
  296. }
  297. s.fss = newFolderSummaryService(s.cfg, s.model)
  298. defer s.fss.Stop()
  299. s.fss.ServeBackground()
  300. l.Infoln("GUI and API listening on", listener.Addr())
  301. l.Infoln("Access the GUI via the following URL:", guiCfg.URL())
  302. if s.started != nil {
  303. // only set when run by the tests
  304. s.started <- listener.Addr().String()
  305. }
  306. // Indicate successful initial startup, to ourselves and to interested
  307. // listeners (i.e. the thing that starts the browser).
  308. select {
  309. case <-s.startedOnce:
  310. default:
  311. close(s.startedOnce)
  312. }
  313. // Serve in the background
  314. serveError := make(chan error, 1)
  315. go func() {
  316. serveError <- srv.Serve(listener)
  317. }()
  318. // Wait for stop, restart or error signals
  319. select {
  320. case <-s.stop:
  321. // Shutting down permanently
  322. l.Debugln("shutting down (stop)")
  323. case <-s.configChanged:
  324. // Soft restart due to configuration change
  325. l.Debugln("restarting (config changed)")
  326. case <-serveError:
  327. // Restart due to listen/serve failure
  328. l.Warnln("GUI/API:", err, "(restarting)")
  329. }
  330. }
  331. func (s *apiService) Stop() {
  332. close(s.stop)
  333. }
  334. func (s *apiService) String() string {
  335. return fmt.Sprintf("apiService@%p", s)
  336. }
  337. func (s *apiService) VerifyConfiguration(from, to config.Configuration) error {
  338. _, err := net.ResolveTCPAddr("tcp", to.GUI.Address())
  339. return err
  340. }
  341. func (s *apiService) CommitConfiguration(from, to config.Configuration) bool {
  342. // No action required when this changes, so mask the fact that it changed at all.
  343. from.GUI.Debugging = to.GUI.Debugging
  344. if to.GUI == from.GUI {
  345. return true
  346. }
  347. if to.GUI.Theme != from.GUI.Theme {
  348. s.statics.setTheme(to.GUI.Theme)
  349. }
  350. // Tell the serve loop to restart
  351. s.configChanged <- struct{}{}
  352. return true
  353. }
  354. func getPostHandler(get, post http.Handler) http.Handler {
  355. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  356. switch r.Method {
  357. case "GET":
  358. get.ServeHTTP(w, r)
  359. case "POST":
  360. post.ServeHTTP(w, r)
  361. default:
  362. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  363. }
  364. })
  365. }
  366. func debugMiddleware(h http.Handler) http.Handler {
  367. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  368. t0 := time.Now()
  369. h.ServeHTTP(w, r)
  370. if shouldDebugHTTP() {
  371. ms := 1000 * time.Since(t0).Seconds()
  372. // The variable `w` is most likely a *http.response, which we can't do
  373. // much with since it's a non exported type. We can however peek into
  374. // it with reflection to get at the status code and number of bytes
  375. // written.
  376. var status, written int64
  377. if rw := reflect.Indirect(reflect.ValueOf(w)); rw.IsValid() && rw.Kind() == reflect.Struct {
  378. if rf := rw.FieldByName("status"); rf.IsValid() && rf.Kind() == reflect.Int {
  379. status = rf.Int()
  380. }
  381. if rf := rw.FieldByName("written"); rf.IsValid() && rf.Kind() == reflect.Int64 {
  382. written = rf.Int()
  383. }
  384. }
  385. httpl.Debugf("http: %s %q: status %d, %d bytes in %.02f ms", r.Method, r.URL.String(), status, written, ms)
  386. }
  387. })
  388. }
  389. func corsMiddleware(next http.Handler) http.Handler {
  390. // Handle CORS headers and CORS OPTIONS request.
  391. // CORS OPTIONS request are typically sent by browser during AJAX preflight
  392. // when the browser initiate a POST request.
  393. //
  394. // As the OPTIONS request is unauthorized, this handler must be the first
  395. // of the chain (hence added at the end).
  396. //
  397. // See https://www.w3.org/TR/cors/ for details.
  398. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  399. // Process OPTIONS requests
  400. if r.Method == "OPTIONS" {
  401. // Add a generous access-control-allow-origin header for CORS requests
  402. w.Header().Add("Access-Control-Allow-Origin", "*")
  403. // Only GET/POST Methods are supported
  404. w.Header().Set("Access-Control-Allow-Methods", "GET, POST")
  405. // Only these headers can be set
  406. w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-API-Key")
  407. // The request is meant to be cached 10 minutes
  408. w.Header().Set("Access-Control-Max-Age", "600")
  409. // Indicate that no content will be returned
  410. w.WriteHeader(204)
  411. return
  412. }
  413. // For everything else, pass to the next handler
  414. next.ServeHTTP(w, r)
  415. return
  416. })
  417. }
  418. func metricsMiddleware(h http.Handler) http.Handler {
  419. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  420. t := metrics.GetOrRegisterTimer(r.URL.Path, nil)
  421. t0 := time.Now()
  422. h.ServeHTTP(w, r)
  423. t.UpdateSince(t0)
  424. })
  425. }
  426. func redirectToHTTPSMiddleware(h http.Handler) http.Handler {
  427. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  428. if r.TLS == nil {
  429. // Redirect HTTP requests to HTTPS
  430. r.URL.Host = r.Host
  431. r.URL.Scheme = "https"
  432. http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect)
  433. } else {
  434. h.ServeHTTP(w, r)
  435. }
  436. })
  437. }
  438. func noCacheMiddleware(h http.Handler) http.Handler {
  439. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  440. w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store")
  441. w.Header().Set("Expires", time.Now().UTC().Format(http.TimeFormat))
  442. w.Header().Set("Pragma", "no-cache")
  443. h.ServeHTTP(w, r)
  444. })
  445. }
  446. func withDetailsMiddleware(id protocol.DeviceID, h http.Handler) http.Handler {
  447. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  448. w.Header().Set("X-Syncthing-Version", Version)
  449. w.Header().Set("X-Syncthing-ID", id.String())
  450. h.ServeHTTP(w, r)
  451. })
  452. }
  453. func localhostMiddleware(h http.Handler) http.Handler {
  454. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  455. if addressIsLocalhost(r.Host) {
  456. h.ServeHTTP(w, r)
  457. return
  458. }
  459. http.Error(w, "Host check error", http.StatusForbidden)
  460. })
  461. }
  462. func (s *apiService) whenDebugging(h http.Handler) http.Handler {
  463. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  464. if s.cfg.GUI().Debugging {
  465. h.ServeHTTP(w, r)
  466. return
  467. }
  468. http.Error(w, "Debugging disabled", http.StatusBadRequest)
  469. })
  470. }
  471. func (s *apiService) restPing(w http.ResponseWriter, r *http.Request) {
  472. sendJSON(w, map[string]string{"ping": "pong"})
  473. }
  474. func (s *apiService) getJSMetadata(w http.ResponseWriter, r *http.Request) {
  475. meta, _ := json.Marshal(map[string]string{
  476. "deviceID": s.id.String(),
  477. })
  478. w.Header().Set("Content-Type", "application/javascript")
  479. fmt.Fprintf(w, "var metadata = %s;\n", meta)
  480. }
  481. func (s *apiService) getSystemVersion(w http.ResponseWriter, r *http.Request) {
  482. sendJSON(w, map[string]string{
  483. "version": Version,
  484. "codename": Codename,
  485. "longVersion": LongVersion,
  486. "os": runtime.GOOS,
  487. "arch": runtime.GOARCH,
  488. })
  489. }
  490. func (s *apiService) getSystemDebug(w http.ResponseWriter, r *http.Request) {
  491. names := l.Facilities()
  492. enabled := l.FacilityDebugging()
  493. sort.Strings(enabled)
  494. sendJSON(w, map[string]interface{}{
  495. "facilities": names,
  496. "enabled": enabled,
  497. })
  498. }
  499. func (s *apiService) postSystemDebug(w http.ResponseWriter, r *http.Request) {
  500. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  501. q := r.URL.Query()
  502. for _, f := range strings.Split(q.Get("enable"), ",") {
  503. if f == "" || l.ShouldDebug(f) {
  504. continue
  505. }
  506. l.SetDebug(f, true)
  507. l.Infof("Enabled debug data for %q", f)
  508. }
  509. for _, f := range strings.Split(q.Get("disable"), ",") {
  510. if f == "" || !l.ShouldDebug(f) {
  511. continue
  512. }
  513. l.SetDebug(f, false)
  514. l.Infof("Disabled debug data for %q", f)
  515. }
  516. }
  517. func (s *apiService) getDBBrowse(w http.ResponseWriter, r *http.Request) {
  518. qs := r.URL.Query()
  519. folder := qs.Get("folder")
  520. prefix := qs.Get("prefix")
  521. dirsonly := qs.Get("dirsonly") != ""
  522. levels, err := strconv.Atoi(qs.Get("levels"))
  523. if err != nil {
  524. levels = -1
  525. }
  526. sendJSON(w, s.model.GlobalDirectoryTree(folder, prefix, levels, dirsonly))
  527. }
  528. func (s *apiService) getDBCompletion(w http.ResponseWriter, r *http.Request) {
  529. var qs = r.URL.Query()
  530. var folder = qs.Get("folder")
  531. var deviceStr = qs.Get("device")
  532. device, err := protocol.DeviceIDFromString(deviceStr)
  533. if err != nil {
  534. http.Error(w, err.Error(), 500)
  535. return
  536. }
  537. comp := s.model.Completion(device, folder)
  538. sendJSON(w, map[string]interface{}{
  539. "completion": comp.CompletionPct,
  540. "needBytes": comp.NeedBytes,
  541. "globalBytes": comp.GlobalBytes,
  542. "needDeletes": comp.NeedDeletes,
  543. })
  544. }
  545. func (s *apiService) getDBStatus(w http.ResponseWriter, r *http.Request) {
  546. qs := r.URL.Query()
  547. folder := qs.Get("folder")
  548. sendJSON(w, folderSummary(s.cfg, s.model, folder))
  549. }
  550. func folderSummary(cfg configIntf, m modelIntf, folder string) map[string]interface{} {
  551. var res = make(map[string]interface{})
  552. res["invalid"] = "" // Deprecated, retains external API for now
  553. global := m.GlobalSize(folder)
  554. res["globalFiles"], res["globalDirectories"], res["globalSymlinks"], res["globalDeleted"], res["globalBytes"] = global.Files, global.Directories, global.Symlinks, global.Deleted, global.Bytes
  555. local := m.LocalSize(folder)
  556. res["localFiles"], res["localDirectories"], res["localSymlinks"], res["localDeleted"], res["localBytes"] = local.Files, local.Directories, local.Symlinks, local.Deleted, local.Bytes
  557. need := m.NeedSize(folder)
  558. res["needFiles"], res["needDirectories"], res["needSymlinks"], res["needDeletes"], res["needBytes"] = need.Files, need.Directories, need.Symlinks, need.Deleted, need.Bytes
  559. res["inSyncFiles"], res["inSyncBytes"] = global.Files-need.Files, global.Bytes-need.Bytes
  560. var err error
  561. res["state"], res["stateChanged"], err = m.State(folder)
  562. if err != nil {
  563. res["error"] = err.Error()
  564. }
  565. ourSeq, _ := m.CurrentSequence(folder)
  566. remoteSeq, _ := m.RemoteSequence(folder)
  567. res["version"] = ourSeq + remoteSeq // legacy
  568. res["sequence"] = ourSeq + remoteSeq // new name
  569. ignorePatterns, _, _ := m.GetIgnores(folder)
  570. res["ignorePatterns"] = false
  571. for _, line := range ignorePatterns {
  572. if len(line) > 0 && !strings.HasPrefix(line, "//") {
  573. res["ignorePatterns"] = true
  574. break
  575. }
  576. }
  577. return res
  578. }
  579. func (s *apiService) postDBOverride(w http.ResponseWriter, r *http.Request) {
  580. var qs = r.URL.Query()
  581. var folder = qs.Get("folder")
  582. go s.model.Override(folder)
  583. }
  584. func (s *apiService) getDBNeed(w http.ResponseWriter, r *http.Request) {
  585. qs := r.URL.Query()
  586. folder := qs.Get("folder")
  587. page, err := strconv.Atoi(qs.Get("page"))
  588. if err != nil || page < 1 {
  589. page = 1
  590. }
  591. perpage, err := strconv.Atoi(qs.Get("perpage"))
  592. if err != nil || perpage < 1 {
  593. perpage = 1 << 16
  594. }
  595. progress, queued, rest, total := s.model.NeedFolderFiles(folder, page, perpage)
  596. // Convert the struct to a more loose structure, and inject the size.
  597. sendJSON(w, map[string]interface{}{
  598. "progress": s.toNeedSlice(progress),
  599. "queued": s.toNeedSlice(queued),
  600. "rest": s.toNeedSlice(rest),
  601. "total": total,
  602. "page": page,
  603. "perpage": perpage,
  604. })
  605. }
  606. func (s *apiService) getSystemConnections(w http.ResponseWriter, r *http.Request) {
  607. sendJSON(w, s.model.ConnectionStats())
  608. }
  609. func (s *apiService) getDeviceStats(w http.ResponseWriter, r *http.Request) {
  610. sendJSON(w, s.model.DeviceStatistics())
  611. }
  612. func (s *apiService) getFolderStats(w http.ResponseWriter, r *http.Request) {
  613. sendJSON(w, s.model.FolderStatistics())
  614. }
  615. func (s *apiService) getDBFile(w http.ResponseWriter, r *http.Request) {
  616. qs := r.URL.Query()
  617. folder := qs.Get("folder")
  618. file := qs.Get("file")
  619. gf, gfOk := s.model.CurrentGlobalFile(folder, file)
  620. lf, lfOk := s.model.CurrentFolderFile(folder, file)
  621. if !(gfOk || lfOk) {
  622. // This file for sure does not exist.
  623. http.Error(w, "No such object in the index", http.StatusNotFound)
  624. return
  625. }
  626. av := s.model.Availability(folder, file, protocol.Vector{}, protocol.BlockInfo{})
  627. sendJSON(w, map[string]interface{}{
  628. "global": jsonFileInfo(gf),
  629. "local": jsonFileInfo(lf),
  630. "availability": av,
  631. })
  632. }
  633. func (s *apiService) getSystemConfig(w http.ResponseWriter, r *http.Request) {
  634. sendJSON(w, s.cfg.RawCopy())
  635. }
  636. func (s *apiService) postSystemConfig(w http.ResponseWriter, r *http.Request) {
  637. s.systemConfigMut.Lock()
  638. defer s.systemConfigMut.Unlock()
  639. to, err := config.ReadJSON(r.Body, myID)
  640. r.Body.Close()
  641. if err != nil {
  642. l.Warnln("Decoding posted config:", err)
  643. http.Error(w, err.Error(), http.StatusBadRequest)
  644. return
  645. }
  646. if to.GUI.Password != s.cfg.GUI().Password {
  647. if to.GUI.Password != "" {
  648. hash, err := bcrypt.GenerateFromPassword([]byte(to.GUI.Password), 0)
  649. if err != nil {
  650. l.Warnln("bcrypting password:", err)
  651. http.Error(w, err.Error(), http.StatusInternalServerError)
  652. return
  653. }
  654. to.GUI.Password = string(hash)
  655. }
  656. }
  657. // Fixup usage reporting settings
  658. if curAcc := s.cfg.Options().URAccepted; to.Options.URAccepted > curAcc {
  659. // UR was enabled
  660. to.Options.URAccepted = usageReportVersion
  661. to.Options.URUniqueID = rand.String(8)
  662. } else if to.Options.URAccepted < curAcc {
  663. // UR was disabled
  664. to.Options.URAccepted = -1
  665. to.Options.URUniqueID = ""
  666. }
  667. // Activate and save
  668. if err := s.cfg.Replace(to); err != nil {
  669. l.Warnln("Replacing config:", err)
  670. http.Error(w, err.Error(), http.StatusInternalServerError)
  671. return
  672. }
  673. if err := s.cfg.Save(); err != nil {
  674. l.Warnln("Saving config:", err)
  675. http.Error(w, err.Error(), http.StatusInternalServerError)
  676. return
  677. }
  678. }
  679. func (s *apiService) getSystemConfigInsync(w http.ResponseWriter, r *http.Request) {
  680. sendJSON(w, map[string]bool{"configInSync": !s.cfg.RequiresRestart()})
  681. }
  682. func (s *apiService) postSystemRestart(w http.ResponseWriter, r *http.Request) {
  683. s.flushResponse(`{"ok": "restarting"}`, w)
  684. go restart()
  685. }
  686. func (s *apiService) postSystemReset(w http.ResponseWriter, r *http.Request) {
  687. var qs = r.URL.Query()
  688. folder := qs.Get("folder")
  689. if len(folder) > 0 {
  690. if _, ok := s.cfg.Folders()[folder]; !ok {
  691. http.Error(w, "Invalid folder ID", 500)
  692. return
  693. }
  694. }
  695. if len(folder) == 0 {
  696. // Reset all folders.
  697. for folder := range s.cfg.Folders() {
  698. s.model.ResetFolder(folder)
  699. }
  700. s.flushResponse(`{"ok": "resetting database"}`, w)
  701. } else {
  702. // Reset a specific folder, assuming it's supposed to exist.
  703. s.model.ResetFolder(folder)
  704. s.flushResponse(`{"ok": "resetting folder `+folder+`"}`, w)
  705. }
  706. go restart()
  707. }
  708. func (s *apiService) postSystemShutdown(w http.ResponseWriter, r *http.Request) {
  709. s.flushResponse(`{"ok": "shutting down"}`, w)
  710. go shutdown()
  711. }
  712. func (s *apiService) flushResponse(resp string, w http.ResponseWriter) {
  713. w.Write([]byte(resp + "\n"))
  714. f := w.(http.Flusher)
  715. f.Flush()
  716. }
  717. var cpuUsagePercent [10]float64 // The last ten seconds
  718. var cpuUsageLock = sync.NewRWMutex()
  719. func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) {
  720. var m runtime.MemStats
  721. runtime.ReadMemStats(&m)
  722. tilde, _ := osutil.ExpandTilde("~")
  723. res := make(map[string]interface{})
  724. res["myID"] = myID.String()
  725. res["goroutines"] = runtime.NumGoroutine()
  726. res["alloc"] = m.Alloc
  727. res["sys"] = m.Sys - m.HeapReleased
  728. res["tilde"] = tilde
  729. if s.cfg.Options().LocalAnnEnabled || s.cfg.Options().GlobalAnnEnabled {
  730. res["discoveryEnabled"] = true
  731. discoErrors := make(map[string]string)
  732. discoMethods := 0
  733. for disco, err := range s.discoverer.ChildErrors() {
  734. discoMethods++
  735. if err != nil {
  736. discoErrors[disco] = err.Error()
  737. }
  738. }
  739. res["discoveryMethods"] = discoMethods
  740. res["discoveryErrors"] = discoErrors
  741. }
  742. res["connectionServiceStatus"] = s.connectionsService.Status()
  743. cpuUsageLock.RLock()
  744. var cpusum float64
  745. for _, p := range cpuUsagePercent {
  746. cpusum += p
  747. }
  748. cpuUsageLock.RUnlock()
  749. res["cpuPercent"] = cpusum / float64(len(cpuUsagePercent)) / float64(runtime.NumCPU())
  750. res["pathSeparator"] = string(filepath.Separator)
  751. res["uptime"] = int(time.Since(startTime).Seconds())
  752. res["startTime"] = startTime
  753. sendJSON(w, res)
  754. }
  755. func (s *apiService) getSystemError(w http.ResponseWriter, r *http.Request) {
  756. sendJSON(w, map[string][]logger.Line{
  757. "errors": s.guiErrors.Since(time.Time{}),
  758. })
  759. }
  760. func (s *apiService) postSystemError(w http.ResponseWriter, r *http.Request) {
  761. bs, _ := ioutil.ReadAll(r.Body)
  762. r.Body.Close()
  763. l.Warnln(string(bs))
  764. }
  765. func (s *apiService) postSystemErrorClear(w http.ResponseWriter, r *http.Request) {
  766. s.guiErrors.Clear()
  767. }
  768. func (s *apiService) getSystemLog(w http.ResponseWriter, r *http.Request) {
  769. q := r.URL.Query()
  770. since, err := time.Parse(time.RFC3339, q.Get("since"))
  771. l.Debugln(err)
  772. sendJSON(w, map[string][]logger.Line{
  773. "messages": s.systemLog.Since(since),
  774. })
  775. }
  776. func (s *apiService) getSystemLogTxt(w http.ResponseWriter, r *http.Request) {
  777. q := r.URL.Query()
  778. since, err := time.Parse(time.RFC3339, q.Get("since"))
  779. l.Debugln(err)
  780. w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  781. for _, line := range s.systemLog.Since(since) {
  782. fmt.Fprintf(w, "%s: %s\n", line.When.Format(time.RFC3339), line.Message)
  783. }
  784. }
  785. func (s *apiService) getSystemHTTPMetrics(w http.ResponseWriter, r *http.Request) {
  786. stats := make(map[string]interface{})
  787. metrics.Each(func(name string, intf interface{}) {
  788. if m, ok := intf.(*metrics.StandardTimer); ok {
  789. pct := m.Percentiles([]float64{0.50, 0.95, 0.99})
  790. for i := range pct {
  791. pct[i] /= 1e6 // ns to ms
  792. }
  793. stats[name] = map[string]interface{}{
  794. "count": m.Count(),
  795. "sumMs": m.Sum() / 1e6, // ns to ms
  796. "ratesPerS": []float64{m.Rate1(), m.Rate5(), m.Rate15()},
  797. "percentilesMs": pct,
  798. }
  799. }
  800. })
  801. bs, _ := json.MarshalIndent(stats, "", " ")
  802. w.Write(bs)
  803. }
  804. func (s *apiService) getSystemDiscovery(w http.ResponseWriter, r *http.Request) {
  805. devices := make(map[string]discover.CacheEntry)
  806. if s.discoverer != nil {
  807. // Device ids can't be marshalled as keys so we need to manually
  808. // rebuild this map using strings. Discoverer may be nil if discovery
  809. // has not started yet.
  810. for device, entry := range s.discoverer.Cache() {
  811. devices[device.String()] = entry
  812. }
  813. }
  814. sendJSON(w, devices)
  815. }
  816. func (s *apiService) getReport(w http.ResponseWriter, r *http.Request) {
  817. sendJSON(w, reportData(s.cfg, s.model))
  818. }
  819. func (s *apiService) getRandomString(w http.ResponseWriter, r *http.Request) {
  820. length := 32
  821. if val, _ := strconv.Atoi(r.URL.Query().Get("length")); val > 0 {
  822. length = val
  823. }
  824. str := rand.String(length)
  825. sendJSON(w, map[string]string{"random": str})
  826. }
  827. func (s *apiService) getDBIgnores(w http.ResponseWriter, r *http.Request) {
  828. qs := r.URL.Query()
  829. ignores, patterns, err := s.model.GetIgnores(qs.Get("folder"))
  830. if err != nil {
  831. http.Error(w, err.Error(), 500)
  832. return
  833. }
  834. sendJSON(w, map[string][]string{
  835. "ignore": ignores,
  836. "expanded": patterns,
  837. })
  838. }
  839. func (s *apiService) postDBIgnores(w http.ResponseWriter, r *http.Request) {
  840. qs := r.URL.Query()
  841. bs, err := ioutil.ReadAll(r.Body)
  842. r.Body.Close()
  843. if err != nil {
  844. http.Error(w, err.Error(), 500)
  845. return
  846. }
  847. var data map[string][]string
  848. err = json.Unmarshal(bs, &data)
  849. if err != nil {
  850. http.Error(w, err.Error(), 500)
  851. return
  852. }
  853. err = s.model.SetIgnores(qs.Get("folder"), data["ignore"])
  854. if err != nil {
  855. http.Error(w, err.Error(), 500)
  856. return
  857. }
  858. s.getDBIgnores(w, r)
  859. }
  860. func (s *apiService) getIndexEvents(w http.ResponseWriter, r *http.Request) {
  861. s.fss.gotEventRequest()
  862. s.getEvents(w, r, s.eventSub)
  863. }
  864. func (s *apiService) getDiskEvents(w http.ResponseWriter, r *http.Request) {
  865. s.getEvents(w, r, s.diskEventSub)
  866. }
  867. func (s *apiService) getEvents(w http.ResponseWriter, r *http.Request, eventSub events.BufferedSubscription) {
  868. qs := r.URL.Query()
  869. sinceStr := qs.Get("since")
  870. limitStr := qs.Get("limit")
  871. timeoutStr := qs.Get("timeout")
  872. since, _ := strconv.Atoi(sinceStr)
  873. limit, _ := strconv.Atoi(limitStr)
  874. timeout := defaultEventTimeout
  875. if timeoutSec, timeoutErr := strconv.Atoi(timeoutStr); timeoutErr == nil && timeoutSec >= 0 { // 0 is a valid timeout
  876. timeout = time.Duration(timeoutSec) * time.Second
  877. }
  878. // Flush before blocking, to indicate that we've received the request and
  879. // that it should not be retried. Must set Content-Type header before
  880. // flushing.
  881. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  882. f := w.(http.Flusher)
  883. f.Flush()
  884. // If there are no events available return an empty slice, as this gets serialized as `[]`
  885. evs := eventSub.Since(since, []events.Event{}, timeout)
  886. if 0 < limit && limit < len(evs) {
  887. evs = evs[len(evs)-limit:]
  888. }
  889. sendJSON(w, evs)
  890. }
  891. func (s *apiService) getSystemUpgrade(w http.ResponseWriter, r *http.Request) {
  892. if noUpgradeFromEnv {
  893. http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), 500)
  894. return
  895. }
  896. opts := s.cfg.Options()
  897. rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
  898. if err != nil {
  899. http.Error(w, err.Error(), 500)
  900. return
  901. }
  902. res := make(map[string]interface{})
  903. res["running"] = Version
  904. res["latest"] = rel.Tag
  905. res["newer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.Newer
  906. res["majorNewer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.MajorNewer
  907. sendJSON(w, res)
  908. }
  909. func (s *apiService) getDeviceID(w http.ResponseWriter, r *http.Request) {
  910. qs := r.URL.Query()
  911. idStr := qs.Get("id")
  912. id, err := protocol.DeviceIDFromString(idStr)
  913. if err == nil {
  914. sendJSON(w, map[string]string{
  915. "id": id.String(),
  916. })
  917. } else {
  918. sendJSON(w, map[string]string{
  919. "error": err.Error(),
  920. })
  921. }
  922. }
  923. func (s *apiService) getLang(w http.ResponseWriter, r *http.Request) {
  924. lang := r.Header.Get("Accept-Language")
  925. var langs []string
  926. for _, l := range strings.Split(lang, ",") {
  927. parts := strings.SplitN(l, ";", 2)
  928. langs = append(langs, strings.ToLower(strings.TrimSpace(parts[0])))
  929. }
  930. sendJSON(w, langs)
  931. }
  932. func (s *apiService) postSystemUpgrade(w http.ResponseWriter, r *http.Request) {
  933. opts := s.cfg.Options()
  934. rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
  935. if err != nil {
  936. l.Warnln("getting latest release:", err)
  937. http.Error(w, err.Error(), 500)
  938. return
  939. }
  940. if upgrade.CompareVersions(rel.Tag, Version) > upgrade.Equal {
  941. err = upgrade.To(rel)
  942. if err != nil {
  943. l.Warnln("upgrading:", err)
  944. http.Error(w, err.Error(), 500)
  945. return
  946. }
  947. s.flushResponse(`{"ok": "restarting"}`, w)
  948. l.Infoln("Upgrading")
  949. stop <- exitUpgrading
  950. }
  951. }
  952. func (s *apiService) makeDevicePauseHandler(paused bool) http.HandlerFunc {
  953. return func(w http.ResponseWriter, r *http.Request) {
  954. var qs = r.URL.Query()
  955. var deviceStr = qs.Get("device")
  956. device, err := protocol.DeviceIDFromString(deviceStr)
  957. if err != nil {
  958. http.Error(w, err.Error(), 500)
  959. return
  960. }
  961. cfg, ok := s.cfg.Devices()[device]
  962. if !ok {
  963. http.Error(w, "not found", http.StatusNotFound)
  964. }
  965. cfg.Paused = paused
  966. if err := s.cfg.SetDevice(cfg); err != nil {
  967. http.Error(w, err.Error(), 500)
  968. }
  969. }
  970. }
  971. func (s *apiService) postDBScan(w http.ResponseWriter, r *http.Request) {
  972. qs := r.URL.Query()
  973. folder := qs.Get("folder")
  974. if folder != "" {
  975. subs := qs["sub"]
  976. err := s.model.ScanFolderSubdirs(folder, subs)
  977. if err != nil {
  978. http.Error(w, err.Error(), 500)
  979. return
  980. }
  981. nextStr := qs.Get("next")
  982. next, err := strconv.Atoi(nextStr)
  983. if err == nil {
  984. s.model.DelayScan(folder, time.Duration(next)*time.Second)
  985. }
  986. } else {
  987. errors := s.model.ScanFolders()
  988. if len(errors) > 0 {
  989. http.Error(w, "Error scanning folders", 500)
  990. sendJSON(w, errors)
  991. return
  992. }
  993. }
  994. }
  995. func (s *apiService) postDBPrio(w http.ResponseWriter, r *http.Request) {
  996. qs := r.URL.Query()
  997. folder := qs.Get("folder")
  998. file := qs.Get("file")
  999. s.model.BringToFront(folder, file)
  1000. s.getDBNeed(w, r)
  1001. }
  1002. func (s *apiService) getQR(w http.ResponseWriter, r *http.Request) {
  1003. var qs = r.URL.Query()
  1004. var text = qs.Get("text")
  1005. code, err := qr.Encode(text, qr.M)
  1006. if err != nil {
  1007. http.Error(w, "Invalid", 500)
  1008. return
  1009. }
  1010. w.Header().Set("Content-Type", "image/png")
  1011. w.Write(code.PNG())
  1012. }
  1013. func (s *apiService) getPeerCompletion(w http.ResponseWriter, r *http.Request) {
  1014. tot := map[string]float64{}
  1015. count := map[string]float64{}
  1016. for _, folder := range s.cfg.Folders() {
  1017. for _, device := range folder.DeviceIDs() {
  1018. deviceStr := device.String()
  1019. if s.model.ConnectedTo(device) {
  1020. tot[deviceStr] += s.model.Completion(device, folder.ID).CompletionPct
  1021. } else {
  1022. tot[deviceStr] = 0
  1023. }
  1024. count[deviceStr]++
  1025. }
  1026. }
  1027. comp := map[string]int{}
  1028. for device := range tot {
  1029. comp[device] = int(tot[device] / count[device])
  1030. }
  1031. sendJSON(w, comp)
  1032. }
  1033. func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
  1034. qs := r.URL.Query()
  1035. current := qs.Get("current")
  1036. if current == "" {
  1037. if roots, err := osutil.GetFilesystemRoots(); err == nil {
  1038. sendJSON(w, roots)
  1039. } else {
  1040. http.Error(w, err.Error(), 500)
  1041. }
  1042. return
  1043. }
  1044. search, _ := osutil.ExpandTilde(current)
  1045. pathSeparator := string(os.PathSeparator)
  1046. if strings.HasSuffix(current, pathSeparator) && !strings.HasSuffix(search, pathSeparator) {
  1047. search = search + pathSeparator
  1048. }
  1049. subdirectories, _ := osutil.Glob(search + "*")
  1050. ret := make([]string, 0, len(subdirectories))
  1051. for _, subdirectory := range subdirectories {
  1052. info, err := os.Stat(subdirectory)
  1053. if err == nil && info.IsDir() {
  1054. ret = append(ret, subdirectory+pathSeparator)
  1055. }
  1056. }
  1057. sendJSON(w, ret)
  1058. }
  1059. func (s *apiService) getCPUProf(w http.ResponseWriter, r *http.Request) {
  1060. duration, err := time.ParseDuration(r.FormValue("duration"))
  1061. if err != nil {
  1062. duration = 30 * time.Second
  1063. }
  1064. filename := fmt.Sprintf("syncthing-cpu-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, Version, time.Now().Format("150405")) // hhmmss
  1065. w.Header().Set("Content-Type", "application/octet-stream")
  1066. w.Header().Set("Content-Disposition", "attachment; filename="+filename)
  1067. pprof.StartCPUProfile(w)
  1068. time.Sleep(duration)
  1069. pprof.StopCPUProfile()
  1070. }
  1071. func (s *apiService) getHeapProf(w http.ResponseWriter, r *http.Request) {
  1072. filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, Version, time.Now().Format("150405")) // hhmmss
  1073. w.Header().Set("Content-Type", "application/octet-stream")
  1074. w.Header().Set("Content-Disposition", "attachment; filename="+filename)
  1075. runtime.GC()
  1076. pprof.WriteHeapProfile(w)
  1077. }
  1078. func (s *apiService) toNeedSlice(fs []db.FileInfoTruncated) []jsonDBFileInfo {
  1079. res := make([]jsonDBFileInfo, len(fs))
  1080. for i, f := range fs {
  1081. res[i] = jsonDBFileInfo(f)
  1082. }
  1083. return res
  1084. }
  1085. // Type wrappers for nice JSON serialization
  1086. type jsonFileInfo protocol.FileInfo
  1087. func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
  1088. return json.Marshal(map[string]interface{}{
  1089. "name": f.Name,
  1090. "type": f.Type,
  1091. "size": f.Size,
  1092. "permissions": fmt.Sprintf("%#o", f.Permissions),
  1093. "deleted": f.Deleted,
  1094. "invalid": f.Invalid,
  1095. "noPermissions": f.NoPermissions,
  1096. "modified": protocol.FileInfo(f).ModTime(),
  1097. "sequence": f.Sequence,
  1098. "numBlocks": len(f.Blocks),
  1099. "version": jsonVersionVector(f.Version),
  1100. })
  1101. }
  1102. type jsonDBFileInfo db.FileInfoTruncated
  1103. func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) {
  1104. return json.Marshal(map[string]interface{}{
  1105. "name": f.Name,
  1106. "type": f.Type,
  1107. "size": f.Size,
  1108. "permissions": fmt.Sprintf("%#o", f.Permissions),
  1109. "deleted": f.Deleted,
  1110. "invalid": f.Invalid,
  1111. "noPermissions": f.NoPermissions,
  1112. "modified": db.FileInfoTruncated(f).ModTime(),
  1113. "sequence": f.Sequence,
  1114. })
  1115. }
  1116. type jsonVersionVector protocol.Vector
  1117. func (v jsonVersionVector) MarshalJSON() ([]byte, error) {
  1118. res := make([]string, len(v.Counters))
  1119. for i, c := range v.Counters {
  1120. res[i] = fmt.Sprintf("%v:%d", c.ID, c.Value)
  1121. }
  1122. return json.Marshal(res)
  1123. }
  1124. func dirNames(dir string) []string {
  1125. fd, err := os.Open(dir)
  1126. if err != nil {
  1127. return nil
  1128. }
  1129. defer fd.Close()
  1130. fis, err := fd.Readdir(-1)
  1131. if err != nil {
  1132. return nil
  1133. }
  1134. var dirs []string
  1135. for _, fi := range fis {
  1136. if fi.IsDir() {
  1137. dirs = append(dirs, filepath.Base(fi.Name()))
  1138. }
  1139. }
  1140. sort.Strings(dirs)
  1141. return dirs
  1142. }
  1143. func addressIsLocalhost(addr string) bool {
  1144. host, _, err := net.SplitHostPort(addr)
  1145. if err != nil {
  1146. // There was no port, so we assume the address was just a hostname
  1147. host = addr
  1148. }
  1149. switch strings.ToLower(host) {
  1150. case "127.0.0.1", "::1", "localhost":
  1151. return true
  1152. default:
  1153. return false
  1154. }
  1155. }