summaryrefslogtreecommitdiffstats
path: root/roseh.moe.go
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-10-01 20:37:26 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-10-01 20:37:26 -0700
commit8f3927cc91b8f9dcc2bd3e05c2ddd225a92c96dc (patch)
treeb86c45195236428f58ec2883a81c53f317532fe7 /roseh.moe.go
parent298f4b4634026f268ad74dadccbca8127ab83076 (diff)
downloadroseh.moe-8f3927cc91b8f9dcc2bd3e05c2ddd225a92c96dc.tar.zst
Linked list is cool, but have you heard of array
Diffstat (limited to 'roseh.moe.go')
-rw-r--r--roseh.moe.go94
1 files changed, 45 insertions, 49 deletions
diff --git a/roseh.moe.go b/roseh.moe.go
index 5600a75..33122d2 100644
--- a/roseh.moe.go
+++ b/roseh.moe.go
@@ -18,6 +18,7 @@ import (
"net/http"
"os"
"path/filepath"
+ "strconv"
"strings"
"sync"
"time"
@@ -407,20 +408,20 @@ func listNotes(req *api.ListNotesRequest) (*api.ListNotesResponse, error) {
return &api.ListNotesResponse{Notes: names}, nil
}
-func readLinkedList(w io.Writer, next []byte, key cryptoutil.EncryptionKey, name string) error {
+func dumpFile(w io.Writer, key cryptoutil.EncryptionKey, name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
- reader := key.NewReader(f, []byte("chunks/"+filepath.Base(name)))
- if _, err := io.ReadFull(reader, next); err != nil {
- return err
- }
- _, err = io.Copy(w, reader)
+ _, err = io.Copy(w, key.NewReader(f, []byte("chunks/"+filepath.Base(name))))
return err
}
+func chunkName(prefix string, i int64) string {
+ return fmt.Sprintf("%s%013s", prefix, strconv.FormatUint(uint64(i), 36))
+}
+
var (
//go:embed wordlist.txt
wordListString string
@@ -429,8 +430,8 @@ var (
func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
type continuationToken struct {
- Head string
- Tail []byte
+ Prefix string
+ I int64
}
encryptionKeyMu.Lock()
@@ -439,10 +440,8 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
if key == nil {
return nil, fmt.Errorf("%w: need login", api.PermissionDenied)
}
- var (
- contToken continuationToken
- f *os.File
- )
+
+ var contToken continuationToken
if len(req.ContinuationToken) > 0 {
msg, ok := secretKey.Verify(req.ContinuationToken, "continuation-token")
if !ok {
@@ -451,8 +450,19 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(&contToken); err != nil {
return nil, fmt.Errorf("bad continuation token")
}
- var err error
- f, err = os.OpenFile(*notepadDir+"/chunks/"+base64.RawURLEncoding.EncodeToString(contToken.Tail), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
+ } else {
+ token := make([]byte, 64)
+ rand.Read(token)
+ contToken.Prefix = base64.RawURLEncoding.EncodeToString(token)
+ contToken.I = 0
+ }
+
+ if req.More {
+ if err := os.MkdirAll(*notepadDir+"/chunks", 0700); err != nil {
+ return nil, err
+ }
+ name := chunkName(contToken.Prefix, contToken.I)
+ f, err := os.OpenFile(*notepadDir+"/chunks/"+name, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
if errors.Is(err, os.ErrExist) {
return nil, fmt.Errorf("%w: continuation token expired", api.InvalidArgument)
@@ -460,39 +470,28 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
return nil, err
}
defer f.Close()
- } else {
- if err := os.MkdirAll(*notepadDir+"/chunks", 0700); err != nil {
+ if contToken.I > 0 {
+ _, err := os.Stat(*notepadDir + "/chunks/" + chunkName(contToken.Prefix, 0))
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ return nil, fmt.Errorf("%w: continuation token expired", api.InvalidArgument)
+ }
+ return nil, err
+ }
+ }
+ encryptingWriter := key.NewWriter(f, []byte("chunks/"+name))
+ if _, err := encryptingWriter.Write(req.Chunk); err != nil {
return nil, err
}
- var err error
- f, err = os.CreateTemp(*notepadDir+"/chunks", "head")
- if err != nil {
+ if err := encryptingWriter.Close(); err != nil {
return nil, err
}
- defer f.Close()
- }
- next := make([]byte, 64)
- rand.Read(next)
- encryptingWriter := key.NewWriter(f, []byte("chunks/"+filepath.Base(f.Name())))
- if _, err := encryptingWriter.Write(next); err != nil {
- return nil, err
- }
- if _, err := encryptingWriter.Write(req.Chunk); err != nil {
- return nil, err
- }
- if err := encryptingWriter.Close(); err != nil {
- return nil, err
- }
- if err := f.Close(); err != nil {
- return nil, err
- }
- head := contToken.Head
- if head == "" {
- head = filepath.Base(f.Name())
- }
- if req.More {
+ if err := f.Close(); err != nil {
+ return nil, err
+ }
+ contToken.I++
buf := new(bytes.Buffer)
- if err := gob.NewEncoder(buf).Encode(continuationToken{Head: head, Tail: next}); err != nil {
+ if err := gob.NewEncoder(buf).Encode(contToken); err != nil {
return nil, err
}
return &api.CreateNoteResponse{ContinuationToken: secretKey.Sign(buf.Bytes(), "continuation-token")}, nil
@@ -520,12 +519,9 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
defer finalNote.Close()
break
}
- var chunks []string
writer := key.NewWriter(finalNote, []byte("notes/"+filepath.Base(finalNote.Name())))
- tailBase64 := base64.RawURLEncoding.EncodeToString(next)
- for chunk := head; chunk != tailBase64; chunk = base64.RawURLEncoding.EncodeToString(next) {
- chunks = append(chunks, chunk)
- if err := readLinkedList(writer, next, key, *notepadDir+"/chunks/"+chunk); err != nil {
+ for i := range contToken.I {
+ if err := dumpFile(writer, key, *notepadDir+"/chunks/"+chunkName(contToken.Prefix, i)); err != nil {
return nil, err
}
}
@@ -538,8 +534,8 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
if err := finalNote.Close(); err != nil {
return nil, err
}
- for _, chunk := range chunks {
- if err := os.Remove(*notepadDir + "/chunks/" + chunk); err != nil {
+ for i := range contToken.I {
+ if err := os.Remove(*notepadDir + "/chunks/" + chunkName(contToken.Prefix, i)); err != nil {
log.Printf("Warning: createNote: failed to delete chunk: %s", err)
}
}