forked from Silverfish/proton-bridge
usage of latest upstream go-imap
This commit is contained in:
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user