forked from Silverfish/proton-bridge
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
// Copyright (c) 2021 Proton Technologies AG
|
|
//
|
|
// This file is part of ProtonMail Bridge.
|
|
//
|
|
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package pmapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"mime/multipart"
|
|
"strconv"
|
|
)
|
|
|
|
// Import errors.
|
|
const (
|
|
ImportMessageTooLarge = 36022
|
|
)
|
|
|
|
// ImportReq is an import request.
|
|
type ImportReq struct {
|
|
// A list of messages that will be imported.
|
|
Messages []*ImportMsgReq
|
|
}
|
|
|
|
// WriteTo writes the import request to a multipart writer.
|
|
func (req *ImportReq) WriteTo(w *multipart.Writer) (err error) {
|
|
// Create Metadata field.
|
|
mw, err := w.CreateFormField("Metadata")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Build metadata.
|
|
metadata := map[string]*ImportMsgReq{}
|
|
for i, msg := range req.Messages {
|
|
name := strconv.Itoa(i)
|
|
metadata[name] = msg
|
|
}
|
|
|
|
// Write metadata.
|
|
if err = json.NewEncoder(mw).Encode(metadata); err != nil {
|
|
return
|
|
}
|
|
|
|
// Write messages.
|
|
for i, msg := range req.Messages {
|
|
name := strconv.Itoa(i)
|
|
|
|
var fw io.Writer
|
|
if fw, err = w.CreateFormFile(name, name+".eml"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = fw.Write(msg.Body); err != nil {
|
|
return
|
|
}
|
|
|
|
// Adding new line to properly fetch the whole body on the API side.
|
|
// The reason is the bug in PHP: https://bugs.php.net/bug.php?id=75923
|
|
// Messages generated by PM already have it but importing already
|
|
// encrypted messages might not have it.
|
|
if _, err = fw.Write([]byte("\r\n")); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// ImportMsgReq is a request to import a message. All fields are optional except AddressID and Body.
|
|
type ImportMsgReq struct {
|
|
// The address where the message will be imported.
|
|
AddressID string
|
|
// The full MIME message.
|
|
Body []byte `json:"-"`
|
|
|
|
// 0: read, 1: unread.
|
|
Unread int
|
|
// 1 if the message has been replied.
|
|
IsReplied int
|
|
// 1 if the message has been replied to all.
|
|
IsRepliedAll int
|
|
// 1 if the message has been forwarded.
|
|
IsForwarded int
|
|
// The time when the message was received as a Unix time.
|
|
Time int64
|
|
// The type of the imported message.
|
|
Flags int64
|
|
// The labels to apply to the imported message. Must contain at least one system label.
|
|
LabelIDs []string
|
|
}
|
|
|
|
func (req ImportMsgReq) String() string {
|
|
data, _ := json.Marshal(req)
|
|
return string(data)
|
|
}
|
|
|
|
// ImportRes is a response to an import request.
|
|
type ImportRes struct {
|
|
Res
|
|
|
|
Responses []struct {
|
|
Name string
|
|
Response struct {
|
|
Res
|
|
MessageID string
|
|
}
|
|
}
|
|
}
|
|
|
|
// ImportMsgRes is a response to a single message import request.
|
|
type ImportMsgRes struct {
|
|
// The error encountered while importing the message, if any.
|
|
Error error
|
|
// The newly created message ID.
|
|
MessageID string
|
|
}
|
|
|
|
// Import imports messages to the user's account.
|
|
func (c *client) Import(reqs []*ImportMsgReq) (resps []*ImportMsgRes, err error) {
|
|
importReq := &ImportReq{Messages: reqs}
|
|
|
|
req, w, err := c.NewMultipartRequest("POST", "/mail/v4/messages/import")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// We will write the request as long as it is sent to the API.
|
|
var importRes ImportRes
|
|
done := make(chan error, 1)
|
|
go (func() {
|
|
done <- c.DoJSON(req, &importRes)
|
|
})()
|
|
|
|
// Write the request.
|
|
if err = importReq.WriteTo(w.Writer); err != nil {
|
|
return
|
|
}
|
|
_ = w.Close()
|
|
|
|
if err = <-done; err != nil {
|
|
return
|
|
}
|
|
if err = importRes.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
resps = make([]*ImportMsgRes, len(importRes.Responses))
|
|
for i, r := range importRes.Responses {
|
|
resps[i] = &ImportMsgRes{
|
|
Error: r.Response.Err(),
|
|
MessageID: r.Response.MessageID,
|
|
}
|
|
}
|
|
|
|
return resps, err
|
|
}
|