aboutsummaryrefslogblamecommitdiffstats
path: root/models/models.go
blob: e05c81acf1661b06128a6a78f2f209b5f29b2cc6 (plain) (tree)
1
2
3
4
5
6
7
8
9


              
                
             
            
                 

              
                                          


                                             
                                                                             
                                                                        
                 


                                                                  
                                  











                                                                 


                                               

 



                                      






















                                    

                                                     

 
                       
                   





                                           

                    


                           
                   

                                                        

                                                                                      

                                        



                                                 











                                           



                                                                   

                                    
                           
                              
                              

                                  
                              

                            
                           

 













                                                              


                                                              


                                                      
                                                                









                                                           


                                   


                                                              
                                                               


                                                           
                        

 










                                                           











                                           
                                                               































                                                                                         














                                                                                  


                           

                                 
                                 


                                 
                        
                        

 

                                                       




                                  
                            
 























                                                                       
package models

import (
	"errors"
	"fmt"
	"io"
	"strings"
	"time"

	"git.sr.ht/~rjarry/aerc/lib/parse"
	"github.com/emersion/go-message/mail"
)

// Flags is an abstraction around the different flags which can be present in
// different email backends and represents a flag that we use in the UI.
type Flags uint32

const (
	// SeenFlag marks a message as having been seen previously
	SeenFlag Flags = 1 << iota

	// RecentFlag marks a message as being recent
	RecentFlag

	// AnsweredFlag marks a message as having been replied to
	AnsweredFlag

	// DeletedFlag marks a message as having been deleted
	DeletedFlag

	// FlaggedFlag marks a message with a user flag
	FlaggedFlag

	// DraftFlag marks a message as a draft
	DraftFlag
)

func (f Flags) Has(flags Flags) bool {
	return f&flags == flags
}

type Role string

var Roles = map[string]Role{
	"all":     AllRole,
	"archive": ArchiveRole,
	"drafts":  DraftsRole,
	"inbox":   InboxRole,
	"junk":    JunkRole,
	"sent":    SentRole,
	"trash":   TrashRole,
	"query":   QueryRole,
}

const (
	AllRole     Role = "all"
	ArchiveRole Role = "archive"
	DraftsRole  Role = "drafts"
	InboxRole   Role = "inbox"
	JunkRole    Role = "junk"
	SentRole    Role = "sent"
	TrashRole   Role = "trash"
	// Custom aerc roles
	QueryRole Role = "query"
	// virtual node created by the directory tree
	VirtualRole Role = "virtual"
)

type Directory struct {
	Name string
	// Exists messages in the Directory
	Exists int
	// Recent messages in the Directory
	Recent int
	// Unseen messages in the Directory
	Unseen int
	// IANA role
	Role Role
}

type DirectoryInfo struct {
	Name string
	// The total number of messages in this mailbox.
	Exists int
	// The number of messages not seen since the last time the mailbox was opened.
	Recent int
	// The number of unread messages
	Unseen int
}

// Capabilities provides the backend capabilities
type Capabilities struct {
	Sort       bool
	Thread     bool
	Extensions []string
}

func (c *Capabilities) Has(s string) bool {
	for _, ext := range c.Extensions {
		if ext == s {
			return true
		}
	}
	return false
}

// A MessageInfo holds information about the structure of a message
type MessageInfo struct {
	BodyStructure *BodyStructure
	Envelope      *Envelope
	Flags         Flags
	Labels        []string
	Filenames     []string
	InternalDate  time.Time
	RFC822Headers *mail.Header
	Refs          []string
	Size          uint32
	Uid           uint32
	Error         error
}

func (mi *MessageInfo) MsgId() (msgid string, err error) {
	if mi == nil {
		return "", errors.New("msg is nil")
	}
	if mi.Envelope == nil {
		return "", errors.New("envelope is nil")
	}
	return mi.Envelope.MessageId, nil
}

func (mi *MessageInfo) InReplyTo() (msgid string, err error) {
	if mi == nil {
		return "", errors.New("msg is nil")
	}
	if mi.Envelope != nil && mi.Envelope.InReplyTo != "" {
		return mi.Envelope.InReplyTo, nil
	}
	if mi.RFC822Headers == nil {
		return "", errors.New("header is nil")
	}
	list := parse.MsgIDList(mi.RFC822Headers, "In-Reply-To")
	if len(list) == 0 {
		return "", errors.New("no results")
	}
	return list[0], err
}

func (mi *MessageInfo) References() ([]string, error) {
	if mi == nil {
		return []string{}, errors.New("msg is nil")
	}
	if mi.Refs != nil {
		return mi.Refs, nil
	}
	if mi.RFC822Headers == nil {
		return []string{}, errors.New("header is nil")
	}
	list := parse.MsgIDList(mi.RFC822Headers, "References")
	if len(list) == 0 {
		return []string{}, errors.New("no results")
	}
	return list, nil
}

// A MessageBodyPart can be displayed in the message viewer
type MessageBodyPart struct {
	Reader io.Reader
	Uid    uint32
}

// A FullMessage is the entire message
type FullMessage struct {
	Reader io.Reader
	Uid    uint32
}

type BodyStructure struct {
	MIMEType          string
	MIMESubType       string
	Params            map[string]string
	Description       string
	Encoding          string
	Parts             []*BodyStructure
	Disposition       string
	DispositionParams map[string]string
}

// PartAtIndex returns the BodyStructure at the requested index
func (bs *BodyStructure) PartAtIndex(index []int) (*BodyStructure, error) {
	if len(index) == 0 {
		return bs, nil
	}
	cur := index[0]
	rest := index[1:]
	// passed indexes are 1 based, we need to convert back to actual indexes
	curidx := cur - 1
	if curidx < 0 {
		return nil, fmt.Errorf("invalid index, expected 1 based input")
	}

	// no children, base case
	if len(bs.Parts) == 0 {
		if len(rest) != 0 {
			return nil, fmt.Errorf("more index levels given than available")
		}
		if cur == 1 {
			return bs, nil
		} else {
			return nil, fmt.Errorf("invalid index %v for non multipart", cur)
		}
	}

	if cur > len(bs.Parts) {
		return nil, fmt.Errorf("invalid index %v, only have %v children",
			cur, len(bs.Parts))
	}

	return bs.Parts[curidx].PartAtIndex(rest)
}

func (bs *BodyStructure) FullMIMEType() string {
	mime := fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType)
	return strings.ToLower(mime)
}

func (bs *BodyStructure) FileName() string {
	if filename, ok := bs.DispositionParams["filename"]; ok {
		return filename
	} else if filename, ok := bs.Params["name"]; ok {
		// workaround golang not supporting RFC2231 besides ASCII and UTF8
		return filename
	}
	return ""
}

type Envelope struct {
	Date      time.Time
	Subject   string
	From      []*mail.Address
	ReplyTo   []*mail.Address
	Sender    []*mail.Address
	To        []*mail.Address
	Cc        []*mail.Address
	Bcc       []*mail.Address
	MessageId string
	InReplyTo string
}

// OriginalMail is helper struct used for reply/forward
type OriginalMail struct {
	Date          time.Time
	From          string
	Text          string
	MIMEType      string
	RFC822Headers *mail.Header
	Folder        string
}

type SignatureValidity int32

const (
	UnknownValidity SignatureValidity = iota
	Valid
	InvalidSignature
	UnknownEntity
	UnsupportedMicalg
	MicalgMismatch
)

type MessageDetails struct {
	IsEncrypted        bool
	IsSigned           bool
	SignedBy           string // Primary identity of signing key
	SignedByKeyId      uint64
	SignatureValidity  SignatureValidity
	SignatureError     string
	DecryptedWith      string // Primary Identity of decryption key
	DecryptedWithKeyId uint64 // Public key id of decryption key
	Body               io.Reader
	Micalg             string
}