Browse Source

Add mail storage base implementation

- Rework gostfix .proto interface
- Add mongo configuration
- Migrate to in-place structures for html templates
- Minor code improvements
Alexey Edelev 5 years ago
parent
commit
ee8633a412
8 changed files with 406 additions and 63 deletions
  1. 134 25
      common/gostfix.pb.go
  2. 13 2
      common/gostfix.proto
  3. 19 0
      config/config.go
  4. 108 0
      db/db.go
  5. 2 0
      go.mod
  6. 103 0
      go.sum
  7. 27 14
      web/server.go
  8. 0 22
      web/templater.go

+ 134 - 25
common/gostfix.pb.go

@@ -21,11 +21,12 @@ var _ = math.Inf
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type MailBody struct {
-	ContentType          string   `protobuf:"bytes,1,opt,name=contentType,proto3" json:"contentType,omitempty"`
-	Content              []byte   `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	PlainText            string              `protobuf:"bytes,1,opt,name=plainText,proto3" json:"plainText,omitempty"`
+	RichText             string              `protobuf:"bytes,2,opt,name=richText,proto3" json:"richText,omitempty"`
+	Attachments          []*AttachmentHeader `protobuf:"bytes,3,rep,name=attachments,proto3" json:"attachments,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
+	XXX_unrecognized     []byte              `json:"-"`
+	XXX_sizecache        int32               `json:"-"`
 }
 
 func (m *MailBody) Reset()         { *m = MailBody{} }
@@ -53,16 +54,23 @@ func (m *MailBody) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_MailBody proto.InternalMessageInfo
 
-func (m *MailBody) GetContentType() string {
+func (m *MailBody) GetPlainText() string {
 	if m != nil {
-		return m.ContentType
+		return m.PlainText
+	}
+	return ""
+}
+
+func (m *MailBody) GetRichText() string {
+	if m != nil {
+		return m.RichText
 	}
 	return ""
 }
 
-func (m *MailBody) GetContent() []byte {
+func (m *MailBody) GetAttachments() []*AttachmentHeader {
 	if m != nil {
-		return m.Content
+		return m.Attachments
 	}
 	return nil
 }
@@ -193,10 +201,106 @@ func (m *Mail) GetBody() *MailBody {
 	return nil
 }
 
+type Attachment struct {
+	Header               *AttachmentHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+	Data                 []byte            `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *Attachment) Reset()         { *m = Attachment{} }
+func (m *Attachment) String() string { return proto.CompactTextString(m) }
+func (*Attachment) ProtoMessage()    {}
+func (*Attachment) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0ab36b6dc6e1dcaa, []int{3}
+}
+
+func (m *Attachment) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Attachment.Unmarshal(m, b)
+}
+func (m *Attachment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Attachment.Marshal(b, m, deterministic)
+}
+func (m *Attachment) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Attachment.Merge(m, src)
+}
+func (m *Attachment) XXX_Size() int {
+	return xxx_messageInfo_Attachment.Size(m)
+}
+func (m *Attachment) XXX_DiscardUnknown() {
+	xxx_messageInfo_Attachment.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Attachment proto.InternalMessageInfo
+
+func (m *Attachment) GetHeader() *AttachmentHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *Attachment) GetData() []byte {
+	if m != nil {
+		return m.Data
+	}
+	return nil
+}
+
+type AttachmentHeader struct {
+	FileName             string   `protobuf:"bytes,1,opt,name=fileName,proto3" json:"fileName,omitempty"`
+	ContentType          string   `protobuf:"bytes,2,opt,name=contentType,proto3" json:"contentType,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *AttachmentHeader) Reset()         { *m = AttachmentHeader{} }
+func (m *AttachmentHeader) String() string { return proto.CompactTextString(m) }
+func (*AttachmentHeader) ProtoMessage()    {}
+func (*AttachmentHeader) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0ab36b6dc6e1dcaa, []int{4}
+}
+
+func (m *AttachmentHeader) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AttachmentHeader.Unmarshal(m, b)
+}
+func (m *AttachmentHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AttachmentHeader.Marshal(b, m, deterministic)
+}
+func (m *AttachmentHeader) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AttachmentHeader.Merge(m, src)
+}
+func (m *AttachmentHeader) XXX_Size() int {
+	return xxx_messageInfo_AttachmentHeader.Size(m)
+}
+func (m *AttachmentHeader) XXX_DiscardUnknown() {
+	xxx_messageInfo_AttachmentHeader.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AttachmentHeader proto.InternalMessageInfo
+
+func (m *AttachmentHeader) GetFileName() string {
+	if m != nil {
+		return m.FileName
+	}
+	return ""
+}
+
+func (m *AttachmentHeader) GetContentType() string {
+	if m != nil {
+		return m.ContentType
+	}
+	return ""
+}
+
 func init() {
 	proto.RegisterType((*MailBody)(nil), "common.MailBody")
 	proto.RegisterType((*MailHeader)(nil), "common.MailHeader")
 	proto.RegisterType((*Mail)(nil), "common.Mail")
+	proto.RegisterType((*Attachment)(nil), "common.Attachment")
+	proto.RegisterType((*AttachmentHeader)(nil), "common.AttachmentHeader")
 }
 
 func init() {
@@ -204,20 +308,25 @@ func init() {
 }
 
 var fileDescriptor_0ab36b6dc6e1dcaa = []byte{
-	// 230 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0x31, 0x4f, 0xc3, 0x40,
-	0x0c, 0x85, 0x95, 0x34, 0x04, 0xea, 0x00, 0xaa, 0x3c, 0xdd, 0x18, 0x45, 0x0c, 0x15, 0x43, 0x86,
-	0xf2, 0x0f, 0x18, 0x10, 0x0b, 0xcb, 0x89, 0x81, 0x35, 0xf1, 0x5d, 0xa1, 0x88, 0xc4, 0x55, 0x6a,
-	0x24, 0xb2, 0xf1, 0xd3, 0x91, 0x9d, 0x54, 0x64, 0x7b, 0xef, 0x9d, 0xef, 0xdd, 0x77, 0x86, 0x9b,
-	0x77, 0x3e, 0xc9, 0xfe, 0xf0, 0x53, 0x1f, 0x07, 0x16, 0xc6, 0x9c, 0xb8, 0xeb, 0xb8, 0xaf, 0x9e,
-	0xe0, 0xea, 0xa5, 0x39, 0x7c, 0x3d, 0x72, 0x18, 0xb1, 0x84, 0x82, 0xb8, 0x97, 0xd8, 0xcb, 0xeb,
-	0x78, 0x8c, 0x2e, 0x29, 0x93, 0xed, 0xda, 0x2f, 0x23, 0x74, 0x70, 0x39, 0x5b, 0x97, 0x96, 0xc9,
-	0xf6, 0xda, 0x9f, 0x6d, 0xf5, 0x9b, 0x00, 0x68, 0xd1, 0x73, 0x6c, 0x42, 0x1c, 0x10, 0x21, 0xdb,
-	0x0f, 0xdc, 0xcd, 0x1d, 0xa6, 0xf1, 0x16, 0x52, 0x61, 0xbb, 0xb7, 0xf6, 0xa9, 0xb0, 0x7a, 0x22,
-	0xb7, 0x9a, 0x3c, 0x11, 0x6e, 0x60, 0xd5, 0x12, 0xb9, 0xcc, 0x02, 0x95, 0xda, 0x12, 0x1a, 0x89,
-	0xee, 0x62, 0x6a, 0x51, 0xad, 0x08, 0xa7, 0xef, 0xf6, 0x33, 0x92, 0xb8, 0xdc, 0xe2, 0xb3, 0xad,
-	0xde, 0x20, 0x53, 0x02, 0xbc, 0x87, 0xfc, 0xc3, 0x28, 0xec, 0xf5, 0x62, 0x87, 0xf5, 0xf4, 0xd7,
-	0xfa, 0x9f, 0xcf, 0xcf, 0x13, 0x78, 0x07, 0x59, 0xcb, 0x61, 0x34, 0xaa, 0x62, 0xb7, 0x59, 0x4e,
-	0xea, 0x4a, 0xbc, 0x9d, 0xb6, 0xb9, 0xed, 0xec, 0xe1, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x81, 0xe6,
-	0x8d, 0x59, 0x44, 0x01, 0x00, 0x00,
+	// 311 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xbf, 0x4b, 0xf4, 0x40,
+	0x10, 0xe5, 0x2e, 0xf9, 0xf2, 0xdd, 0x4d, 0x54, 0x8e, 0xa9, 0x16, 0xb1, 0x38, 0x82, 0xc5, 0x61,
+	0x71, 0xc8, 0xd9, 0xd9, 0x69, 0x65, 0xa3, 0x48, 0xb8, 0xc2, 0x76, 0xb3, 0xd9, 0x98, 0x48, 0x92,
+	0x0d, 0xc9, 0x08, 0x97, 0xce, 0x3f, 0x5d, 0x76, 0xf2, 0x93, 0x03, 0xbb, 0x79, 0xf3, 0x86, 0x79,
+	0x6f, 0xde, 0x2e, 0x5c, 0x7e, 0x9a, 0x86, 0x92, 0xec, 0xb4, 0xaf, 0x6a, 0x43, 0x06, 0x3d, 0x65,
+	0x8a, 0xc2, 0x94, 0xc1, 0xcf, 0x02, 0x56, 0xaf, 0x32, 0xcb, 0x9f, 0x4d, 0xdc, 0xe2, 0x0d, 0xac,
+	0xab, 0x5c, 0x66, 0xe5, 0x51, 0x9f, 0x48, 0x2c, 0xb6, 0x8b, 0xdd, 0x3a, 0x9c, 0x1a, 0x78, 0x0d,
+	0xab, 0x3a, 0x53, 0x29, 0x93, 0x4b, 0x26, 0x47, 0x8c, 0x8f, 0xe0, 0x4b, 0x22, 0xa9, 0xd2, 0x42,
+	0x97, 0xd4, 0x08, 0x67, 0xeb, 0xec, 0xfc, 0x83, 0xd8, 0x77, 0x22, 0xfb, 0xa7, 0x91, 0x7a, 0xd1,
+	0x32, 0xd6, 0x75, 0x38, 0x1f, 0xb6, 0x16, 0xc0, 0x5a, 0xe8, 0x38, 0x44, 0x70, 0x93, 0xda, 0x14,
+	0xbd, 0x3e, 0xd7, 0x78, 0x05, 0x4b, 0x32, 0xbd, 0xe8, 0x92, 0x8c, 0xc5, 0x4a, 0x09, 0xa7, 0xc3,
+	0x4a, 0xe1, 0x06, 0x9c, 0x48, 0x29, 0xe1, 0x72, 0xc3, 0x96, 0x76, 0x4b, 0x2c, 0x49, 0x8b, 0x7f,
+	0xdd, 0x16, 0x5b, 0xa3, 0x80, 0xff, 0xcd, 0x77, 0xf4, 0xa5, 0x15, 0x09, 0x8f, 0xdb, 0x03, 0x0c,
+	0x3e, 0xc0, 0xb5, 0x0e, 0xf0, 0x0e, 0xbc, 0x94, 0x5d, 0xb0, 0xba, 0x7f, 0xc0, 0xe1, 0x82, 0xc9,
+	0x5f, 0xd8, 0x4f, 0xe0, 0x2d, 0xb8, 0x91, 0x89, 0x5b, 0x76, 0xe5, 0x1f, 0x36, 0xf3, 0x49, 0x1b,
+	0x66, 0xc8, 0x6c, 0x10, 0x02, 0x4c, 0xd7, 0xe3, 0xfd, 0xd9, 0xfe, 0xbf, 0x13, 0x1a, 0x54, 0xba,
+	0x3b, 0x24, 0xab, 0x5c, 0xf0, 0x1d, 0x32, 0x78, 0x87, 0xcd, 0xf9, 0xbc, 0x7d, 0x9c, 0x24, 0xcb,
+	0xf5, 0x9b, 0x2c, 0x74, 0x9f, 0xdc, 0x88, 0x71, 0x0b, 0xbe, 0x32, 0x25, 0xe9, 0x92, 0x8e, 0x6d,
+	0xa5, 0xfb, 0x18, 0xe7, 0xad, 0xc8, 0xe3, 0x4f, 0xf1, 0xf0, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x51,
+	0xa7, 0x56, 0xb4, 0x25, 0x02, 0x00, 0x00,
 }

+ 13 - 2
common/gostfix.proto

@@ -3,8 +3,9 @@ syntax = "proto3";
 package common;
 
 message MailBody {
-    string contentType = 1;
-    bytes content = 2;
+	string plainText = 1;
+	string richText = 2;
+	repeated AttachmentHeader attachments = 3;
 }
 
 message MailHeader {
@@ -20,3 +21,13 @@ message Mail {
     MailHeader header = 1;
     MailBody body = 2;
 }
+
+message Attachment {
+	AttachmentHeader header = 1;
+	bytes data = 2;
+}
+
+message AttachmentHeader {
+	string fileName = 1;
+	string contentType = 2;
+}

+ 19 - 0
config/config.go

@@ -38,6 +38,9 @@ const configPath = "data/main.ini"
 
 const (
 	KeyPostfixConfig = "postfix_config"
+	KeyMongoAddress  = "mongo_address"
+	KeyMongoUser     = "mongo_user"
+	KeyMongoPassword = "mongo_password"
 )
 
 const (
@@ -66,6 +69,9 @@ type gostfixConfig struct {
 	VMailboxMaps    string
 	VMailboxBase    string
 	VMailboxDomains []string
+	MongoUser       string
+	MongoPassword   string
+	MongoAddress    string
 }
 
 func newConfig() (config *gostfixConfig, err error) {
@@ -123,10 +129,23 @@ func newConfig() (config *gostfixConfig, err error) {
 		return
 	}
 
+	mongoUser := cfg.Section("").Key(KeyMongoUser).String()
+
+	mongoPassword := cfg.Section("").Key(KeyMongoPassword).String()
+
+	mongoAddress := cfg.Section("").Key(KeyMongoAddress).String()
+
+	if mongoAddress == "" {
+		mongoAddress = "localhost:27017"
+	}
+
 	config = &gostfixConfig{
 		VMailboxBase:    baseDir,
 		VMailboxMaps:    mapsList[1],
 		VMailboxDomains: validDomains,
+		MongoUser:       mongoUser,
+		MongoPassword:   mongoPassword,
+		MongoAddress:    mongoAddress,
 	}
 	return
 }

+ 108 - 0
db/db.go

@@ -24,3 +24,111 @@
  */
 
 package db
+
+import (
+	"context"
+	"time"
+
+	common "git.semlanik.org/semlanik/gostfix/common"
+	bcrypt "golang.org/x/crypto/bcrypt"
+
+	bson "go.mongodb.org/mongo-driver/bson"
+	mongo "go.mongodb.org/mongo-driver/mongo"
+	options "go.mongodb.org/mongo-driver/mongo/options"
+
+	config "git.semlanik.org/semlanik/gostfix/config"
+)
+
+type Storage struct {
+	usersCollection  *mongo.Collection
+	tokensCollection *mongo.Collection
+}
+
+func NewStorage() (s *Storage, err error) {
+	fullUrl := "mongodb://"
+	if config.ConfigInstance().MongoUser != "" {
+		fullUrl += config.ConfigInstance().MongoUser
+		if config.ConfigInstance().MongoPassword != "" {
+			fullUrl += ":" + config.ConfigInstance().MongoPassword
+		}
+		fullUrl += "@"
+	}
+
+	fullUrl += config.ConfigInstance().MongoAddress
+
+	client, err := mongo.NewClient(options.Client().ApplyURI(fullUrl))
+	if err != nil {
+		return nil, err
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+	defer cancel()
+
+	err = client.Connect(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	s = &Storage{
+		usersCollection:  client.Database("gostfix").Collection("users"),
+		tokensCollection: client.Database("gostfix").Collection("tokens"),
+	}
+	return
+}
+
+func (s *Storage) AddUser(user, password, fullName string) error {
+	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+	if err != nil {
+		return err
+	}
+
+	hashString := string(hash)
+	userInfo := bson.M{
+		"user":     user,
+		"password": hashString,
+		"fullName": fullName,
+	}
+	_, err = s.usersCollection.InsertOne(context.Background(), userInfo)
+	return err
+}
+
+func (s *Storage) CheckUser(user, password string) error {
+	result := struct {
+		User     string
+		Password string
+	}{}
+	err := s.usersCollection.FindOne(context.Background(), bson.M{"user": user}).Decode(&result)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *Storage) AddToken(user, token string) error {
+	return nil
+}
+
+func (s *Storage) CheckToken(user, token string) error {
+	return nil
+}
+
+func (s *Storage) SaveMail(user string, m *common.Mail) error {
+	return nil
+}
+
+func (s *Storage) RemoveMail(user string, m *common.Mail) error {
+	return nil
+}
+
+func (s *Storage) MailList(user string) ([]*common.MailHeader, error) {
+	return nil, nil
+}
+
+func (s *Storage) GetMail(user string, header *common.MailHeader) (m *common.Mail, err error) {
+	return nil, nil
+}
+
+func (s *Storage) GetAttachment(user string, attachmentId string) (filePath string, err error) {
+	return "", nil
+}

+ 2 - 0
go.mod

@@ -6,6 +6,8 @@ require (
 	github.com/fsnotify/fsnotify v1.4.7
 	github.com/golang/protobuf v1.3.4
 	github.com/gorilla/sessions v1.2.0
+	go.mongodb.org/mongo-driver v1.3.0
+	golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
 	golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
 	gopkg.in/go-ini/ini.v1 v1.52.0
 )

+ 103 - 0
go.sum

@@ -1,12 +1,115 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
 github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
 github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
 github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
 github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
 github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+go.mongodb.org/mongo-driver v1.3.0 h1:ew6uUIeJOo+qdUUv7LxFCUhtWmVv7ZV/Xuy4FAUsw2E=
+go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/go-ini/ini.v1 v1.52.0 h1:yMvPaeTxANcf7r7drd9RfUElqzXt7BMoVb9u+Yi0JMo=
 gopkg.in/go-ini/ini.v1 v1.52.0/go.mod h1:M74/hG4RTwbkZyTEZ9iQwM4v6dFD4u6QBjoqT/pM8Kg=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 27 - 14
web/server.go

@@ -61,9 +61,7 @@ const (
 func NewEmail() *common.Mail {
 	return &common.Mail{
 		Header: &common.MailHeader{},
-		Body: &common.MailBody{
-			ContentType: "plain/text",
-		},
+		Body:   &common.MailBody{},
 	}
 }
 
@@ -130,9 +128,9 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
 
 	//Otherwise make sure user logged out and show login page
 	s.logout(w, r)
-	fmt.Fprint(w, s.templater.ExecuteLogin(&LoginTemplateData{
-		common.Version,
-	}))
+	fmt.Fprint(w, s.templater.ExecuteLogin(&struct {
+		Version string
+	}{common.Version}))
 }
 
 func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
@@ -158,7 +156,11 @@ func (s *Server) handleStatusLine(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	fmt.Fprint(w, s.templater.ExecuteStatusLine(&StatusLineTemplateData{
+	fmt.Fprint(w, s.templater.ExecuteStatusLine(&struct {
+		Name   string
+		Read   int
+		Unread int
+	}{
 		Name:   "No name", //TODO: read from database
 		Read:   0,         //TODO: read from database
 		Unread: 0,         //TODO: read from database
@@ -194,17 +196,18 @@ func (s *Server) handleMailbox(w http.ResponseWriter, r *http.Request) {
 	mandatoryHeaders := 0
 	email := NewEmail()
 	state := StateHeaderScan
+	contentType := "plain/text"
 	for scanner.Scan() {
 		if scanner.Text() == "" {
 			if state == StateHeaderScan && mandatoryHeaders&AtLeastOneHeaderMask == AtLeastOneHeaderMask {
-				boundaryCapture := utils.RegExpUtilsInstance().BoundaryFinder.FindStringSubmatch(email.Body.ContentType)
+				boundaryCapture := utils.RegExpUtilsInstance().BoundaryFinder.FindStringSubmatch(contentType)
 				if len(boundaryCapture) == 2 {
 					activeBoundary = boundaryCapture[1]
 				} else {
 					activeBoundary = ""
 				}
 				state = StateBodyScan
-				// fmt.Printf("--------------------------Start body scan content type:%s boundary: %s -------------------------\n", email.Body.ContentType, activeBoundary)
+				// fmt.Printf("--------------------------Start body scan content type:%s boundary: %s -------------------------\n", contentType, activeBoundary)
 			} else if state == StateBodyScan {
 				// fmt.Printf("--------------------------Previous email-------------------------\n%v\n", email)
 				if activeBoundary == "" {
@@ -215,6 +218,7 @@ func (s *Server) handleMailbox(w http.ResponseWriter, r *http.Request) {
 						emails = append(emails, email)
 					}
 					email = NewEmail()
+					contentType = "plain/text"
 					state = StateHeaderScan
 					mandatoryHeaders = 0
 				} else {
@@ -250,7 +254,7 @@ func (s *Server) handleMailbox(w http.ResponseWriter, r *http.Request) {
 					previousHeader = &email.Header.Date
 					mandatoryHeaders |= DateHeaderMask
 				case "content-type":
-					previousHeader = &email.Body.ContentType
+					previousHeader = &contentType
 				default:
 					previousHeader = nil
 				}
@@ -297,7 +301,11 @@ func (s *Server) handleMailbox(w http.ResponseWriter, r *http.Request) {
 		state = StateHeaderScan
 	}
 
-	fmt.Fprint(w, s.templater.ExecuteIndex(&IndexTemplateData{
+	fmt.Fprint(w, s.templater.ExecuteIndex(&struct {
+		Folders  template.HTML
+		MailList template.HTML
+		Version  template.HTML
+	}{
 		MailList: template.HTML(s.templater.ExecuteMailList(emails)),
 		Folders:  "Folders",
 		Version:  common.Version,
@@ -323,9 +331,14 @@ func (s *Server) login(user, token string, w http.ResponseWriter, r *http.Reques
 
 func (s *Server) error(code int, text string, w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusInternalServerError)
-	fmt.Fprint(w, s.templater.ExecuteError(&ErrorTemplateData{
-		Code: code,
-		Text: "Unable to access your mailbox. Please contact Administrator.",
+	fmt.Fprint(w, s.templater.ExecuteError(&struct {
+		Code    int
+		Text    string
+		Version string
+	}{
+		Code:    code,
+		Text:    "Unable to access your mailbox. Please contact Administrator.",
+		Version: common.Version,
 	}))
 }
 

+ 0 - 22
web/templater.go

@@ -50,28 +50,6 @@ type Templater struct {
 	statusLineTemplate *template.Template
 }
 
-type IndexTemplateData struct {
-	Folders  template.HTML
-	MailList template.HTML
-	Version  template.HTML
-}
-
-type ErrorTemplateData struct {
-	Code    int
-	Text    string
-	Version string
-}
-
-type LoginTemplateData struct {
-	Version string
-}
-
-type StatusLineTemplateData struct {
-	Name   string
-	Read   int
-	Unread int
-}
-
 func NewTemplater(templatesPath string) (t *Templater) {
 	t = nil
 	index, err := parseTemplate(templatesPath + "/" + IndexTemplateName)