diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-10-05 18:00:06 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-10-05 18:00:06 -0700 |
| commit | d89b325294201d95498ae9d08f102e12ac2f2876 (patch) | |
| tree | ffe65e33d0d01e811a5323d5e40c47564ccb8f2d /tools | |
| parent | 624985afaaa8b94e4d9685bcdc935d467f9e7060 (diff) | |
| download | roseh.moe-d89b325294201d95498ae9d08f102e12ac2f2876.tar.zst | |
Implement a "magic wormhole" file-sharing solution
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/finditers/finditers.go | 53 | ||||
| -rw-r--r-- | tools/hashpw/hashpw.go | 31 | ||||
| -rw-r--r-- | tools/notes/notes.go | 351 |
3 files changed, 0 insertions, 435 deletions
diff --git a/tools/finditers/finditers.go b/tools/finditers/finditers.go deleted file mode 100644 index 746242b..0000000 --- a/tools/finditers/finditers.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "fmt" - "math/rand/v2" - "os" - "sort" - "testing" - "time" - - "gitlab.com/rhogenson/roseh.moe/internal/cryptoutil" -) - -var ( - iterations int - - salt = func() []byte { - r := rand.New(new(rand.PCG)) - salt := make([]byte, cryptoutil.SaltSize) - for i := range salt { - salt[i] = byte(r.IntN(256)) - } - return salt - }() -) - -func BenchmarkHashIter(b *testing.B) { - const password = "oboe shortness ether ideology undesired fresh freezable catching mashing glimpse" - for b.Loop() { - cryptoutil.HashIter(password, salt, iterations) - } -} - -func run() error { - const targetDuration = 2 * time.Second - - for iterations = 8192; time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) < targetDuration; iterations *= 2 { - } - lo := iterations / 2 - hi := iterations - fmt.Println(lo + sort.Search(hi-lo, func(i int) bool { - iterations = lo + i - return time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) > targetDuration - })) - return nil -} - -func main() { - if err := run(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} diff --git a/tools/hashpw/hashpw.go b/tools/hashpw/hashpw.go deleted file mode 100644 index 7705b31..0000000 --- a/tools/hashpw/hashpw.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "gitlab.com/rhogenson/roseh.moe/internal/cryptoutil" - "golang.org/x/term" -) - -func run() error { - fmt.Print("Enter password:") - password, err := term.ReadPassword(int(os.Stdin.Fd())) - fmt.Println() - if err != nil { - return err - } - hashedPassword, err := cryptoutil.Hash(string(password)) - if err != nil { - return err - } - fmt.Printf("notepad-password=%s\n", hashedPassword) - return nil -} - -func main() { - if err := run(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} diff --git a/tools/notes/notes.go b/tools/notes/notes.go deleted file mode 100644 index 7714a89..0000000 --- a/tools/notes/notes.go +++ /dev/null @@ -1,351 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/gob" - "errors" - "flag" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "time" - - "github.com/google/subcommands" - "gitlab.com/rhogenson/roseh.moe/internal/api" - "golang.org/x/term" -) - -var serverURL = flag.String("url", "https://roseh.moe", "server url") - -const tokenCache = "/dev/shm/roseh.moe-upload-token" - -var errUnavailable = errors.New("unavailable") - -func readGobResp[Response any](req *http.Request) (*Response, error) { - httpResp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, fmt.Errorf("%w: read gob response: http: %s", errUnavailable, err) - } - defer httpResp.Body.Close() - if httpResp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(httpResp.Body) - return nil, fmt.Errorf("read gob response: error status: %s\n%s", httpResp.Status, body) - } - apiResp := new(api.Response) - if err := gob.NewDecoder(httpResp.Body).Decode(apiResp); err != nil { - return nil, fmt.Errorf("read gob response: decode response: %s", err) - } - if apiResp.Status != api.Ok { - return nil, fmt.Errorf("read gob response: api error: %s: %s", apiResp.Status, apiResp.Err) - } - resp, ok := apiResp.Ok.(*Response) - if !ok { - return nil, fmt.Errorf("read gob response: invalid response") - } - return resp, nil -} - -func login(ctx context.Context) (*api.LoginResponse, error) { - fmt.Fprint(os.Stderr, "Enter password:") - password, err := term.ReadPassword(int(os.Stdin.Fd())) - fmt.Fprintln(os.Stderr) - if err != nil { - return nil, err - } - req, err := http.NewRequestWithContext(ctx, "POST", *serverURL+"/api/login", nil) - if err != nil { - return nil, err - } - req.Header.Set("Roseh-Password", string(password)) - return readGobResp[api.LoginResponse](req) -} - -func loadToken(ctx context.Context) (string, error) { - token, err := os.ReadFile(tokenCache) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - token, err := login(ctx) - if err != nil { - return "", err - } - os.WriteFile(tokenCache, []byte(token.Token), 0600) - return token.Token, nil - } - return "", err - } - return string(token), nil -} - -func gobReq(ctx context.Context, method, url string, req any) (*http.Request, error) { - buf := new(bytes.Buffer) - if err := gob.NewEncoder(buf).Encode(req); err != nil { - return nil, err - } - httpReq, err := http.NewRequestWithContext(ctx, method, url, buf) - if err != nil { - return nil, err - } - token, err := loadToken(ctx) - if err != nil { - return nil, err - } - httpReq.Header.Set("Roseh-Token", token) - return httpReq, nil -} - -func gobReqResp[Response any](ctx context.Context, method, url string, req any) (*Response, error) { - httpReq, err := gobReq(ctx, method, url, req) - if err != nil { - return nil, err - } - return readGobResp[Response](httpReq) -} - -func listNotes(ctx context.Context, req *api.ListNotesRequest) (*api.ListNotesResponse, error) { - return gobReqResp[api.ListNotesResponse](ctx, "GET", *serverURL+"/api/list-notes", req) -} - -func createNote(ctx context.Context, req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) { - return gobReqResp[api.CreateNoteResponse](ctx, "POST", *serverURL+"/api/create-note", req) -} - -func readNote(ctx context.Context, req *api.ReadNoteRequest) (*gob.Decoder, func() error, error) { - httpReq, err := gobReq(ctx, "GET", *serverURL+"/api/read-note", req) - if err != nil { - return nil, nil, err - } - resp, err := http.DefaultClient.Do(httpReq) - if err != nil { - return nil, nil, err - } - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - resp.Body.Close() - return nil, nil, fmt.Errorf("error status: %s\n%s", resp.Status, body) - } - return gob.NewDecoder(resp.Body), resp.Body.Close, nil -} - -type loginCommand struct{} - -func (*loginCommand) Name() string { - return "login" -} - -func (*loginCommand) Synopsis() string { - return "/api/login" -} - -func (*loginCommand) Usage() string { - return "makes a request to /api/login for debugging" -} - -func (*loginCommand) SetFlags(*flag.FlagSet) {} - -func (*loginCommand) login(ctx context.Context) error { - token, err := login(ctx) - if err != nil { - return err - } - return os.WriteFile(tokenCache, []byte(token.Token), 0600) -} - -func (c *loginCommand) Execute(ctx context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus { - if err := c.login(ctx); err != nil { - fmt.Fprintln(os.Stderr, err) - return subcommands.ExitFailure - } - return subcommands.ExitSuccess -} - -type listCommand struct{} - -func (*listCommand) Name() string { - return "list" -} - -func (*listCommand) Synopsis() string { - return "list notes" -} - -func (*listCommand) Usage() string { - return "list all the notes" -} - -func (*listCommand) SetFlags(*flag.FlagSet) {} - -func (*listCommand) list(ctx context.Context) error { - resp, err := listNotes(ctx, &api.ListNotesRequest{}) - if err != nil { - return err - } - for _, note := range resp.Notes { - fmt.Println(note) - } - return nil -} - -func (c *listCommand) Execute(ctx context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus { - if err := c.list(ctx); err != nil { - fmt.Fprintln(os.Stderr, err) - return subcommands.ExitFailure - } - return subcommands.ExitSuccess -} - -type newCommand struct{} - -func (*newCommand) Name() string { - return "new" -} - -func (*newCommand) Synopsis() string { - return "create note" -} - -func (*newCommand) Usage() string { - return "upload a new note" -} - -func (*newCommand) SetFlags(*flag.FlagSet) {} - -type createNoteRequestStreamWriter struct { - ctx context.Context - fileName string - continuationToken []byte -} - -func (w *createNoteRequestStreamWriter) Write(buf []byte) (int, error) { - var ( - resp *api.CreateNoteResponse - err error - ) - delay := time.Second - for i := 0; i < 3; i++ { - resp, err = createNote(w.ctx, &api.CreateNoteRequest{ - ContinuationToken: w.continuationToken, - FileName: w.fileName, - Chunk: buf, - More: true, - }) - if !errors.Is(err, errUnavailable) { - break - } - fmt.Fprintf(os.Stderr, "Warning: %s (retrying)\n", err) - select { - case <-time.After(delay): - case <-w.ctx.Done(): - return 0, w.ctx.Err() - } - delay *= 2 - } - if err != nil { - return 0, err - } - w.fileName = "" - w.continuationToken = resp.ContinuationToken - return len(buf), nil -} - -func (*newCommand) new(ctx context.Context, fileName string) error { - f, err := os.Open(fileName) - if err != nil { - return err - } - defer f.Close() - streamWriter := &createNoteRequestStreamWriter{ctx: ctx, fileName: filepath.Base(fileName)} - if _, err := io.Copy(streamWriter, f); err != nil { - return err - } - resp, err := createNote(ctx, &api.CreateNoteRequest{ContinuationToken: streamWriter.continuationToken}) - if err != nil { - return err - } - fmt.Println(resp.Name) - return nil -} - -func (c *newCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...any) subcommands.ExitStatus { - args := fs.Args() - if len(args) != 1 { - fmt.Fprintln(os.Stderr, "usage error") - return subcommands.ExitUsageError - } - if err := c.new(ctx, args[0]); err != nil { - fmt.Fprintln(os.Stderr, err) - return subcommands.ExitFailure - } - return subcommands.ExitSuccess -} - -type readCommand struct{} - -func (*readCommand) Name() string { - return "read" -} - -func (*readCommand) Synopsis() string { - return "read a note" -} - -func (*readCommand) Usage() string { - return "read a note by name" -} - -func (*readCommand) SetFlags(*flag.FlagSet) {} - -func (*readCommand) read(ctx context.Context, key string) error { - decoder, close, err := readNote(ctx, &api.ReadNoteRequest{Note: key}) - if err != nil { - return err - } - defer close() - for { - apiResp := new(api.Response) - if err := decoder.Decode(apiResp); err != nil { - if errors.Is(err, io.EOF) { - break - } - return err - } - if apiResp.Status != api.Ok { - return fmt.Errorf("%s: %s", apiResp.Status, apiResp.Err) - } - resp, ok := apiResp.Ok.(*api.ReadNoteResponseStream) - if !ok { - return fmt.Errorf("invalid response") - } - fmt.Printf("%s", resp.Chunk) - } - return nil -} - -func (c *readCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...any) subcommands.ExitStatus { - args := fs.Args() - if len(args) != 1 { - fmt.Fprintln(os.Stderr, "usage error") - return subcommands.ExitUsageError - } - if err := c.read(ctx, args[0]); err != nil { - fmt.Fprintln(os.Stderr, err) - return subcommands.ExitFailure - } - return subcommands.ExitSuccess -} - -func main() { - subcommands.ImportantFlag("url") - subcommands.Register(subcommands.HelpCommand(), "") - subcommands.Register(subcommands.FlagsCommand(), "") - subcommands.Register(subcommands.CommandsCommand(), "") - subcommands.Register(&loginCommand{}, "") - subcommands.Register(&listCommand{}, "") - subcommands.Register(&newCommand{}, "") - subcommands.Register(&readCommand{}, "") - - flag.Parse() - os.Exit(int(subcommands.Execute(context.Background()))) -} |
