package share import ( "encoding/json" "fmt" "net/http" "strings" . "github.com/mickael-kerjean/filestash/server/pkg/core" . "github.com/mickael-kerjean/filestash/server/pkg/env" . "github.com/mickael-kerjean/filestash/server/pkg/kernel" . "github.com/mickael-kerjean/filestash/server/pkg/utils" "github.com/mickael-kerjean/filestash/server/pkg/token" "github.com/gorilla/mux" ) func ShareListHandler(ctx *App, res http.ResponseWriter, req *http.Request) { path, err := PathBuilder(ctx, req.URL.Query().Get("path")) if err != nil { SendErrorResult(res, err) return } listOfSharedLinks, err := ShareList( GenerateID(ctx.Session), path, ) if err != nil { Log.Debug("share::list '%s'", err.Error()) SendErrorResult(res, err) return } for i := 0; i < len(listOfSharedLinks); i++ { listOfSharedLinks[i].Path = "/" + strings.TrimPrefix(listOfSharedLinks[i].Path, path) listOfSharedLinks[i] = ShareRedact(listOfSharedLinks[i]) } SendSuccessResults(res, listOfSharedLinks) } func ShareUpsertHandler(ctx *App, res http.ResponseWriter, req *http.Request) { share_id := mux.Vars(req)["share"] if share_id == "private" { Log.Debug("share::upsert 'private'") SendErrorResult(res, ErrNotValid) return } s := Share{ Id: share_id, Auth: func() string { if ctx.Share.Id == "" { return token.From(req) } return ctx.Share.Auth }(), Backend: func() string { if ctx.Share.Id == "" { return GenerateID(ctx.Session) } return ctx.Share.Backend }(), Path: func() string { leftPath := "/" rightPath := strings.TrimPrefix(NewStringFromInterface(ctx.Body["path"]), "/") if ctx.Share.Id != "" { leftPath = ctx.Share.Path } else { if ctx.Session["path"] != "" { leftPath = EnforceDirectory(ctx.Session["path"]) } } return leftPath + rightPath }(), Password: NewStringpFromInterface(ctx.Body["password"]), Users: NewStringpFromInterface(ctx.Body["users"]), Expire: NewInt64pFromInterface(ctx.Body["expire"]), Url: NewStringpFromInterface(ctx.Body["url"]), CanManageOwn: NewBoolFromInterface(ctx.Body["can_manage_own"]), CanShare: NewBoolFromInterface(ctx.Body["can_share"]), CanRead: NewBoolFromInterface(ctx.Body["can_read"]), CanWrite: NewBoolFromInterface(ctx.Body["can_write"]), CanUpload: NewBoolFromInterface(ctx.Body["can_upload"]), } if err := ShareUpsert(&s); err != nil { Log.Debug("share::upsert '%s'", err.Error()) SendErrorResult(res, err) return } SendSuccessResult(res, nil) } func ShareDeleteHandler(ctx *App, res http.ResponseWriter, req *http.Request) { share_target := mux.Vars(req)["share"] if err := ShareDelete(share_target); err != nil { Log.Debug("share::delete '%s'", err.Error()) SendErrorResult(res, err) return } SendSuccessResult(res, nil) } func ShareVerifyProofHandler(ctx *App, res http.ResponseWriter, req *http.Request) { var submittedProof Proof var verifiedProof []Proof var requiredProof []Proof var remainingProof []Proof var s Share var err error // 1) initialise the current context share_id := mux.Vars(req)["share"] s, err = ShareGet(share_id) if err != nil { Log.Debug("share::verify::init '%s'", err.Error()) SendErrorResult(res, err) return } submittedProof = Proof{ Key: fmt.Sprint(ctx.Body["type"]), Value: fmt.Sprint(ctx.Body["value"]), } verifiedProof = ShareVerified(req) requiredProof = ShareRequired(s) // 2) validate the current context if len(verifiedProof) > 20 || len(requiredProof) > 20 { http.SetCookie(res, &http.Cookie{ Name: COOKIE_NAME_PROOF, Value: "", MaxAge: -1, Path: COOKIE_PATH, }) Log.Debug("share::verify::validate 'proof issue' len(verifiedProof)[%d] len(requiredProof)[%d]", len(verifiedProof), len(requiredProof)) SendErrorResult(res, ErrNotValid) return } if err := ShareIsValid(s); err != nil { Log.Debug("share::verify::validate '%s'", err.Error()) SendErrorResult(res, err) return } // 3) process the proof sent by the user submittedProof, err = ShareVerify(s, submittedProof) if err != nil { Log.Debug("share::verify::process '%s'", err.Error()) submittedProof.Error = NewString(err.Error()) SendSuccessResult(res, submittedProof) return } if submittedProof.Key == "code" { submittedProof.Value = "" submittedProof.Message = NewString("We've sent you a message with a verification code") SendSuccessResult(res, submittedProof) return } if submittedProof.Key != "" { submittedProof.Id = Hash(submittedProof.Key+"::"+submittedProof.Value, 20) alreadyExist := false for i := 0; i < len(verifiedProof); i++ { if verifiedProof[i].Id == submittedProof.Id { alreadyExist = true break } } if alreadyExist == false { verifiedProof = append(verifiedProof, submittedProof) } } // 4) Find remaining proofs: requiredProof - verifiedProof remainingProof = ShareRemainings(requiredProof, verifiedProof) // 5) persist proofs in client cookie cookie := http.Cookie{ Name: COOKIE_NAME_PROOF, Value: func(p []Proof) string { j, _ := json.Marshal(p) str, _ := EncryptString(SECRET_KEY_DERIVATE_FOR_PROOF, string(j)) return str }(verifiedProof), Path: COOKIE_PATH, MaxAge: 60 * 60 * 24 * 30, HttpOnly: true, SameSite: http.SameSiteNoneMode, Secure: true, } http.SetCookie(res, &cookie) if len(remainingProof) > 0 { SendSuccessResult(res, remainingProof[0]) return } SendSuccessResult(res, struct { Id string `json:"id"` Path string `json:"path"` CanRead bool `json:"can_read"` CanWrite bool `json:"can_write"` CanUpload bool `json:"can_upload"` }{ Id: s.Id, Path: s.Path, CanRead: s.CanRead, CanWrite: s.CanWrite, CanUpload: s.CanUpload, }) }