webclient.go 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package httpd
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "html/template"
  21. "io"
  22. "math"
  23. "net/http"
  24. "net/url"
  25. "os"
  26. "path"
  27. "path/filepath"
  28. "strconv"
  29. "strings"
  30. "time"
  31. "github.com/go-chi/render"
  32. "github.com/rs/xid"
  33. "github.com/sftpgo/sdk"
  34. "github.com/unrolled/secure"
  35. "github.com/drakkan/sftpgo/v2/internal/common"
  36. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  37. "github.com/drakkan/sftpgo/v2/internal/logger"
  38. "github.com/drakkan/sftpgo/v2/internal/mfa"
  39. "github.com/drakkan/sftpgo/v2/internal/smtp"
  40. "github.com/drakkan/sftpgo/v2/internal/util"
  41. "github.com/drakkan/sftpgo/v2/internal/version"
  42. "github.com/drakkan/sftpgo/v2/internal/vfs"
  43. )
  44. const (
  45. templateClientDir = "webclient"
  46. templateClientBase = "base.html"
  47. templateClientBaseLogin = "baselogin.html"
  48. templateClientLogin = "login.html"
  49. templateClientFiles = "files.html"
  50. templateClientMessage = "message.html"
  51. templateClientProfile = "profile.html"
  52. templateClientChangePwd = "changepassword.html"
  53. templateClientTwoFactor = "twofactor.html"
  54. templateClientTwoFactorRecovery = "twofactor-recovery.html"
  55. templateClientMFA = "mfa.html"
  56. templateClientEditFile = "editfile.html"
  57. templateClientShare = "share.html"
  58. templateClientShares = "shares.html"
  59. templateClientViewPDF = "viewpdf.html"
  60. templateShareLogin = "sharelogin.html"
  61. templateShareDownload = "sharedownload.html"
  62. templateUploadToShare = "shareupload.html"
  63. pageClientFilesTitle = "Files"
  64. pageClientSharesTitle = "Shares"
  65. pageClientProfileTitle = "My Profile"
  66. pageClientChangePwdTitle = "Change password"
  67. pageClient2FATitle = "Two-factor auth"
  68. pageClientEditFileTitle = "Edit file"
  69. pageClientForgotPwdTitle = "SFTPGo WebClient - Forgot password"
  70. pageClientResetPwdTitle = "SFTPGo WebClient - Reset password"
  71. pageExtShareTitle = "Shared files"
  72. pageUploadToShareTitle = "Upload to share"
  73. pageDownloadFromShareTitle = "Download shared file"
  74. )
  75. // condResult is the result of an HTTP request precondition check.
  76. // See https://tools.ietf.org/html/rfc7232 section 3.
  77. type condResult int
  78. const (
  79. condNone condResult = iota
  80. condTrue
  81. condFalse
  82. )
  83. var (
  84. clientTemplates = make(map[string]*template.Template)
  85. unixEpochTime = time.Unix(0, 0)
  86. )
  87. // isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
  88. func isZeroTime(t time.Time) bool {
  89. return t.IsZero() || t.Equal(unixEpochTime)
  90. }
  91. type baseClientPage struct {
  92. Title string
  93. CurrentURL string
  94. FilesURL string
  95. SharesURL string
  96. ShareURL string
  97. ProfileURL string
  98. PingURL string
  99. ChangePwdURL string
  100. StaticURL string
  101. LogoutURL string
  102. LoginURL string
  103. EditURL string
  104. MFAURL string
  105. MFATitle string
  106. FilesTitle string
  107. SharesTitle string
  108. ProfileTitle string
  109. Version string
  110. CSRFToken string
  111. CSPNonce string
  112. LoggedUser *dataprovider.User
  113. Branding UIBranding
  114. }
  115. type dirMapping struct {
  116. DirName string
  117. Href string
  118. }
  119. type viewPDFPage struct {
  120. Title string
  121. URL string
  122. StaticURL string
  123. CSPNonce string
  124. Branding UIBranding
  125. }
  126. type editFilePage struct {
  127. baseClientPage
  128. CurrentDir string
  129. FileURL string
  130. Path string
  131. Name string
  132. ReadOnly bool
  133. Data string
  134. }
  135. type filesPage struct {
  136. baseClientPage
  137. CurrentDir string
  138. DirsURL string
  139. FileActionsURL string
  140. DownloadURL string
  141. ViewPDFURL string
  142. FileURL string
  143. CanAddFiles bool
  144. CanCreateDirs bool
  145. CanRename bool
  146. CanDelete bool
  147. CanDownload bool
  148. CanShare bool
  149. ShareUploadBaseURL string
  150. Error string
  151. Paths []dirMapping
  152. QuotaUsage *userQuotaUsage
  153. }
  154. type shareLoginPage struct {
  155. CurrentURL string
  156. Version string
  157. Error string
  158. CSRFToken string
  159. CSPNonce string
  160. StaticURL string
  161. Branding UIBranding
  162. }
  163. type shareDownloadPage struct {
  164. baseClientPage
  165. DownloadLink string
  166. }
  167. type shareUploadPage struct {
  168. baseClientPage
  169. Share *dataprovider.Share
  170. UploadBasePath string
  171. }
  172. type clientMessagePage struct {
  173. baseClientPage
  174. Error string
  175. Success string
  176. }
  177. type clientProfilePage struct {
  178. baseClientPage
  179. PublicKeys []string
  180. CanSubmit bool
  181. AllowAPIKeyAuth bool
  182. Email string
  183. Description string
  184. Error string
  185. }
  186. type changeClientPasswordPage struct {
  187. baseClientPage
  188. Error string
  189. }
  190. type clientMFAPage struct {
  191. baseClientPage
  192. TOTPConfigs []string
  193. TOTPConfig dataprovider.UserTOTPConfig
  194. GenerateTOTPURL string
  195. ValidateTOTPURL string
  196. SaveTOTPURL string
  197. RecCodesURL string
  198. Protocols []string
  199. }
  200. type clientSharesPage struct {
  201. baseClientPage
  202. Shares []dataprovider.Share
  203. BasePublicSharesURL string
  204. }
  205. type clientSharePage struct {
  206. baseClientPage
  207. Share *dataprovider.Share
  208. Error string
  209. IsAdd bool
  210. }
  211. type userQuotaUsage struct {
  212. QuotaSize int64
  213. QuotaFiles int
  214. UsedQuotaSize int64
  215. UsedQuotaFiles int
  216. UploadDataTransfer int64
  217. DownloadDataTransfer int64
  218. TotalDataTransfer int64
  219. UsedUploadDataTransfer int64
  220. UsedDownloadDataTransfer int64
  221. }
  222. func (u *userQuotaUsage) HasQuotaInfo() bool {
  223. if dataprovider.GetQuotaTracking() == 0 {
  224. return false
  225. }
  226. if u.HasDiskQuota() {
  227. return true
  228. }
  229. return u.HasTranferQuota()
  230. }
  231. func (u *userQuotaUsage) HasDiskQuota() bool {
  232. if u.QuotaSize > 0 || u.UsedQuotaSize > 0 {
  233. return true
  234. }
  235. return u.QuotaFiles > 0 || u.UsedQuotaFiles > 0
  236. }
  237. func (u *userQuotaUsage) HasTranferQuota() bool {
  238. if u.TotalDataTransfer > 0 || u.UploadDataTransfer > 0 || u.DownloadDataTransfer > 0 {
  239. return true
  240. }
  241. return u.UsedDownloadDataTransfer > 0 || u.UsedUploadDataTransfer > 0
  242. }
  243. func (u *userQuotaUsage) GetQuotaSize() string {
  244. if u.QuotaSize > 0 {
  245. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedQuotaSize), util.ByteCountIEC(u.QuotaSize))
  246. }
  247. if u.UsedQuotaSize > 0 {
  248. return util.ByteCountIEC(u.UsedQuotaSize)
  249. }
  250. return ""
  251. }
  252. func (u *userQuotaUsage) GetQuotaFiles() string {
  253. if u.QuotaFiles > 0 {
  254. return fmt.Sprintf("%d/%d", u.UsedQuotaFiles, u.QuotaFiles)
  255. }
  256. if u.UsedQuotaFiles > 0 {
  257. return strconv.FormatInt(int64(u.UsedQuotaFiles), 10)
  258. }
  259. return ""
  260. }
  261. func (u *userQuotaUsage) GetQuotaSizePercentage() int {
  262. if u.QuotaSize > 0 {
  263. return int(math.Round(100 * float64(u.UsedQuotaSize) / float64(u.QuotaSize)))
  264. }
  265. return 0
  266. }
  267. func (u *userQuotaUsage) GetQuotaFilesPercentage() int {
  268. if u.QuotaFiles > 0 {
  269. return int(math.Round(100 * float64(u.UsedQuotaFiles) / float64(u.QuotaFiles)))
  270. }
  271. return 0
  272. }
  273. func (u *userQuotaUsage) IsQuotaSizeLow() bool {
  274. return u.GetQuotaSizePercentage() > 85
  275. }
  276. func (u *userQuotaUsage) IsQuotaFilesLow() bool {
  277. return u.GetQuotaFilesPercentage() > 85
  278. }
  279. func (u *userQuotaUsage) IsDiskQuotaLow() bool {
  280. return u.IsQuotaSizeLow() || u.IsQuotaFilesLow()
  281. }
  282. func (u *userQuotaUsage) GetTotalTransferQuota() string {
  283. total := u.UsedUploadDataTransfer + u.UsedDownloadDataTransfer
  284. if u.TotalDataTransfer > 0 {
  285. return fmt.Sprintf("%s/%s", util.ByteCountIEC(total), util.ByteCountIEC(u.TotalDataTransfer*1048576))
  286. }
  287. if total > 0 {
  288. return util.ByteCountIEC(total)
  289. }
  290. return ""
  291. }
  292. func (u *userQuotaUsage) GetUploadTransferQuota() string {
  293. if u.UploadDataTransfer > 0 {
  294. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedUploadDataTransfer),
  295. util.ByteCountIEC(u.UploadDataTransfer*1048576))
  296. }
  297. if u.UsedUploadDataTransfer > 0 {
  298. return util.ByteCountIEC(u.UsedUploadDataTransfer)
  299. }
  300. return ""
  301. }
  302. func (u *userQuotaUsage) GetDownloadTransferQuota() string {
  303. if u.DownloadDataTransfer > 0 {
  304. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedDownloadDataTransfer),
  305. util.ByteCountIEC(u.DownloadDataTransfer*1048576))
  306. }
  307. if u.UsedDownloadDataTransfer > 0 {
  308. return util.ByteCountIEC(u.UsedDownloadDataTransfer)
  309. }
  310. return ""
  311. }
  312. func (u *userQuotaUsage) GetTotalTransferQuotaPercentage() int {
  313. if u.TotalDataTransfer > 0 {
  314. return int(math.Round(100 * float64(u.UsedDownloadDataTransfer+u.UsedUploadDataTransfer) / float64(u.TotalDataTransfer*1048576)))
  315. }
  316. return 0
  317. }
  318. func (u *userQuotaUsage) GetUploadTransferQuotaPercentage() int {
  319. if u.UploadDataTransfer > 0 {
  320. return int(math.Round(100 * float64(u.UsedUploadDataTransfer) / float64(u.UploadDataTransfer*1048576)))
  321. }
  322. return 0
  323. }
  324. func (u *userQuotaUsage) GetDownloadTransferQuotaPercentage() int {
  325. if u.DownloadDataTransfer > 0 {
  326. return int(math.Round(100 * float64(u.UsedDownloadDataTransfer) / float64(u.DownloadDataTransfer*1048576)))
  327. }
  328. return 0
  329. }
  330. func (u *userQuotaUsage) IsTotalTransferQuotaLow() bool {
  331. if u.TotalDataTransfer > 0 {
  332. return u.GetTotalTransferQuotaPercentage() > 85
  333. }
  334. return false
  335. }
  336. func (u *userQuotaUsage) IsUploadTransferQuotaLow() bool {
  337. if u.UploadDataTransfer > 0 {
  338. return u.GetUploadTransferQuotaPercentage() > 85
  339. }
  340. return false
  341. }
  342. func (u *userQuotaUsage) IsDownloadTransferQuotaLow() bool {
  343. if u.DownloadDataTransfer > 0 {
  344. return u.GetDownloadTransferQuotaPercentage() > 85
  345. }
  346. return false
  347. }
  348. func (u *userQuotaUsage) IsTransferQuotaLow() bool {
  349. return u.IsTotalTransferQuotaLow() || u.IsUploadTransferQuotaLow() || u.IsDownloadTransferQuotaLow()
  350. }
  351. func (u *userQuotaUsage) IsQuotaLow() bool {
  352. return u.IsDiskQuotaLow() || u.IsTransferQuotaLow()
  353. }
  354. func newUserQuotaUsage(u *dataprovider.User) *userQuotaUsage {
  355. return &userQuotaUsage{
  356. QuotaSize: u.QuotaSize,
  357. QuotaFiles: u.QuotaFiles,
  358. UsedQuotaSize: u.UsedQuotaSize,
  359. UsedQuotaFiles: u.UsedQuotaFiles,
  360. TotalDataTransfer: u.TotalDataTransfer,
  361. UploadDataTransfer: u.UploadDataTransfer,
  362. DownloadDataTransfer: u.DownloadDataTransfer,
  363. UsedUploadDataTransfer: u.UsedUploadDataTransfer,
  364. UsedDownloadDataTransfer: u.UsedDownloadDataTransfer,
  365. }
  366. }
  367. func getFileObjectURL(baseDir, name, baseWebPath string) string {
  368. return fmt.Sprintf("%v?path=%v&_=%v", baseWebPath, url.QueryEscape(path.Join(baseDir, name)), time.Now().UTC().Unix())
  369. }
  370. func getFileObjectModTime(t time.Time) string {
  371. if isZeroTime(t) {
  372. return ""
  373. }
  374. return t.Format("2006-01-02 15:04")
  375. }
  376. func loadClientTemplates(templatesPath string) {
  377. filesPaths := []string{
  378. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  379. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  380. filepath.Join(templatesPath, templateClientDir, templateClientFiles),
  381. }
  382. editFilePath := []string{
  383. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  384. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  385. filepath.Join(templatesPath, templateClientDir, templateClientEditFile),
  386. }
  387. sharesPaths := []string{
  388. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  389. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  390. filepath.Join(templatesPath, templateClientDir, templateClientShares),
  391. }
  392. sharePaths := []string{
  393. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  394. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  395. filepath.Join(templatesPath, templateClientDir, templateClientShare),
  396. }
  397. profilePaths := []string{
  398. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  399. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  400. filepath.Join(templatesPath, templateClientDir, templateClientProfile),
  401. }
  402. changePwdPaths := []string{
  403. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  404. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  405. filepath.Join(templatesPath, templateClientDir, templateClientChangePwd),
  406. }
  407. loginPath := []string{
  408. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  409. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  410. filepath.Join(templatesPath, templateClientDir, templateClientLogin),
  411. }
  412. messagePath := []string{
  413. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  414. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  415. filepath.Join(templatesPath, templateClientDir, templateClientMessage),
  416. }
  417. mfaPath := []string{
  418. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  419. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  420. filepath.Join(templatesPath, templateClientDir, templateClientMFA),
  421. }
  422. twoFactorPath := []string{
  423. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  424. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  425. filepath.Join(templatesPath, templateClientDir, templateClientTwoFactor),
  426. }
  427. twoFactorRecoveryPath := []string{
  428. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  429. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  430. filepath.Join(templatesPath, templateClientDir, templateClientTwoFactorRecovery),
  431. }
  432. forgotPwdPaths := []string{
  433. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  434. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  435. filepath.Join(templatesPath, templateClientDir, templateForgotPassword),
  436. }
  437. resetPwdPaths := []string{
  438. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  439. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  440. filepath.Join(templatesPath, templateClientDir, templateResetPassword),
  441. }
  442. viewPDFPaths := []string{
  443. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  444. filepath.Join(templatesPath, templateClientDir, templateClientViewPDF),
  445. }
  446. shareLoginPath := []string{
  447. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  448. filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
  449. filepath.Join(templatesPath, templateClientDir, templateShareLogin),
  450. }
  451. shareUploadPath := []string{
  452. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  453. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  454. filepath.Join(templatesPath, templateClientDir, templateUploadToShare),
  455. }
  456. shareDownloadPath := []string{
  457. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  458. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  459. filepath.Join(templatesPath, templateClientDir, templateShareDownload),
  460. }
  461. filesTmpl := util.LoadTemplate(nil, filesPaths...)
  462. profileTmpl := util.LoadTemplate(nil, profilePaths...)
  463. changePwdTmpl := util.LoadTemplate(nil, changePwdPaths...)
  464. loginTmpl := util.LoadTemplate(nil, loginPath...)
  465. messageTmpl := util.LoadTemplate(nil, messagePath...)
  466. mfaTmpl := util.LoadTemplate(nil, mfaPath...)
  467. twoFactorTmpl := util.LoadTemplate(nil, twoFactorPath...)
  468. twoFactorRecoveryTmpl := util.LoadTemplate(nil, twoFactorRecoveryPath...)
  469. editFileTmpl := util.LoadTemplate(nil, editFilePath...)
  470. shareLoginTmpl := util.LoadTemplate(nil, shareLoginPath...)
  471. sharesTmpl := util.LoadTemplate(nil, sharesPaths...)
  472. shareTmpl := util.LoadTemplate(nil, sharePaths...)
  473. forgotPwdTmpl := util.LoadTemplate(nil, forgotPwdPaths...)
  474. resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
  475. viewPDFTmpl := util.LoadTemplate(nil, viewPDFPaths...)
  476. shareUploadTmpl := util.LoadTemplate(nil, shareUploadPath...)
  477. shareDownloadTmpl := util.LoadTemplate(nil, shareDownloadPath...)
  478. clientTemplates[templateClientFiles] = filesTmpl
  479. clientTemplates[templateClientProfile] = profileTmpl
  480. clientTemplates[templateClientChangePwd] = changePwdTmpl
  481. clientTemplates[templateClientLogin] = loginTmpl
  482. clientTemplates[templateClientMessage] = messageTmpl
  483. clientTemplates[templateClientMFA] = mfaTmpl
  484. clientTemplates[templateClientTwoFactor] = twoFactorTmpl
  485. clientTemplates[templateClientTwoFactorRecovery] = twoFactorRecoveryTmpl
  486. clientTemplates[templateClientEditFile] = editFileTmpl
  487. clientTemplates[templateClientShares] = sharesTmpl
  488. clientTemplates[templateClientShare] = shareTmpl
  489. clientTemplates[templateForgotPassword] = forgotPwdTmpl
  490. clientTemplates[templateResetPassword] = resetPwdTmpl
  491. clientTemplates[templateClientViewPDF] = viewPDFTmpl
  492. clientTemplates[templateShareLogin] = shareLoginTmpl
  493. clientTemplates[templateUploadToShare] = shareUploadTmpl
  494. clientTemplates[templateShareDownload] = shareDownloadTmpl
  495. }
  496. func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Request) baseClientPage {
  497. var csrfToken string
  498. if currentURL != "" {
  499. csrfToken = createCSRFToken(util.GetIPFromRemoteAddress(r.RemoteAddr))
  500. }
  501. v := version.Get()
  502. data := baseClientPage{
  503. Title: title,
  504. CurrentURL: currentURL,
  505. FilesURL: webClientFilesPath,
  506. SharesURL: webClientSharesPath,
  507. ShareURL: webClientSharePath,
  508. ProfileURL: webClientProfilePath,
  509. PingURL: webClientPingPath,
  510. ChangePwdURL: webChangeClientPwdPath,
  511. StaticURL: webStaticFilesPath,
  512. LogoutURL: webClientLogoutPath,
  513. EditURL: webClientEditFilePath,
  514. MFAURL: webClientMFAPath,
  515. MFATitle: pageClient2FATitle,
  516. FilesTitle: pageClientFilesTitle,
  517. SharesTitle: pageClientSharesTitle,
  518. ProfileTitle: pageClientProfileTitle,
  519. Version: fmt.Sprintf("%v-%v", v.Version, v.CommitHash),
  520. CSRFToken: csrfToken,
  521. CSPNonce: secure.CSPNonce(r.Context()),
  522. LoggedUser: getUserFromToken(r),
  523. Branding: s.binding.Branding.WebClient,
  524. }
  525. if !strings.HasPrefix(r.RequestURI, webClientPubSharesPath) {
  526. data.LoginURL = webClientLoginPath
  527. }
  528. return data
  529. }
  530. func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
  531. data := forgotPwdPage{
  532. CurrentURL: webClientForgotPwdPath,
  533. Error: error,
  534. CSRFToken: createCSRFToken(ip),
  535. CSPNonce: secure.CSPNonce(r.Context()),
  536. StaticURL: webStaticFilesPath,
  537. LoginURL: webClientLoginPath,
  538. Title: pageClientForgotPwdTitle,
  539. Branding: s.binding.Branding.WebClient,
  540. }
  541. renderClientTemplate(w, templateForgotPassword, data)
  542. }
  543. func (s *httpdServer) renderClientResetPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
  544. data := resetPwdPage{
  545. CurrentURL: webClientResetPwdPath,
  546. Error: error,
  547. CSRFToken: createCSRFToken(ip),
  548. CSPNonce: secure.CSPNonce(r.Context()),
  549. StaticURL: webStaticFilesPath,
  550. LoginURL: webClientLoginPath,
  551. Title: pageClientResetPwdTitle,
  552. Branding: s.binding.Branding.WebClient,
  553. }
  554. renderClientTemplate(w, templateResetPassword, data)
  555. }
  556. func (s *httpdServer) renderShareLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) {
  557. data := shareLoginPage{
  558. CurrentURL: r.RequestURI,
  559. Version: version.Get().Version,
  560. Error: error,
  561. CSRFToken: createCSRFToken(ip),
  562. CSPNonce: secure.CSPNonce(r.Context()),
  563. StaticURL: webStaticFilesPath,
  564. Branding: s.binding.Branding.WebClient,
  565. }
  566. renderClientTemplate(w, templateShareLogin, data)
  567. }
  568. func renderClientTemplate(w http.ResponseWriter, tmplName string, data any) {
  569. err := clientTemplates[tmplName].ExecuteTemplate(w, tmplName, data)
  570. if err != nil {
  571. http.Error(w, err.Error(), http.StatusInternalServerError)
  572. }
  573. }
  574. func (s *httpdServer) renderClientMessagePage(w http.ResponseWriter, r *http.Request, title, body string, statusCode int, err error, message string) {
  575. var errorString strings.Builder
  576. if body != "" {
  577. errorString.WriteString(body)
  578. errorString.WriteString(" ")
  579. }
  580. if err != nil {
  581. errorString.WriteString(err.Error())
  582. }
  583. data := clientMessagePage{
  584. baseClientPage: s.getBaseClientPageData(title, "", r),
  585. Error: errorString.String(),
  586. Success: message,
  587. }
  588. w.WriteHeader(statusCode)
  589. renderClientTemplate(w, templateClientMessage, data)
  590. }
  591. func (s *httpdServer) renderClientInternalServerErrorPage(w http.ResponseWriter, r *http.Request, err error) {
  592. s.renderClientMessagePage(w, r, page500Title, page500Body, http.StatusInternalServerError, err, "")
  593. }
  594. func (s *httpdServer) renderClientBadRequestPage(w http.ResponseWriter, r *http.Request, err error) {
  595. s.renderClientMessagePage(w, r, page400Title, "", http.StatusBadRequest, err, "")
  596. }
  597. func (s *httpdServer) renderClientForbiddenPage(w http.ResponseWriter, r *http.Request, body string) {
  598. s.renderClientMessagePage(w, r, page403Title, "", http.StatusForbidden, nil, body)
  599. }
  600. func (s *httpdServer) renderClientNotFoundPage(w http.ResponseWriter, r *http.Request, err error) {
  601. s.renderClientMessagePage(w, r, page404Title, page404Body, http.StatusNotFound, err, "")
  602. }
  603. func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.Request, error, ip string) {
  604. data := twoFactorPage{
  605. CurrentURL: webClientTwoFactorPath,
  606. Version: version.Get().Version,
  607. Error: error,
  608. CSRFToken: createCSRFToken(ip),
  609. CSPNonce: secure.CSPNonce(r.Context()),
  610. StaticURL: webStaticFilesPath,
  611. RecoveryURL: webClientTwoFactorRecoveryPath,
  612. Branding: s.binding.Branding.WebClient,
  613. }
  614. if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) {
  615. data.CurrentURL += "?next=" + url.QueryEscape(next)
  616. }
  617. renderClientTemplate(w, templateTwoFactor, data)
  618. }
  619. func (s *httpdServer) renderClientTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, error, ip string) {
  620. data := twoFactorPage{
  621. CurrentURL: webClientTwoFactorRecoveryPath,
  622. Version: version.Get().Version,
  623. Error: error,
  624. CSRFToken: createCSRFToken(ip),
  625. CSPNonce: secure.CSPNonce(r.Context()),
  626. StaticURL: webStaticFilesPath,
  627. Branding: s.binding.Branding.WebClient,
  628. }
  629. renderClientTemplate(w, templateTwoFactorRecovery, data)
  630. }
  631. func (s *httpdServer) renderClientMFAPage(w http.ResponseWriter, r *http.Request) {
  632. data := clientMFAPage{
  633. baseClientPage: s.getBaseClientPageData(pageMFATitle, webClientMFAPath, r),
  634. TOTPConfigs: mfa.GetAvailableTOTPConfigNames(),
  635. GenerateTOTPURL: webClientTOTPGeneratePath,
  636. ValidateTOTPURL: webClientTOTPValidatePath,
  637. SaveTOTPURL: webClientTOTPSavePath,
  638. RecCodesURL: webClientRecoveryCodesPath,
  639. Protocols: dataprovider.MFAProtocols,
  640. }
  641. user, err := dataprovider.UserExists(data.LoggedUser.Username, "")
  642. if err != nil {
  643. s.renderInternalServerErrorPage(w, r, err)
  644. return
  645. }
  646. data.TOTPConfig = user.Filters.TOTPConfig
  647. renderClientTemplate(w, templateClientMFA, data)
  648. }
  649. func (s *httpdServer) renderEditFilePage(w http.ResponseWriter, r *http.Request, fileName, fileData string, readOnly bool) {
  650. data := editFilePage{
  651. baseClientPage: s.getBaseClientPageData(pageClientEditFileTitle, webClientEditFilePath, r),
  652. Path: fileName,
  653. Name: path.Base(fileName),
  654. CurrentDir: path.Dir(fileName),
  655. FileURL: webClientFilePath,
  656. ReadOnly: readOnly,
  657. Data: fileData,
  658. }
  659. renderClientTemplate(w, templateClientEditFile, data)
  660. }
  661. func (s *httpdServer) renderAddUpdateSharePage(w http.ResponseWriter, r *http.Request, share *dataprovider.Share,
  662. error string, isAdd bool) {
  663. currentURL := webClientSharePath
  664. title := "Add a new share"
  665. if !isAdd {
  666. currentURL = fmt.Sprintf("%v/%v", webClientSharePath, url.PathEscape(share.ShareID))
  667. title = "Update share"
  668. }
  669. data := clientSharePage{
  670. baseClientPage: s.getBaseClientPageData(title, currentURL, r),
  671. Share: share,
  672. Error: error,
  673. IsAdd: isAdd,
  674. }
  675. renderClientTemplate(w, templateClientShare, data)
  676. }
  677. func getDirMapping(dirName, baseWebPath string) []dirMapping {
  678. paths := []dirMapping{}
  679. if dirName != "/" {
  680. paths = append(paths, dirMapping{
  681. DirName: path.Base(dirName),
  682. Href: getFileObjectURL("/", dirName, baseWebPath),
  683. })
  684. for {
  685. dirName = path.Dir(dirName)
  686. if dirName == "/" || dirName == "." {
  687. break
  688. }
  689. paths = append([]dirMapping{{
  690. DirName: path.Base(dirName),
  691. Href: getFileObjectURL("/", dirName, baseWebPath)},
  692. }, paths...)
  693. }
  694. }
  695. return paths
  696. }
  697. func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Request, dirName, error string,
  698. share dataprovider.Share,
  699. ) {
  700. currentURL := path.Join(webClientPubSharesPath, share.ShareID, "browse")
  701. baseData := s.getBaseClientPageData(pageExtShareTitle, currentURL, r)
  702. baseData.FilesURL = currentURL
  703. data := filesPage{
  704. baseClientPage: baseData,
  705. Error: error,
  706. CurrentDir: url.QueryEscape(dirName),
  707. DownloadURL: path.Join(webClientPubSharesPath, share.ShareID, "partial"),
  708. ShareUploadBaseURL: path.Join(webClientPubSharesPath, share.ShareID, url.PathEscape(dirName)),
  709. ViewPDFURL: path.Join(webClientPubSharesPath, share.ShareID, "viewpdf"),
  710. DirsURL: path.Join(webClientPubSharesPath, share.ShareID, "dirs"),
  711. FileURL: "",
  712. FileActionsURL: "",
  713. CanAddFiles: share.Scope == dataprovider.ShareScopeReadWrite,
  714. CanCreateDirs: false,
  715. CanRename: false,
  716. CanDelete: false,
  717. CanDownload: share.Scope != dataprovider.ShareScopeWrite,
  718. CanShare: false,
  719. Paths: getDirMapping(dirName, currentURL),
  720. QuotaUsage: newUserQuotaUsage(&dataprovider.User{}),
  721. }
  722. renderClientTemplate(w, templateClientFiles, data)
  723. }
  724. func (s *httpdServer) renderShareDownloadPage(w http.ResponseWriter, r *http.Request, downloadLink string) {
  725. data := shareDownloadPage{
  726. baseClientPage: s.getBaseClientPageData(pageDownloadFromShareTitle, "", r),
  727. DownloadLink: downloadLink,
  728. }
  729. renderClientTemplate(w, templateShareDownload, data)
  730. }
  731. func (s *httpdServer) renderUploadToSharePage(w http.ResponseWriter, r *http.Request, share dataprovider.Share) {
  732. currentURL := path.Join(webClientPubSharesPath, share.ShareID, "upload")
  733. data := shareUploadPage{
  734. baseClientPage: s.getBaseClientPageData(pageUploadToShareTitle, currentURL, r),
  735. Share: &share,
  736. UploadBasePath: path.Join(webClientPubSharesPath, share.ShareID),
  737. }
  738. renderClientTemplate(w, templateUploadToShare, data)
  739. }
  740. func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, dirName, error string, user *dataprovider.User) {
  741. data := filesPage{
  742. baseClientPage: s.getBaseClientPageData(pageClientFilesTitle, webClientFilesPath, r),
  743. Error: error,
  744. CurrentDir: url.QueryEscape(dirName),
  745. DownloadURL: webClientDownloadZipPath,
  746. ViewPDFURL: webClientViewPDFPath,
  747. DirsURL: webClientDirsPath,
  748. FileURL: webClientFilePath,
  749. FileActionsURL: webClientFileActionsPath,
  750. CanAddFiles: user.CanAddFilesFromWeb(dirName),
  751. CanCreateDirs: user.CanAddDirsFromWeb(dirName),
  752. CanRename: user.CanRenameFromWeb(dirName, dirName),
  753. CanDelete: user.CanDeleteFromWeb(dirName),
  754. CanDownload: user.HasPerm(dataprovider.PermDownload, dirName),
  755. CanShare: user.CanManageShares(),
  756. ShareUploadBaseURL: "",
  757. Paths: getDirMapping(dirName, webClientFilesPath),
  758. QuotaUsage: newUserQuotaUsage(user),
  759. }
  760. renderClientTemplate(w, templateClientFiles, data)
  761. }
  762. func (s *httpdServer) renderClientProfilePage(w http.ResponseWriter, r *http.Request, error string) {
  763. data := clientProfilePage{
  764. baseClientPage: s.getBaseClientPageData(pageClientProfileTitle, webClientProfilePath, r),
  765. Error: error,
  766. }
  767. user, userMerged, err := dataprovider.GetUserVariants(data.LoggedUser.Username, "")
  768. if err != nil {
  769. s.renderClientInternalServerErrorPage(w, r, err)
  770. return
  771. }
  772. data.PublicKeys = user.PublicKeys
  773. data.AllowAPIKeyAuth = user.Filters.AllowAPIKeyAuth
  774. data.Email = user.Email
  775. data.Description = user.Description
  776. data.CanSubmit = userMerged.CanChangeAPIKeyAuth() || userMerged.CanManagePublicKeys() || userMerged.CanChangeInfo()
  777. renderClientTemplate(w, templateClientProfile, data)
  778. }
  779. func (s *httpdServer) renderClientChangePasswordPage(w http.ResponseWriter, r *http.Request, error string) {
  780. data := changeClientPasswordPage{
  781. baseClientPage: s.getBaseClientPageData(pageClientChangePwdTitle, webChangeClientPwdPath, r),
  782. Error: error,
  783. }
  784. renderClientTemplate(w, templateClientChangePwd, data)
  785. }
  786. func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.Request) {
  787. r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
  788. claims, err := getTokenClaims(r)
  789. if err != nil || claims.Username == "" {
  790. s.renderClientMessagePage(w, r, "Invalid token claims", "", http.StatusForbidden, nil, "")
  791. return
  792. }
  793. if err := r.ParseForm(); err != nil {
  794. s.renderClientMessagePage(w, r, "Invalid request", err.Error(), getRespStatus(err), nil, "")
  795. return
  796. }
  797. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  798. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  799. s.renderClientForbiddenPage(w, r, err.Error())
  800. return
  801. }
  802. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  803. if err != nil {
  804. s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
  805. return
  806. }
  807. connID := xid.New().String()
  808. protocol := getProtocolFromRequest(r)
  809. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  810. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  811. s.renderClientForbiddenPage(w, r, err.Error())
  812. return
  813. }
  814. connection := &Connection{
  815. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  816. r.RemoteAddr, user),
  817. request: r,
  818. }
  819. if err = common.Connections.Add(connection); err != nil {
  820. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  821. return
  822. }
  823. defer common.Connections.Remove(connection.GetID())
  824. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  825. files := r.Form.Get("files")
  826. var filesList []string
  827. err = json.Unmarshal([]byte(files), &filesList)
  828. if err != nil {
  829. s.renderClientMessagePage(w, r, "Unable to get files list", "", http.StatusInternalServerError, err, "")
  830. return
  831. }
  832. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"",
  833. getCompressedFileName(connection.GetUsername(), filesList)))
  834. renderCompressedFiles(w, connection, name, filesList, nil)
  835. }
  836. func (s *httpdServer) handleClientSharePartialDownload(w http.ResponseWriter, r *http.Request) {
  837. r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
  838. if err := r.ParseForm(); err != nil {
  839. s.renderClientMessagePage(w, r, "Invalid request", err.Error(), getRespStatus(err), nil, "")
  840. return
  841. }
  842. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  843. share, connection, err := s.checkPublicShare(w, r, validScopes)
  844. if err != nil {
  845. return
  846. }
  847. if err := validateBrowsableShare(share, connection); err != nil {
  848. s.renderClientMessagePage(w, r, "Unable to validate share", "", getRespStatus(err), err, "")
  849. return
  850. }
  851. name, err := getBrowsableSharedPath(share, r)
  852. if err != nil {
  853. s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
  854. return
  855. }
  856. if err = common.Connections.Add(connection); err != nil {
  857. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  858. return
  859. }
  860. defer common.Connections.Remove(connection.GetID())
  861. transferQuota := connection.GetTransferQuota()
  862. if !transferQuota.HasDownloadSpace() {
  863. err = connection.GetReadQuotaExceededError()
  864. connection.Log(logger.LevelInfo, "denying share read due to quota limits")
  865. s.renderClientMessagePage(w, r, "Denying share read due to quota limits", "", getMappedStatusCode(err), err, "")
  866. return
  867. }
  868. files := r.Form.Get("files")
  869. var filesList []string
  870. err = json.Unmarshal([]byte(files), &filesList)
  871. if err != nil {
  872. s.renderClientMessagePage(w, r, "Unable to get files list", "", http.StatusInternalServerError, err, "")
  873. return
  874. }
  875. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  876. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"",
  877. getCompressedFileName(fmt.Sprintf("share-%s", share.Name), filesList)))
  878. renderCompressedFiles(w, connection, name, filesList, &share)
  879. }
  880. func (s *httpdServer) handleShareGetDirContents(w http.ResponseWriter, r *http.Request) {
  881. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  882. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  883. share, connection, err := s.checkPublicShare(w, r, validScopes)
  884. if err != nil {
  885. return
  886. }
  887. if err := validateBrowsableShare(share, connection); err != nil {
  888. s.renderClientMessagePage(w, r, "Unable to validate share", "", getRespStatus(err), err, "")
  889. return
  890. }
  891. name, err := getBrowsableSharedPath(share, r)
  892. if err != nil {
  893. s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
  894. return
  895. }
  896. if err = common.Connections.Add(connection); err != nil {
  897. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  898. return
  899. }
  900. defer common.Connections.Remove(connection.GetID())
  901. contents, err := connection.ReadDir(name)
  902. if err != nil {
  903. sendAPIResponse(w, r, err, "Unable to get directory contents", getMappedStatusCode(err))
  904. return
  905. }
  906. results := make([]map[string]any, 0, len(contents))
  907. for _, info := range contents {
  908. if !info.Mode().IsDir() && !info.Mode().IsRegular() {
  909. continue
  910. }
  911. res := make(map[string]any)
  912. if info.IsDir() {
  913. res["type"] = "1"
  914. res["size"] = ""
  915. } else {
  916. res["type"] = "2"
  917. res["size"] = info.Size()
  918. }
  919. res["meta"] = fmt.Sprintf("%v_%v", res["type"], info.Name())
  920. res["name"] = info.Name()
  921. res["url"] = getFileObjectURL(share.GetRelativePath(name), info.Name(),
  922. path.Join(webClientPubSharesPath, share.ShareID, "browse"))
  923. res["last_modified"] = getFileObjectModTime(info.ModTime())
  924. results = append(results, res)
  925. }
  926. render.JSON(w, r, results)
  927. }
  928. func (s *httpdServer) handleClientUploadToShare(w http.ResponseWriter, r *http.Request) {
  929. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  930. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeWrite, dataprovider.ShareScopeReadWrite}
  931. share, _, err := s.checkPublicShare(w, r, validScopes)
  932. if err != nil {
  933. return
  934. }
  935. if share.Scope == dataprovider.ShareScopeReadWrite {
  936. http.Redirect(w, r, path.Join(webClientPubSharesPath, share.ShareID, "browse"), http.StatusFound)
  937. return
  938. }
  939. s.renderUploadToSharePage(w, r, share)
  940. }
  941. func (s *httpdServer) handleShareGetFiles(w http.ResponseWriter, r *http.Request) {
  942. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  943. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  944. share, connection, err := s.checkPublicShare(w, r, validScopes)
  945. if err != nil {
  946. return
  947. }
  948. if err := validateBrowsableShare(share, connection); err != nil {
  949. s.renderClientMessagePage(w, r, "Unable to validate share", "", getRespStatus(err), err, "")
  950. return
  951. }
  952. name, err := getBrowsableSharedPath(share, r)
  953. if err != nil {
  954. s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
  955. return
  956. }
  957. if err = common.Connections.Add(connection); err != nil {
  958. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  959. return
  960. }
  961. defer common.Connections.Remove(connection.GetID())
  962. var info os.FileInfo
  963. if name == "/" {
  964. info = vfs.NewFileInfo(name, true, 0, time.Unix(0, 0), false)
  965. } else {
  966. info, err = connection.Stat(name, 1)
  967. }
  968. if err != nil {
  969. s.renderSharedFilesPage(w, r, path.Dir(share.GetRelativePath(name)), err.Error(), share)
  970. return
  971. }
  972. if info.IsDir() {
  973. s.renderSharedFilesPage(w, r, share.GetRelativePath(name), "", share)
  974. return
  975. }
  976. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  977. if status, err := downloadFile(w, r, connection, name, info, false, &share); err != nil {
  978. dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
  979. if status > 0 {
  980. s.renderSharedFilesPage(w, r, path.Dir(share.GetRelativePath(name)), err.Error(), share)
  981. }
  982. }
  983. }
  984. func (s *httpdServer) handleShareViewPDF(w http.ResponseWriter, r *http.Request) {
  985. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  986. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  987. share, _, err := s.checkPublicShare(w, r, validScopes)
  988. if err != nil {
  989. return
  990. }
  991. name := util.CleanPath(r.URL.Query().Get("path"))
  992. data := viewPDFPage{
  993. Title: path.Base(name),
  994. URL: fmt.Sprintf("%s?path=%s&_=%d", path.Join(webClientPubSharesPath, share.ShareID, "getpdf"),
  995. url.QueryEscape(name), time.Now().UTC().Unix()),
  996. StaticURL: webStaticFilesPath,
  997. CSPNonce: secure.CSPNonce(r.Context()),
  998. Branding: s.binding.Branding.WebClient,
  999. }
  1000. renderClientTemplate(w, templateClientViewPDF, data)
  1001. }
  1002. func (s *httpdServer) handleShareGetPDF(w http.ResponseWriter, r *http.Request) {
  1003. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1004. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  1005. share, connection, err := s.checkPublicShare(w, r, validScopes)
  1006. if err != nil {
  1007. return
  1008. }
  1009. if err := validateBrowsableShare(share, connection); err != nil {
  1010. s.renderClientMessagePage(w, r, "Unable to validate share", "", getRespStatus(err), err, "")
  1011. return
  1012. }
  1013. name, err := getBrowsableSharedPath(share, r)
  1014. if err != nil {
  1015. s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
  1016. return
  1017. }
  1018. if err = common.Connections.Add(connection); err != nil {
  1019. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  1020. return
  1021. }
  1022. defer common.Connections.Remove(connection.GetID())
  1023. info, err := connection.Stat(name, 1)
  1024. if err != nil {
  1025. s.renderClientMessagePage(w, r, "Unable to get file", "", getRespStatus(err), err, "")
  1026. return
  1027. }
  1028. if info.IsDir() {
  1029. s.renderClientMessagePage(w, r, "Invalid file", fmt.Sprintf("%q is not a file", name),
  1030. http.StatusBadRequest, nil, "")
  1031. return
  1032. }
  1033. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1034. if err := s.ensurePDF(w, r, name, connection); err != nil {
  1035. return
  1036. }
  1037. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  1038. if _, err := downloadFile(w, r, connection, name, info, true, &share); err != nil {
  1039. dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
  1040. }
  1041. }
  1042. func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.Request) {
  1043. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1044. claims, err := getTokenClaims(r)
  1045. if err != nil || claims.Username == "" {
  1046. sendAPIResponse(w, r, nil, "invalid token claims", http.StatusForbidden)
  1047. return
  1048. }
  1049. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1050. if err != nil {
  1051. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  1052. return
  1053. }
  1054. connID := xid.New().String()
  1055. protocol := getProtocolFromRequest(r)
  1056. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1057. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1058. sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  1059. return
  1060. }
  1061. connection := &Connection{
  1062. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1063. r.RemoteAddr, user),
  1064. request: r,
  1065. }
  1066. if err = common.Connections.Add(connection); err != nil {
  1067. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  1068. return
  1069. }
  1070. defer common.Connections.Remove(connection.GetID())
  1071. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1072. contents, err := connection.ReadDir(name)
  1073. if err != nil {
  1074. sendAPIResponse(w, r, err, "Unable to get directory contents", getMappedStatusCode(err))
  1075. return
  1076. }
  1077. dirTree := r.URL.Query().Get("dirtree") == "1"
  1078. results := make([]map[string]any, 0, len(contents))
  1079. for _, info := range contents {
  1080. res := make(map[string]any)
  1081. res["url"] = getFileObjectURL(name, info.Name(), webClientFilesPath)
  1082. if info.IsDir() {
  1083. res["type"] = "1"
  1084. res["size"] = ""
  1085. res["dir_path"] = url.QueryEscape(path.Join(name, info.Name()))
  1086. } else {
  1087. if dirTree {
  1088. continue
  1089. }
  1090. res["type"] = "2"
  1091. if info.Mode()&os.ModeSymlink != 0 {
  1092. res["size"] = ""
  1093. } else {
  1094. res["size"] = info.Size()
  1095. if info.Size() < httpdMaxEditFileSize {
  1096. res["edit_url"] = strings.Replace(res["url"].(string), webClientFilesPath, webClientEditFilePath, 1)
  1097. }
  1098. }
  1099. }
  1100. res["meta"] = fmt.Sprintf("%v_%v", res["type"], info.Name())
  1101. res["name"] = info.Name()
  1102. res["last_modified"] = getFileObjectModTime(info.ModTime())
  1103. results = append(results, res)
  1104. }
  1105. render.JSON(w, r, results)
  1106. }
  1107. func (s *httpdServer) handleClientGetFiles(w http.ResponseWriter, r *http.Request) {
  1108. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1109. claims, err := getTokenClaims(r)
  1110. if err != nil || claims.Username == "" {
  1111. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1112. return
  1113. }
  1114. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1115. if err != nil {
  1116. s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
  1117. return
  1118. }
  1119. connID := xid.New().String()
  1120. protocol := getProtocolFromRequest(r)
  1121. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1122. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1123. s.renderClientForbiddenPage(w, r, err.Error())
  1124. return
  1125. }
  1126. connection := &Connection{
  1127. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1128. r.RemoteAddr, user),
  1129. request: r,
  1130. }
  1131. if err = common.Connections.Add(connection); err != nil {
  1132. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  1133. return
  1134. }
  1135. defer common.Connections.Remove(connection.GetID())
  1136. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1137. var info os.FileInfo
  1138. if name == "/" {
  1139. info = vfs.NewFileInfo(name, true, 0, time.Unix(0, 0), false)
  1140. } else {
  1141. info, err = connection.Stat(name, 0)
  1142. }
  1143. if err != nil {
  1144. s.renderFilesPage(w, r, path.Dir(name), fmt.Sprintf("unable to stat file %q: %v", name, err), &user)
  1145. return
  1146. }
  1147. if info.IsDir() {
  1148. s.renderFilesPage(w, r, name, "", &user)
  1149. return
  1150. }
  1151. if status, err := downloadFile(w, r, connection, name, info, false, nil); err != nil && status != 0 {
  1152. if status > 0 {
  1153. if status == http.StatusRequestedRangeNotSatisfiable {
  1154. s.renderClientMessagePage(w, r, http.StatusText(status), "", status, err, "")
  1155. return
  1156. }
  1157. s.renderFilesPage(w, r, path.Dir(name), err.Error(), &user)
  1158. }
  1159. }
  1160. }
  1161. func (s *httpdServer) handleClientEditFile(w http.ResponseWriter, r *http.Request) {
  1162. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1163. claims, err := getTokenClaims(r)
  1164. if err != nil || claims.Username == "" {
  1165. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1166. return
  1167. }
  1168. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1169. if err != nil {
  1170. s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
  1171. return
  1172. }
  1173. connID := xid.New().String()
  1174. protocol := getProtocolFromRequest(r)
  1175. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1176. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1177. s.renderClientForbiddenPage(w, r, err.Error())
  1178. return
  1179. }
  1180. connection := &Connection{
  1181. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1182. r.RemoteAddr, user),
  1183. request: r,
  1184. }
  1185. if err = common.Connections.Add(connection); err != nil {
  1186. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  1187. return
  1188. }
  1189. defer common.Connections.Remove(connection.GetID())
  1190. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1191. info, err := connection.Stat(name, 0)
  1192. if err != nil {
  1193. s.renderClientMessagePage(w, r, fmt.Sprintf("Unable to stat file %q", name), "",
  1194. getRespStatus(err), nil, "")
  1195. return
  1196. }
  1197. if info.IsDir() {
  1198. s.renderClientMessagePage(w, r, fmt.Sprintf("The path %q does not point to a file", name), "",
  1199. http.StatusBadRequest, nil, "")
  1200. return
  1201. }
  1202. if info.Size() > httpdMaxEditFileSize {
  1203. s.renderClientMessagePage(w, r, fmt.Sprintf("The file size %v for %q exceeds the maximum allowed size",
  1204. util.ByteCountIEC(info.Size()), name), "", http.StatusBadRequest, nil, "")
  1205. return
  1206. }
  1207. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1208. reader, err := connection.getFileReader(name, 0, r.Method)
  1209. if err != nil {
  1210. s.renderClientMessagePage(w, r, fmt.Sprintf("Unable to get a reader for the file %q", name), "",
  1211. getRespStatus(err), nil, "")
  1212. return
  1213. }
  1214. defer reader.Close()
  1215. var b bytes.Buffer
  1216. _, err = io.Copy(&b, reader)
  1217. if err != nil {
  1218. s.renderClientMessagePage(w, r, fmt.Sprintf("Unable to read the file %q", name), "", http.StatusInternalServerError,
  1219. nil, "")
  1220. return
  1221. }
  1222. s.renderEditFilePage(w, r, name, b.String(), !user.CanAddFilesFromWeb(path.Dir(name)))
  1223. }
  1224. func (s *httpdServer) handleClientAddShareGet(w http.ResponseWriter, r *http.Request) {
  1225. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1226. claims, err := getTokenClaims(r)
  1227. if err != nil || claims.Username == "" {
  1228. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1229. return
  1230. }
  1231. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1232. if err != nil {
  1233. s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
  1234. return
  1235. }
  1236. share := &dataprovider.Share{Scope: dataprovider.ShareScopeRead}
  1237. if user.Filters.DefaultSharesExpiration > 0 {
  1238. share.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(24 * time.Hour * time.Duration(user.Filters.DefaultSharesExpiration)))
  1239. }
  1240. dirName := "/"
  1241. if _, ok := r.URL.Query()["path"]; ok {
  1242. dirName = util.CleanPath(r.URL.Query().Get("path"))
  1243. }
  1244. if _, ok := r.URL.Query()["files"]; ok {
  1245. files := r.URL.Query().Get("files")
  1246. var filesList []string
  1247. err := json.Unmarshal([]byte(files), &filesList)
  1248. if err != nil {
  1249. s.renderClientMessagePage(w, r, "Invalid share list", "", http.StatusBadRequest, err, "")
  1250. return
  1251. }
  1252. for _, f := range filesList {
  1253. if f != "" {
  1254. share.Paths = append(share.Paths, path.Join(dirName, f))
  1255. }
  1256. }
  1257. }
  1258. s.renderAddUpdateSharePage(w, r, share, "", true)
  1259. }
  1260. func (s *httpdServer) handleClientUpdateShareGet(w http.ResponseWriter, r *http.Request) {
  1261. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1262. claims, err := getTokenClaims(r)
  1263. if err != nil || claims.Username == "" {
  1264. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1265. return
  1266. }
  1267. shareID := getURLParam(r, "id")
  1268. share, err := dataprovider.ShareExists(shareID, claims.Username)
  1269. if err == nil {
  1270. share.HideConfidentialData()
  1271. s.renderAddUpdateSharePage(w, r, &share, "", false)
  1272. } else if errors.Is(err, util.ErrNotFound) {
  1273. s.renderClientNotFoundPage(w, r, err)
  1274. } else {
  1275. s.renderClientInternalServerErrorPage(w, r, err)
  1276. }
  1277. }
  1278. func (s *httpdServer) handleClientAddSharePost(w http.ResponseWriter, r *http.Request) {
  1279. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1280. claims, err := getTokenClaims(r)
  1281. if err != nil || claims.Username == "" {
  1282. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1283. return
  1284. }
  1285. share, err := getShareFromPostFields(r)
  1286. if err != nil {
  1287. s.renderAddUpdateSharePage(w, r, share, err.Error(), true)
  1288. return
  1289. }
  1290. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1291. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1292. s.renderClientForbiddenPage(w, r, err.Error())
  1293. return
  1294. }
  1295. share.ID = 0
  1296. share.ShareID = util.GenerateUniqueID()
  1297. share.LastUseAt = 0
  1298. share.Username = claims.Username
  1299. if share.Password == "" {
  1300. if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
  1301. s.renderClientForbiddenPage(w, r, "You are not authorized to share files/folders without a password")
  1302. return
  1303. }
  1304. }
  1305. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1306. if err != nil {
  1307. s.renderAddUpdateSharePage(w, r, share, "Unable to retrieve your user", true)
  1308. return
  1309. }
  1310. if err := user.CheckMaxShareExpiration(util.GetTimeFromMsecSinceEpoch(share.ExpiresAt)); err != nil {
  1311. s.renderAddUpdateSharePage(w, r, share, err.Error(), true)
  1312. return
  1313. }
  1314. err = dataprovider.AddShare(share, claims.Username, ipAddr, claims.Role)
  1315. if err == nil {
  1316. http.Redirect(w, r, webClientSharesPath, http.StatusSeeOther)
  1317. } else {
  1318. s.renderAddUpdateSharePage(w, r, share, err.Error(), true)
  1319. }
  1320. }
  1321. func (s *httpdServer) handleClientUpdateSharePost(w http.ResponseWriter, r *http.Request) {
  1322. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1323. claims, err := getTokenClaims(r)
  1324. if err != nil || claims.Username == "" {
  1325. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1326. return
  1327. }
  1328. shareID := getURLParam(r, "id")
  1329. share, err := dataprovider.ShareExists(shareID, claims.Username)
  1330. if errors.Is(err, util.ErrNotFound) {
  1331. s.renderClientNotFoundPage(w, r, err)
  1332. return
  1333. } else if err != nil {
  1334. s.renderClientInternalServerErrorPage(w, r, err)
  1335. return
  1336. }
  1337. updatedShare, err := getShareFromPostFields(r)
  1338. if err != nil {
  1339. s.renderAddUpdateSharePage(w, r, updatedShare, err.Error(), false)
  1340. return
  1341. }
  1342. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1343. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1344. s.renderClientForbiddenPage(w, r, err.Error())
  1345. return
  1346. }
  1347. updatedShare.ShareID = shareID
  1348. updatedShare.Username = claims.Username
  1349. if updatedShare.Password == redactedSecret {
  1350. updatedShare.Password = share.Password
  1351. }
  1352. if updatedShare.Password == "" {
  1353. if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
  1354. s.renderClientForbiddenPage(w, r, "You are not authorized to share files/folders without a password")
  1355. return
  1356. }
  1357. }
  1358. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1359. if err != nil {
  1360. s.renderAddUpdateSharePage(w, r, updatedShare, "Unable to retrieve your user", false)
  1361. return
  1362. }
  1363. if err := user.CheckMaxShareExpiration(util.GetTimeFromMsecSinceEpoch(updatedShare.ExpiresAt)); err != nil {
  1364. s.renderAddUpdateSharePage(w, r, updatedShare, err.Error(), false)
  1365. return
  1366. }
  1367. err = dataprovider.UpdateShare(updatedShare, claims.Username, ipAddr, claims.Role)
  1368. if err == nil {
  1369. http.Redirect(w, r, webClientSharesPath, http.StatusSeeOther)
  1370. } else {
  1371. s.renderAddUpdateSharePage(w, r, updatedShare, err.Error(), false)
  1372. }
  1373. }
  1374. func (s *httpdServer) handleClientGetShares(w http.ResponseWriter, r *http.Request) {
  1375. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1376. claims, err := getTokenClaims(r)
  1377. if err != nil || claims.Username == "" {
  1378. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1379. return
  1380. }
  1381. limit := defaultQueryLimit
  1382. if _, ok := r.URL.Query()["qlimit"]; ok {
  1383. var err error
  1384. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  1385. if err != nil {
  1386. limit = defaultQueryLimit
  1387. }
  1388. }
  1389. shares := make([]dataprovider.Share, 0, limit)
  1390. for {
  1391. sh, err := dataprovider.GetShares(limit, len(shares), dataprovider.OrderASC, claims.Username)
  1392. if err != nil {
  1393. s.renderInternalServerErrorPage(w, r, err)
  1394. return
  1395. }
  1396. shares = append(shares, sh...)
  1397. if len(sh) < limit {
  1398. break
  1399. }
  1400. }
  1401. data := clientSharesPage{
  1402. baseClientPage: s.getBaseClientPageData(pageClientSharesTitle, webClientSharesPath, r),
  1403. Shares: shares,
  1404. BasePublicSharesURL: webClientPubSharesPath,
  1405. }
  1406. renderClientTemplate(w, templateClientShares, data)
  1407. }
  1408. func (s *httpdServer) handleClientGetProfile(w http.ResponseWriter, r *http.Request) {
  1409. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1410. s.renderClientProfilePage(w, r, "")
  1411. }
  1412. func (s *httpdServer) handleWebClientChangePwd(w http.ResponseWriter, r *http.Request) {
  1413. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1414. s.renderClientChangePasswordPage(w, r, "")
  1415. }
  1416. func (s *httpdServer) handleWebClientProfilePost(w http.ResponseWriter, r *http.Request) {
  1417. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1418. err := r.ParseForm()
  1419. if err != nil {
  1420. s.renderClientProfilePage(w, r, err.Error())
  1421. return
  1422. }
  1423. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1424. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1425. s.renderClientForbiddenPage(w, r, err.Error())
  1426. return
  1427. }
  1428. claims, err := getTokenClaims(r)
  1429. if err != nil || claims.Username == "" {
  1430. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1431. return
  1432. }
  1433. user, userMerged, err := dataprovider.GetUserVariants(claims.Username, "")
  1434. if err != nil {
  1435. s.renderClientProfilePage(w, r, err.Error())
  1436. return
  1437. }
  1438. if !userMerged.CanManagePublicKeys() && !userMerged.CanChangeAPIKeyAuth() && !userMerged.CanChangeInfo() {
  1439. s.renderClientForbiddenPage(w, r, "You are not allowed to change anything")
  1440. return
  1441. }
  1442. if userMerged.CanManagePublicKeys() {
  1443. for k := range r.Form {
  1444. if hasPrefixAndSuffix(k, "public_keys[", "][public_key]") {
  1445. r.Form.Add("public_keys", r.Form.Get(k))
  1446. }
  1447. }
  1448. user.PublicKeys = r.Form["public_keys"]
  1449. }
  1450. if userMerged.CanChangeAPIKeyAuth() {
  1451. user.Filters.AllowAPIKeyAuth = r.Form.Get("allow_api_key_auth") != ""
  1452. }
  1453. if userMerged.CanChangeInfo() {
  1454. user.Email = strings.TrimSpace(r.Form.Get("email"))
  1455. user.Description = r.Form.Get("description")
  1456. }
  1457. err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSelf, ipAddr, user.Role)
  1458. if err != nil {
  1459. s.renderClientProfilePage(w, r, err.Error())
  1460. return
  1461. }
  1462. s.renderClientMessagePage(w, r, "Profile updated", "", http.StatusOK, nil,
  1463. "Your profile has been successfully updated")
  1464. }
  1465. func (s *httpdServer) handleWebClientMFA(w http.ResponseWriter, r *http.Request) {
  1466. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1467. s.renderClientMFAPage(w, r)
  1468. }
  1469. func (s *httpdServer) handleWebClientTwoFactor(w http.ResponseWriter, r *http.Request) {
  1470. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1471. s.renderClientTwoFactorPage(w, r, "", util.GetIPFromRemoteAddress(r.RemoteAddr))
  1472. }
  1473. func (s *httpdServer) handleWebClientTwoFactorRecovery(w http.ResponseWriter, r *http.Request) {
  1474. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1475. s.renderClientTwoFactorRecoveryPage(w, r, "", util.GetIPFromRemoteAddress(r.RemoteAddr))
  1476. }
  1477. func getShareFromPostFields(r *http.Request) (*dataprovider.Share, error) {
  1478. share := &dataprovider.Share{}
  1479. if err := r.ParseForm(); err != nil {
  1480. return share, err
  1481. }
  1482. for k := range r.Form {
  1483. if hasPrefixAndSuffix(k, "paths[", "][path]") {
  1484. r.Form.Add("paths", r.Form.Get(k))
  1485. }
  1486. }
  1487. share.Name = strings.TrimSpace(r.Form.Get("name"))
  1488. share.Description = r.Form.Get("description")
  1489. for _, p := range r.Form["paths"] {
  1490. if strings.TrimSpace(p) != "" {
  1491. share.Paths = append(share.Paths, p)
  1492. }
  1493. }
  1494. share.Password = strings.TrimSpace(r.Form.Get("password"))
  1495. share.AllowFrom = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  1496. scope, err := strconv.Atoi(r.Form.Get("scope"))
  1497. if err != nil {
  1498. return share, err
  1499. }
  1500. share.Scope = dataprovider.ShareScope(scope)
  1501. maxTokens, err := strconv.Atoi(r.Form.Get("max_tokens"))
  1502. if err != nil {
  1503. return share, err
  1504. }
  1505. share.MaxTokens = maxTokens
  1506. expirationDateMillis := int64(0)
  1507. expirationDateString := strings.TrimSpace(r.Form.Get("expiration_date"))
  1508. if expirationDateString != "" {
  1509. expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
  1510. if err != nil {
  1511. return share, err
  1512. }
  1513. expirationDateMillis = util.GetTimeAsMsSinceEpoch(expirationDate)
  1514. }
  1515. share.ExpiresAt = expirationDateMillis
  1516. return share, nil
  1517. }
  1518. func (s *httpdServer) handleWebClientForgotPwd(w http.ResponseWriter, r *http.Request) {
  1519. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1520. if !smtp.IsEnabled() {
  1521. s.renderClientNotFoundPage(w, r, errors.New("this page does not exist"))
  1522. return
  1523. }
  1524. s.renderClientForgotPwdPage(w, r, "", util.GetIPFromRemoteAddress(r.RemoteAddr))
  1525. }
  1526. func (s *httpdServer) handleWebClientForgotPwdPost(w http.ResponseWriter, r *http.Request) {
  1527. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1528. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1529. err := r.ParseForm()
  1530. if err != nil {
  1531. s.renderClientForgotPwdPage(w, r, err.Error(), ipAddr)
  1532. return
  1533. }
  1534. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1535. s.renderClientForbiddenPage(w, r, err.Error())
  1536. return
  1537. }
  1538. username := strings.TrimSpace(r.Form.Get("username"))
  1539. err = handleForgotPassword(r, username, false)
  1540. if err != nil {
  1541. if e, ok := err.(*util.ValidationError); ok {
  1542. s.renderClientForgotPwdPage(w, r, e.GetErrorString(), ipAddr)
  1543. return
  1544. }
  1545. s.renderClientForgotPwdPage(w, r, err.Error(), ipAddr)
  1546. return
  1547. }
  1548. http.Redirect(w, r, webClientResetPwdPath, http.StatusFound)
  1549. }
  1550. func (s *httpdServer) handleWebClientPasswordReset(w http.ResponseWriter, r *http.Request) {
  1551. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1552. if !smtp.IsEnabled() {
  1553. s.renderClientNotFoundPage(w, r, errors.New("this page does not exist"))
  1554. return
  1555. }
  1556. s.renderClientResetPwdPage(w, r, "", util.GetIPFromRemoteAddress(r.RemoteAddr))
  1557. }
  1558. func (s *httpdServer) handleClientViewPDF(w http.ResponseWriter, r *http.Request) {
  1559. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1560. name := r.URL.Query().Get("path")
  1561. if name == "" {
  1562. s.renderClientBadRequestPage(w, r, errors.New("no file specified"))
  1563. return
  1564. }
  1565. name = util.CleanPath(name)
  1566. data := viewPDFPage{
  1567. Title: path.Base(name),
  1568. URL: fmt.Sprintf("%s?path=%s&_=%d", webClientGetPDFPath, url.QueryEscape(name), time.Now().UTC().Unix()),
  1569. StaticURL: webStaticFilesPath,
  1570. CSPNonce: secure.CSPNonce(r.Context()),
  1571. Branding: s.binding.Branding.WebClient,
  1572. }
  1573. renderClientTemplate(w, templateClientViewPDF, data)
  1574. }
  1575. func (s *httpdServer) handleClientGetPDF(w http.ResponseWriter, r *http.Request) {
  1576. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1577. claims, err := getTokenClaims(r)
  1578. if err != nil || claims.Username == "" {
  1579. s.renderClientForbiddenPage(w, r, "Invalid token claims")
  1580. return
  1581. }
  1582. name := r.URL.Query().Get("path")
  1583. if name == "" {
  1584. s.renderClientBadRequestPage(w, r, errors.New("no file specified"))
  1585. return
  1586. }
  1587. name = util.CleanPath(name)
  1588. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1589. if err != nil {
  1590. s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
  1591. return
  1592. }
  1593. connID := xid.New().String()
  1594. protocol := getProtocolFromRequest(r)
  1595. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1596. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1597. s.renderClientForbiddenPage(w, r, err.Error())
  1598. return
  1599. }
  1600. connection := &Connection{
  1601. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1602. r.RemoteAddr, user),
  1603. request: r,
  1604. }
  1605. if err = common.Connections.Add(connection); err != nil {
  1606. s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
  1607. return
  1608. }
  1609. defer common.Connections.Remove(connection.GetID())
  1610. info, err := connection.Stat(name, 0)
  1611. if err != nil {
  1612. s.renderClientMessagePage(w, r, "Unable to get file", "", getRespStatus(err), err, "")
  1613. return
  1614. }
  1615. if info.IsDir() {
  1616. s.renderClientMessagePage(w, r, "Invalid file", fmt.Sprintf("%q is not a file", name),
  1617. http.StatusBadRequest, nil, "")
  1618. return
  1619. }
  1620. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1621. if err := s.ensurePDF(w, r, name, connection); err != nil {
  1622. return
  1623. }
  1624. downloadFile(w, r, connection, name, info, true, nil) //nolint:errcheck
  1625. }
  1626. func (s *httpdServer) ensurePDF(w http.ResponseWriter, r *http.Request, name string, connection *Connection) error {
  1627. reader, err := connection.getFileReader(name, 0, r.Method)
  1628. if err != nil {
  1629. s.renderClientMessagePage(w, r, fmt.Sprintf("Unable to get a reader for the file %q", name), "",
  1630. getRespStatus(err), err, "")
  1631. return err
  1632. }
  1633. defer reader.Close()
  1634. var b bytes.Buffer
  1635. _, err = io.CopyN(&b, reader, 128)
  1636. if err != nil {
  1637. s.renderClientMessagePage(w, r, "Invalid PDF file", fmt.Sprintf("Unable to validate the file %q as PDF", name),
  1638. http.StatusBadRequest, nil, "")
  1639. return err
  1640. }
  1641. if ctype := http.DetectContentType(b.Bytes()); ctype != "application/pdf" {
  1642. connection.Log(logger.LevelDebug, "detected %q content type, expected PDF, file %q", ctype, name)
  1643. s.renderClientBadRequestPage(w, r, fmt.Errorf("the file %q does not look like a PDF", name))
  1644. return errors.New("invalid PDF")
  1645. }
  1646. return nil
  1647. }
  1648. func (s *httpdServer) handleClientShareLoginGet(w http.ResponseWriter, r *http.Request) {
  1649. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1650. s.renderShareLoginPage(w, r, "", util.GetIPFromRemoteAddress(r.RemoteAddr))
  1651. }
  1652. func (s *httpdServer) handleClientShareLoginPost(w http.ResponseWriter, r *http.Request) {
  1653. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1654. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1655. if err := r.ParseForm(); err != nil {
  1656. s.renderShareLoginPage(w, r, err.Error(), ipAddr)
  1657. return
  1658. }
  1659. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1660. s.renderShareLoginPage(w, r, err.Error(), ipAddr)
  1661. return
  1662. }
  1663. shareID := getURLParam(r, "id")
  1664. share, err := dataprovider.ShareExists(shareID, "")
  1665. if err != nil {
  1666. s.renderShareLoginPage(w, r, dataprovider.ErrInvalidCredentials.Error(), ipAddr)
  1667. return
  1668. }
  1669. match, err := share.CheckCredentials(strings.TrimSpace(r.Form.Get("share_password")))
  1670. if !match || err != nil {
  1671. s.renderShareLoginPage(w, r, dataprovider.ErrInvalidCredentials.Error(), ipAddr)
  1672. return
  1673. }
  1674. c := jwtTokenClaims{
  1675. Username: shareID,
  1676. }
  1677. err = c.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebShare, ipAddr)
  1678. if err != nil {
  1679. s.renderShareLoginPage(w, r, common.ErrInternalFailure.Error(), ipAddr)
  1680. return
  1681. }
  1682. next := path.Clean(r.URL.Query().Get("next"))
  1683. baseShareURL := path.Join(webClientPubSharesPath, share.ShareID)
  1684. isRedirect, redirectTo := checkShareRedirectURL(next, baseShareURL)
  1685. if isRedirect {
  1686. http.Redirect(w, r, redirectTo, http.StatusFound)
  1687. return
  1688. }
  1689. s.renderClientMessagePage(w, r, "Share Login OK", "Share login successful, you can now use your link",
  1690. http.StatusOK, nil, "")
  1691. }
  1692. func (s *httpdServer) handleClientSharedFile(w http.ResponseWriter, r *http.Request) {
  1693. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1694. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead}
  1695. share, _, err := s.checkPublicShare(w, r, validScopes)
  1696. if err != nil {
  1697. return
  1698. }
  1699. query := ""
  1700. if r.URL.RawQuery != "" {
  1701. query = "?" + r.URL.RawQuery
  1702. }
  1703. s.renderShareDownloadPage(w, r, path.Join(webClientPubSharesPath, share.ShareID)+query)
  1704. }
  1705. func (s *httpdServer) handleClientPing(w http.ResponseWriter, r *http.Request) {
  1706. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1707. render.PlainText(w, r, "PONG")
  1708. }
  1709. func checkShareRedirectURL(next, base string) (bool, string) {
  1710. if !strings.HasPrefix(next, base) {
  1711. return false, ""
  1712. }
  1713. if next == base {
  1714. return true, path.Join(next, "download")
  1715. }
  1716. baseURL, err := url.Parse(base)
  1717. if err != nil {
  1718. return false, ""
  1719. }
  1720. nextURL, err := url.Parse(next)
  1721. if err != nil {
  1722. return false, ""
  1723. }
  1724. if nextURL.Path == baseURL.Path {
  1725. redirectURL := nextURL.JoinPath("download")
  1726. return true, redirectURL.String()
  1727. }
  1728. return true, next
  1729. }