webclient.go 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235
  1. // Copyright (C) 2019 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/vfs"
  41. )
  42. const (
  43. templateClientDir = "webclient"
  44. templateClientBase = "base.html"
  45. templateClientFiles = "files.html"
  46. templateClientProfile = "profile.html"
  47. templateClientMFA = "mfa.html"
  48. templateClientEditFile = "editfile.html"
  49. templateClientShare = "share.html"
  50. templateClientShares = "shares.html"
  51. templateClientViewPDF = "viewpdf.html"
  52. templateShareLogin = "sharelogin.html"
  53. templateShareDownload = "sharedownload.html"
  54. templateUploadToShare = "shareupload.html"
  55. )
  56. // condResult is the result of an HTTP request precondition check.
  57. // See https://tools.ietf.org/html/rfc7232 section 3.
  58. type condResult int
  59. const (
  60. condNone condResult = iota
  61. condTrue
  62. condFalse
  63. )
  64. var (
  65. clientTemplates = make(map[string]*template.Template)
  66. unixEpochTime = time.Unix(0, 0)
  67. )
  68. // isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
  69. func isZeroTime(t time.Time) bool {
  70. return t.IsZero() || t.Equal(unixEpochTime)
  71. }
  72. type baseClientPage struct {
  73. commonBasePage
  74. Title string
  75. CurrentURL string
  76. FilesURL string
  77. SharesURL string
  78. ShareURL string
  79. ProfileURL string
  80. PingURL string
  81. ChangePwdURL string
  82. LogoutURL string
  83. LoginURL string
  84. EditURL string
  85. MFAURL string
  86. CSRFToken string
  87. LoggedUser *dataprovider.User
  88. Branding UIBranding
  89. }
  90. type dirMapping struct {
  91. DirName string
  92. Href string
  93. }
  94. type viewPDFPage struct {
  95. commonBasePage
  96. Title string
  97. URL string
  98. Branding UIBranding
  99. }
  100. type editFilePage struct {
  101. baseClientPage
  102. CurrentDir string
  103. FileURL string
  104. Path string
  105. Name string
  106. ReadOnly bool
  107. Data string
  108. }
  109. type filesPage struct {
  110. baseClientPage
  111. CurrentDir string
  112. DirsURL string
  113. FileActionsURL string
  114. CheckExistURL string
  115. DownloadURL string
  116. ViewPDFURL string
  117. FileURL string
  118. TasksURL string
  119. CanAddFiles bool
  120. CanCreateDirs bool
  121. CanRename bool
  122. CanDelete bool
  123. CanDownload bool
  124. CanShare bool
  125. CanCopy bool
  126. ShareUploadBaseURL string
  127. Error *util.I18nError
  128. Paths []dirMapping
  129. QuotaUsage *userQuotaUsage
  130. }
  131. type shareLoginPage struct {
  132. commonBasePage
  133. CurrentURL string
  134. Error *util.I18nError
  135. CSRFToken string
  136. Title string
  137. Branding UIBranding
  138. CheckRedirect bool
  139. }
  140. type shareDownloadPage struct {
  141. baseClientPage
  142. DownloadLink string
  143. }
  144. type shareUploadPage struct {
  145. baseClientPage
  146. Share *dataprovider.Share
  147. UploadBasePath string
  148. }
  149. type clientMessagePage struct {
  150. baseClientPage
  151. Error *util.I18nError
  152. Success string
  153. Text string
  154. }
  155. type clientProfilePage struct {
  156. baseClientPage
  157. PublicKeys []string
  158. TLSCerts []string
  159. CanSubmit bool
  160. AllowAPIKeyAuth bool
  161. Email string
  162. Description string
  163. Error *util.I18nError
  164. }
  165. type changeClientPasswordPage struct {
  166. baseClientPage
  167. Error *util.I18nError
  168. }
  169. type clientMFAPage struct {
  170. baseClientPage
  171. TOTPConfigs []string
  172. TOTPConfig dataprovider.UserTOTPConfig
  173. GenerateTOTPURL string
  174. ValidateTOTPURL string
  175. SaveTOTPURL string
  176. RecCodesURL string
  177. Protocols []string
  178. RequiredProtocols []string
  179. }
  180. type clientSharesPage struct {
  181. baseClientPage
  182. BasePublicSharesURL string
  183. }
  184. type clientSharePage struct {
  185. baseClientPage
  186. Share *dataprovider.Share
  187. Error *util.I18nError
  188. IsAdd bool
  189. }
  190. type userQuotaUsage struct {
  191. QuotaSize int64
  192. QuotaFiles int
  193. UsedQuotaSize int64
  194. UsedQuotaFiles int
  195. UploadDataTransfer int64
  196. DownloadDataTransfer int64
  197. TotalDataTransfer int64
  198. UsedUploadDataTransfer int64
  199. UsedDownloadDataTransfer int64
  200. }
  201. func (u *userQuotaUsage) HasQuotaInfo() bool {
  202. if dataprovider.GetQuotaTracking() == 0 {
  203. return false
  204. }
  205. if u.HasDiskQuota() {
  206. return true
  207. }
  208. return u.HasTranferQuota()
  209. }
  210. func (u *userQuotaUsage) HasDiskQuota() bool {
  211. if u.QuotaSize > 0 || u.UsedQuotaSize > 0 {
  212. return true
  213. }
  214. return u.QuotaFiles > 0 || u.UsedQuotaFiles > 0
  215. }
  216. func (u *userQuotaUsage) HasTranferQuota() bool {
  217. if u.TotalDataTransfer > 0 || u.UploadDataTransfer > 0 || u.DownloadDataTransfer > 0 {
  218. return true
  219. }
  220. return u.UsedDownloadDataTransfer > 0 || u.UsedUploadDataTransfer > 0
  221. }
  222. func (u *userQuotaUsage) GetQuotaSize() string {
  223. if u.QuotaSize > 0 {
  224. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedQuotaSize), util.ByteCountIEC(u.QuotaSize))
  225. }
  226. if u.UsedQuotaSize > 0 {
  227. return util.ByteCountIEC(u.UsedQuotaSize)
  228. }
  229. return ""
  230. }
  231. func (u *userQuotaUsage) GetQuotaFiles() string {
  232. if u.QuotaFiles > 0 {
  233. return fmt.Sprintf("%d/%d", u.UsedQuotaFiles, u.QuotaFiles)
  234. }
  235. if u.UsedQuotaFiles > 0 {
  236. return strconv.FormatInt(int64(u.UsedQuotaFiles), 10)
  237. }
  238. return ""
  239. }
  240. func (u *userQuotaUsage) GetQuotaSizePercentage() int {
  241. if u.QuotaSize > 0 {
  242. return int(math.Round(100 * float64(u.UsedQuotaSize) / float64(u.QuotaSize)))
  243. }
  244. return 0
  245. }
  246. func (u *userQuotaUsage) GetQuotaFilesPercentage() int {
  247. if u.QuotaFiles > 0 {
  248. return int(math.Round(100 * float64(u.UsedQuotaFiles) / float64(u.QuotaFiles)))
  249. }
  250. return 0
  251. }
  252. func (u *userQuotaUsage) IsQuotaSizeLow() bool {
  253. return u.GetQuotaSizePercentage() > 85
  254. }
  255. func (u *userQuotaUsage) IsQuotaFilesLow() bool {
  256. return u.GetQuotaFilesPercentage() > 85
  257. }
  258. func (u *userQuotaUsage) IsDiskQuotaLow() bool {
  259. return u.IsQuotaSizeLow() || u.IsQuotaFilesLow()
  260. }
  261. func (u *userQuotaUsage) GetTotalTransferQuota() string {
  262. total := u.UsedUploadDataTransfer + u.UsedDownloadDataTransfer
  263. if u.TotalDataTransfer > 0 {
  264. return fmt.Sprintf("%s/%s", util.ByteCountIEC(total), util.ByteCountIEC(u.TotalDataTransfer*1048576))
  265. }
  266. if total > 0 {
  267. return util.ByteCountIEC(total)
  268. }
  269. return ""
  270. }
  271. func (u *userQuotaUsage) GetUploadTransferQuota() string {
  272. if u.UploadDataTransfer > 0 {
  273. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedUploadDataTransfer),
  274. util.ByteCountIEC(u.UploadDataTransfer*1048576))
  275. }
  276. if u.UsedUploadDataTransfer > 0 {
  277. return util.ByteCountIEC(u.UsedUploadDataTransfer)
  278. }
  279. return ""
  280. }
  281. func (u *userQuotaUsage) GetDownloadTransferQuota() string {
  282. if u.DownloadDataTransfer > 0 {
  283. return fmt.Sprintf("%s/%s", util.ByteCountIEC(u.UsedDownloadDataTransfer),
  284. util.ByteCountIEC(u.DownloadDataTransfer*1048576))
  285. }
  286. if u.UsedDownloadDataTransfer > 0 {
  287. return util.ByteCountIEC(u.UsedDownloadDataTransfer)
  288. }
  289. return ""
  290. }
  291. func (u *userQuotaUsage) GetTotalTransferQuotaPercentage() int {
  292. if u.TotalDataTransfer > 0 {
  293. return int(math.Round(100 * float64(u.UsedDownloadDataTransfer+u.UsedUploadDataTransfer) / float64(u.TotalDataTransfer*1048576)))
  294. }
  295. return 0
  296. }
  297. func (u *userQuotaUsage) GetUploadTransferQuotaPercentage() int {
  298. if u.UploadDataTransfer > 0 {
  299. return int(math.Round(100 * float64(u.UsedUploadDataTransfer) / float64(u.UploadDataTransfer*1048576)))
  300. }
  301. return 0
  302. }
  303. func (u *userQuotaUsage) GetDownloadTransferQuotaPercentage() int {
  304. if u.DownloadDataTransfer > 0 {
  305. return int(math.Round(100 * float64(u.UsedDownloadDataTransfer) / float64(u.DownloadDataTransfer*1048576)))
  306. }
  307. return 0
  308. }
  309. func (u *userQuotaUsage) IsTotalTransferQuotaLow() bool {
  310. if u.TotalDataTransfer > 0 {
  311. return u.GetTotalTransferQuotaPercentage() > 85
  312. }
  313. return false
  314. }
  315. func (u *userQuotaUsage) IsUploadTransferQuotaLow() bool {
  316. if u.UploadDataTransfer > 0 {
  317. return u.GetUploadTransferQuotaPercentage() > 85
  318. }
  319. return false
  320. }
  321. func (u *userQuotaUsage) IsDownloadTransferQuotaLow() bool {
  322. if u.DownloadDataTransfer > 0 {
  323. return u.GetDownloadTransferQuotaPercentage() > 85
  324. }
  325. return false
  326. }
  327. func (u *userQuotaUsage) IsTransferQuotaLow() bool {
  328. return u.IsTotalTransferQuotaLow() || u.IsUploadTransferQuotaLow() || u.IsDownloadTransferQuotaLow()
  329. }
  330. func (u *userQuotaUsage) IsQuotaLow() bool {
  331. return u.IsDiskQuotaLow() || u.IsTransferQuotaLow()
  332. }
  333. func newUserQuotaUsage(u *dataprovider.User) *userQuotaUsage {
  334. return &userQuotaUsage{
  335. QuotaSize: u.QuotaSize,
  336. QuotaFiles: u.QuotaFiles,
  337. UsedQuotaSize: u.UsedQuotaSize,
  338. UsedQuotaFiles: u.UsedQuotaFiles,
  339. TotalDataTransfer: u.TotalDataTransfer,
  340. UploadDataTransfer: u.UploadDataTransfer,
  341. DownloadDataTransfer: u.DownloadDataTransfer,
  342. UsedUploadDataTransfer: u.UsedUploadDataTransfer,
  343. UsedDownloadDataTransfer: u.UsedDownloadDataTransfer,
  344. }
  345. }
  346. func getFileObjectURL(baseDir, name, baseWebPath string) string {
  347. return fmt.Sprintf("%v?path=%v&_=%v", baseWebPath, url.QueryEscape(path.Join(baseDir, name)), time.Now().UTC().Unix())
  348. }
  349. func getFileObjectModTime(t time.Time) int64 {
  350. if isZeroTime(t) {
  351. return 0
  352. }
  353. return t.UnixMilli()
  354. }
  355. func loadClientTemplates(templatesPath string) {
  356. filesPaths := []string{
  357. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  358. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  359. filepath.Join(templatesPath, templateClientDir, templateClientFiles),
  360. }
  361. editFilePath := []string{
  362. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  363. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  364. filepath.Join(templatesPath, templateClientDir, templateClientEditFile),
  365. }
  366. sharesPaths := []string{
  367. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  368. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  369. filepath.Join(templatesPath, templateClientDir, templateClientShares),
  370. }
  371. sharePaths := []string{
  372. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  373. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  374. filepath.Join(templatesPath, templateClientDir, templateClientShare),
  375. }
  376. profilePaths := []string{
  377. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  378. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  379. filepath.Join(templatesPath, templateClientDir, templateClientProfile),
  380. }
  381. changePwdPaths := []string{
  382. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  383. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  384. filepath.Join(templatesPath, templateCommonDir, templateChangePwd),
  385. }
  386. loginPaths := []string{
  387. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  388. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  389. filepath.Join(templatesPath, templateCommonDir, templateCommonLogin),
  390. }
  391. messagePaths := []string{
  392. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  393. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  394. filepath.Join(templatesPath, templateCommonDir, templateMessage),
  395. }
  396. mfaPaths := []string{
  397. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  398. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  399. filepath.Join(templatesPath, templateClientDir, templateClientMFA),
  400. }
  401. twoFactorPaths := []string{
  402. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  403. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  404. filepath.Join(templatesPath, templateCommonDir, templateTwoFactor),
  405. }
  406. twoFactorRecoveryPaths := []string{
  407. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  408. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  409. filepath.Join(templatesPath, templateCommonDir, templateTwoFactorRecovery),
  410. }
  411. forgotPwdPaths := []string{
  412. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  413. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  414. filepath.Join(templatesPath, templateCommonDir, templateForgotPassword),
  415. }
  416. resetPwdPaths := []string{
  417. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  418. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  419. filepath.Join(templatesPath, templateCommonDir, templateResetPassword),
  420. }
  421. viewPDFPaths := []string{
  422. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  423. filepath.Join(templatesPath, templateClientDir, templateClientViewPDF),
  424. }
  425. shareLoginPath := []string{
  426. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  427. filepath.Join(templatesPath, templateCommonDir, templateCommonBaseLogin),
  428. filepath.Join(templatesPath, templateClientDir, templateShareLogin),
  429. }
  430. shareUploadPath := []string{
  431. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  432. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  433. filepath.Join(templatesPath, templateClientDir, templateUploadToShare),
  434. }
  435. shareDownloadPath := []string{
  436. filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
  437. filepath.Join(templatesPath, templateClientDir, templateClientBase),
  438. filepath.Join(templatesPath, templateClientDir, templateShareDownload),
  439. }
  440. filesTmpl := util.LoadTemplate(nil, filesPaths...)
  441. profileTmpl := util.LoadTemplate(nil, profilePaths...)
  442. changePwdTmpl := util.LoadTemplate(nil, changePwdPaths...)
  443. loginTmpl := util.LoadTemplate(nil, loginPaths...)
  444. messageTmpl := util.LoadTemplate(nil, messagePaths...)
  445. mfaTmpl := util.LoadTemplate(nil, mfaPaths...)
  446. twoFactorTmpl := util.LoadTemplate(nil, twoFactorPaths...)
  447. twoFactorRecoveryTmpl := util.LoadTemplate(nil, twoFactorRecoveryPaths...)
  448. editFileTmpl := util.LoadTemplate(nil, editFilePath...)
  449. shareLoginTmpl := util.LoadTemplate(nil, shareLoginPath...)
  450. sharesTmpl := util.LoadTemplate(nil, sharesPaths...)
  451. shareTmpl := util.LoadTemplate(nil, sharePaths...)
  452. forgotPwdTmpl := util.LoadTemplate(nil, forgotPwdPaths...)
  453. resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
  454. viewPDFTmpl := util.LoadTemplate(nil, viewPDFPaths...)
  455. shareUploadTmpl := util.LoadTemplate(nil, shareUploadPath...)
  456. shareDownloadTmpl := util.LoadTemplate(nil, shareDownloadPath...)
  457. clientTemplates[templateClientFiles] = filesTmpl
  458. clientTemplates[templateClientProfile] = profileTmpl
  459. clientTemplates[templateChangePwd] = changePwdTmpl
  460. clientTemplates[templateCommonLogin] = loginTmpl
  461. clientTemplates[templateMessage] = messageTmpl
  462. clientTemplates[templateClientMFA] = mfaTmpl
  463. clientTemplates[templateTwoFactor] = twoFactorTmpl
  464. clientTemplates[templateTwoFactorRecovery] = twoFactorRecoveryTmpl
  465. clientTemplates[templateClientEditFile] = editFileTmpl
  466. clientTemplates[templateClientShares] = sharesTmpl
  467. clientTemplates[templateClientShare] = shareTmpl
  468. clientTemplates[templateForgotPassword] = forgotPwdTmpl
  469. clientTemplates[templateResetPassword] = resetPwdTmpl
  470. clientTemplates[templateClientViewPDF] = viewPDFTmpl
  471. clientTemplates[templateShareLogin] = shareLoginTmpl
  472. clientTemplates[templateUploadToShare] = shareUploadTmpl
  473. clientTemplates[templateShareDownload] = shareDownloadTmpl
  474. }
  475. func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Request) baseClientPage {
  476. var csrfToken string
  477. if currentURL != "" {
  478. csrfToken = createCSRFToken(util.GetIPFromRemoteAddress(r.RemoteAddr))
  479. }
  480. data := baseClientPage{
  481. commonBasePage: getCommonBasePage(r),
  482. Title: title,
  483. CurrentURL: currentURL,
  484. FilesURL: webClientFilesPath,
  485. SharesURL: webClientSharesPath,
  486. ShareURL: webClientSharePath,
  487. ProfileURL: webClientProfilePath,
  488. PingURL: webClientPingPath,
  489. ChangePwdURL: webChangeClientPwdPath,
  490. LogoutURL: webClientLogoutPath,
  491. EditURL: webClientEditFilePath,
  492. MFAURL: webClientMFAPath,
  493. CSRFToken: csrfToken,
  494. LoggedUser: getUserFromToken(r),
  495. Branding: s.binding.Branding.WebClient,
  496. }
  497. if !strings.HasPrefix(r.RequestURI, webClientPubSharesPath) {
  498. data.LoginURL = webClientLoginPath
  499. }
  500. return data
  501. }
  502. func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, r *http.Request, err *util.I18nError, ip string) {
  503. data := forgotPwdPage{
  504. commonBasePage: getCommonBasePage(r),
  505. CurrentURL: webClientForgotPwdPath,
  506. Error: err,
  507. CSRFToken: createCSRFToken(ip),
  508. LoginURL: webClientLoginPath,
  509. Title: util.I18nForgotPwdTitle,
  510. Branding: s.binding.Branding.WebClient,
  511. }
  512. renderClientTemplate(w, templateForgotPassword, data)
  513. }
  514. func (s *httpdServer) renderClientResetPwdPage(w http.ResponseWriter, r *http.Request, err *util.I18nError, ip string) {
  515. data := resetPwdPage{
  516. commonBasePage: getCommonBasePage(r),
  517. CurrentURL: webClientResetPwdPath,
  518. Error: err,
  519. CSRFToken: createCSRFToken(ip),
  520. LoginURL: webClientLoginPath,
  521. Title: util.I18nResetPwdTitle,
  522. Branding: s.binding.Branding.WebClient,
  523. }
  524. renderClientTemplate(w, templateResetPassword, data)
  525. }
  526. func (s *httpdServer) renderShareLoginPage(w http.ResponseWriter, r *http.Request, err *util.I18nError, ip string) {
  527. data := shareLoginPage{
  528. commonBasePage: getCommonBasePage(r),
  529. Title: util.I18nShareLoginTitle,
  530. CurrentURL: r.RequestURI,
  531. Error: err,
  532. CSRFToken: createCSRFToken(ip),
  533. Branding: s.binding.Branding.WebClient,
  534. CheckRedirect: false,
  535. }
  536. renderClientTemplate(w, templateShareLogin, data)
  537. }
  538. func renderClientTemplate(w http.ResponseWriter, tmplName string, data any) {
  539. err := clientTemplates[tmplName].ExecuteTemplate(w, tmplName, data)
  540. if err != nil {
  541. http.Error(w, err.Error(), http.StatusInternalServerError)
  542. }
  543. }
  544. func (s *httpdServer) renderClientMessagePage(w http.ResponseWriter, r *http.Request, title string, statusCode int, err error, message string) {
  545. data := clientMessagePage{
  546. baseClientPage: s.getBaseClientPageData(title, "", r),
  547. Error: getI18nError(err),
  548. Success: message,
  549. }
  550. w.WriteHeader(statusCode)
  551. renderClientTemplate(w, templateMessage, data)
  552. }
  553. func (s *httpdServer) renderClientInternalServerErrorPage(w http.ResponseWriter, r *http.Request, err error) {
  554. s.renderClientMessagePage(w, r, util.I18nError500Title, http.StatusInternalServerError,
  555. util.NewI18nError(err, util.I18nError500Message), "")
  556. }
  557. func (s *httpdServer) renderClientBadRequestPage(w http.ResponseWriter, r *http.Request, err error) {
  558. s.renderClientMessagePage(w, r, util.I18nError400Title, http.StatusBadRequest,
  559. util.NewI18nError(err, util.I18nError400Message), "")
  560. }
  561. func (s *httpdServer) renderClientForbiddenPage(w http.ResponseWriter, r *http.Request, err error) {
  562. s.renderClientMessagePage(w, r, util.I18nError403Title, http.StatusForbidden,
  563. util.NewI18nError(err, util.I18nError403Message), "")
  564. }
  565. func (s *httpdServer) renderClientNotFoundPage(w http.ResponseWriter, r *http.Request, err error) {
  566. s.renderClientMessagePage(w, r, util.I18nError404Title, http.StatusNotFound,
  567. util.NewI18nError(err, util.I18nError404Message), "")
  568. }
  569. func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.Request, err *util.I18nError, ip string) {
  570. data := twoFactorPage{
  571. commonBasePage: getCommonBasePage(r),
  572. Title: pageTwoFactorTitle,
  573. CurrentURL: webClientTwoFactorPath,
  574. Error: err,
  575. CSRFToken: createCSRFToken(ip),
  576. RecoveryURL: webClientTwoFactorRecoveryPath,
  577. Branding: s.binding.Branding.WebClient,
  578. }
  579. if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) {
  580. data.CurrentURL += "?next=" + url.QueryEscape(next)
  581. }
  582. renderClientTemplate(w, templateTwoFactor, data)
  583. }
  584. func (s *httpdServer) renderClientTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, err *util.I18nError, ip string) {
  585. data := twoFactorPage{
  586. commonBasePage: getCommonBasePage(r),
  587. Title: pageTwoFactorRecoveryTitle,
  588. CurrentURL: webClientTwoFactorRecoveryPath,
  589. Error: err,
  590. CSRFToken: createCSRFToken(ip),
  591. Branding: s.binding.Branding.WebClient,
  592. }
  593. renderClientTemplate(w, templateTwoFactorRecovery, data)
  594. }
  595. func (s *httpdServer) renderClientMFAPage(w http.ResponseWriter, r *http.Request) {
  596. data := clientMFAPage{
  597. baseClientPage: s.getBaseClientPageData(util.I18n2FATitle, webClientMFAPath, r),
  598. TOTPConfigs: mfa.GetAvailableTOTPConfigNames(),
  599. GenerateTOTPURL: webClientTOTPGeneratePath,
  600. ValidateTOTPURL: webClientTOTPValidatePath,
  601. SaveTOTPURL: webClientTOTPSavePath,
  602. RecCodesURL: webClientRecoveryCodesPath,
  603. Protocols: dataprovider.MFAProtocols,
  604. }
  605. user, err := dataprovider.GetUserWithGroupSettings(data.LoggedUser.Username, "")
  606. if err != nil {
  607. s.renderClientInternalServerErrorPage(w, r, err)
  608. return
  609. }
  610. data.TOTPConfig = user.Filters.TOTPConfig
  611. data.RequiredProtocols = user.Filters.TwoFactorAuthProtocols
  612. renderClientTemplate(w, templateClientMFA, data)
  613. }
  614. func (s *httpdServer) renderEditFilePage(w http.ResponseWriter, r *http.Request, fileName, fileData string, readOnly bool) {
  615. title := util.I18nViewFileTitle
  616. if !readOnly {
  617. title = util.I18nEditFileTitle
  618. }
  619. data := editFilePage{
  620. baseClientPage: s.getBaseClientPageData(title, webClientEditFilePath, r),
  621. Path: fileName,
  622. Name: path.Base(fileName),
  623. CurrentDir: path.Dir(fileName),
  624. FileURL: webClientFilePath,
  625. ReadOnly: readOnly,
  626. Data: fileData,
  627. }
  628. renderClientTemplate(w, templateClientEditFile, data)
  629. }
  630. func (s *httpdServer) renderAddUpdateSharePage(w http.ResponseWriter, r *http.Request, share *dataprovider.Share,
  631. err *util.I18nError, isAdd bool) {
  632. currentURL := webClientSharePath
  633. title := util.I18nShareAddTitle
  634. if !isAdd {
  635. currentURL = fmt.Sprintf("%v/%v", webClientSharePath, url.PathEscape(share.ShareID))
  636. title = util.I18nShareUpdateTitle
  637. }
  638. if share.IsPasswordHashed() {
  639. share.Password = redactedSecret
  640. }
  641. data := clientSharePage{
  642. baseClientPage: s.getBaseClientPageData(title, currentURL, r),
  643. Share: share,
  644. Error: err,
  645. IsAdd: isAdd,
  646. }
  647. renderClientTemplate(w, templateClientShare, data)
  648. }
  649. func getDirMapping(dirName, baseWebPath string) []dirMapping {
  650. paths := []dirMapping{}
  651. if dirName != "/" {
  652. paths = append(paths, dirMapping{
  653. DirName: path.Base(dirName),
  654. Href: getFileObjectURL("/", dirName, baseWebPath),
  655. })
  656. for {
  657. dirName = path.Dir(dirName)
  658. if dirName == "/" || dirName == "." {
  659. break
  660. }
  661. paths = append([]dirMapping{{
  662. DirName: path.Base(dirName),
  663. Href: getFileObjectURL("/", dirName, baseWebPath)},
  664. }, paths...)
  665. }
  666. }
  667. return paths
  668. }
  669. func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Request, dirName string,
  670. err *util.I18nError, share dataprovider.Share,
  671. ) {
  672. currentURL := path.Join(webClientPubSharesPath, share.ShareID, "browse")
  673. baseData := s.getBaseClientPageData(util.I18nSharedFilesTitle, currentURL, r)
  674. baseData.FilesURL = currentURL
  675. baseSharePath := path.Join(webClientPubSharesPath, share.ShareID)
  676. data := filesPage{
  677. baseClientPage: baseData,
  678. Error: err,
  679. CurrentDir: url.QueryEscape(dirName),
  680. DownloadURL: path.Join(baseSharePath, "partial"),
  681. // dirName must be escaped because the router expects the full path as single argument
  682. ShareUploadBaseURL: path.Join(baseSharePath, url.PathEscape(dirName)),
  683. ViewPDFURL: path.Join(baseSharePath, "viewpdf"),
  684. DirsURL: path.Join(baseSharePath, "dirs"),
  685. FileURL: "",
  686. FileActionsURL: "",
  687. CheckExistURL: path.Join(baseSharePath, "browse", "exist"),
  688. TasksURL: "",
  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. CanCopy: false,
  696. Paths: getDirMapping(dirName, currentURL),
  697. QuotaUsage: newUserQuotaUsage(&dataprovider.User{}),
  698. }
  699. renderClientTemplate(w, templateClientFiles, data)
  700. }
  701. func (s *httpdServer) renderShareDownloadPage(w http.ResponseWriter, r *http.Request, downloadLink string) {
  702. data := shareDownloadPage{
  703. baseClientPage: s.getBaseClientPageData(util.I18nShareDownloadTitle, "", r),
  704. DownloadLink: downloadLink,
  705. }
  706. renderClientTemplate(w, templateShareDownload, data)
  707. }
  708. func (s *httpdServer) renderUploadToSharePage(w http.ResponseWriter, r *http.Request, share dataprovider.Share) {
  709. currentURL := path.Join(webClientPubSharesPath, share.ShareID, "upload")
  710. data := shareUploadPage{
  711. baseClientPage: s.getBaseClientPageData(util.I18nShareUploadTitle, currentURL, r),
  712. Share: &share,
  713. UploadBasePath: path.Join(webClientPubSharesPath, share.ShareID),
  714. }
  715. renderClientTemplate(w, templateUploadToShare, data)
  716. }
  717. func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, dirName string,
  718. err *util.I18nError, user *dataprovider.User) {
  719. data := filesPage{
  720. baseClientPage: s.getBaseClientPageData(util.I18nFilesTitle, webClientFilesPath, r),
  721. Error: err,
  722. CurrentDir: url.QueryEscape(dirName),
  723. DownloadURL: webClientDownloadZipPath,
  724. ViewPDFURL: webClientViewPDFPath,
  725. DirsURL: webClientDirsPath,
  726. FileURL: webClientFilePath,
  727. FileActionsURL: webClientFileActionsPath,
  728. CheckExistURL: webClientExistPath,
  729. TasksURL: webClientTasksPath,
  730. CanAddFiles: user.CanAddFilesFromWeb(dirName),
  731. CanCreateDirs: user.CanAddDirsFromWeb(dirName),
  732. CanRename: user.CanRenameFromWeb(dirName, dirName),
  733. CanDelete: user.CanDeleteFromWeb(dirName),
  734. CanDownload: user.HasPerm(dataprovider.PermDownload, dirName),
  735. CanShare: user.CanManageShares(),
  736. CanCopy: user.CanCopyFromWeb(dirName, dirName),
  737. ShareUploadBaseURL: "",
  738. Paths: getDirMapping(dirName, webClientFilesPath),
  739. QuotaUsage: newUserQuotaUsage(user),
  740. }
  741. renderClientTemplate(w, templateClientFiles, data)
  742. }
  743. func (s *httpdServer) renderClientProfilePage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
  744. data := clientProfilePage{
  745. baseClientPage: s.getBaseClientPageData(util.I18nProfileTitle, webClientProfilePath, r),
  746. Error: err,
  747. }
  748. user, userMerged, errUser := dataprovider.GetUserVariants(data.LoggedUser.Username, "")
  749. if errUser != nil {
  750. s.renderClientInternalServerErrorPage(w, r, errUser)
  751. return
  752. }
  753. data.PublicKeys = user.PublicKeys
  754. data.TLSCerts = user.Filters.TLSCerts
  755. data.AllowAPIKeyAuth = user.Filters.AllowAPIKeyAuth
  756. data.Email = user.Email
  757. data.Description = user.Description
  758. data.CanSubmit = userMerged.CanUpdateProfile()
  759. renderClientTemplate(w, templateClientProfile, data)
  760. }
  761. func (s *httpdServer) renderClientChangePasswordPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
  762. data := changeClientPasswordPage{
  763. baseClientPage: s.getBaseClientPageData(util.I18nChangePwdTitle, webChangeClientPwdPath, r),
  764. Error: err,
  765. }
  766. renderClientTemplate(w, templateChangePwd, data)
  767. }
  768. func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.Request) {
  769. r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
  770. claims, err := getTokenClaims(r)
  771. if err != nil || claims.Username == "" {
  772. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  773. return
  774. }
  775. if err := r.ParseForm(); err != nil {
  776. s.renderClientBadRequestPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidForm))
  777. return
  778. }
  779. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  780. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  781. s.renderClientForbiddenPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF))
  782. return
  783. }
  784. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  785. if err != nil {
  786. s.renderClientMessagePage(w, r, util.I18nError500Title, getRespStatus(err),
  787. util.NewI18nError(err, util.I18nErrorGetUser), "")
  788. return
  789. }
  790. connID := xid.New().String()
  791. protocol := getProtocolFromRequest(r)
  792. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  793. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  794. s.renderClientForbiddenPage(w, r, err)
  795. return
  796. }
  797. connection := &Connection{
  798. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  799. r.RemoteAddr, user),
  800. request: r,
  801. }
  802. if err = common.Connections.Add(connection); err != nil {
  803. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  804. util.NewI18nError(err, util.I18nError429Message), "")
  805. return
  806. }
  807. defer common.Connections.Remove(connection.GetID())
  808. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  809. files := r.Form.Get("files")
  810. var filesList []string
  811. err = json.Unmarshal(util.StringToBytes(files), &filesList)
  812. if err != nil {
  813. s.renderClientBadRequestPage(w, r, err)
  814. return
  815. }
  816. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"",
  817. getCompressedFileName(connection.GetUsername(), filesList)))
  818. renderCompressedFiles(w, connection, name, filesList, nil)
  819. }
  820. func (s *httpdServer) handleClientSharePartialDownload(w http.ResponseWriter, r *http.Request) {
  821. r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
  822. if err := r.ParseForm(); err != nil {
  823. s.renderClientBadRequestPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidForm))
  824. return
  825. }
  826. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  827. share, connection, err := s.checkPublicShare(w, r, validScopes)
  828. if err != nil {
  829. return
  830. }
  831. if err := validateBrowsableShare(share, connection); err != nil {
  832. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  833. return
  834. }
  835. name, err := getBrowsableSharedPath(share.Paths[0], r)
  836. if err != nil {
  837. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  838. return
  839. }
  840. if err = common.Connections.Add(connection); err != nil {
  841. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  842. util.NewI18nError(err, util.I18nError429Message), "")
  843. return
  844. }
  845. defer common.Connections.Remove(connection.GetID())
  846. transferQuota := connection.GetTransferQuota()
  847. if !transferQuota.HasDownloadSpace() {
  848. err = util.NewI18nError(connection.GetReadQuotaExceededError(), util.I18nErrorQuotaRead)
  849. connection.Log(logger.LevelInfo, "denying share read due to quota limits")
  850. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getMappedStatusCode(err), err, "")
  851. return
  852. }
  853. files := r.Form.Get("files")
  854. var filesList []string
  855. err = json.Unmarshal(util.StringToBytes(files), &filesList)
  856. if err != nil {
  857. s.renderClientBadRequestPage(w, r, err)
  858. return
  859. }
  860. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  861. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"",
  862. getCompressedFileName(fmt.Sprintf("share-%s", share.Name), filesList)))
  863. renderCompressedFiles(w, connection, name, filesList, &share)
  864. }
  865. func (s *httpdServer) handleShareGetDirContents(w http.ResponseWriter, r *http.Request) {
  866. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  867. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  868. share, connection, err := s.checkPublicShare(w, r, validScopes)
  869. if err != nil {
  870. return
  871. }
  872. if err := validateBrowsableShare(share, connection); err != nil {
  873. sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError500Message), getRespStatus(err))
  874. return
  875. }
  876. name, err := getBrowsableSharedPath(share.Paths[0], r)
  877. if err != nil {
  878. sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError500Message), getRespStatus(err))
  879. return
  880. }
  881. if err = common.Connections.Add(connection); err != nil {
  882. sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError429Message), http.StatusTooManyRequests)
  883. return
  884. }
  885. defer common.Connections.Remove(connection.GetID())
  886. lister, err := connection.ReadDir(name)
  887. if err != nil {
  888. sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nErrorDirListGeneric), getMappedStatusCode(err))
  889. return
  890. }
  891. defer lister.Close()
  892. dataGetter := func(limit, _ int) ([]byte, int, error) {
  893. contents, err := lister.Next(limit)
  894. if errors.Is(err, io.EOF) {
  895. err = nil
  896. }
  897. if err != nil {
  898. return nil, 0, err
  899. }
  900. results := make([]map[string]any, 0, len(contents))
  901. for _, info := range contents {
  902. if !info.Mode().IsDir() && !info.Mode().IsRegular() {
  903. continue
  904. }
  905. res := make(map[string]any)
  906. if info.IsDir() {
  907. res["type"] = "1"
  908. res["size"] = ""
  909. } else {
  910. res["type"] = "2"
  911. res["size"] = info.Size()
  912. }
  913. res["meta"] = fmt.Sprintf("%v_%v", res["type"], info.Name())
  914. res["name"] = info.Name()
  915. res["url"] = getFileObjectURL(share.GetRelativePath(name), info.Name(),
  916. path.Join(webClientPubSharesPath, share.ShareID, "browse"))
  917. res["last_modified"] = getFileObjectModTime(info.ModTime())
  918. results = append(results, res)
  919. }
  920. data, err := json.Marshal(results)
  921. count := limit
  922. if len(results) == 0 {
  923. count = 0
  924. }
  925. return data, count, err
  926. }
  927. streamJSONArray(w, defaultQueryLimit, dataGetter)
  928. }
  929. func (s *httpdServer) handleClientUploadToShare(w http.ResponseWriter, r *http.Request) {
  930. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  931. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeWrite, dataprovider.ShareScopeReadWrite}
  932. share, _, err := s.checkPublicShare(w, r, validScopes)
  933. if err != nil {
  934. return
  935. }
  936. if share.Scope == dataprovider.ShareScopeReadWrite {
  937. http.Redirect(w, r, path.Join(webClientPubSharesPath, share.ShareID, "browse"), http.StatusFound)
  938. return
  939. }
  940. s.renderUploadToSharePage(w, r, share)
  941. }
  942. func (s *httpdServer) handleShareGetFiles(w http.ResponseWriter, r *http.Request) {
  943. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  944. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  945. share, connection, err := s.checkPublicShare(w, r, validScopes)
  946. if err != nil {
  947. return
  948. }
  949. if err := validateBrowsableShare(share, connection); err != nil {
  950. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  951. return
  952. }
  953. name, err := getBrowsableSharedPath(share.Paths[0], r)
  954. if err != nil {
  955. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  956. return
  957. }
  958. if err = common.Connections.Add(connection); err != nil {
  959. s.renderSharedFilesPage(w, r, path.Dir(share.GetRelativePath(name)),
  960. util.NewI18nError(err, util.I18nError429Message), share)
  961. return
  962. }
  963. defer common.Connections.Remove(connection.GetID())
  964. var info os.FileInfo
  965. if name == "/" {
  966. info = vfs.NewFileInfo(name, true, 0, time.Unix(0, 0), false)
  967. } else {
  968. info, err = connection.Stat(name, 1)
  969. }
  970. if err != nil {
  971. s.renderSharedFilesPage(w, r, path.Dir(share.GetRelativePath(name)),
  972. util.NewI18nError(err, i18nFsMsg(getRespStatus(err))), share)
  973. return
  974. }
  975. if info.IsDir() {
  976. s.renderSharedFilesPage(w, r, share.GetRelativePath(name), nil, share)
  977. return
  978. }
  979. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  980. if status, err := downloadFile(w, r, connection, name, info, false, &share); err != nil {
  981. dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
  982. if status > 0 {
  983. s.renderSharedFilesPage(w, r, path.Dir(share.GetRelativePath(name)),
  984. util.NewI18nError(err, i18nFsMsg(getRespStatus(err))), share)
  985. }
  986. }
  987. }
  988. func (s *httpdServer) handleShareViewPDF(w http.ResponseWriter, r *http.Request) {
  989. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  990. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  991. share, _, err := s.checkPublicShare(w, r, validScopes)
  992. if err != nil {
  993. return
  994. }
  995. name := util.CleanPath(r.URL.Query().Get("path"))
  996. data := viewPDFPage{
  997. commonBasePage: getCommonBasePage(r),
  998. Title: path.Base(name),
  999. URL: fmt.Sprintf("%s?path=%s&_=%d", path.Join(webClientPubSharesPath, share.ShareID, "getpdf"),
  1000. url.QueryEscape(name), time.Now().UTC().Unix()),
  1001. Branding: s.binding.Branding.WebClient,
  1002. }
  1003. renderClientTemplate(w, templateClientViewPDF, data)
  1004. }
  1005. func (s *httpdServer) handleShareGetPDF(w http.ResponseWriter, r *http.Request) {
  1006. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1007. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
  1008. share, connection, err := s.checkPublicShare(w, r, validScopes)
  1009. if err != nil {
  1010. return
  1011. }
  1012. if err := validateBrowsableShare(share, connection); err != nil {
  1013. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  1014. return
  1015. }
  1016. name, err := getBrowsableSharedPath(share.Paths[0], r)
  1017. if err != nil {
  1018. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, getRespStatus(err), err, "")
  1019. return
  1020. }
  1021. if err = common.Connections.Add(connection); err != nil {
  1022. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  1023. util.NewI18nError(err, util.I18nError429Message), "")
  1024. return
  1025. }
  1026. defer common.Connections.Remove(connection.GetID())
  1027. info, err := connection.Stat(name, 1)
  1028. if err != nil {
  1029. status := getRespStatus(err)
  1030. s.renderClientMessagePage(w, r, util.I18nShareAccessErrorTitle, status,
  1031. util.NewI18nError(err, i18nFsMsg(status)), "")
  1032. return
  1033. }
  1034. if info.IsDir() {
  1035. s.renderClientBadRequestPage(w, r, util.NewI18nError(fmt.Errorf("%q is not a file", name), util.I18nErrorPDFMessage))
  1036. return
  1037. }
  1038. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1039. if err := s.ensurePDF(w, r, name, connection); err != nil {
  1040. return
  1041. }
  1042. dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
  1043. if _, err := downloadFile(w, r, connection, name, info, true, &share); err != nil {
  1044. dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
  1045. }
  1046. }
  1047. func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.Request) {
  1048. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1049. claims, err := getTokenClaims(r)
  1050. if err != nil || claims.Username == "" {
  1051. sendAPIResponse(w, r, nil, util.I18nErrorDirList403, http.StatusForbidden)
  1052. return
  1053. }
  1054. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1055. if err != nil {
  1056. sendAPIResponse(w, r, nil, util.I18nErrorDirListUser, getRespStatus(err))
  1057. return
  1058. }
  1059. connID := xid.New().String()
  1060. protocol := getProtocolFromRequest(r)
  1061. connectionID := fmt.Sprintf("%s_%s", protocol, connID)
  1062. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1063. sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nErrorDirList403), http.StatusForbidden)
  1064. return
  1065. }
  1066. connection := &Connection{
  1067. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1068. r.RemoteAddr, user),
  1069. request: r,
  1070. }
  1071. if err = common.Connections.Add(connection); err != nil {
  1072. sendAPIResponse(w, r, err, util.I18nErrorDirList429, http.StatusTooManyRequests)
  1073. return
  1074. }
  1075. defer common.Connections.Remove(connection.GetID())
  1076. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1077. lister, err := connection.ReadDir(name)
  1078. if err != nil {
  1079. statusCode := getMappedStatusCode(err)
  1080. sendAPIResponse(w, r, err, i18nListDirMsg(statusCode), statusCode)
  1081. return
  1082. }
  1083. defer lister.Close()
  1084. dirTree := r.URL.Query().Get("dirtree") == "1"
  1085. dataGetter := func(limit, _ int) ([]byte, int, error) {
  1086. contents, err := lister.Next(limit)
  1087. if errors.Is(err, io.EOF) {
  1088. err = nil
  1089. }
  1090. if err != nil {
  1091. return nil, 0, err
  1092. }
  1093. results := make([]map[string]any, 0, len(contents))
  1094. for _, info := range contents {
  1095. res := make(map[string]any)
  1096. res["url"] = getFileObjectURL(name, info.Name(), webClientFilesPath)
  1097. if info.IsDir() {
  1098. res["type"] = "1"
  1099. res["size"] = ""
  1100. res["dir_path"] = url.QueryEscape(path.Join(name, info.Name()))
  1101. } else {
  1102. if dirTree {
  1103. continue
  1104. }
  1105. res["type"] = "2"
  1106. if info.Mode()&os.ModeSymlink != 0 {
  1107. res["size"] = ""
  1108. } else {
  1109. res["size"] = info.Size()
  1110. if info.Size() < httpdMaxEditFileSize {
  1111. res["edit_url"] = strings.Replace(res["url"].(string), webClientFilesPath, webClientEditFilePath, 1)
  1112. }
  1113. }
  1114. }
  1115. res["meta"] = fmt.Sprintf("%v_%v", res["type"], info.Name())
  1116. res["name"] = info.Name()
  1117. res["last_modified"] = getFileObjectModTime(info.ModTime())
  1118. results = append(results, res)
  1119. }
  1120. data, err := json.Marshal(results)
  1121. count := limit
  1122. if len(results) == 0 {
  1123. count = 0
  1124. }
  1125. return data, count, err
  1126. }
  1127. streamJSONArray(w, defaultQueryLimit, dataGetter)
  1128. }
  1129. func (s *httpdServer) handleClientGetFiles(w http.ResponseWriter, r *http.Request) {
  1130. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1131. claims, err := getTokenClaims(r)
  1132. if err != nil || claims.Username == "" {
  1133. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1134. return
  1135. }
  1136. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1137. if err != nil {
  1138. s.renderClientMessagePage(w, r, util.I18nError500Title, getRespStatus(err),
  1139. util.NewI18nError(err, util.I18nErrorGetUser), "")
  1140. return
  1141. }
  1142. connID := xid.New().String()
  1143. protocol := getProtocolFromRequest(r)
  1144. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1145. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1146. s.renderClientForbiddenPage(w, r, err)
  1147. return
  1148. }
  1149. connection := &Connection{
  1150. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1151. r.RemoteAddr, user),
  1152. request: r,
  1153. }
  1154. if err = common.Connections.Add(connection); err != nil {
  1155. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  1156. util.NewI18nError(err, util.I18nError429Message), "")
  1157. return
  1158. }
  1159. defer common.Connections.Remove(connection.GetID())
  1160. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1161. var info os.FileInfo
  1162. if name == "/" {
  1163. info = vfs.NewFileInfo(name, true, 0, time.Unix(0, 0), false)
  1164. } else {
  1165. info, err = connection.Stat(name, 0)
  1166. }
  1167. if err != nil {
  1168. s.renderFilesPage(w, r, path.Dir(name), util.NewI18nError(err, i18nFsMsg(getRespStatus(err))), &user)
  1169. return
  1170. }
  1171. if info.IsDir() {
  1172. s.renderFilesPage(w, r, name, nil, &user)
  1173. return
  1174. }
  1175. if status, err := downloadFile(w, r, connection, name, info, false, nil); err != nil && status != 0 {
  1176. if status > 0 {
  1177. if status == http.StatusRequestedRangeNotSatisfiable {
  1178. s.renderClientMessagePage(w, r, util.I18nError416Title, status,
  1179. util.NewI18nError(err, util.I18nError416Message), "")
  1180. return
  1181. }
  1182. s.renderFilesPage(w, r, path.Dir(name), util.NewI18nError(err, i18nFsMsg(status)), &user)
  1183. }
  1184. }
  1185. }
  1186. func (s *httpdServer) handleClientEditFile(w http.ResponseWriter, r *http.Request) {
  1187. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1188. claims, err := getTokenClaims(r)
  1189. if err != nil || claims.Username == "" {
  1190. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1191. return
  1192. }
  1193. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1194. if err != nil {
  1195. s.renderClientMessagePage(w, r, util.I18nError500Title, getRespStatus(err),
  1196. util.NewI18nError(err, util.I18nErrorGetUser), "")
  1197. return
  1198. }
  1199. connID := xid.New().String()
  1200. protocol := getProtocolFromRequest(r)
  1201. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1202. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1203. s.renderClientForbiddenPage(w, r, err)
  1204. return
  1205. }
  1206. connection := &Connection{
  1207. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1208. r.RemoteAddr, user),
  1209. request: r,
  1210. }
  1211. if err = common.Connections.Add(connection); err != nil {
  1212. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  1213. util.NewI18nError(err, util.I18nError429Message), "")
  1214. return
  1215. }
  1216. defer common.Connections.Remove(connection.GetID())
  1217. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1218. info, err := connection.Stat(name, 0)
  1219. if err != nil {
  1220. status := getRespStatus(err)
  1221. s.renderClientMessagePage(w, r, util.I18nErrorEditorTitle, status, util.NewI18nError(err, i18nFsMsg(status)), "")
  1222. return
  1223. }
  1224. if info.IsDir() {
  1225. s.renderClientMessagePage(w, r, util.I18nErrorEditorTitle, http.StatusBadRequest,
  1226. util.NewI18nError(
  1227. util.NewValidationError(fmt.Sprintf("The path %q does not point to a file", name)),
  1228. util.I18nErrorEditDir,
  1229. ), "")
  1230. return
  1231. }
  1232. if info.Size() > httpdMaxEditFileSize {
  1233. s.renderClientMessagePage(w, r, util.I18nErrorEditorTitle, http.StatusBadRequest,
  1234. util.NewI18nError(
  1235. util.NewValidationError(fmt.Sprintf("The file size %v for %q exceeds the maximum allowed size",
  1236. util.ByteCountIEC(info.Size()), name)),
  1237. util.I18nErrorEditSize,
  1238. ), "")
  1239. return
  1240. }
  1241. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1242. reader, err := connection.getFileReader(name, 0, r.Method)
  1243. if err != nil {
  1244. s.renderClientMessagePage(w, r, util.I18nErrorEditorTitle, getRespStatus(err),
  1245. util.NewI18nError(err, util.I18nError500Message), "")
  1246. return
  1247. }
  1248. defer reader.Close()
  1249. var b bytes.Buffer
  1250. _, err = io.Copy(&b, reader)
  1251. if err != nil {
  1252. s.renderClientMessagePage(w, r, util.I18nErrorEditorTitle, getRespStatus(err),
  1253. util.NewI18nError(err, util.I18nError500Message), "")
  1254. return
  1255. }
  1256. s.renderEditFilePage(w, r, name, b.String(), !user.CanAddFilesFromWeb(path.Dir(name)))
  1257. }
  1258. func (s *httpdServer) handleClientAddShareGet(w http.ResponseWriter, r *http.Request) {
  1259. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1260. claims, err := getTokenClaims(r)
  1261. if err != nil || claims.Username == "" {
  1262. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1263. return
  1264. }
  1265. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1266. if err != nil {
  1267. s.renderClientMessagePage(w, r, util.I18nError500Title, getRespStatus(err),
  1268. util.NewI18nError(err, util.I18nErrorGetUser), "")
  1269. return
  1270. }
  1271. share := &dataprovider.Share{Scope: dataprovider.ShareScopeRead}
  1272. if user.Filters.DefaultSharesExpiration > 0 {
  1273. share.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(24 * time.Hour * time.Duration(user.Filters.DefaultSharesExpiration)))
  1274. } else if user.Filters.MaxSharesExpiration > 0 {
  1275. share.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(24 * time.Hour * time.Duration(user.Filters.MaxSharesExpiration)))
  1276. }
  1277. dirName := "/"
  1278. if _, ok := r.URL.Query()["path"]; ok {
  1279. dirName = util.CleanPath(r.URL.Query().Get("path"))
  1280. }
  1281. if _, ok := r.URL.Query()["files"]; ok {
  1282. files := r.URL.Query().Get("files")
  1283. var filesList []string
  1284. err := json.Unmarshal(util.StringToBytes(files), &filesList)
  1285. if err != nil {
  1286. s.renderClientBadRequestPage(w, r, err)
  1287. return
  1288. }
  1289. for _, f := range filesList {
  1290. if f != "" {
  1291. share.Paths = append(share.Paths, path.Join(dirName, f))
  1292. }
  1293. }
  1294. }
  1295. s.renderAddUpdateSharePage(w, r, share, nil, true)
  1296. }
  1297. func (s *httpdServer) handleClientUpdateShareGet(w http.ResponseWriter, r *http.Request) {
  1298. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1299. claims, err := getTokenClaims(r)
  1300. if err != nil || claims.Username == "" {
  1301. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1302. return
  1303. }
  1304. shareID := getURLParam(r, "id")
  1305. share, err := dataprovider.ShareExists(shareID, claims.Username)
  1306. if err == nil {
  1307. s.renderAddUpdateSharePage(w, r, &share, nil, false)
  1308. } else if errors.Is(err, util.ErrNotFound) {
  1309. s.renderClientNotFoundPage(w, r, err)
  1310. } else {
  1311. s.renderClientInternalServerErrorPage(w, r, err)
  1312. }
  1313. }
  1314. func (s *httpdServer) handleClientAddSharePost(w http.ResponseWriter, r *http.Request) {
  1315. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1316. claims, err := getTokenClaims(r)
  1317. if err != nil || claims.Username == "" {
  1318. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1319. return
  1320. }
  1321. share, err := getShareFromPostFields(r)
  1322. if err != nil {
  1323. s.renderAddUpdateSharePage(w, r, share, util.NewI18nError(err, util.I18nError500Message), true)
  1324. return
  1325. }
  1326. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1327. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1328. s.renderClientForbiddenPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF))
  1329. return
  1330. }
  1331. share.ID = 0
  1332. share.ShareID = util.GenerateUniqueID()
  1333. share.LastUseAt = 0
  1334. share.Username = claims.Username
  1335. if share.Password == "" {
  1336. if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
  1337. s.renderAddUpdateSharePage(w, r, share,
  1338. util.NewI18nError(util.NewValidationError("You are not allowed to share files/folders without password"), util.I18nErrorShareNoPwd),
  1339. true)
  1340. return
  1341. }
  1342. }
  1343. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1344. if err != nil {
  1345. s.renderAddUpdateSharePage(w, r, share, util.NewI18nError(err, util.I18nErrorGetUser), true)
  1346. return
  1347. }
  1348. if err := user.CheckMaxShareExpiration(util.GetTimeFromMsecSinceEpoch(share.ExpiresAt)); err != nil {
  1349. s.renderAddUpdateSharePage(w, r, share, util.NewI18nError(
  1350. err,
  1351. util.I18nErrorShareExpirationOutOfRange,
  1352. util.I18nErrorArgs(
  1353. map[string]any{
  1354. "val": time.Now().Add(24 * time.Hour * time.Duration(user.Filters.MaxSharesExpiration+1)).UnixMilli(),
  1355. "formatParams": map[string]string{
  1356. "year": "numeric",
  1357. "month": "numeric",
  1358. "day": "numeric",
  1359. },
  1360. },
  1361. ),
  1362. ), true)
  1363. return
  1364. }
  1365. err = dataprovider.AddShare(share, claims.Username, ipAddr, claims.Role)
  1366. if err == nil {
  1367. http.Redirect(w, r, webClientSharesPath, http.StatusSeeOther)
  1368. } else {
  1369. s.renderAddUpdateSharePage(w, r, share, util.NewI18nError(err, util.I18nErrorShareGeneric), true)
  1370. }
  1371. }
  1372. func (s *httpdServer) handleClientUpdateSharePost(w http.ResponseWriter, r *http.Request) {
  1373. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1374. claims, err := getTokenClaims(r)
  1375. if err != nil || claims.Username == "" {
  1376. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1377. return
  1378. }
  1379. shareID := getURLParam(r, "id")
  1380. share, err := dataprovider.ShareExists(shareID, claims.Username)
  1381. if errors.Is(err, util.ErrNotFound) {
  1382. s.renderClientNotFoundPage(w, r, err)
  1383. return
  1384. } else if err != nil {
  1385. s.renderClientInternalServerErrorPage(w, r, err)
  1386. return
  1387. }
  1388. updatedShare, err := getShareFromPostFields(r)
  1389. if err != nil {
  1390. s.renderAddUpdateSharePage(w, r, updatedShare, util.NewI18nError(err, util.I18nError500Message), false)
  1391. return
  1392. }
  1393. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1394. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1395. s.renderClientForbiddenPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF))
  1396. return
  1397. }
  1398. updatedShare.ShareID = shareID
  1399. updatedShare.Username = claims.Username
  1400. if updatedShare.Password == redactedSecret {
  1401. updatedShare.Password = share.Password
  1402. }
  1403. if updatedShare.Password == "" {
  1404. if util.Contains(claims.Permissions, sdk.WebClientShareNoPasswordDisabled) {
  1405. s.renderAddUpdateSharePage(w, r, updatedShare,
  1406. util.NewI18nError(util.NewValidationError("You are not allowed to share files/folders without password"), util.I18nErrorShareNoPwd),
  1407. false)
  1408. return
  1409. }
  1410. }
  1411. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1412. if err != nil {
  1413. s.renderAddUpdateSharePage(w, r, updatedShare, util.NewI18nError(err, util.I18nErrorGetUser), false)
  1414. return
  1415. }
  1416. if err := user.CheckMaxShareExpiration(util.GetTimeFromMsecSinceEpoch(updatedShare.ExpiresAt)); err != nil {
  1417. s.renderAddUpdateSharePage(w, r, updatedShare, util.NewI18nError(
  1418. err,
  1419. util.I18nErrorShareExpirationOutOfRange,
  1420. util.I18nErrorArgs(
  1421. map[string]any{
  1422. "val": time.Now().Add(24 * time.Hour * time.Duration(user.Filters.MaxSharesExpiration+1)).UnixMilli(),
  1423. "formatParams": map[string]string{
  1424. "year": "numeric",
  1425. "month": "numeric",
  1426. "day": "numeric",
  1427. },
  1428. },
  1429. ),
  1430. ), false)
  1431. return
  1432. }
  1433. err = dataprovider.UpdateShare(updatedShare, claims.Username, ipAddr, claims.Role)
  1434. if err == nil {
  1435. http.Redirect(w, r, webClientSharesPath, http.StatusSeeOther)
  1436. } else {
  1437. s.renderAddUpdateSharePage(w, r, updatedShare, util.NewI18nError(err, util.I18nErrorShareGeneric), false)
  1438. }
  1439. }
  1440. func getAllShares(w http.ResponseWriter, r *http.Request) {
  1441. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1442. claims, err := getTokenClaims(r)
  1443. if err != nil || claims.Username == "" {
  1444. sendAPIResponse(w, r, nil, util.I18nErrorInvalidToken, http.StatusForbidden)
  1445. return
  1446. }
  1447. dataGetter := func(limit, offset int) ([]byte, int, error) {
  1448. shares, err := dataprovider.GetShares(limit, offset, dataprovider.OrderASC, claims.Username)
  1449. if err != nil {
  1450. return nil, 0, err
  1451. }
  1452. data, err := json.Marshal(shares)
  1453. return data, len(shares), err
  1454. }
  1455. streamJSONArray(w, defaultQueryLimit, dataGetter)
  1456. }
  1457. func (s *httpdServer) handleClientGetShares(w http.ResponseWriter, r *http.Request) {
  1458. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1459. data := clientSharesPage{
  1460. baseClientPage: s.getBaseClientPageData(util.I18nSharesTitle, webClientSharesPath, r),
  1461. BasePublicSharesURL: webClientPubSharesPath,
  1462. }
  1463. renderClientTemplate(w, templateClientShares, data)
  1464. }
  1465. func (s *httpdServer) handleClientGetProfile(w http.ResponseWriter, r *http.Request) {
  1466. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1467. s.renderClientProfilePage(w, r, nil)
  1468. }
  1469. func (s *httpdServer) handleWebClientChangePwd(w http.ResponseWriter, r *http.Request) {
  1470. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1471. s.renderClientChangePasswordPage(w, r, nil)
  1472. }
  1473. func (s *httpdServer) handleWebClientProfilePost(w http.ResponseWriter, r *http.Request) { //nolint:gocyclo
  1474. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1475. err := r.ParseForm()
  1476. if err != nil {
  1477. s.renderClientProfilePage(w, r, util.NewI18nError(err, util.I18nErrorInvalidForm))
  1478. return
  1479. }
  1480. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1481. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1482. s.renderClientForbiddenPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF))
  1483. return
  1484. }
  1485. claims, err := getTokenClaims(r)
  1486. if err != nil || claims.Username == "" {
  1487. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1488. return
  1489. }
  1490. user, userMerged, err := dataprovider.GetUserVariants(claims.Username, "")
  1491. if err != nil {
  1492. s.renderClientProfilePage(w, r, util.NewI18nError(err, util.I18nErrorGetUser))
  1493. return
  1494. }
  1495. if !userMerged.CanUpdateProfile() {
  1496. s.renderClientForbiddenPage(w, r, util.NewI18nError(
  1497. errors.New("you are not allowed to change anything"),
  1498. util.I18nErrorNoPermissions,
  1499. ))
  1500. return
  1501. }
  1502. if userMerged.CanManagePublicKeys() {
  1503. for k := range r.Form {
  1504. if hasPrefixAndSuffix(k, "public_keys[", "][public_key]") {
  1505. r.Form.Add("public_keys", r.Form.Get(k))
  1506. }
  1507. }
  1508. user.PublicKeys = r.Form["public_keys"]
  1509. }
  1510. if userMerged.CanManageTLSCerts() {
  1511. for k := range r.Form {
  1512. if hasPrefixAndSuffix(k, "tls_certs[", "][tls_cert]") {
  1513. r.Form.Add("tls_certs", r.Form.Get(k))
  1514. }
  1515. }
  1516. user.Filters.TLSCerts = r.Form["tls_certs"]
  1517. }
  1518. if userMerged.CanChangeAPIKeyAuth() {
  1519. user.Filters.AllowAPIKeyAuth = r.Form.Get("allow_api_key_auth") != ""
  1520. }
  1521. if userMerged.CanChangeInfo() {
  1522. user.Email = strings.TrimSpace(r.Form.Get("email"))
  1523. user.Description = r.Form.Get("description")
  1524. }
  1525. err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSelf, ipAddr, user.Role)
  1526. if err != nil {
  1527. s.renderClientProfilePage(w, r, util.NewI18nError(err, util.I18nError500Message))
  1528. return
  1529. }
  1530. s.renderClientMessagePage(w, r, util.I18nProfileTitle, http.StatusOK, nil, util.I18nProfileUpdated)
  1531. }
  1532. func (s *httpdServer) handleWebClientMFA(w http.ResponseWriter, r *http.Request) {
  1533. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1534. s.renderClientMFAPage(w, r)
  1535. }
  1536. func (s *httpdServer) handleWebClientTwoFactor(w http.ResponseWriter, r *http.Request) {
  1537. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1538. s.renderClientTwoFactorPage(w, r, nil, util.GetIPFromRemoteAddress(r.RemoteAddr))
  1539. }
  1540. func (s *httpdServer) handleWebClientTwoFactorRecovery(w http.ResponseWriter, r *http.Request) {
  1541. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1542. s.renderClientTwoFactorRecoveryPage(w, r, nil, util.GetIPFromRemoteAddress(r.RemoteAddr))
  1543. }
  1544. func getShareFromPostFields(r *http.Request) (*dataprovider.Share, error) {
  1545. share := &dataprovider.Share{}
  1546. if err := r.ParseForm(); err != nil {
  1547. return share, util.NewI18nError(err, util.I18nErrorInvalidForm)
  1548. }
  1549. for k := range r.Form {
  1550. if hasPrefixAndSuffix(k, "paths[", "][path]") {
  1551. r.Form.Add("paths", r.Form.Get(k))
  1552. }
  1553. }
  1554. share.Name = strings.TrimSpace(r.Form.Get("name"))
  1555. share.Description = r.Form.Get("description")
  1556. for _, p := range r.Form["paths"] {
  1557. if strings.TrimSpace(p) != "" {
  1558. share.Paths = append(share.Paths, p)
  1559. }
  1560. }
  1561. share.Password = strings.TrimSpace(r.Form.Get("password"))
  1562. share.AllowFrom = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  1563. scope, err := strconv.Atoi(r.Form.Get("scope"))
  1564. if err != nil {
  1565. return share, util.NewI18nError(err, util.I18nErrorShareScope)
  1566. }
  1567. share.Scope = dataprovider.ShareScope(scope)
  1568. maxTokens, err := strconv.Atoi(r.Form.Get("max_tokens"))
  1569. if err != nil {
  1570. return share, util.NewI18nError(err, util.I18nErrorShareMaxTokens)
  1571. }
  1572. share.MaxTokens = maxTokens
  1573. expirationDateMillis := int64(0)
  1574. expirationDateString := strings.TrimSpace(r.Form.Get("expiration_date"))
  1575. if expirationDateString != "" {
  1576. expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
  1577. if err != nil {
  1578. return share, util.NewI18nError(err, util.I18nErrorShareExpiration)
  1579. }
  1580. expirationDateMillis = util.GetTimeAsMsSinceEpoch(expirationDate)
  1581. }
  1582. share.ExpiresAt = expirationDateMillis
  1583. return share, nil
  1584. }
  1585. func (s *httpdServer) handleWebClientForgotPwd(w http.ResponseWriter, r *http.Request) {
  1586. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1587. if !smtp.IsEnabled() {
  1588. s.renderClientNotFoundPage(w, r, errors.New("this page does not exist"))
  1589. return
  1590. }
  1591. s.renderClientForgotPwdPage(w, r, nil, util.GetIPFromRemoteAddress(r.RemoteAddr))
  1592. }
  1593. func (s *httpdServer) handleWebClientForgotPwdPost(w http.ResponseWriter, r *http.Request) {
  1594. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1595. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1596. err := r.ParseForm()
  1597. if err != nil {
  1598. s.renderClientForgotPwdPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidForm), ipAddr)
  1599. return
  1600. }
  1601. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1602. s.renderClientForbiddenPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF))
  1603. return
  1604. }
  1605. username := strings.TrimSpace(r.Form.Get("username"))
  1606. err = handleForgotPassword(r, username, false)
  1607. if err != nil {
  1608. s.renderClientForgotPwdPage(w, r, util.NewI18nError(err, util.I18nErrorPwdResetGeneric), ipAddr)
  1609. return
  1610. }
  1611. http.Redirect(w, r, webClientResetPwdPath, http.StatusFound)
  1612. }
  1613. func (s *httpdServer) handleWebClientPasswordReset(w http.ResponseWriter, r *http.Request) {
  1614. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1615. if !smtp.IsEnabled() {
  1616. s.renderClientNotFoundPage(w, r, errors.New("this page does not exist"))
  1617. return
  1618. }
  1619. s.renderClientResetPwdPage(w, r, nil, util.GetIPFromRemoteAddress(r.RemoteAddr))
  1620. }
  1621. func (s *httpdServer) handleClientViewPDF(w http.ResponseWriter, r *http.Request) {
  1622. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1623. name := r.URL.Query().Get("path")
  1624. if name == "" {
  1625. s.renderClientBadRequestPage(w, r, errors.New("no file specified"))
  1626. return
  1627. }
  1628. name = util.CleanPath(name)
  1629. data := viewPDFPage{
  1630. commonBasePage: getCommonBasePage(r),
  1631. Title: path.Base(name),
  1632. URL: fmt.Sprintf("%s?path=%s&_=%d", webClientGetPDFPath, url.QueryEscape(name), time.Now().UTC().Unix()),
  1633. Branding: s.binding.Branding.WebClient,
  1634. }
  1635. renderClientTemplate(w, templateClientViewPDF, data)
  1636. }
  1637. func (s *httpdServer) handleClientGetPDF(w http.ResponseWriter, r *http.Request) {
  1638. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1639. claims, err := getTokenClaims(r)
  1640. if err != nil || claims.Username == "" {
  1641. s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken))
  1642. return
  1643. }
  1644. name := r.URL.Query().Get("path")
  1645. if name == "" {
  1646. s.renderClientBadRequestPage(w, r, util.NewI18nError(errors.New("no file specified"), util.I18nError400Message))
  1647. return
  1648. }
  1649. name = util.CleanPath(name)
  1650. user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
  1651. if err != nil {
  1652. s.renderClientMessagePage(w, r, util.I18nError500Title, getRespStatus(err),
  1653. util.NewI18nError(err, util.I18nErrorGetUser), "")
  1654. return
  1655. }
  1656. connID := xid.New().String()
  1657. protocol := getProtocolFromRequest(r)
  1658. connectionID := fmt.Sprintf("%v_%v", protocol, connID)
  1659. if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
  1660. s.renderClientForbiddenPage(w, r, err)
  1661. return
  1662. }
  1663. connection := &Connection{
  1664. BaseConnection: common.NewBaseConnection(connID, protocol, util.GetHTTPLocalAddress(r),
  1665. r.RemoteAddr, user),
  1666. request: r,
  1667. }
  1668. if err = common.Connections.Add(connection); err != nil {
  1669. s.renderClientMessagePage(w, r, util.I18nError429Title, http.StatusTooManyRequests,
  1670. util.NewI18nError(err, util.I18nError429Message), "")
  1671. return
  1672. }
  1673. defer common.Connections.Remove(connection.GetID())
  1674. info, err := connection.Stat(name, 0)
  1675. if err != nil {
  1676. status := getRespStatus(err)
  1677. s.renderClientMessagePage(w, r, util.I18nErrorPDFTitle, status, util.NewI18nError(err, i18nFsMsg(status)), "")
  1678. return
  1679. }
  1680. if info.IsDir() {
  1681. s.renderClientBadRequestPage(w, r, util.NewI18nError(fmt.Errorf("%q is not a file", name), util.I18nErrorPDFMessage))
  1682. return
  1683. }
  1684. connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
  1685. if err := s.ensurePDF(w, r, name, connection); err != nil {
  1686. return
  1687. }
  1688. downloadFile(w, r, connection, name, info, true, nil) //nolint:errcheck
  1689. }
  1690. func (s *httpdServer) ensurePDF(w http.ResponseWriter, r *http.Request, name string, connection *Connection) error {
  1691. reader, err := connection.getFileReader(name, 0, r.Method)
  1692. if err != nil {
  1693. s.renderClientMessagePage(w, r, util.I18nErrorPDFTitle,
  1694. getRespStatus(err), util.NewI18nError(err, util.I18nError500Message), "")
  1695. return err
  1696. }
  1697. defer reader.Close()
  1698. var b bytes.Buffer
  1699. _, err = io.CopyN(&b, reader, 128)
  1700. if err != nil {
  1701. s.renderClientMessagePage(w, r, util.I18nErrorPDFTitle, getRespStatus(err),
  1702. util.NewI18nError(err, util.I18nErrorPDFMessage), "")
  1703. return err
  1704. }
  1705. if ctype := http.DetectContentType(b.Bytes()); ctype != "application/pdf" {
  1706. connection.Log(logger.LevelDebug, "detected %q content type, expected PDF, file %q", ctype, name)
  1707. err := fmt.Errorf("the file %q does not look like a PDF", name)
  1708. s.renderClientBadRequestPage(w, r, util.NewI18nError(err, util.I18nErrorPDFMessage))
  1709. return err
  1710. }
  1711. return nil
  1712. }
  1713. func (s *httpdServer) handleClientShareLoginGet(w http.ResponseWriter, r *http.Request) {
  1714. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1715. s.renderShareLoginPage(w, r, nil, util.GetIPFromRemoteAddress(r.RemoteAddr))
  1716. }
  1717. func (s *httpdServer) handleClientShareLoginPost(w http.ResponseWriter, r *http.Request) {
  1718. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1719. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  1720. if err := r.ParseForm(); err != nil {
  1721. s.renderShareLoginPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidForm), ipAddr)
  1722. return
  1723. }
  1724. if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
  1725. s.renderShareLoginPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCSRF), ipAddr)
  1726. return
  1727. }
  1728. shareID := getURLParam(r, "id")
  1729. share, err := dataprovider.ShareExists(shareID, "")
  1730. if err != nil {
  1731. s.renderShareLoginPage(w, r, util.NewI18nError(err, util.I18nErrorInvalidCredentials), ipAddr)
  1732. return
  1733. }
  1734. match, err := share.CheckCredentials(strings.TrimSpace(r.Form.Get("share_password")))
  1735. if !match || err != nil {
  1736. s.renderShareLoginPage(w, r, util.NewI18nError(dataprovider.ErrInvalidCredentials, util.I18nErrorInvalidCredentials),
  1737. ipAddr)
  1738. return
  1739. }
  1740. c := jwtTokenClaims{
  1741. Username: shareID,
  1742. }
  1743. err = c.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebShare, ipAddr)
  1744. if err != nil {
  1745. s.renderShareLoginPage(w, r, util.NewI18nError(err, util.I18nError500Message), ipAddr)
  1746. return
  1747. }
  1748. next := path.Clean(r.URL.Query().Get("next"))
  1749. baseShareURL := path.Join(webClientPubSharesPath, share.ShareID)
  1750. isRedirect, redirectTo := checkShareRedirectURL(next, baseShareURL)
  1751. if isRedirect {
  1752. http.Redirect(w, r, redirectTo, http.StatusFound)
  1753. return
  1754. }
  1755. s.renderClientMessagePage(w, r, util.I18nSharedFilesTitle, http.StatusOK, nil, util.I18nShareLoginOK)
  1756. }
  1757. func (s *httpdServer) handleClientSharedFile(w http.ResponseWriter, r *http.Request) {
  1758. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1759. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead}
  1760. share, _, err := s.checkPublicShare(w, r, validScopes)
  1761. if err != nil {
  1762. return
  1763. }
  1764. query := ""
  1765. if r.URL.RawQuery != "" {
  1766. query = "?" + r.URL.RawQuery
  1767. }
  1768. s.renderShareDownloadPage(w, r, path.Join(webClientPubSharesPath, share.ShareID)+query)
  1769. }
  1770. func (s *httpdServer) handleClientCheckExist(w http.ResponseWriter, r *http.Request) {
  1771. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1772. connection, err := getUserConnection(w, r)
  1773. if err != nil {
  1774. return
  1775. }
  1776. defer common.Connections.Remove(connection.GetID())
  1777. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1778. doCheckExist(w, r, connection, name)
  1779. }
  1780. func (s *httpdServer) handleClientShareCheckExist(w http.ResponseWriter, r *http.Request) {
  1781. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1782. validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeReadWrite}
  1783. share, connection, err := s.checkPublicShare(w, r, validScopes)
  1784. if err != nil {
  1785. return
  1786. }
  1787. if err := validateBrowsableShare(share, connection); err != nil {
  1788. sendAPIResponse(w, r, err, "", getRespStatus(err))
  1789. return
  1790. }
  1791. name, err := getBrowsableSharedPath(share.Paths[0], r)
  1792. if err != nil {
  1793. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  1794. return
  1795. }
  1796. if err = common.Connections.Add(connection); err != nil {
  1797. sendAPIResponse(w, r, err, "Unable to add connection", http.StatusTooManyRequests)
  1798. return
  1799. }
  1800. defer common.Connections.Remove(connection.GetID())
  1801. doCheckExist(w, r, connection, name)
  1802. }
  1803. type filesToCheck struct {
  1804. Files []string `json:"files"`
  1805. }
  1806. func doCheckExist(w http.ResponseWriter, r *http.Request, connection *Connection, name string) {
  1807. var filesList filesToCheck
  1808. err := render.DecodeJSON(r.Body, &filesList)
  1809. if err != nil {
  1810. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  1811. return
  1812. }
  1813. if len(filesList.Files) == 0 {
  1814. sendAPIResponse(w, r, errors.New("files to be checked are mandatory"), "", http.StatusBadRequest)
  1815. return
  1816. }
  1817. lister, err := connection.ListDir(name)
  1818. if err != nil {
  1819. sendAPIResponse(w, r, err, "Unable to get directory contents", getMappedStatusCode(err))
  1820. return
  1821. }
  1822. defer lister.Close()
  1823. dataGetter := func(limit, _ int) ([]byte, int, error) {
  1824. contents, err := lister.Next(limit)
  1825. if errors.Is(err, io.EOF) {
  1826. err = nil
  1827. }
  1828. if err != nil {
  1829. return nil, 0, err
  1830. }
  1831. existing := make([]map[string]any, 0)
  1832. for _, info := range contents {
  1833. if util.Contains(filesList.Files, info.Name()) {
  1834. res := make(map[string]any)
  1835. res["name"] = info.Name()
  1836. if info.IsDir() {
  1837. res["type"] = "1"
  1838. res["size"] = ""
  1839. } else {
  1840. res["type"] = "2"
  1841. res["size"] = info.Size()
  1842. }
  1843. existing = append(existing, res)
  1844. }
  1845. }
  1846. data, err := json.Marshal(existing)
  1847. count := limit
  1848. if len(existing) == 0 {
  1849. count = 0
  1850. }
  1851. return data, count, err
  1852. }
  1853. streamJSONArray(w, defaultQueryLimit, dataGetter)
  1854. }
  1855. func checkShareRedirectURL(next, base string) (bool, string) {
  1856. if !strings.HasPrefix(next, base) {
  1857. return false, ""
  1858. }
  1859. if next == base {
  1860. return true, path.Join(next, "download")
  1861. }
  1862. baseURL, err := url.Parse(base)
  1863. if err != nil {
  1864. return false, ""
  1865. }
  1866. nextURL, err := url.Parse(next)
  1867. if err != nil {
  1868. return false, ""
  1869. }
  1870. if nextURL.Path == baseURL.Path {
  1871. redirectURL := nextURL.JoinPath("download")
  1872. return true, redirectURL.String()
  1873. }
  1874. return true, next
  1875. }
  1876. func getWebTask(w http.ResponseWriter, r *http.Request) {
  1877. r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
  1878. claims, err := getTokenClaims(r)
  1879. if err != nil || claims.Username == "" {
  1880. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  1881. return
  1882. }
  1883. taskID := getURLParam(r, "id")
  1884. task, err := webTaskMgr.Get(taskID)
  1885. if err != nil {
  1886. sendAPIResponse(w, r, err, "Unable to get task", getMappedStatusCode(err))
  1887. return
  1888. }
  1889. if task.User != claims.Username {
  1890. sendAPIResponse(w, r, nil, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  1891. return
  1892. }
  1893. render.JSON(w, r, task)
  1894. }
  1895. func taskDeleteDir(w http.ResponseWriter, r *http.Request) {
  1896. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1897. connection, err := getUserConnection(w, r)
  1898. if err != nil {
  1899. return
  1900. }
  1901. name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1902. task := webTaskData{
  1903. ID: connection.GetID(),
  1904. User: connection.GetUsername(),
  1905. Path: name,
  1906. Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
  1907. Status: 0,
  1908. }
  1909. if err := webTaskMgr.Add(task); err != nil {
  1910. common.Connections.Remove(connection.GetID())
  1911. sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
  1912. return
  1913. }
  1914. go executeDeleteTask(connection, task)
  1915. sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
  1916. }
  1917. func taskRenameFsEntry(w http.ResponseWriter, r *http.Request) {
  1918. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1919. connection, err := getUserConnection(w, r)
  1920. if err != nil {
  1921. return
  1922. }
  1923. oldName := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
  1924. newName := connection.User.GetCleanedPath(r.URL.Query().Get("target"))
  1925. task := webTaskData{
  1926. ID: connection.GetID(),
  1927. User: connection.GetUsername(),
  1928. Path: oldName,
  1929. Target: newName,
  1930. Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
  1931. Status: 0,
  1932. }
  1933. if err := webTaskMgr.Add(task); err != nil {
  1934. common.Connections.Remove(connection.GetID())
  1935. sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
  1936. return
  1937. }
  1938. go executeRenameTask(connection, task)
  1939. sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
  1940. }
  1941. func taskCopyFsEntry(w http.ResponseWriter, r *http.Request) {
  1942. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1943. connection, err := getUserConnection(w, r)
  1944. if err != nil {
  1945. return
  1946. }
  1947. source := r.URL.Query().Get("path")
  1948. target := r.URL.Query().Get("target")
  1949. copyFromSource := strings.HasSuffix(source, "/")
  1950. copyInTarget := strings.HasSuffix(target, "/")
  1951. source = connection.User.GetCleanedPath(source)
  1952. target = connection.User.GetCleanedPath(target)
  1953. if copyFromSource {
  1954. source += "/"
  1955. }
  1956. if copyInTarget {
  1957. target += "/"
  1958. }
  1959. task := webTaskData{
  1960. ID: connection.GetID(),
  1961. User: connection.GetUsername(),
  1962. Path: source,
  1963. Target: target,
  1964. Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
  1965. Status: 0,
  1966. }
  1967. if err := webTaskMgr.Add(task); err != nil {
  1968. common.Connections.Remove(connection.GetID())
  1969. sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
  1970. return
  1971. }
  1972. go executeCopyTask(connection, task)
  1973. sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
  1974. }
  1975. func executeDeleteTask(conn *Connection, task webTaskData) {
  1976. done := make(chan bool)
  1977. defer func() {
  1978. close(done)
  1979. common.Connections.Remove(conn.GetID())
  1980. }()
  1981. go keepAliveTask(task, done, 2*time.Minute)
  1982. status := http.StatusOK
  1983. if err := conn.RemoveAll(task.Path); err != nil {
  1984. status = getMappedStatusCode(err)
  1985. }
  1986. task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
  1987. task.Status = status
  1988. err := webTaskMgr.Add(task)
  1989. conn.Log(logger.LevelDebug, "delete task finished, status: %d, update task err: %v", status, err)
  1990. }
  1991. func executeRenameTask(conn *Connection, task webTaskData) {
  1992. done := make(chan bool)
  1993. defer func() {
  1994. close(done)
  1995. common.Connections.Remove(conn.GetID())
  1996. }()
  1997. go keepAliveTask(task, done, 2*time.Minute)
  1998. status := http.StatusOK
  1999. if !conn.IsSameResource(task.Path, task.Target) {
  2000. if err := conn.Copy(task.Path, task.Target); err != nil {
  2001. status = getMappedStatusCode(err)
  2002. task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
  2003. task.Status = status
  2004. err = webTaskMgr.Add(task)
  2005. conn.Log(logger.LevelDebug, "copy step for rename task finished, status: %d, update task err: %v", status, err)
  2006. return
  2007. }
  2008. if err := conn.RemoveAll(task.Path); err != nil {
  2009. status = getMappedStatusCode(err)
  2010. }
  2011. } else {
  2012. if err := conn.Rename(task.Path, task.Target); err != nil {
  2013. status = getMappedStatusCode(err)
  2014. }
  2015. }
  2016. task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
  2017. task.Status = status
  2018. err := webTaskMgr.Add(task)
  2019. conn.Log(logger.LevelDebug, "rename task finished, status: %d, update task err: %v", status, err)
  2020. }
  2021. func executeCopyTask(conn *Connection, task webTaskData) {
  2022. done := make(chan bool)
  2023. defer func() {
  2024. close(done)
  2025. common.Connections.Remove(conn.GetID())
  2026. }()
  2027. go keepAliveTask(task, done, 2*time.Minute)
  2028. status := http.StatusOK
  2029. if err := conn.Copy(task.Path, task.Target); err != nil {
  2030. status = getMappedStatusCode(err)
  2031. }
  2032. task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
  2033. task.Status = status
  2034. err := webTaskMgr.Add(task)
  2035. conn.Log(logger.LevelDebug, "copy task finished, status: %d, update task err: %v", status, err)
  2036. }
  2037. func keepAliveTask(task webTaskData, done chan bool, interval time.Duration) {
  2038. ticker := time.NewTicker(interval)
  2039. defer func() {
  2040. ticker.Stop()
  2041. }()
  2042. for {
  2043. select {
  2044. case <-done:
  2045. return
  2046. case <-ticker.C:
  2047. task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
  2048. err := webTaskMgr.Add(task)
  2049. logger.Debug(logSender, task.ID, "task timestamp updated, err: %v", err)
  2050. }
  2051. }
  2052. }