webclient.go 61 KB

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