WIP: new WebClient UI

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2023-11-04 10:54:52 +01:00
parent 2fdcb44c14
commit 9322701615
79 changed files with 7688 additions and 15710 deletions

View File

@@ -291,8 +291,14 @@ func (s *httpdServer) handleWebClientPasswordResetPost(w http.ResponseWriter, r
s.renderClientForbiddenPage(w, r, err.Error())
return
}
newPassword := strings.TrimSpace(r.Form.Get("password"))
confirmPassword := strings.TrimSpace(r.Form.Get("confirm_password"))
if newPassword != confirmPassword {
s.renderClientResetPwdPage(w, r, "The two password fields do not match", ipAddr)
return
}
_, user, err := handleResetPassword(r, strings.TrimSpace(r.Form.Get("code")),
strings.TrimSpace(r.Form.Get("password")), false)
newPassword, false)
if err != nil {
s.renderClientResetPwdPage(w, r, err.Error(), ipAddr)
return
@@ -1506,12 +1512,14 @@ func (s *httpdServer) setupWebClientRoutes() {
s.router.Get(webClientPubSharesPath+"/{id}/login", s.handleClientShareLoginGet)
s.router.Post(webClientPubSharesPath+"/{id}/login", s.handleClientShareLoginPost)
s.router.Get(webClientPubSharesPath+"/{id}", s.downloadFromShare)
s.router.Get(webClientPubSharesPath+"/{id}/partial", s.handleClientSharePartialDownload)
s.router.Post(webClientPubSharesPath+"/{id}/partial", s.handleClientSharePartialDownload)
s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles)
s.router.Get(webClientPubSharesPath+"/{id}/upload", s.handleClientUploadToShare)
s.router.With(compressor.Handler).Get(webClientPubSharesPath+"/{id}/dirs", s.handleShareGetDirContents)
s.router.Post(webClientPubSharesPath+"/{id}", s.uploadFilesToShare)
s.router.Post(webClientPubSharesPath+"/{id}/{name}", s.uploadFileToShare)
s.router.Get(webClientPubSharesPath+"/{id}/viewpdf", s.handleShareViewPDF)
s.router.Get(webClientPubSharesPath+"/{id}/getpdf", s.handleShareGetPDF)
s.router.Group(func(router chi.Router) {
if s.binding.OIDC.isEnabled() {
@@ -1541,7 +1549,7 @@ func (s *httpdServer) setupWebClientRoutes() {
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
Post(webClientFileActionsPath+"/copy", copyUserFsEntry)
router.With(s.checkAuthRequirements, s.refreshCookie).
Get(webClientDownloadZipPath, s.handleWebClientDownloadZip)
Post(webClientDownloadZipPath, s.handleWebClientDownloadZip)
router.With(s.checkAuthRequirements, s.refreshCookie).Get(webClientProfilePath,
s.handleClientGetProfile)
router.With(s.checkAuthRequirements).Post(webClientProfilePath, s.handleWebClientProfilePost)

View File

@@ -36,6 +36,7 @@ const (
templateForgotPassword = "forgot-password.html"
templateResetPassword = "reset-password.html"
templateCommonCSS = "sftpgo.css"
templateCommonBase = "base.html"
)
type loginPage struct {
@@ -67,6 +68,7 @@ type forgotPwdPage struct {
Error string
CSRFToken string
StaticURL string
LoginURL string
Title string
Branding UIBranding
}
@@ -76,6 +78,7 @@ type resetPwdPage struct {
Error string
CSRFToken string
StaticURL string
LoginURL string
Title string
Branding UIBranding
}

View File

@@ -62,9 +62,8 @@ const (
templateClientShares = "shares.html"
templateClientViewPDF = "viewpdf.html"
templateShareLogin = "sharelogin.html"
templateShareFiles = "sharefiles.html"
templateUploadToShare = "shareupload.html"
pageClientFilesTitle = "My Files"
pageClientFilesTitle = "Files"
pageClientSharesTitle = "Shares"
pageClientProfileTitle = "My Profile"
pageClientChangePwdTitle = "Change password"
@@ -106,6 +105,8 @@ type baseClientPage struct {
ChangePwdURL string
StaticURL string
LogoutURL string
LoginURL string
EditURL string
MFAURL string
MFATitle string
FilesTitle string
@@ -141,22 +142,23 @@ type editFilePage struct {
type filesPage struct {
baseClientPage
CurrentDir string
DirsURL string
FileActionsURL string
DownloadURL string
ViewPDFURL string
FileURL string
CanAddFiles bool
CanCreateDirs bool
CanRename bool
CanDelete bool
CanDownload bool
CanShare bool
Error string
Paths []dirMapping
HasIntegrations bool
QuotaUsage *userQuotaUsage
CurrentDir string
DirsURL string
FileActionsURL string
DownloadURL string
ViewPDFURL string
FileURL string
CanAddFiles bool
CanCreateDirs bool
CanRename bool
CanDelete bool
CanDownload bool
CanShare bool
ShareUploadBaseURL string
Error string
Paths []dirMapping
HasIntegrations bool
QuotaUsage *userQuotaUsage
}
type shareLoginPage struct {
@@ -168,18 +170,6 @@ type shareLoginPage struct {
Branding UIBranding
}
type shareFilesPage struct {
baseClientPage
CurrentDir string
DirsURL string
FilesURL string
DownloadURL string
UploadBaseURL string
Error string
Paths []dirMapping
Scope dataprovider.ShareScope
}
type shareUploadPage struct {
baseClientPage
Share *dataprovider.Share
@@ -423,84 +413,81 @@ func getFileObjectModTime(t time.Time) string {
func loadClientTemplates(templatesPath string) {
filesPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientFiles),
}
editFilePath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientEditFile),
}
sharesPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientShares),
}
sharePaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientShare),
}
profilePaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientProfile),
}
changePwdPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientChangePwd),
}
loginPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateClientLogin),
}
messagePath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientMessage),
}
mfaPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateClientMFA),
}
twoFactorPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateClientTwoFactor),
}
twoFactorRecoveryPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateClientTwoFactorRecovery),
}
forgotPwdPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateForgotPassword),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateForgotPassword),
}
resetPwdPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateResetPassword),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateResetPassword),
}
viewPDFPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientViewPDF),
}
shareLoginPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBaseLogin),
filepath.Join(templatesPath, templateClientDir, templateShareLogin),
}
shareFilesPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateShareFiles),
}
shareUploadPath := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateClientDir, templateClientBase),
filepath.Join(templatesPath, templateClientDir, templateUploadToShare),
}
@@ -520,7 +507,6 @@ func loadClientTemplates(templatesPath string) {
forgotPwdTmpl := util.LoadTemplate(nil, forgotPwdPaths...)
resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
viewPDFTmpl := util.LoadTemplate(nil, viewPDFPaths...)
shareFilesTmpl := util.LoadTemplate(nil, shareFilesPath...)
shareUploadTmpl := util.LoadTemplate(nil, shareUploadPath...)
clientTemplates[templateClientFiles] = filesTmpl
@@ -538,7 +524,6 @@ func loadClientTemplates(templatesPath string) {
clientTemplates[templateResetPassword] = resetPwdTmpl
clientTemplates[templateClientViewPDF] = viewPDFTmpl
clientTemplates[templateShareLogin] = shareLoginTmpl
clientTemplates[templateShareFiles] = shareFilesTmpl
clientTemplates[templateUploadToShare] = shareUploadTmpl
}
@@ -549,7 +534,7 @@ func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Re
}
v := version.Get()
return baseClientPage{
data := baseClientPage{
Title: title,
CurrentURL: currentURL,
FilesURL: webClientFilesPath,
@@ -559,6 +544,7 @@ func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Re
ChangePwdURL: webChangeClientPwdPath,
StaticURL: webStaticFilesPath,
LogoutURL: webClientLogoutPath,
EditURL: webClientEditFilePath,
MFAURL: webClientMFAPath,
MFATitle: pageClient2FATitle,
FilesTitle: pageClientFilesTitle,
@@ -569,6 +555,10 @@ func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Re
LoggedUser: getUserFromToken(r),
Branding: s.binding.Branding.WebClient,
}
if !strings.HasPrefix(r.RequestURI, webClientPubSharesPath) {
data.LoginURL = webClientLoginPath
}
return data
}
func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, error, ip string) {
@@ -577,6 +567,7 @@ func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, error, ip
Error: error,
CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath,
LoginURL: webClientLoginPath,
Title: pageClientForgotPwdTitle,
Branding: s.binding.Branding.WebClient,
}
@@ -589,6 +580,7 @@ func (s *httpdServer) renderClientResetPwdPage(w http.ResponseWriter, _ *http.Re
Error: error,
CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath,
LoginURL: webClientLoginPath,
Title: pageClientResetPwdTitle,
Branding: s.binding.Branding.WebClient,
}
@@ -732,7 +724,7 @@ func getDirMapping(dirName, baseWebPath string) []dirMapping {
if dirName != "/" {
paths = append(paths, dirMapping{
DirName: path.Base(dirName),
Href: "",
Href: getFileObjectURL("/", dirName, baseWebPath),
})
for {
dirName = path.Dir(dirName)
@@ -752,18 +744,30 @@ func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Reque
share dataprovider.Share,
) {
currentURL := path.Join(webClientPubSharesPath, share.ShareID, "browse")
data := shareFilesPage{
baseClientPage: s.getBaseClientPageData(pageExtShareTitle, currentURL, r),
CurrentDir: url.QueryEscape(dirName),
DirsURL: path.Join(webClientPubSharesPath, share.ShareID, "dirs"),
FilesURL: currentURL,
DownloadURL: path.Join(webClientPubSharesPath, share.ShareID, "partial"),
UploadBaseURL: path.Join(webClientPubSharesPath, share.ShareID, url.PathEscape(dirName)),
Error: error,
Paths: getDirMapping(dirName, currentURL),
Scope: share.Scope,
baseData := s.getBaseClientPageData(pageExtShareTitle, currentURL, r)
baseData.FilesURL = currentURL
data := filesPage{
baseClientPage: baseData,
Error: error,
CurrentDir: url.QueryEscape(dirName),
DownloadURL: path.Join(webClientPubSharesPath, share.ShareID, "partial"),
ShareUploadBaseURL: path.Join(webClientPubSharesPath, share.ShareID, url.PathEscape(dirName)),
ViewPDFURL: path.Join(webClientPubSharesPath, share.ShareID, "viewpdf"),
DirsURL: path.Join(webClientPubSharesPath, share.ShareID, "dirs"),
FileURL: "",
FileActionsURL: "",
CanAddFiles: share.Scope == dataprovider.ShareScopeReadWrite,
CanCreateDirs: false,
CanRename: false,
CanDelete: false,
CanDownload: share.Scope != dataprovider.ShareScopeWrite,
CanShare: false,
HasIntegrations: false,
Paths: getDirMapping(dirName, currentURL),
QuotaUsage: newUserQuotaUsage(&dataprovider.User{}),
}
renderClientTemplate(w, templateShareFiles, data)
renderClientTemplate(w, templateClientFiles, data)
}
func (s *httpdServer) renderUploadToSharePage(w http.ResponseWriter, r *http.Request, share dataprovider.Share) {
@@ -780,23 +784,24 @@ func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, di
hasIntegrations bool,
) {
data := filesPage{
baseClientPage: s.getBaseClientPageData(pageClientFilesTitle, webClientFilesPath, r),
Error: error,
CurrentDir: url.QueryEscape(dirName),
DownloadURL: webClientDownloadZipPath,
ViewPDFURL: webClientViewPDFPath,
DirsURL: webClientDirsPath,
FileURL: webClientFilePath,
FileActionsURL: webClientFileActionsPath,
CanAddFiles: user.CanAddFilesFromWeb(dirName),
CanCreateDirs: user.CanAddDirsFromWeb(dirName),
CanRename: user.CanRenameFromWeb(dirName, dirName),
CanDelete: user.CanDeleteFromWeb(dirName),
CanDownload: user.HasPerm(dataprovider.PermDownload, dirName),
CanShare: user.CanManageShares(),
HasIntegrations: hasIntegrations,
Paths: getDirMapping(dirName, webClientFilesPath),
QuotaUsage: newUserQuotaUsage(user),
baseClientPage: s.getBaseClientPageData(pageClientFilesTitle, webClientFilesPath, r),
Error: error,
CurrentDir: url.QueryEscape(dirName),
DownloadURL: webClientDownloadZipPath,
ViewPDFURL: webClientViewPDFPath,
DirsURL: webClientDirsPath,
FileURL: webClientFilePath,
FileActionsURL: webClientFileActionsPath,
CanAddFiles: user.CanAddFilesFromWeb(dirName),
CanCreateDirs: user.CanAddDirsFromWeb(dirName),
CanRename: user.CanRenameFromWeb(dirName, dirName),
CanDelete: user.CanDeleteFromWeb(dirName),
CanDownload: user.HasPerm(dataprovider.PermDownload, dirName),
CanShare: user.CanManageShares(),
ShareUploadBaseURL: "",
HasIntegrations: hasIntegrations,
Paths: getDirMapping(dirName, webClientFilesPath),
QuotaUsage: newUserQuotaUsage(user),
}
renderClientTemplate(w, templateClientFiles, data)
}
@@ -829,12 +834,21 @@ func (s *httpdServer) renderClientChangePasswordPage(w http.ResponseWriter, r *h
}
func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
claims, err := getTokenClaims(r)
if err != nil || claims.Username == "" {
s.renderClientMessagePage(w, r, "Invalid token claims", "", http.StatusForbidden, nil, "")
return
}
if err := r.ParseForm(); err != nil {
s.renderClientMessagePage(w, r, "Invalid request", err.Error(), getRespStatus(err), nil, "")
return
}
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
s.renderClientForbiddenPage(w, r, err.Error())
return
}
user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
if err != nil {
@@ -861,7 +875,7 @@ func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.
defer common.Connections.Remove(connection.GetID())
name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
files := r.URL.Query().Get("files")
files := r.Form.Get("files")
var filesList []string
err = json.Unmarshal([]byte(files), &filesList)
if err != nil {
@@ -875,7 +889,11 @@ func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.
}
func (s *httpdServer) handleClientSharePartialDownload(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
r.Body = http.MaxBytesReader(w, r.Body, maxMultipartMem)
if err := r.ParseForm(); err != nil {
s.renderClientMessagePage(w, r, "Invalid request", err.Error(), getRespStatus(err), nil, "")
return
}
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
share, connection, err := s.checkPublicShare(w, r, validScopes)
if err != nil {
@@ -903,7 +921,7 @@ func (s *httpdServer) handleClientSharePartialDownload(w http.ResponseWriter, r
s.renderClientMessagePage(w, r, "Denying share read due to quota limits", "", getMappedStatusCode(err), err, "")
return
}
files := r.URL.Query().Get("files")
files := r.Form.Get("files")
var filesList []string
err = json.Unmarshal([]byte(files), &filesList)
if err != nil {
@@ -1028,6 +1046,71 @@ func (s *httpdServer) handleShareGetFiles(w http.ResponseWriter, r *http.Request
}
}
func (s *httpdServer) handleShareViewPDF(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
share, _, err := s.checkPublicShare(w, r, validScopes)
if err != nil {
return
}
name, err := getBrowsableSharedPath(share, r)
if err != nil {
s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
return
}
data := viewPDFPage{
Title: path.Base(name),
URL: fmt.Sprintf("%s?path=%s&_=%d", path.Join(webClientPubSharesPath, share.ShareID, "getpdf"),
url.QueryEscape(name), time.Now().UTC().Unix()),
StaticURL: webStaticFilesPath,
Branding: s.binding.Branding.WebClient,
}
renderClientTemplate(w, templateClientViewPDF, data)
}
func (s *httpdServer) handleShareGetPDF(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
share, connection, err := s.checkPublicShare(w, r, validScopes)
if err != nil {
return
}
if err := validateBrowsableShare(share, connection); err != nil {
s.renderClientMessagePage(w, r, "Unable to validate share", "", getRespStatus(err), err, "")
return
}
name, err := getBrowsableSharedPath(share, r)
if err != nil {
s.renderClientMessagePage(w, r, "Invalid share path", "", getRespStatus(err), err, "")
return
}
if err = common.Connections.Add(connection); err != nil {
s.renderClientMessagePage(w, r, "Unable to add connection", "", http.StatusTooManyRequests, err, "")
return
}
defer common.Connections.Remove(connection.GetID())
info, err := connection.Stat(name, 0)
if err != nil {
s.renderClientMessagePage(w, r, "Unable to get file", "", getRespStatus(err), err, "")
return
}
if info.IsDir() {
s.renderClientMessagePage(w, r, "Invalid file", fmt.Sprintf("%q is not a file", name),
http.StatusBadRequest, nil, "")
return
}
connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
if err := s.ensurePDF(w, r, name, connection); err != nil {
return
}
dataprovider.UpdateShareLastUse(&share, 1) //nolint:errcheck
if _, err := downloadFile(w, r, connection, name, info, true, &share); err != nil {
dataprovider.UpdateShareLastUse(&share, -1) //nolint:errcheck
}
}
func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
claims, err := getTokenClaims(r)
@@ -1067,6 +1150,7 @@ func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.
return
}
dirTree := r.URL.Query().Get("dirtree") == "1"
results := make([]map[string]any, 0, len(contents))
for _, info := range contents {
res := make(map[string]any)
@@ -1074,7 +1158,11 @@ func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.
if info.IsDir() {
res["type"] = "1"
res["size"] = ""
res["dir_path"] = url.QueryEscape(path.Join(name, info.Name()))
} else {
if dirTree {
continue
}
res["type"] = "2"
if info.Mode()&os.ModeSymlink != 0 {
res["size"] = ""
@@ -1462,6 +1550,11 @@ func (s *httpdServer) handleWebClientProfilePost(w http.ResponseWriter, r *http.
return
}
if userMerged.CanManagePublicKeys() {
for k := range r.Form {
if strings.HasPrefix(k, "public_keys[") {
r.Form.Add("public_keys", r.Form.Get(k))
}
}
user.PublicKeys = r.Form["public_keys"]
}
if userMerged.CanChangeAPIKeyAuth() {
@@ -1500,6 +1593,12 @@ func getShareFromPostFields(r *http.Request) (*dataprovider.Share, error) {
if err := r.ParseForm(); err != nil {
return share, err
}
for k := range r.Form {
if strings.HasPrefix(k, "paths[") {
r.Form.Add("paths", r.Form.Get(k))
}
}
share.Name = strings.TrimSpace(r.Form.Get("name"))
share.Description = r.Form.Get("description")
for _, p := range r.Form["paths"] {
@@ -1641,11 +1740,18 @@ func (s *httpdServer) handleClientGetPDF(w http.ResponseWriter, r *http.Request)
return
}
connection.User.CheckFsRoot(connection.ID) //nolint:errcheck
if err := s.ensurePDF(w, r, name, connection); err != nil {
return
}
downloadFile(w, r, connection, name, info, true, nil) //nolint:errcheck
}
func (s *httpdServer) ensurePDF(w http.ResponseWriter, r *http.Request, name string, connection *Connection) error {
reader, err := connection.getFileReader(name, 0, r.Method)
if err != nil {
s.renderClientMessagePage(w, r, fmt.Sprintf("Unable to get a reader for the file %q", name), "",
getRespStatus(err), err, "")
return
return err
}
defer reader.Close()
@@ -1654,14 +1760,14 @@ func (s *httpdServer) handleClientGetPDF(w http.ResponseWriter, r *http.Request)
if err != nil {
s.renderClientMessagePage(w, r, "Invalid PDF file", fmt.Sprintf("Unable to validate the file %q as PDF", name),
http.StatusBadRequest, nil, "")
return
return err
}
if ctype := http.DetectContentType(b.Bytes()); ctype != "application/pdf" {
connection.Log(logger.LevelDebug, "detected %q content type, expected PDF, file %q", ctype, name)
s.renderClientBadRequestPage(w, r, fmt.Errorf("the file %q does not look like a PDF", name))
return
return errors.New("invalid PDF")
}
downloadFile(w, r, connection, name, info, true, nil) //nolint:errcheck
return nil
}
func (s *httpdServer) handleClientShareLoginGet(w http.ResponseWriter, r *http.Request) {