web.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "html/template"
  6. "io/ioutil"
  7. "net/http"
  8. "path"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/go-chi/chi"
  14. "github.com/drakkan/sftpgo/common"
  15. "github.com/drakkan/sftpgo/dataprovider"
  16. "github.com/drakkan/sftpgo/utils"
  17. "github.com/drakkan/sftpgo/version"
  18. "github.com/drakkan/sftpgo/vfs"
  19. )
  20. const (
  21. templateBase = "base.html"
  22. templateUsers = "users.html"
  23. templateUser = "user.html"
  24. templateConnections = "connections.html"
  25. templateFolders = "folders.html"
  26. templateFolder = "folder.html"
  27. templateMessage = "message.html"
  28. pageUsersTitle = "Users"
  29. pageConnectionsTitle = "Connections"
  30. pageFoldersTitle = "Folders"
  31. page400Title = "Bad request"
  32. page404Title = "Not found"
  33. page404Body = "The page you are looking for does not exist."
  34. page500Title = "Internal Server Error"
  35. page500Body = "The server is unable to fulfill your request."
  36. defaultQueryLimit = 500
  37. webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
  38. )
  39. var (
  40. templates = make(map[string]*template.Template)
  41. )
  42. type basePage struct {
  43. Title string
  44. CurrentURL string
  45. UsersURL string
  46. UserURL string
  47. APIUserURL string
  48. APIConnectionsURL string
  49. APIQuotaScanURL string
  50. ConnectionsURL string
  51. FoldersURL string
  52. FolderURL string
  53. APIFoldersURL string
  54. APIFolderQuotaScanURL string
  55. UsersTitle string
  56. ConnectionsTitle string
  57. FoldersTitle string
  58. Version string
  59. }
  60. type usersPage struct {
  61. basePage
  62. Users []dataprovider.User
  63. }
  64. type foldersPage struct {
  65. basePage
  66. Folders []vfs.BaseVirtualFolder
  67. }
  68. type connectionsPage struct {
  69. basePage
  70. Connections []common.ConnectionStatus
  71. }
  72. type userPage struct {
  73. basePage
  74. IsAdd bool
  75. User dataprovider.User
  76. RootPerms []string
  77. Error string
  78. ValidPerms []string
  79. ValidSSHLoginMethods []string
  80. ValidProtocols []string
  81. RootDirPerms []string
  82. }
  83. type folderPage struct {
  84. basePage
  85. Folder vfs.BaseVirtualFolder
  86. Error string
  87. }
  88. type messagePage struct {
  89. basePage
  90. Error string
  91. Success string
  92. }
  93. func loadTemplates(templatesPath string) {
  94. usersPaths := []string{
  95. filepath.Join(templatesPath, templateBase),
  96. filepath.Join(templatesPath, templateUsers),
  97. }
  98. userPaths := []string{
  99. filepath.Join(templatesPath, templateBase),
  100. filepath.Join(templatesPath, templateUser),
  101. }
  102. connectionsPaths := []string{
  103. filepath.Join(templatesPath, templateBase),
  104. filepath.Join(templatesPath, templateConnections),
  105. }
  106. messagePath := []string{
  107. filepath.Join(templatesPath, templateBase),
  108. filepath.Join(templatesPath, templateMessage),
  109. }
  110. foldersPath := []string{
  111. filepath.Join(templatesPath, templateBase),
  112. filepath.Join(templatesPath, templateFolders),
  113. }
  114. folderPath := []string{
  115. filepath.Join(templatesPath, templateBase),
  116. filepath.Join(templatesPath, templateFolder),
  117. }
  118. usersTmpl := utils.LoadTemplate(template.ParseFiles(usersPaths...))
  119. userTmpl := utils.LoadTemplate(template.ParseFiles(userPaths...))
  120. connectionsTmpl := utils.LoadTemplate(template.ParseFiles(connectionsPaths...))
  121. messageTmpl := utils.LoadTemplate(template.ParseFiles(messagePath...))
  122. foldersTmpl := utils.LoadTemplate(template.ParseFiles(foldersPath...))
  123. folderTmpl := utils.LoadTemplate(template.ParseFiles(folderPath...))
  124. templates[templateUsers] = usersTmpl
  125. templates[templateUser] = userTmpl
  126. templates[templateConnections] = connectionsTmpl
  127. templates[templateMessage] = messageTmpl
  128. templates[templateFolders] = foldersTmpl
  129. templates[templateFolder] = folderTmpl
  130. }
  131. func getBasePageData(title, currentURL string) basePage {
  132. return basePage{
  133. Title: title,
  134. CurrentURL: currentURL,
  135. UsersURL: webUsersPath,
  136. UserURL: webUserPath,
  137. FoldersURL: webFoldersPath,
  138. FolderURL: webFolderPath,
  139. APIUserURL: userPath,
  140. APIConnectionsURL: activeConnectionsPath,
  141. APIQuotaScanURL: quotaScanPath,
  142. APIFoldersURL: folderPath,
  143. APIFolderQuotaScanURL: quotaScanVFolderPath,
  144. ConnectionsURL: webConnectionsPath,
  145. UsersTitle: pageUsersTitle,
  146. ConnectionsTitle: pageConnectionsTitle,
  147. FoldersTitle: pageFoldersTitle,
  148. Version: version.GetAsString(),
  149. }
  150. }
  151. func renderTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
  152. err := templates[tmplName].ExecuteTemplate(w, tmplName, data)
  153. if err != nil {
  154. http.Error(w, err.Error(), http.StatusInternalServerError)
  155. }
  156. }
  157. func renderMessagePage(w http.ResponseWriter, title, body string, statusCode int, err error, message string) {
  158. var errorString string
  159. if len(body) > 0 {
  160. errorString = body + " "
  161. }
  162. if err != nil {
  163. errorString += err.Error()
  164. }
  165. data := messagePage{
  166. basePage: getBasePageData(title, ""),
  167. Error: errorString,
  168. Success: message,
  169. }
  170. w.WriteHeader(statusCode)
  171. renderTemplate(w, templateMessage, data)
  172. }
  173. func renderInternalServerErrorPage(w http.ResponseWriter, err error) {
  174. renderMessagePage(w, page500Title, page500Body, http.StatusInternalServerError, err, "")
  175. }
  176. func renderBadRequestPage(w http.ResponseWriter, err error) {
  177. renderMessagePage(w, page400Title, "", http.StatusBadRequest, err, "")
  178. }
  179. func renderNotFoundPage(w http.ResponseWriter, err error) {
  180. renderMessagePage(w, page404Title, page404Body, http.StatusNotFound, err, "")
  181. }
  182. func renderAddUserPage(w http.ResponseWriter, user dataprovider.User, error string) {
  183. data := userPage{
  184. basePage: getBasePageData("Add a new user", webUserPath),
  185. IsAdd: true,
  186. Error: error,
  187. User: user,
  188. ValidPerms: dataprovider.ValidPerms,
  189. ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
  190. ValidProtocols: dataprovider.ValidProtocols,
  191. RootDirPerms: user.GetPermissionsForPath("/"),
  192. }
  193. renderTemplate(w, templateUser, data)
  194. }
  195. func renderUpdateUserPage(w http.ResponseWriter, user dataprovider.User, error string) {
  196. data := userPage{
  197. basePage: getBasePageData("Update user", fmt.Sprintf("%v/%v", webUserPath, user.ID)),
  198. IsAdd: false,
  199. Error: error,
  200. User: user,
  201. ValidPerms: dataprovider.ValidPerms,
  202. ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
  203. ValidProtocols: dataprovider.ValidProtocols,
  204. RootDirPerms: user.GetPermissionsForPath("/"),
  205. }
  206. renderTemplate(w, templateUser, data)
  207. }
  208. func renderAddFolderPage(w http.ResponseWriter, folder vfs.BaseVirtualFolder, error string) {
  209. data := folderPage{
  210. basePage: getBasePageData("Add a new folder", webFolderPath),
  211. Error: error,
  212. Folder: folder,
  213. }
  214. renderTemplate(w, templateFolder, data)
  215. }
  216. func getVirtualFoldersFromPostFields(r *http.Request) []vfs.VirtualFolder {
  217. var virtualFolders []vfs.VirtualFolder
  218. formValue := r.Form.Get("virtual_folders")
  219. for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
  220. if strings.Contains(cleaned, "::") {
  221. mapping := strings.Split(cleaned, "::")
  222. if len(mapping) > 1 {
  223. vfolder := vfs.VirtualFolder{
  224. BaseVirtualFolder: vfs.BaseVirtualFolder{
  225. MappedPath: strings.TrimSpace(mapping[1]),
  226. },
  227. VirtualPath: strings.TrimSpace(mapping[0]),
  228. QuotaFiles: -1,
  229. QuotaSize: -1,
  230. }
  231. if len(mapping) > 2 {
  232. quotaFiles, err := strconv.Atoi(strings.TrimSpace(mapping[2]))
  233. if err == nil {
  234. vfolder.QuotaFiles = quotaFiles
  235. }
  236. }
  237. if len(mapping) > 3 {
  238. quotaSize, err := strconv.ParseInt(strings.TrimSpace(mapping[3]), 10, 64)
  239. if err == nil {
  240. vfolder.QuotaSize = quotaSize
  241. }
  242. }
  243. virtualFolders = append(virtualFolders, vfolder)
  244. }
  245. }
  246. }
  247. return virtualFolders
  248. }
  249. func getUserPermissionsFromPostFields(r *http.Request) map[string][]string {
  250. permissions := make(map[string][]string)
  251. permissions["/"] = r.Form["permissions"]
  252. subDirsPermsValue := r.Form.Get("sub_dirs_permissions")
  253. for _, cleaned := range getSliceFromDelimitedValues(subDirsPermsValue, "\n") {
  254. if strings.Contains(cleaned, "::") {
  255. dirPerms := strings.Split(cleaned, "::")
  256. if len(dirPerms) > 1 {
  257. dir := dirPerms[0]
  258. dir = strings.TrimSpace(dir)
  259. perms := []string{}
  260. for _, p := range strings.Split(dirPerms[1], ",") {
  261. cleanedPerm := strings.TrimSpace(p)
  262. if len(cleanedPerm) > 0 {
  263. perms = append(perms, cleanedPerm)
  264. }
  265. }
  266. if len(dir) > 0 {
  267. permissions[dir] = perms
  268. }
  269. }
  270. }
  271. }
  272. return permissions
  273. }
  274. func getSliceFromDelimitedValues(values, delimiter string) []string {
  275. result := []string{}
  276. for _, v := range strings.Split(values, delimiter) {
  277. cleaned := strings.TrimSpace(v)
  278. if len(cleaned) > 0 {
  279. result = append(result, cleaned)
  280. }
  281. }
  282. return result
  283. }
  284. func getFileExtensionsFromPostField(value string, extesionsType int) []dataprovider.ExtensionsFilter {
  285. var result []dataprovider.ExtensionsFilter
  286. for _, cleaned := range getSliceFromDelimitedValues(value, "\n") {
  287. if strings.Contains(cleaned, "::") {
  288. dirExts := strings.Split(cleaned, "::")
  289. if len(dirExts) > 1 {
  290. dir := dirExts[0]
  291. dir = strings.TrimSpace(dir)
  292. exts := []string{}
  293. for _, e := range strings.Split(dirExts[1], ",") {
  294. cleanedExt := strings.TrimSpace(e)
  295. if len(cleanedExt) > 0 {
  296. exts = append(exts, cleanedExt)
  297. }
  298. }
  299. if len(dir) > 0 {
  300. filter := dataprovider.ExtensionsFilter{
  301. Path: dir,
  302. }
  303. if extesionsType == 1 {
  304. filter.AllowedExtensions = exts
  305. filter.DeniedExtensions = []string{}
  306. } else {
  307. filter.DeniedExtensions = exts
  308. filter.AllowedExtensions = []string{}
  309. }
  310. result = append(result, filter)
  311. }
  312. }
  313. }
  314. }
  315. return result
  316. }
  317. func getFiltersFromUserPostFields(r *http.Request) dataprovider.UserFilters {
  318. var filters dataprovider.UserFilters
  319. filters.AllowedIP = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  320. filters.DeniedIP = getSliceFromDelimitedValues(r.Form.Get("denied_ip"), ",")
  321. filters.DeniedLoginMethods = r.Form["ssh_login_methods"]
  322. filters.DeniedProtocols = r.Form["denied_protocols"]
  323. allowedExtensions := getFileExtensionsFromPostField(r.Form.Get("allowed_extensions"), 1)
  324. deniedExtensions := getFileExtensionsFromPostField(r.Form.Get("denied_extensions"), 2)
  325. extensions := []dataprovider.ExtensionsFilter{}
  326. if len(allowedExtensions) > 0 && len(deniedExtensions) > 0 {
  327. for _, allowed := range allowedExtensions {
  328. for _, denied := range deniedExtensions {
  329. if path.Clean(allowed.Path) == path.Clean(denied.Path) {
  330. allowed.DeniedExtensions = append(allowed.DeniedExtensions, denied.DeniedExtensions...)
  331. }
  332. }
  333. extensions = append(extensions, allowed)
  334. }
  335. for _, denied := range deniedExtensions {
  336. found := false
  337. for _, allowed := range allowedExtensions {
  338. if path.Clean(denied.Path) == path.Clean(allowed.Path) {
  339. found = true
  340. break
  341. }
  342. }
  343. if !found {
  344. extensions = append(extensions, denied)
  345. }
  346. }
  347. } else if len(allowedExtensions) > 0 {
  348. extensions = append(extensions, allowedExtensions...)
  349. } else if len(deniedExtensions) > 0 {
  350. extensions = append(extensions, deniedExtensions...)
  351. }
  352. filters.FileExtensions = extensions
  353. return filters
  354. }
  355. func getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, error) {
  356. var fs dataprovider.Filesystem
  357. provider, err := strconv.Atoi(r.Form.Get("fs_provider"))
  358. if err != nil {
  359. provider = int(dataprovider.LocalFilesystemProvider)
  360. }
  361. fs.Provider = dataprovider.FilesystemProvider(provider)
  362. if fs.Provider == dataprovider.S3FilesystemProvider {
  363. fs.S3Config.Bucket = r.Form.Get("s3_bucket")
  364. fs.S3Config.Region = r.Form.Get("s3_region")
  365. fs.S3Config.AccessKey = r.Form.Get("s3_access_key")
  366. fs.S3Config.AccessSecret = r.Form.Get("s3_access_secret")
  367. fs.S3Config.Endpoint = r.Form.Get("s3_endpoint")
  368. fs.S3Config.StorageClass = r.Form.Get("s3_storage_class")
  369. fs.S3Config.KeyPrefix = r.Form.Get("s3_key_prefix")
  370. fs.S3Config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
  371. if err != nil {
  372. return fs, err
  373. }
  374. fs.S3Config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
  375. if err != nil {
  376. return fs, err
  377. }
  378. } else if fs.Provider == dataprovider.GCSFilesystemProvider {
  379. fs.GCSConfig.Bucket = r.Form.Get("gcs_bucket")
  380. fs.GCSConfig.StorageClass = r.Form.Get("gcs_storage_class")
  381. fs.GCSConfig.KeyPrefix = r.Form.Get("gcs_key_prefix")
  382. autoCredentials := r.Form.Get("gcs_auto_credentials")
  383. if len(autoCredentials) > 0 {
  384. fs.GCSConfig.AutomaticCredentials = 1
  385. } else {
  386. fs.GCSConfig.AutomaticCredentials = 0
  387. }
  388. credentials, _, err := r.FormFile("gcs_credential_file")
  389. if err == http.ErrMissingFile {
  390. return fs, nil
  391. }
  392. if err != nil {
  393. return fs, err
  394. }
  395. defer credentials.Close()
  396. fileBytes, err := ioutil.ReadAll(credentials)
  397. if err != nil || len(fileBytes) == 0 {
  398. if len(fileBytes) == 0 {
  399. err = errors.New("credentials file size must be greater than 0")
  400. }
  401. return fs, err
  402. }
  403. fs.GCSConfig.Credentials = fileBytes
  404. fs.GCSConfig.AutomaticCredentials = 0
  405. }
  406. return fs, nil
  407. }
  408. func getUserFromPostFields(r *http.Request) (dataprovider.User, error) {
  409. var user dataprovider.User
  410. err := r.ParseMultipartForm(maxRequestSize)
  411. if err != nil {
  412. return user, err
  413. }
  414. publicKeysFormValue := r.Form.Get("public_keys")
  415. publicKeys := getSliceFromDelimitedValues(publicKeysFormValue, "\n")
  416. uid, err := strconv.Atoi(r.Form.Get("uid"))
  417. if err != nil {
  418. return user, err
  419. }
  420. gid, err := strconv.Atoi(r.Form.Get("gid"))
  421. if err != nil {
  422. return user, err
  423. }
  424. maxSessions, err := strconv.Atoi(r.Form.Get("max_sessions"))
  425. if err != nil {
  426. return user, err
  427. }
  428. quotaSize, err := strconv.ParseInt(r.Form.Get("quota_size"), 10, 64)
  429. if err != nil {
  430. return user, err
  431. }
  432. quotaFiles, err := strconv.Atoi(r.Form.Get("quota_files"))
  433. if err != nil {
  434. return user, err
  435. }
  436. bandwidthUL, err := strconv.ParseInt(r.Form.Get("upload_bandwidth"), 10, 64)
  437. if err != nil {
  438. return user, err
  439. }
  440. bandwidthDL, err := strconv.ParseInt(r.Form.Get("download_bandwidth"), 10, 64)
  441. if err != nil {
  442. return user, err
  443. }
  444. status, err := strconv.Atoi(r.Form.Get("status"))
  445. if err != nil {
  446. return user, err
  447. }
  448. expirationDateMillis := int64(0)
  449. expirationDateString := r.Form.Get("expiration_date")
  450. if len(strings.TrimSpace(expirationDateString)) > 0 {
  451. expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
  452. if err != nil {
  453. return user, err
  454. }
  455. expirationDateMillis = utils.GetTimeAsMsSinceEpoch(expirationDate)
  456. }
  457. fsConfig, err := getFsConfigFromUserPostFields(r)
  458. if err != nil {
  459. return user, err
  460. }
  461. user = dataprovider.User{
  462. Username: r.Form.Get("username"),
  463. Password: r.Form.Get("password"),
  464. PublicKeys: publicKeys,
  465. HomeDir: r.Form.Get("home_dir"),
  466. VirtualFolders: getVirtualFoldersFromPostFields(r),
  467. UID: uid,
  468. GID: gid,
  469. Permissions: getUserPermissionsFromPostFields(r),
  470. MaxSessions: maxSessions,
  471. QuotaSize: quotaSize,
  472. QuotaFiles: quotaFiles,
  473. UploadBandwidth: bandwidthUL,
  474. DownloadBandwidth: bandwidthDL,
  475. Status: status,
  476. ExpirationDate: expirationDateMillis,
  477. Filters: getFiltersFromUserPostFields(r),
  478. FsConfig: fsConfig,
  479. }
  480. maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
  481. user.Filters.MaxUploadFileSize = maxFileSize
  482. return user, err
  483. }
  484. func handleGetWebUsers(w http.ResponseWriter, r *http.Request) {
  485. limit := defaultQueryLimit
  486. if _, ok := r.URL.Query()["qlimit"]; ok {
  487. var err error
  488. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  489. if err != nil {
  490. limit = defaultQueryLimit
  491. }
  492. }
  493. users := make([]dataprovider.User, 0, limit)
  494. for {
  495. u, err := dataprovider.GetUsers(limit, len(users), dataprovider.OrderASC, "")
  496. if err != nil {
  497. renderInternalServerErrorPage(w, err)
  498. return
  499. }
  500. users = append(users, u...)
  501. if len(u) < limit {
  502. break
  503. }
  504. }
  505. data := usersPage{
  506. basePage: getBasePageData(pageUsersTitle, webUsersPath),
  507. Users: users,
  508. }
  509. renderTemplate(w, templateUsers, data)
  510. }
  511. func handleWebAddUserGet(w http.ResponseWriter, r *http.Request) {
  512. renderAddUserPage(w, dataprovider.User{Status: 1}, "")
  513. }
  514. func handleWebUpdateUserGet(w http.ResponseWriter, r *http.Request) {
  515. id, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  516. if err != nil {
  517. renderBadRequestPage(w, err)
  518. return
  519. }
  520. user, err := dataprovider.GetUserByID(id)
  521. if err == nil {
  522. renderUpdateUserPage(w, user, "")
  523. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  524. renderNotFoundPage(w, err)
  525. } else {
  526. renderInternalServerErrorPage(w, err)
  527. }
  528. }
  529. func handleWebAddUserPost(w http.ResponseWriter, r *http.Request) {
  530. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  531. user, err := getUserFromPostFields(r)
  532. if err != nil {
  533. renderAddUserPage(w, user, err.Error())
  534. return
  535. }
  536. err = dataprovider.AddUser(user)
  537. if err == nil {
  538. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  539. } else {
  540. renderAddUserPage(w, user, err.Error())
  541. }
  542. }
  543. func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
  544. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  545. id, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  546. if err != nil {
  547. renderBadRequestPage(w, err)
  548. return
  549. }
  550. user, err := dataprovider.GetUserByID(id)
  551. if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  552. renderNotFoundPage(w, err)
  553. return
  554. } else if err != nil {
  555. renderInternalServerErrorPage(w, err)
  556. return
  557. }
  558. updatedUser, err := getUserFromPostFields(r)
  559. if err != nil {
  560. renderUpdateUserPage(w, user, err.Error())
  561. return
  562. }
  563. updatedUser.ID = user.ID
  564. if len(updatedUser.Password) == 0 {
  565. updatedUser.Password = user.Password
  566. }
  567. err = dataprovider.UpdateUser(updatedUser)
  568. if err == nil {
  569. if len(r.Form.Get("disconnect")) > 0 {
  570. disconnectUser(user.Username)
  571. }
  572. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  573. } else {
  574. renderUpdateUserPage(w, user, err.Error())
  575. }
  576. }
  577. func handleWebGetConnections(w http.ResponseWriter, r *http.Request) {
  578. connectionStats := common.Connections.GetStats()
  579. data := connectionsPage{
  580. basePage: getBasePageData(pageConnectionsTitle, webConnectionsPath),
  581. Connections: connectionStats,
  582. }
  583. renderTemplate(w, templateConnections, data)
  584. }
  585. func handleWebAddFolderGet(w http.ResponseWriter, r *http.Request) {
  586. renderAddFolderPage(w, vfs.BaseVirtualFolder{}, "")
  587. }
  588. func handleWebAddFolderPost(w http.ResponseWriter, r *http.Request) {
  589. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  590. folder := vfs.BaseVirtualFolder{}
  591. err := r.ParseForm()
  592. if err != nil {
  593. renderAddFolderPage(w, folder, err.Error())
  594. return
  595. }
  596. folder.MappedPath = r.Form.Get("mapped_path")
  597. err = dataprovider.AddFolder(folder)
  598. if err == nil {
  599. http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
  600. } else {
  601. renderAddFolderPage(w, folder, err.Error())
  602. }
  603. }
  604. func handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
  605. limit := defaultQueryLimit
  606. if _, ok := r.URL.Query()["qlimit"]; ok {
  607. var err error
  608. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  609. if err != nil {
  610. limit = defaultQueryLimit
  611. }
  612. }
  613. folders := make([]vfs.BaseVirtualFolder, 0, limit)
  614. for {
  615. f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC, "")
  616. if err != nil {
  617. renderInternalServerErrorPage(w, err)
  618. return
  619. }
  620. folders = append(folders, f...)
  621. if len(f) < limit {
  622. break
  623. }
  624. }
  625. data := foldersPage{
  626. basePage: getBasePageData(pageFoldersTitle, webFoldersPath),
  627. Folders: folders,
  628. }
  629. renderTemplate(w, templateFolders, data)
  630. }