Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions garbagecollect/collect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,42 +39,42 @@ func TestCollectExpiredFile(t *testing.T) {
expireInFiveMins := mustParseExpirationTime("2025-01-01T00:05:00Z")
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("AAAAAAAAAAAA"),
ID: picoshare.MustCreateEntryID("AAAAAAAAAA"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("2024-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntryDownload(
picoshare.EntryID("AAAAAAAAAAAA"),
picoshare.MustCreateEntryID("AAAAAAAAAA"),
picoshare.DownloadRecord{
Time: mustParseTime("2023-06-01T12:00:00Z"),
ClientIP: "192.168.1.1",
UserAgent: "test-agent",
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("BBBBBBBBBBBB"),
ID: picoshare.MustCreateEntryID("BBBBBBBBBB"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("3000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("CCCCCCCCCCCC"),
ID: picoshare.MustCreateEntryID("CCCCCCCCCC"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: picoshare.NeverExpire,
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("DDDDDDDDDDDD"),
ID: picoshare.MustCreateEntryID("DDDDDDDDDD"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("2024-12-31T23:59:59Z"),
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("EEEEEEEEEEEE"),
ID: picoshare.MustCreateEntryID("EEEEEEEEEE"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: expireInFiveMins,
Size: mustParseFileSize(len(d)),
Expand All @@ -93,19 +93,19 @@ func TestCollectExpiredFile(t *testing.T) {

expected := []picoshare.UploadMetadata{
{
ID: picoshare.EntryID("BBBBBBBBBBBB"),
ID: picoshare.MustCreateEntryID("BBBBBBBBBB"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("3000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
},
{
ID: picoshare.EntryID("CCCCCCCCCCCC"),
ID: picoshare.MustCreateEntryID("CCCCCCCCCC"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: picoshare.NeverExpire,
Size: mustParseFileSize(len(d)),
},
{
ID: picoshare.EntryID("EEEEEEEEEEEE"),
ID: picoshare.MustCreateEntryID("EEEEEEEEEE"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: expireInFiveMins,
Size: mustParseFileSize(len(d)),
Expand All @@ -125,21 +125,21 @@ func TestCollectDoesNothingWhenNoFilesAreExpired(t *testing.T) {
d := "dummy data"
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("AAAAAAAAAAAA"),
ID: picoshare.MustCreateEntryID("AAAAAAAAAA"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("4000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("BBBBBBBBBBBB"),
ID: picoshare.MustCreateEntryID("BBBBBBBBBB"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("3000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
})
dataStore.InsertEntry(strings.NewReader(d),
picoshare.UploadMetadata{
ID: picoshare.EntryID("CCCCCCCCCCCC"),
ID: picoshare.MustCreateEntryID("CCCCCCCCCC"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: picoshare.NeverExpire,
Size: mustParseFileSize(len(d)),
Expand All @@ -163,19 +163,19 @@ func TestCollectDoesNothingWhenNoFilesAreExpired(t *testing.T) {

expected := []picoshare.UploadMetadata{
{
ID: picoshare.EntryID("AAAAAAAAAAAA"),
ID: picoshare.MustCreateEntryID("AAAAAAAAAA"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("4000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
},
{
ID: picoshare.EntryID("BBBBBBBBBBBB"),
ID: picoshare.MustCreateEntryID("BBBBBBBBBB"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("3000-01-01T00:00:00Z"),
Size: mustParseFileSize(len(d)),
},
{
ID: picoshare.EntryID("CCCCCCCCCCCC"),
ID: picoshare.MustCreateEntryID("CCCCCCCCCC"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: picoshare.NeverExpire,
Size: mustParseFileSize(len(d)),
Expand Down
4 changes: 3 additions & 1 deletion handlers/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"net/http"

"github.com/gorilla/mux"

"github.com/mtlynch/picoshare/picoshare"
)

func (s Server) entryDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id, err := parseEntryID(mux.Vars(r)["id"])
id, err := picoshare.EntryIDFromString(mux.Vars(r)["id"])
if err != nil {
log.Printf("error parsing ID: %v", err)
http.Error(w, fmt.Sprintf("bad entry ID: %v", err), http.StatusBadRequest)
Expand Down
6 changes: 3 additions & 3 deletions handlers/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestDeleteExistingFile(t *testing.T) {
fileContents := "dummy data"
dataStore.InsertEntry(strings.NewReader(fileContents),
picoshare.UploadMetadata{
ID: picoshare.EntryID("hR87apiUCj"),
ID: picoshare.MustCreateEntryID("hR87apiUCj"),
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expires: mustParseExpirationTime("2024-01-01T00:00:00Z"),
Size: mustParseFileSize(len(fileContents)),
Expand All @@ -40,9 +40,9 @@ func TestDeleteExistingFile(t *testing.T) {
status, http.StatusOK)
}

_, err := dataStore.GetEntryMetadata(picoshare.EntryID("hR87apiUCj"))
_, err := dataStore.GetEntryMetadata(picoshare.MustCreateEntryID("hR87apiUCj"))
if _, ok := err.(store.EntryNotFoundError); !ok {
t.Fatalf("expected entry %v to be deleted", picoshare.EntryID("hR87apiUCj"))
t.Fatalf("expected entry %v to be deleted", picoshare.MustCreateEntryID("hR87apiUCj"))
}
}

Expand Down
6 changes: 3 additions & 3 deletions handlers/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type entryUnlockRequest struct {

func (s Server) entryGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id, err := parseEntryID(mux.Vars(r)["id"])
id, err := picoshare.EntryIDFromString(mux.Vars(r)["id"])
if err != nil {
log.Printf("error parsing ID: %v", err)
http.Error(w, fmt.Sprintf("bad entry ID: %v", err), http.StatusBadRequest)
Expand Down Expand Up @@ -60,7 +60,7 @@ func (s Server) entryGet() http.HandlerFunc {
func (s Server) entryUnlockGet() http.HandlerFunc {
t := parseTemplates("templates/pages/entry-unlock.html")
return func(w http.ResponseWriter, r *http.Request) {
id, err := parseEntryID(mux.Vars(r)["id"])
id, err := picoshare.EntryIDFromString(mux.Vars(r)["id"])
if err != nil {
log.Printf("error parsing ID: %v", err)
http.Error(w, fmt.Sprintf("bad entry ID: %v", err), http.StatusBadRequest)
Expand Down Expand Up @@ -140,7 +140,7 @@ func (s Server) entryUnlockPost() http.HandlerFunc {
}

func parseEntryUnlockRequest(r *http.Request) (entryUnlockRequest, error) {
id, err := parseEntryID(mux.Vars(r)["id"])
id, err := picoshare.EntryIDFromString(mux.Vars(r)["id"])
if err != nil {
return entryUnlockRequest{}, err
}
Expand Down
10 changes: 5 additions & 5 deletions handlers/download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

type mockEntry struct {
ID picoshare.EntryID
ID string
Filename picoshare.Filename
ContentType picoshare.ContentType
}
Expand Down Expand Up @@ -128,7 +128,7 @@ func TestEntryGet(t *testing.T) {
data := "dummy data"
entry := picoshare.UploadEntry{
UploadMetadata: picoshare.UploadMetadata{
ID: mockEntry.ID,
ID: picoshare.MustCreateEntryID(mockEntry.ID),
Filename: mockEntry.Filename,
ContentType: mockEntry.ContentType,
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expand Down Expand Up @@ -176,7 +176,7 @@ func TestEntryGet(t *testing.T) {

func TestProtectedEntryDownload(t *testing.T) {
type fakeEntry struct {
ID picoshare.EntryID
ID string
Contents string
DownloadPassphrase picoshare.DownloadPassphrase
}
Expand Down Expand Up @@ -395,7 +395,7 @@ func TestProtectedEntryDownload(t *testing.T) {
t.Run(tt.explanation, func(t *testing.T) {
dataStore := test_sqlite.New(t)
if err := dataStore.InsertEntry(strings.NewReader(tt.entryInStore.Contents), picoshare.UploadMetadata{
ID: tt.entryInStore.ID,
ID: picoshare.MustCreateEntryID(tt.entryInStore.ID),
Filename: "test.txt",
ContentType: "text/plain",
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expand Down Expand Up @@ -448,7 +448,7 @@ func TestProtectedEntryDownloadRequiresPassphraseEveryDownload(t *testing.T) {
dataStore := test_sqlite.New(t)
data := "protected file contents"
if err := dataStore.InsertEntry(strings.NewReader(data), picoshare.UploadMetadata{
ID: "PPPPPPPPPP",
ID: picoshare.MustCreateEntryID("PPPPPPPPPP"),
Filename: "protected.txt",
ContentType: "text/plain",
Uploaded: mustParseTime("2023-01-01T00:00:00Z"),
Expand Down
2 changes: 1 addition & 1 deletion handlers/guest_links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func TestDeleteExistingGuestLink(t *testing.T) {

_, err := dataStore.GetGuestLink(picoshare.GuestLinkID("dummy-guest-link-id"))
if _, ok := err.(store.GuestLinkNotFoundError); !ok {
t.Fatalf("expected entry %v to be deleted, got: %v", picoshare.EntryID("abcdefgh23456789"), err)
t.Fatalf("expected entry %v to be deleted, got: %v", "abcdefgh23456789", err)
}
}

Expand Down
53 changes: 12 additions & 41 deletions handlers/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ import (

"github.com/mtlynch/picoshare/handlers/parse"
"github.com/mtlynch/picoshare/picoshare"
"github.com/mtlynch/picoshare/random"
"github.com/mtlynch/picoshare/store"
)

const EntryIDLength = 10

// Omit visually similar characters (I,l,1), (0,O)
var entryIDCharacters = []rune("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789")

type (
EntryPostResponse struct {
ID string `json:"id"`
Expand Down Expand Up @@ -69,7 +63,7 @@ func (s Server) entryPost() http.HandlerFunc {

func (s Server) entryPut() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id, err := parseEntryID(mux.Vars(r)["id"])
id, err := picoshare.EntryIDFromString(mux.Vars(r)["id"])
if err != nil {
log.Printf("error parsing ID: %v", err)
http.Error(w, fmt.Sprintf("bad entry ID: %v", err), http.StatusBadRequest)
Expand Down Expand Up @@ -208,35 +202,12 @@ func (s Server) entryMetadataFromRequest(r *http.Request) (picoshare.UploadMetad
}, nil
}

func generateEntryID() picoshare.EntryID {
return picoshare.EntryID(random.String(EntryIDLength, entryIDCharacters))
}

func parseEntryID(s string) (picoshare.EntryID, error) {
if len(s) != EntryIDLength {
return picoshare.EntryID(""), fmt.Errorf("entry ID (%v) has invalid length: got %d, want %d", s, len(s), EntryIDLength)
}

// We could do this outside the function and store the result.
idCharsHash := map[rune]bool{}
for _, c := range entryIDCharacters {
idCharsHash[c] = true
}

for _, c := range s {
if _, ok := idCharsHash[c]; !ok {
return picoshare.EntryID(""), fmt.Errorf("entry ID (%s) contains invalid character: %v", s, c)
}
}
return picoshare.EntryID(s), nil
}

func (s Server) insertFileFromRequest(r *http.Request, expiration picoshare.ExpirationTime, guestLinkID picoshare.GuestLinkID) (picoshare.EntryID, error) {
// ParseMultipartForm can go above the limit we set, so set a conservative RAM
// limit to avoid exhausting RAM on servers with limited resources.
multipartMaxMemory := mibToBytes(1)
if err := r.ParseMultipartForm(multipartMaxMemory); err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}
defer func() {
if err := r.MultipartForm.RemoveAll(); err != nil {
Expand All @@ -246,45 +217,45 @@ func (s Server) insertFileFromRequest(r *http.Request, expiration picoshare.Expi

reader, metadata, err := r.FormFile("file")
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}

fileSize, err := picoshare.FileSizeFromInt64(metadata.Size)
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}

filename, err := parse.Filename(metadata.Filename)
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}

contentType, err := parseContentType(metadata.Header.Get("Content-Type"))
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}

note, err := parse.FileNote(r.FormValue("note"))
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}

if guestLinkID != "" && note.Value != nil {
return picoshare.EntryID(""), errors.New("guest uploads cannot have file notes")
return picoshare.EntryID{}, errors.New("guest uploads cannot have file notes")
}

downloadPassphrase := picoshare.DownloadPassphrase{}
if rawDownloadPassphrase := r.FormValue("downloadPassphrase"); rawDownloadPassphrase != "" {
if guestLinkID != "" {
return picoshare.EntryID(""), errors.New("guest uploads cannot have download passphrases")
return picoshare.EntryID{}, errors.New("guest uploads cannot have download passphrases")
}
downloadPassphrase, err = picoshare.NewDownloadPassphrase(rawDownloadPassphrase)
if err != nil {
return picoshare.EntryID(""), err
return picoshare.EntryID{}, err
}
}

id := generateEntryID()
id := picoshare.NewEntryID()
err = s.store.InsertEntry(reader,
picoshare.UploadMetadata{
ID: id,
Expand All @@ -301,7 +272,7 @@ func (s Server) insertFileFromRequest(r *http.Request, expiration picoshare.Expi
})
if err != nil {
log.Printf("failed to save entry: %v", err)
return picoshare.EntryID(""), dbError{err}
return picoshare.EntryID{}, dbError{err}
}

return id, nil
Expand Down
Loading
Loading