diff options
author | Drew DeVault <sir@cmpwn.com> | 2020-10-29 15:18:36 -0400 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2020-10-29 15:18:36 -0400 |
commit | a393429f01e63aa37f58f8cbe4a810e59852fa61 (patch) | |
tree | ce24cbc869e3cdda0b13e9fa3d9e34ff192c868a /session.go | |
parent | 490420726952bb3834e6a1cdda7a26c90ba9a7cb (diff) | |
download | alps-a393429f01e63aa37f58f8cbe4a810e59852fa61.tar.gz alps-a393429f01e63aa37f58f8cbe4a810e59852fa61.zip |
Implement JavaScript UI for attachments
This one is a bit of a doozy. A summary of the changes:
- Session has grown storage for attachments which have been uploaded but
not yet sent.
- The list of attachments on a message is refcounted so that we can
clean up the temporary files only after it's done with - i.e. after
copying to Sent and after all of the SMTP attempts are done.
- Abandoned attachments are cleared out on process shutdown.
Future work:
- Add a limit to the maximum number of pending attachments the user can
have in the session.
- Periodically clean out abandoned attachments?
Diffstat (limited to 'session.go')
-rw-r--r-- | session.go | 73 |
1 files changed, 66 insertions, 7 deletions
@@ -5,6 +5,7 @@ import ( "encoding/base64" "errors" "fmt" + "mime/multipart" "net/http" "os" "sync" @@ -13,6 +14,7 @@ import ( imapclient "github.com/emersion/go-imap/client" "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" + "github.com/google/uuid" "github.com/labstack/echo/v4" ) @@ -54,6 +56,14 @@ type Session struct { imapLocker sync.Mutex imapConn *imapclient.Client // protected by locker, can be nil + + attachmentsLocker sync.Mutex + attachments map[string]*Attachment // protected by attachmentsLocker +} + +type Attachment struct { + File *multipart.FileHeader + Form *multipart.Form } func (s *Session) ping() { @@ -117,6 +127,13 @@ func (s *Session) SetHTTPBasicAuth(req *http.Request) { // Close destroys the session. This can be used to log the user out. func (s *Session) Close() { + s.attachmentsLocker.Lock() + defer s.attachmentsLocker.Unlock() + + for _, f := range s.attachments { + f.Form.RemoveAll() + } + select { case <-s.closed: // This space is intentionally left blank @@ -125,6 +142,41 @@ func (s *Session) Close() { } } +// Puts an attachment and returns a generated UUID +func (s *Session) PutAttachment(in *multipart.FileHeader, + form *multipart.Form) (string, error) { + // TODO: Prevent users from uploading too many attachments, or too large + // + // Probably just set a cap on the maximum combined size of all files in the + // user's session + // + // TODO: Figure out what to do if the user abandons the compose window + // after adding some attachments + id := uuid.New() + s.attachmentsLocker.Lock() + s.attachments[id.String()] = &Attachment{ + File: in, + Form: form, + } + s.attachmentsLocker.Unlock() + return id.String(), nil +} + +// Removes an attachment from the session. Returns nil if there was no such +// attachment. +func (s *Session) PopAttachment(uuid string) *Attachment { + s.attachmentsLocker.Lock() + defer s.attachmentsLocker.Unlock() + + a, ok := s.attachments[uuid] + if !ok { + return nil + } + delete(s.attachments, uuid) + + return a +} + // Store returns a store suitable for storing persistent user data. func (s *Session) Store() Store { return s.store @@ -159,6 +211,12 @@ func newSessionManager(dialIMAP DialIMAPFunc, dialSMTP DialSMTPFunc, logger echo } } +func (sm *SessionManager) Close() { + for _, s := range sm.sessions { + s.Close() + } +} + func (sm *SessionManager) connectIMAP(username, password string) (*imapclient.Client, error) { c, err := sm.dialIMAP() if err != nil { @@ -213,13 +271,14 @@ func (sm *SessionManager) Put(username, password string) (*Session, error) { } s := &Session{ - manager: sm, - closed: make(chan struct{}), - pings: make(chan struct{}, 5), - imapConn: c, - username: username, - password: password, - token: token, + manager: sm, + closed: make(chan struct{}), + pings: make(chan struct{}, 5), + imapConn: c, + username: username, + password: password, + token: token, + attachments: make(map[string]*Attachment), } s.store, err = newStore(s, sm.logger) |