Add export-messages command

Right now only supports exporting conversations, and doesn't support
attachments.
This commit is contained in:
Simon Ser 2020-09-14 12:46:47 +02:00
parent ea188ff133
commit bd861cdb00
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
4 changed files with 115 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import (
imapmove "github.com/emersion/go-imap-move" imapmove "github.com/emersion/go-imap-move"
imapspacialuse "github.com/emersion/go-imap-specialuse" imapspacialuse "github.com/emersion/go-imap-specialuse"
imapserver "github.com/emersion/go-imap/server" imapserver "github.com/emersion/go-imap/server"
"github.com/emersion/go-mbox"
"github.com/emersion/go-smtp" "github.com/emersion/go-smtp"
"github.com/howeyc/gopass" "github.com/howeyc/gopass"
"golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp"
@ -20,6 +21,7 @@ import (
"github.com/emersion/hydroxide/auth" "github.com/emersion/hydroxide/auth"
"github.com/emersion/hydroxide/carddav" "github.com/emersion/hydroxide/carddav"
"github.com/emersion/hydroxide/events" "github.com/emersion/hydroxide/events"
"github.com/emersion/hydroxide/exports"
imapbackend "github.com/emersion/hydroxide/imap" imapbackend "github.com/emersion/hydroxide/imap"
"github.com/emersion/hydroxide/imports" "github.com/emersion/hydroxide/imports"
"github.com/emersion/hydroxide/protonmail" "github.com/emersion/hydroxide/protonmail"
@ -116,6 +118,7 @@ Commands:
export-secret-keys <username> Export secret keys export-secret-keys <username> Export secret keys
imap Run hydroxide as an IMAP server imap Run hydroxide as an IMAP server
import-messages <username> <file> Import messages import-messages <username> <file> Import messages
export-messages [options...] <username> Export messages
serve Run all servers serve Run all servers
smtp Run hydroxide as an SMTP server smtp Run hydroxide as an SMTP server
status View hydroxide status status View hydroxide status
@ -151,6 +154,7 @@ func main() {
authCmd := flag.NewFlagSet("auth", flag.ExitOnError) authCmd := flag.NewFlagSet("auth", flag.ExitOnError)
exportSecretKeysCmd := flag.NewFlagSet("export-secret-keys", flag.ExitOnError) exportSecretKeysCmd := flag.NewFlagSet("export-secret-keys", flag.ExitOnError)
importMessagesCmd := flag.NewFlagSet("import-messages", flag.ExitOnError) importMessagesCmd := flag.NewFlagSet("import-messages", flag.ExitOnError)
exportMessagesCmd := flag.NewFlagSet("export-messages", flag.ExitOnError)
flag.Parse() flag.Parse()
@ -335,6 +339,38 @@ func main() {
if err := imports.ImportMessage(c, f); err != nil { if err := imports.ImportMessage(c, f); err != nil {
log.Fatal(err) log.Fatal(err)
} }
case "export-messages":
var convID string
exportMessagesCmd.StringVar(&convID, "conversation-id", "", "conversation ID")
exportMessagesCmd.Parse(flag.Args()[1:])
username := exportMessagesCmd.Arg(0)
log.Println(convID, username)
if convID == "" || username == "" {
log.Fatal("usage: hydroxide export-messages -conversation-id <id> <username>")
}
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)
if err := exports.ExportConversation(c, privateKeys, mboxWriter, convID); err != nil {
log.Fatal(err)
}
if err := mboxWriter.Close(); err != nil {
log.Fatal(err)
}
case "smtp": case "smtp":
addr := *smtpHost + ":" + *smtpPort addr := *smtpHost + ":" + *smtpPort
authManager := auth.NewManager(newClient) authManager := auth.NewManager(newClient)

76
exports/messages.go Normal file
View File

@ -0,0 +1,76 @@
package exports
import (
"fmt"
"io"
"bufio"
"strings"
"github.com/emersion/go-mbox"
"github.com/emersion/go-message"
"github.com/emersion/go-message/mail"
"github.com/emersion/go-message/textproto"
"golang.org/x/crypto/openpgp"
"github.com/emersion/hydroxide/protonmail"
)
func ExportMessage(c *protonmail.Client, privateKeys openpgp.KeyRing, w io.Writer, id string) error {
msg, err := c.GetMessage(id)
if err != nil {
return fmt.Errorf("failed to fetch message: %v", err)
}
mimeType := msg.MIMEType
if mimeType == "" {
mimeType = "text/html"
}
br := bufio.NewReader(strings.NewReader(msg.Header))
th, err := textproto.ReadHeader(br)
if err != nil {
return fmt.Errorf("failed to read message header: %v", err)
}
mh := mail.Header{message.Header{th}}
mh.SetContentType(mimeType, nil)
mh.Set("Content-Transfer-Encoding", "quoted-printable")
// TODO: add support for attachments
mw, err := mail.CreateSingleInlineWriter(w, mh)
if err != nil {
return fmt.Errorf("failed to create message writer: %v", err)
}
md, err := msg.Read(privateKeys, nil)
if err != nil {
return err
}
// TODO: check signature
if _, err := io.Copy(mw, md.UnverifiedBody); err != nil {
return err
}
return mw.Close()
}
func ExportConversation(c *protonmail.Client, privateKeys openpgp.KeyRing, mbox *mbox.Writer, id string) error {
_, msgs, err := c.GetConversation(id, "")
if err != nil {
return fmt.Errorf("failed to fetch conversation: %v", err)
}
for _, msg := range msgs {
w, err := mbox.CreateMessage(msg.Sender.Address, msg.Time.Time())
if err != nil {
return fmt.Errorf("failed to create mbox message: %v", err)
}
if err := ExportMessage(c, privateKeys, w, msg.ID); err != nil {
return fmt.Errorf("failed to export conversation message: %v", err)
}
}
return nil
}

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/emersion/go-imap v1.0.4 github.com/emersion/go-imap v1.0.4
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62
github.com/emersion/go-mbox v1.0.0
github.com/emersion/go-message v0.11.1 github.com/emersion/go-message v0.11.1
github.com/emersion/go-smtp v0.12.1 github.com/emersion/go-smtp v0.12.1
github.com/emersion/go-vcard v0.0.0-20191221110513-5f81fa0d3cc7 github.com/emersion/go-vcard v0.0.0-20191221110513-5f81fa0d3cc7

2
go.sum
View File

@ -16,6 +16,8 @@ github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 h1:5p1t3e1Po
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w= github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 h1:4ZAfwfc8aDlj26kkEap1UDSwwDnJp9Ie8Uj1MSXAkPk= github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 h1:4ZAfwfc8aDlj26kkEap1UDSwwDnJp9Ie8Uj1MSXAkPk=
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4= github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4=
github.com/emersion/go-mbox v1.0.0 h1:HN6aKbyqmgIfK9fS/gen+NRr2wXLSxZXWfdAIAnzQPc=
github.com/emersion/go-mbox v1.0.0/go.mod h1:Yp9IVuuOYLEuMv4yjgDHvhb5mHOcYH6x92Oas3QqEZI=
github.com/emersion/go-message v0.11.1 h1:0C/S4JIXDTSfXB1vpqdimAYyK4+79fgEAMQ0dSL+Kac= github.com/emersion/go-message v0.11.1 h1:0C/S4JIXDTSfXB1vpqdimAYyK4+79fgEAMQ0dSL+Kac=
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY= github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q= github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q=