usage of latest upstream go-imap

This commit is contained in:
Michal Horejsek
2020-04-03 13:59:14 +02:00
parent 3d3b91b242
commit ea0f3115a3
13 changed files with 129 additions and 173 deletions

View File

@ -27,6 +27,7 @@ import (
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/events"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/emersion/go-imap"
goIMAPBackend "github.com/emersion/go-imap/backend"
)
@ -150,7 +151,7 @@ func (ib *imapBackend) deleteUser(address string) {
}
// Login authenticates a user.
func (ib *imapBackend) Login(username, password string) (goIMAPBackend.User, error) {
func (ib *imapBackend) Login(_ *imap.ConnInfo, username, password string) (goIMAPBackend.User, error) {
// Called from go-imap in goroutines - we need to handle panics for each function.
defer ib.panicHandler.HandlePanic()

View File

@ -106,7 +106,7 @@ func (im *imapMailbox) getFlags() []string {
//
// It always returns the state of DB (which could be different to server status).
// Additionally it checks that all stored numbers are same as in DB and polls events if needed.
func (im *imapMailbox) Status(items []string) (*imap.MailboxStatus, error) {
func (im *imapMailbox) Status(items []imap.StatusItem) (*imap.MailboxStatus, error) {
// Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic()
@ -139,29 +139,19 @@ func (im *imapMailbox) Status(items []string) (*imap.MailboxStatus, error) {
return status, nil
}
// Subscribe adds the mailbox to the server's set of "active" or "subscribed" mailboxes.
func (im *imapMailbox) Subscribe() error {
// SetSubscribed adds or removes the mailbox to the server's set of "active"
// or "subscribed" mailboxes.
func (im *imapMailbox) SetSubscribed(subscribed bool) error {
// Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic()
label := im.storeMailbox.LabelID()
if !im.user.isSubscribed(label) {
if subscribed && !im.user.isSubscribed(label) {
im.user.removeFromCache(SubscriptionException, label)
}
return nil
}
// Unsubscribe removes the mailbox to the server's set of "active" or "subscribed" mailboxes.
func (im *imapMailbox) Unsubscribe() error {
// Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic()
label := im.storeMailbox.LabelID()
if im.user.isSubscribed(label) {
if !subscribed && im.user.isSubscribed(label) {
im.user.addToCache(SubscriptionException, label)
}
return nil
}

View File

@ -265,7 +265,7 @@ func (im *imapMailbox) importMessage(m *pmapi.Message, readers []io.Reader, kr *
return im.storeMailbox.ImportMessage(m, b.Bytes(), labels)
}
func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []string) (msg *imap.Message, err error) {
func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []imap.FetchItem) (msg *imap.Message, err error) {
im.log.WithField("msgID", storeMessage.ID()).Trace("Getting message")
seqNum, err := storeMessage.SequenceNumber()
@ -278,9 +278,9 @@ func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []str
msg = imap.NewMessage(seqNum, items)
for _, item := range items {
switch item {
case imap.EnvelopeMsgAttr:
case imap.FetchEnvelope:
msg.Envelope = message.GetEnvelope(m)
case imap.BodyMsgAttr, imap.BodyStructureMsgAttr:
case imap.FetchBody, imap.FetchBodyStructure:
var structure *message.BodyStructure
if structure, _, err = im.getBodyStructure(storeMessage); err != nil {
return
@ -288,11 +288,11 @@ func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []str
if msg.BodyStructure, err = structure.IMAPBodyStructure([]int{}); err != nil {
return
}
case imap.FlagsMsgAttr:
case imap.FetchFlags:
msg.Flags = message.GetFlags(m)
case imap.InternalDateMsgAttr:
case imap.FetchInternalDate:
msg.InternalDate = time.Unix(m.Time, 0)
case imap.SizeMsgAttr:
case imap.FetchRFC822Size:
// Size attribute on the server counts encrypted data. The value is cleared
// on our part and we need to compute "real" size of decrypted data.
if m.Size <= 0 {
@ -301,7 +301,7 @@ func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []str
}
}
msg.Size = uint32(m.Size)
case imap.UidMsgAttr:
case imap.FetchUid:
msg.Uid, err = storeMessage.UID()
if err != nil {
return nil, err
@ -310,7 +310,7 @@ func (im *imapMailbox) getMessage(storeMessage storeMessageProvider, items []str
s := item
var section *imap.BodySectionName
if section, err = imap.NewBodySectionName(s); err != nil {
if section, err = imap.ParseBodySectionName(s); err != nil {
err = nil // Ignore error
break
}
@ -421,13 +421,13 @@ func (im *imapMailbox) getMessageBodySection(storeMessage storeMessageProvider,
// The TEXT specifier refers to the content of the message (or section), omitting the [RFC-2822] header.
// Non-empty section with no specifier (imap.EntireSpecifier) refers to section content without header.
response, err = structure.GetSectionContent(bodyReader, section.Path)
case section.Specifier == imap.MimeSpecifier:
case section.Specifier == imap.MIMESpecifier:
// The MIME part specifier refers to the [MIME-IMB] header for this part.
fallthrough
case section.Specifier == imap.HeaderSpecifier:
header, err = structure.GetSectionHeader(section.Path)
default:
err = errors.New("Unknown specifier " + section.Specifier)
err = errors.New("Unknown specifier " + string(section.Specifier))
}
}

View File

@ -152,17 +152,17 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
// Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic()
if criteria.Not != nil || criteria.Or[0] != nil {
if criteria.Not != nil || criteria.Or != nil {
return nil, errors.New("unsupported search query")
}
if criteria.Body != "" || criteria.Text != "" {
if criteria.Body != nil || criteria.Text != nil {
log.Warn("Body and Text criteria not applied.")
}
var apiIDs []string
if criteria.SeqSet != nil {
apiIDs, err = im.apiIDsFromSeqSet(false, criteria.SeqSet)
if criteria.SeqNum != nil {
apiIDs, err = im.apiIDsFromSeqSet(false, criteria.SeqNum)
} else {
apiIDs, err = im.storeMailbox.GetAPIIDsFromSequenceRange(1, 0)
}
@ -193,7 +193,7 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
m := storeMessage.Message()
// Filter addresses.
if criteria.From != "" && !addressMatch([]*mail.Address{m.Sender}, criteria.From) {
/*if criteria.From != "" && !addressMatch([]*mail.Address{m.Sender}, criteria.From) {
continue
}
if criteria.To != "" && !addressMatch(m.ToList, criteria.To) {
@ -204,10 +204,10 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
}
if criteria.Bcc != "" && !addressMatch(m.BCCList, criteria.Bcc) {
continue
}
}*/
// Filter strings.
if criteria.Subject != "" && !strings.Contains(strings.ToLower(m.Subject), strings.ToLower(criteria.Subject)) {
/*if criteria.Subject != "" && !strings.Contains(strings.ToLower(m.Subject), strings.ToLower(criteria.Subject)) {
continue
}
if criteria.Keyword != "" && !hasKeyword(m, criteria.Keyword) {
@ -262,7 +262,7 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
}
if criteria.New && !(!m.Has(pmapi.FlagOpened) && m.Unread == 1) {
continue
}
}*/
// Filter internal date.
if !criteria.Before.IsZero() {
@ -275,13 +275,13 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
continue
}
}
if !criteria.On.IsZero() {
/*if !criteria.On.IsZero() {
truncated := criteria.On.Truncate(24 * time.Hour)
if m.Time < truncated.Unix() || m.Time > truncated.Add(24*time.Hour).Unix() {
continue
}
}
if !(criteria.SentBefore.IsZero() && criteria.SentSince.IsZero() && criteria.SentOn.IsZero()) {
}*/
if !(criteria.SentBefore.IsZero() && criteria.SentSince.IsZero() /*&& criteria.SentOn.IsZero()*/) {
if t, err := m.Header.Date(); err == nil && !t.IsZero() {
// Filter header date.
if !criteria.SentBefore.IsZero() {
@ -294,12 +294,12 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
continue
}
}
if !criteria.SentOn.IsZero() {
/*if !criteria.SentOn.IsZero() {
truncated := criteria.SentOn.Truncate(24 * time.Hour)
if t.Unix() < truncated.Unix() || t.Unix() > truncated.Add(24*time.Hour).Unix() {
continue
}
}
}*/
}
}
@ -337,7 +337,7 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
// 3501 section 6.4.5 for a list of items that can be requested.
//
// Messages must be sent to msgResponse. When the function returns, msgResponse must be closed.
func (im *imapMailbox) ListMessages(isUID bool, seqSet *imap.SeqSet, items []string, msgResponse chan<- *imap.Message) (err error) { //nolint[funlen]
func (im *imapMailbox) ListMessages(isUID bool, seqSet *imap.SeqSet, items []imap.FetchItem, msgResponse chan<- *imap.Message) (err error) { //nolint[funlen]
defer func() {
close(msgResponse)
if err != nil {

View File

@ -71,29 +71,25 @@ func (m *imapRootMailbox) Info() (info *imap.MailboxInfo, err error) {
return
}
func (m *imapRootMailbox) Status(items []string) (status *imap.MailboxStatus, err error) {
status = &imap.MailboxStatus{}
func (m *imapRootMailbox) Status(_ []imap.StatusItem) (*imap.MailboxStatus, error) {
status := &imap.MailboxStatus{}
if m.isFolder {
status.Name = store.UserFoldersMailboxName
} else {
status.Name = store.UserLabelsMailboxName
}
return
return status, nil
}
func (m *imapRootMailbox) Subscribe() error {
return errors.New("cannot subscribe to Labels or Folders mailboxes")
}
func (m *imapRootMailbox) Unsubscribe() error {
return errors.New("cannot unsubscribe from Labels or Folders mailboxes")
func (m *imapRootMailbox) SetSubscribed(_ bool) error {
return errors.New("cannot subscribe or unsubsribe to Labels or Folders mailboxes")
}
func (m *imapRootMailbox) Check() error {
return nil
}
func (m *imapRootMailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch chan<- *imap.Message) error {
func (m *imapRootMailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error {
close(ch)
return nil
}

View File

@ -21,6 +21,7 @@ import (
"crypto/tls"
"fmt"
"io"
"net"
"strings"
"time"
@ -44,6 +45,8 @@ import (
type imapServer struct {
server *imapserver.Server
eventListener listener.Listener
debugClient bool
debugServer bool
}
// NewIMAPServer constructs a new IMAP server configured with the given options.
@ -55,17 +58,6 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima
s.ErrorLog = newServerErrorLogger("server-imap")
s.AutoLogout = 30 * time.Minute
if debugClient || debugServer {
var localDebug, remoteDebug imap.WriterWithFields
if debugClient {
remoteDebug = &logWithFields{log: log.WithField("pkg", "imap/client"), fields: logrus.Fields{}}
}
if debugServer {
localDebug = &logWithFields{log: log.WithField("pkg", "imap/server"), fields: logrus.Fields{}}
}
s.Debug = imap.NewDebugWithFields(localDebug, remoteDebug)
}
serverID := imapid.ID{
imapid.FieldName: "ProtonMail",
imapid.FieldVendor: "Proton Technologies AG",
@ -96,7 +88,7 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima
})
return sasl.NewLoginServer(func(address, password string) error {
user, err := conn.Server().Backend.Login(address, password)
user, err := conn.Server().Backend.Login(nil, address, password)
if err != nil {
return err
}
@ -122,6 +114,8 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima
return &imapServer{
server: s,
eventListener: eventListener,
debugClient: debugClient,
debugServer: debugServer,
}
}
@ -130,7 +124,17 @@ func (s *imapServer) ListenAndServe() {
go s.monitorDisconnectedUsers()
log.Info("IMAP server listening at ", s.server.Addr)
err := s.server.ListenAndServe()
l, err := net.Listen("tcp", s.server.Addr)
if err != nil {
s.eventListener.Emit(events.ErrorEvent, "IMAP failed: "+err.Error())
log.Error("IMAP failed: ", err)
return
}
err = s.server.Serve(&debugListener{
Listener: l,
server: s,
})
if err != nil {
s.eventListener.Emit(events.ErrorEvent, "IMAP failed: "+err.Error())
log.Error("IMAP failed: ", err)
@ -163,20 +167,38 @@ func (s *imapServer) monitorDisconnectedUsers() {
}
}
// logWithFields is used for debuging with additional field.
type logWithFields struct {
log *logrus.Entry
fields logrus.Fields
// debugListener sets debug loggers on server containing fields with local
// and remote addresses right after new connection is accepted.
type debugListener struct {
net.Listener
server *imapServer
}
func (lf *logWithFields) Writer() io.Writer {
w := lf.log.WithFields(lf.fields).WriterLevel(logrus.DebugLevel)
lf.fields = logrus.Fields{}
return w
}
func (dl *debugListener) Accept() (net.Conn, error) {
conn, err := dl.Listener.Accept()
func (lf *logWithFields) SetField(key, value string) {
lf.fields[key] = value
if err == nil && (dl.server.debugServer || dl.server.debugClient) {
debugLog := log
if addr := conn.LocalAddr(); addr != nil {
debugLog = debugLog.WithField("loc", addr.String())
}
if addr := conn.RemoteAddr(); addr != nil {
debugLog = debugLog.WithField("rem", addr.String())
}
var localDebug, remoteDebug io.Writer
if dl.server.debugServer {
localDebug = debugLog.WithField("pkg", "imap/server").WriterLevel(logrus.DebugLevel)
}
if dl.server.debugClient {
remoteDebug = debugLog.WithField("pkg", "imap/client").WriterLevel(logrus.DebugLevel)
}
dl.server.server.Debug = imap.NewDebugWriter(localDebug, remoteDebug)
}
return conn, err
}
// serverErrorLogger implements go-imap/logger interface.
@ -188,17 +210,12 @@ func newServerErrorLogger(tag string) *serverErrorLogger {
return &serverErrorLogger{tag}
}
func (s *serverErrorLogger) CheckErrorForReport(serverErr string) {
}
func (s *serverErrorLogger) Printf(format string, args ...interface{}) {
err := fmt.Sprintf(format, args...)
s.CheckErrorForReport(err)
log.WithField("pkg", s.tag).Error(err)
}
func (s *serverErrorLogger) Println(args ...interface{}) {
err := fmt.Sprintln(args...)
s.CheckErrorForReport(err)
log.WithField("pkg", s.tag).Error(err)
}

View File

@ -141,7 +141,7 @@ func (ext *extension) Capabilities(c server.Conn) []string {
}
func (ext *extension) Command(name string) server.HandlerFactory {
if name == imap.Expunge {
if name == "EXPUNGE" {
return func() server.Handler {
return &UIDExpunge{}
}
@ -165,7 +165,7 @@ func getStatusResponseCopy(uidValidity uint32, sourceSeq, targetSeq *OrderedSeq)
}
return &imap.StatusResp{
Type: imap.StatusOk,
Type: imap.StatusRespOk,
Info: info,
}
}
@ -187,7 +187,7 @@ func getStatusResponseAppend(uidValidity uint32, targetSeq *OrderedSeq) *imap.St
}
return &imap.StatusResp{
Type: imap.StatusOk,
Type: imap.StatusRespOk,
Info: info,
}
}