aboutsummaryrefslogtreecommitdiff
path: root/session.go
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2020-10-29 15:18:36 -0400
committerDrew DeVault <sir@cmpwn.com>2020-10-29 15:18:36 -0400
commita393429f01e63aa37f58f8cbe4a810e59852fa61 (patch)
treece24cbc869e3cdda0b13e9fa3d9e34ff192c868a /session.go
parent490420726952bb3834e6a1cdda7a26c90ba9a7cb (diff)
downloadalps-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.go73
1 files changed, 66 insertions, 7 deletions
diff --git a/session.go b/session.go
index 9428cd9..0aca9ed 100644
--- a/session.go
+++ b/session.go
@@ -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)