2017-08-22 01:04:16 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2017-09-09 16:37:03 +03:00
|
|
|
"flag"
|
2017-08-22 01:04:16 +03:00
|
|
|
"fmt"
|
2017-09-09 16:37:03 +03:00
|
|
|
"io"
|
2017-08-22 01:04:16 +03:00
|
|
|
"log"
|
2017-09-03 21:11:01 +03:00
|
|
|
"net/http"
|
2017-08-22 01:04:16 +03:00
|
|
|
"os"
|
|
|
|
|
2018-01-12 21:01:08 +02:00
|
|
|
imapmove "github.com/emersion/go-imap-move"
|
2017-12-03 17:05:24 +02:00
|
|
|
imapspacialuse "github.com/emersion/go-imap-specialuse"
|
2018-10-21 13:15:20 +03:00
|
|
|
imapserver "github.com/emersion/go-imap/server"
|
2020-09-14 13:46:47 +03:00
|
|
|
"github.com/emersion/go-mbox"
|
2017-12-02 17:23:06 +02:00
|
|
|
"github.com/emersion/go-smtp"
|
2017-12-03 13:51:12 +02:00
|
|
|
"github.com/howeyc/gopass"
|
2019-04-23 00:03:20 +03:00
|
|
|
"golang.org/x/crypto/openpgp"
|
|
|
|
"golang.org/x/crypto/openpgp/armor"
|
2017-12-02 17:23:06 +02:00
|
|
|
|
2017-09-19 15:57:29 +03:00
|
|
|
"github.com/emersion/hydroxide/auth"
|
2017-09-03 21:11:01 +03:00
|
|
|
"github.com/emersion/hydroxide/carddav"
|
2018-01-11 19:17:11 +02:00
|
|
|
"github.com/emersion/hydroxide/events"
|
2020-09-14 13:46:47 +03:00
|
|
|
"github.com/emersion/hydroxide/exports"
|
2017-12-03 15:58:24 +02:00
|
|
|
imapbackend "github.com/emersion/hydroxide/imap"
|
2019-07-30 20:51:46 +03:00
|
|
|
"github.com/emersion/hydroxide/imports"
|
2018-10-21 13:15:20 +03:00
|
|
|
"github.com/emersion/hydroxide/protonmail"
|
2017-12-02 17:23:06 +02:00
|
|
|
smtpbackend "github.com/emersion/hydroxide/smtp"
|
2017-08-22 01:04:16 +03:00
|
|
|
)
|
|
|
|
|
2020-03-07 19:12:53 +02:00
|
|
|
var debug bool
|
|
|
|
|
2017-09-09 16:37:03 +03:00
|
|
|
func newClient() *protonmail.Client {
|
|
|
|
return &protonmail.Client{
|
2019-12-09 13:27:16 +02:00
|
|
|
RootURL: "https://mail.protonmail.com/api",
|
|
|
|
AppVersion: "Web_3.16.6",
|
2020-03-07 19:12:53 +02:00
|
|
|
Debug: debug,
|
2017-08-22 01:04:16 +03:00
|
|
|
}
|
2017-09-09 16:37:03 +03:00
|
|
|
}
|
2017-08-22 01:04:16 +03:00
|
|
|
|
2020-02-29 13:34:20 +02:00
|
|
|
func listenAndServeSMTP(addr string, debug bool, authManager *auth.Manager) error {
|
2019-06-22 13:06:30 +03:00
|
|
|
be := smtpbackend.New(authManager)
|
|
|
|
s := smtp.NewServer(be)
|
|
|
|
s.Addr = addr
|
|
|
|
s.Domain = "localhost" // TODO: make this configurable
|
|
|
|
s.AllowInsecureAuth = true // TODO: remove this
|
2020-02-29 13:34:20 +02:00
|
|
|
if debug {
|
|
|
|
s.Debug = os.Stdout
|
|
|
|
}
|
2019-06-22 13:06:30 +03:00
|
|
|
|
|
|
|
log.Println("SMTP server listening on", s.Addr)
|
|
|
|
return s.ListenAndServe()
|
|
|
|
}
|
|
|
|
|
2020-02-29 13:34:20 +02:00
|
|
|
func listenAndServeIMAP(addr string, debug bool, authManager *auth.Manager, eventsManager *events.Manager) error {
|
2019-06-22 13:06:30 +03:00
|
|
|
be := imapbackend.New(authManager, eventsManager)
|
|
|
|
s := imapserver.New(be)
|
|
|
|
s.Addr = addr
|
|
|
|
s.AllowInsecureAuth = true // TODO: remove this
|
2020-02-29 13:34:20 +02:00
|
|
|
if debug {
|
|
|
|
s.Debug = os.Stdout
|
|
|
|
}
|
2019-06-22 13:06:30 +03:00
|
|
|
|
|
|
|
s.Enable(imapspacialuse.NewExtension())
|
|
|
|
s.Enable(imapmove.NewExtension())
|
|
|
|
|
|
|
|
log.Println("IMAP server listening on", s.Addr)
|
|
|
|
return s.ListenAndServe()
|
|
|
|
}
|
|
|
|
|
|
|
|
func listenAndServeCardDAV(addr string, authManager *auth.Manager, eventsManager *events.Manager) error {
|
|
|
|
handlers := make(map[string]http.Handler)
|
|
|
|
|
|
|
|
s := &http.Server{
|
|
|
|
Addr: addr,
|
|
|
|
Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
resp.Header().Set("WWW-Authenticate", "Basic")
|
|
|
|
|
|
|
|
username, password, ok := req.BasicAuth()
|
|
|
|
if !ok {
|
|
|
|
resp.WriteHeader(http.StatusUnauthorized)
|
|
|
|
io.WriteString(resp, "Credentials are required")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c, privateKeys, err := authManager.Auth(username, password)
|
|
|
|
if err != nil {
|
|
|
|
if err == auth.ErrUnauthorized {
|
|
|
|
resp.WriteHeader(http.StatusUnauthorized)
|
|
|
|
} else {
|
|
|
|
resp.WriteHeader(http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
io.WriteString(resp, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
h, ok := handlers[username]
|
|
|
|
if !ok {
|
|
|
|
ch := make(chan *protonmail.Event)
|
|
|
|
eventsManager.Register(c, username, ch, nil)
|
|
|
|
h = carddav.NewHandler(c, privateKeys, ch)
|
|
|
|
|
|
|
|
handlers[username] = h
|
|
|
|
}
|
|
|
|
|
|
|
|
h.ServeHTTP(resp, req)
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("CardDAV server listening on", s.Addr)
|
|
|
|
return s.ListenAndServe()
|
|
|
|
}
|
|
|
|
|
2020-09-14 12:30:12 +03:00
|
|
|
const usage = `usage: hydroxide [options...] <command>
|
2020-02-29 13:36:48 +02:00
|
|
|
Commands:
|
|
|
|
auth <username> Login to ProtonMail via hydroxide
|
|
|
|
carddav Run hydroxide as a CardDAV server
|
|
|
|
export-secret-keys <username> Export secret keys
|
|
|
|
imap Run hydroxide as an IMAP server
|
|
|
|
import-messages <username> <file> Import messages
|
2020-09-14 13:46:47 +03:00
|
|
|
export-messages [options...] <username> Export messages
|
2020-02-29 13:36:48 +02:00
|
|
|
serve Run all servers
|
|
|
|
smtp Run hydroxide as an SMTP server
|
|
|
|
status View hydroxide status
|
|
|
|
|
2020-09-14 12:30:12 +03:00
|
|
|
Global options:
|
2020-02-29 13:36:48 +02:00
|
|
|
-debug
|
|
|
|
Enable debug logs
|
|
|
|
-smtp-host example.com
|
|
|
|
Allowed SMTP email hostname on which hydroxide listens, defaults to 127.0.0.1
|
|
|
|
-imap-host example.com
|
|
|
|
Allowed IMAP email hostname on which hydroxide listens, defaults to 127.0.0.1
|
|
|
|
-carddav-host example.com
|
|
|
|
Allowed SMTP email hostname on which hydroxide listens, defaults to 127.0.0.1
|
|
|
|
-smtp-port example.com
|
|
|
|
SMTP port on which hydroxide listens, defaults to 1025
|
|
|
|
-imap-port example.com
|
|
|
|
IMAP port on which hydroxide listens, defaults to 1143
|
|
|
|
-carddav-port example.com
|
|
|
|
CardDAV port on which hydroxide listens, defaults to 8080`
|
|
|
|
|
2017-09-09 16:37:03 +03:00
|
|
|
func main() {
|
2020-03-07 19:12:53 +02:00
|
|
|
flag.BoolVar(&debug, "debug", false, "Enable debug logs")
|
2020-02-29 13:34:20 +02:00
|
|
|
|
2019-07-29 06:27:54 +03:00
|
|
|
smtpHost := flag.String("smtp-host", "127.0.0.1", "Allowed SMTP email hostname on which hydroxide listens, defaults to 127.0.0.1")
|
|
|
|
smtpPort := flag.String("smtp-port", "1025", "SMTP port on which hydroxide listens, defaults to 1025")
|
|
|
|
|
|
|
|
imapHost := flag.String("imap-host", "127.0.0.1", "Allowed IMAP email hostname on which hydroxide listens, defaults to 127.0.0.1")
|
|
|
|
imapPort := flag.String("imap-port", "1143", "IMAP port on which hydroxide listens, defaults to 1143")
|
|
|
|
|
|
|
|
carddavHost := flag.String("carddav-host", "127.0.0.1", "Allowed CardDAV email hostname on which hydroxide listens, defaults to 127.0.0.1")
|
|
|
|
carddavPort := flag.String("carddav-port", "8080", "CardDAV port on which hydroxide listens, defaults to 8080")
|
|
|
|
|
|
|
|
authCmd := flag.NewFlagSet("auth", flag.ExitOnError)
|
|
|
|
exportSecretKeysCmd := flag.NewFlagSet("export-secret-keys", flag.ExitOnError)
|
2019-07-30 20:51:46 +03:00
|
|
|
importMessagesCmd := flag.NewFlagSet("import-messages", flag.ExitOnError)
|
2020-09-14 13:46:47 +03:00
|
|
|
exportMessagesCmd := flag.NewFlagSet("export-messages", flag.ExitOnError)
|
2019-07-29 06:27:54 +03:00
|
|
|
|
2017-09-09 16:37:03 +03:00
|
|
|
flag.Parse()
|
|
|
|
|
2019-04-23 00:06:49 +03:00
|
|
|
cmd := flag.Arg(0)
|
|
|
|
switch cmd {
|
2017-09-09 16:37:03 +03:00
|
|
|
case "auth":
|
2020-03-07 18:54:40 +02:00
|
|
|
authCmd.Parse(flag.Args()[1:])
|
2019-07-30 20:51:46 +03:00
|
|
|
username := authCmd.Arg(0)
|
2019-04-23 00:03:20 +03:00
|
|
|
if username == "" {
|
|
|
|
log.Fatal("usage: hydroxide auth <username>")
|
|
|
|
}
|
2017-09-09 16:37:03 +03:00
|
|
|
|
|
|
|
c := newClient()
|
|
|
|
|
2017-09-19 15:57:29 +03:00
|
|
|
var a *protonmail.Auth
|
2017-09-09 16:37:03 +03:00
|
|
|
/*if cachedAuth, ok := auths[username]; ok {
|
|
|
|
var err error
|
2017-09-19 15:57:29 +03:00
|
|
|
a, err = c.AuthRefresh(a)
|
2017-09-09 16:37:03 +03:00
|
|
|
if err != nil {
|
|
|
|
// TODO: handle expired token error
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
|
|
|
var loginPassword string
|
2017-09-19 15:57:29 +03:00
|
|
|
if a == nil {
|
2017-09-09 16:37:03 +03:00
|
|
|
fmt.Printf("Password: ")
|
2017-12-03 13:51:12 +02:00
|
|
|
if pass, err := gopass.GetPasswd(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
} else {
|
|
|
|
loginPassword = string(pass)
|
|
|
|
}
|
2017-09-09 16:37:03 +03:00
|
|
|
|
|
|
|
authInfo, err := c.AuthInfo(username)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-12-08 13:14:41 +02:00
|
|
|
a, err = c.Auth(username, loginPassword, authInfo)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.TwoFactor.Enabled == 1 {
|
|
|
|
if a.TwoFactor.TOTP != 1 {
|
|
|
|
log.Fatal("Only TOTP is supported as a 2FA method")
|
|
|
|
}
|
|
|
|
|
2017-12-03 13:51:12 +02:00
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
2019-12-08 13:14:41 +02:00
|
|
|
fmt.Printf("2FA TOTP code: ")
|
2017-09-09 16:37:03 +03:00
|
|
|
scanner.Scan()
|
2019-12-08 13:14:41 +02:00
|
|
|
code := scanner.Text()
|
2017-09-09 16:37:03 +03:00
|
|
|
|
2019-12-08 13:14:41 +02:00
|
|
|
scope, err := c.AuthTOTP(code)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
a.Scope = scope
|
2017-09-09 16:37:03 +03:00
|
|
|
}
|
2017-08-22 13:07:31 +03:00
|
|
|
}
|
2017-08-22 11:51:48 +03:00
|
|
|
|
2017-09-09 16:37:03 +03:00
|
|
|
var mailboxPassword string
|
2017-09-19 15:57:29 +03:00
|
|
|
if a.PasswordMode == protonmail.PasswordSingle {
|
2017-09-09 16:37:03 +03:00
|
|
|
mailboxPassword = loginPassword
|
|
|
|
}
|
|
|
|
if mailboxPassword == "" {
|
2017-09-19 15:57:29 +03:00
|
|
|
if a.PasswordMode == protonmail.PasswordTwo {
|
2017-09-09 16:37:03 +03:00
|
|
|
fmt.Printf("Mailbox password: ")
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Password: ")
|
|
|
|
}
|
2017-12-03 13:51:12 +02:00
|
|
|
if pass, err := gopass.GetPasswd(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
} else {
|
|
|
|
mailboxPassword = string(pass)
|
|
|
|
}
|
2017-09-09 16:37:03 +03:00
|
|
|
}
|
2017-08-22 01:04:16 +03:00
|
|
|
|
2019-08-31 17:43:46 +03:00
|
|
|
keySalts, err := c.ListKeySalts()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.Unlock(a, keySalts, mailboxPassword)
|
2017-08-22 13:07:31 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-09-19 15:57:29 +03:00
|
|
|
secretKey, bridgePassword, err := auth.GeneratePassword()
|
|
|
|
if err != nil {
|
2017-09-09 16:37:03 +03:00
|
|
|
log.Fatal(err)
|
2017-08-22 13:07:31 +03:00
|
|
|
}
|
|
|
|
|
2017-09-19 15:57:29 +03:00
|
|
|
err = auth.EncryptAndSave(&auth.CachedAuth{
|
2019-12-09 13:27:16 +02:00
|
|
|
Auth: *a,
|
|
|
|
LoginPassword: loginPassword,
|
2019-08-31 17:43:46 +03:00
|
|
|
MailboxPassword: mailboxPassword,
|
2019-12-09 13:27:16 +02:00
|
|
|
KeySalts: keySalts,
|
2017-09-19 15:57:29 +03:00
|
|
|
}, username, secretKey)
|
2017-08-22 13:07:31 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2017-08-22 10:41:47 +03:00
|
|
|
|
2017-09-09 16:37:03 +03:00
|
|
|
fmt.Println("Bridge password:", bridgePassword)
|
2019-03-22 22:35:10 +02:00
|
|
|
case "status":
|
|
|
|
usernames, err := auth.ListUsernames()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(usernames) == 0 {
|
|
|
|
fmt.Printf("No logged in user.\n")
|
|
|
|
} else {
|
|
|
|
fmt.Printf("%v logged in user(s):\n", len(usernames))
|
|
|
|
for _, u := range usernames {
|
|
|
|
fmt.Printf("- %v\n", u)
|
|
|
|
}
|
|
|
|
}
|
2019-04-23 00:03:20 +03:00
|
|
|
case "export-secret-keys":
|
2020-03-07 18:54:40 +02:00
|
|
|
exportSecretKeysCmd.Parse(flag.Args()[1:])
|
2019-07-30 20:51:46 +03:00
|
|
|
username := exportSecretKeysCmd.Arg(0)
|
2019-04-23 00:03:20 +03:00
|
|
|
if username == "" {
|
|
|
|
log.Fatal("usage: hydroxide export-secret-keys <username>")
|
|
|
|
}
|
|
|
|
|
|
|
|
var bridgePassword string
|
|
|
|
fmt.Printf("Bridge password: ")
|
|
|
|
if pass, err := gopass.GetPasswd(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
} else {
|
|
|
|
bridgePassword = string(pass)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, privateKeys, err := auth.NewManager(newClient).Auth(username, bridgePassword)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
wc, err := armor.Encode(os.Stdout, openpgp.PrivateKeyType, nil)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, key := range privateKeys {
|
|
|
|
if err := key.SerializePrivate(wc, nil); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := wc.Close(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-07-30 20:51:46 +03:00
|
|
|
case "import-messages":
|
|
|
|
// TODO: support for mbox
|
|
|
|
|
2020-03-07 18:54:40 +02:00
|
|
|
importMessagesCmd.Parse(flag.Args()[1:])
|
2019-07-30 20:51:46 +03:00
|
|
|
username := importMessagesCmd.Arg(0)
|
|
|
|
archivePath := importMessagesCmd.Arg(1)
|
|
|
|
if username == "" || archivePath == "" {
|
|
|
|
log.Fatal("usage: hydroxide import-messages <username> <file>")
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(archivePath)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
var bridgePassword string
|
|
|
|
fmt.Printf("Bridge password: ")
|
|
|
|
if pass, err := gopass.GetPasswd(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
} else {
|
|
|
|
bridgePassword = string(pass)
|
|
|
|
}
|
|
|
|
|
|
|
|
c, _, err := auth.NewManager(newClient).Auth(username, bridgePassword)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := imports.ImportMessage(c, f); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2020-09-14 13:46:47 +03:00
|
|
|
case "export-messages":
|
2020-09-15 11:37:01 +03:00
|
|
|
// TODO: allow specifying multiple IDs
|
|
|
|
var convID, msgID string
|
2020-09-14 13:46:47 +03:00
|
|
|
exportMessagesCmd.StringVar(&convID, "conversation-id", "", "conversation ID")
|
2020-09-15 11:37:01 +03:00
|
|
|
exportMessagesCmd.StringVar(&msgID, "message-id", "", "message ID")
|
2020-09-14 13:46:47 +03:00
|
|
|
exportMessagesCmd.Parse(flag.Args()[1:])
|
|
|
|
username := exportMessagesCmd.Arg(0)
|
2020-09-15 11:37:01 +03:00
|
|
|
if (convID == "" && msgID == "") || username == "" {
|
|
|
|
log.Fatal("usage: hydroxide export-messages [-conversation-id <id>] [-message-id <id>] <username>")
|
2020-09-14 13:46:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var bridgePassword string
|
|
|
|
fmt.Fprintf(os.Stderr, "Bridge password: ")
|
|
|
|
if pass, err := gopass.GetPasswd(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
} else {
|
|
|
|
bridgePassword = string(pass)
|
|
|
|
}
|
|
|
|
|
|
|
|
c, privateKeys, err := auth.NewManager(newClient).Auth(username, bridgePassword)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mboxWriter := mbox.NewWriter(os.Stdout)
|
|
|
|
|
2020-09-15 11:37:01 +03:00
|
|
|
if convID != "" {
|
|
|
|
if err := exports.ExportConversationMbox(c, privateKeys, mboxWriter, convID); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if msgID != "" {
|
|
|
|
if err := exports.ExportMessageMbox(c, privateKeys, mboxWriter, msgID); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2020-09-14 13:46:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := mboxWriter.Close(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2017-12-02 17:23:06 +02:00
|
|
|
case "smtp":
|
2019-07-29 06:27:54 +03:00
|
|
|
addr := *smtpHost + ":" + *smtpPort
|
2019-06-22 13:06:30 +03:00
|
|
|
authManager := auth.NewManager(newClient)
|
2020-03-07 19:12:53 +02:00
|
|
|
log.Fatal(listenAndServeSMTP(addr, debug, authManager))
|
2017-12-03 15:58:24 +02:00
|
|
|
case "imap":
|
2019-07-29 06:27:54 +03:00
|
|
|
addr := *imapHost + ":" + *imapPort
|
2019-06-22 13:06:30 +03:00
|
|
|
authManager := auth.NewManager(newClient)
|
2018-01-12 14:20:17 +02:00
|
|
|
eventsManager := events.NewManager()
|
2020-03-07 19:12:53 +02:00
|
|
|
log.Fatal(listenAndServeIMAP(addr, debug, authManager, eventsManager))
|
2017-12-02 17:23:06 +02:00
|
|
|
case "carddav":
|
2019-07-29 06:27:54 +03:00
|
|
|
addr := *carddavHost + ":" + *carddavPort
|
2019-06-22 13:06:30 +03:00
|
|
|
authManager := auth.NewManager(newClient)
|
2018-01-12 14:20:17 +02:00
|
|
|
eventsManager := events.NewManager()
|
2019-06-22 13:06:30 +03:00
|
|
|
log.Fatal(listenAndServeCardDAV(addr, authManager, eventsManager))
|
2019-06-22 13:10:13 +03:00
|
|
|
case "serve":
|
2019-07-29 06:27:54 +03:00
|
|
|
smtpAddr := *smtpHost + ":" + *smtpPort
|
|
|
|
imapAddr := *imapHost + ":" + *imapPort
|
|
|
|
carddavAddr := *carddavHost + ":" + *carddavPort
|
|
|
|
|
2019-06-22 13:10:13 +03:00
|
|
|
authManager := auth.NewManager(newClient)
|
|
|
|
eventsManager := events.NewManager()
|
|
|
|
|
|
|
|
done := make(chan error, 3)
|
|
|
|
go func() {
|
2020-03-07 19:12:53 +02:00
|
|
|
done <- listenAndServeSMTP(smtpAddr, debug, authManager)
|
2019-06-22 13:10:13 +03:00
|
|
|
}()
|
|
|
|
go func() {
|
2020-03-07 19:12:53 +02:00
|
|
|
done <- listenAndServeIMAP(imapAddr, debug, authManager, eventsManager)
|
2019-06-22 13:10:13 +03:00
|
|
|
}()
|
|
|
|
go func() {
|
2019-07-29 06:27:54 +03:00
|
|
|
done <- listenAndServeCardDAV(carddavAddr, authManager, eventsManager)
|
2019-06-22 13:10:13 +03:00
|
|
|
}()
|
|
|
|
log.Fatal(<-done)
|
2017-09-09 16:37:03 +03:00
|
|
|
default:
|
2020-02-29 13:36:48 +02:00
|
|
|
log.Println(usage)
|
2019-04-23 00:06:49 +03:00
|
|
|
if cmd != "help" {
|
|
|
|
log.Fatal("Unrecognized command")
|
|
|
|
}
|
2017-09-09 16:37:03 +03:00
|
|
|
}
|
2017-08-22 01:04:16 +03:00
|
|
|
}
|