2017-09-18 15:41:50 +03:00
|
|
|
package protonmail
|
|
|
|
|
|
|
|
import (
|
2017-09-20 11:09:31 +03:00
|
|
|
"bytes"
|
2017-09-23 20:20:47 +03:00
|
|
|
"encoding/base64"
|
|
|
|
"errors"
|
2017-09-20 11:09:31 +03:00
|
|
|
"io"
|
2017-09-18 15:41:50 +03:00
|
|
|
"net/http"
|
2018-01-08 00:38:13 +02:00
|
|
|
"net/url"
|
2017-09-23 20:20:47 +03:00
|
|
|
"strconv"
|
2017-09-20 11:09:31 +03:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"golang.org/x/crypto/openpgp"
|
|
|
|
"golang.org/x/crypto/openpgp/armor"
|
2017-09-23 20:20:47 +03:00
|
|
|
"golang.org/x/crypto/openpgp/packet"
|
2017-09-18 15:41:50 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type MessageType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
MessageInbox MessageType = iota
|
|
|
|
MessageDraft
|
|
|
|
MessageSent
|
|
|
|
MessageInboxAndSent
|
|
|
|
)
|
|
|
|
|
|
|
|
type MessageEncryption int
|
|
|
|
|
2017-09-20 11:09:31 +03:00
|
|
|
const (
|
|
|
|
MessageUnencrypted MessageEncryption = iota
|
|
|
|
MessageEncryptedInternal
|
|
|
|
MessageEncryptedExternal
|
|
|
|
MessageEncryptedOutside
|
|
|
|
_
|
|
|
|
_
|
|
|
|
_
|
|
|
|
MessageEncryptedInlinePGP
|
|
|
|
MessageEncryptedPGPMIME
|
|
|
|
_
|
|
|
|
_
|
|
|
|
)
|
|
|
|
|
2017-09-18 15:41:50 +03:00
|
|
|
type MessageAddress struct {
|
|
|
|
Address string
|
2017-09-19 15:54:47 +03:00
|
|
|
Name string
|
2017-09-18 15:41:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type Message struct {
|
2017-09-19 15:54:47 +03:00
|
|
|
ID string
|
|
|
|
Order int64
|
|
|
|
Subject string
|
|
|
|
IsRead int
|
|
|
|
Type MessageType
|
|
|
|
Sender *MessageAddress
|
|
|
|
ReplyTo *MessageAddress
|
|
|
|
ToList []*MessageAddress
|
|
|
|
Time int64
|
|
|
|
Size int64
|
2018-01-08 11:30:45 +02:00
|
|
|
NumAttachments int
|
2017-09-19 15:54:47 +03:00
|
|
|
IsEncrypted MessageEncryption
|
2017-09-18 15:41:50 +03:00
|
|
|
ExpirationTime int64
|
2017-09-19 15:54:47 +03:00
|
|
|
IsReplied int
|
|
|
|
IsRepliedAll int
|
|
|
|
IsForwarded int
|
|
|
|
SpamScore int
|
|
|
|
AddressID string
|
|
|
|
Body string
|
2017-12-03 12:55:59 +02:00
|
|
|
MIMEType string `json:",omitempty"`
|
2017-09-19 15:54:47 +03:00
|
|
|
CCList []*MessageAddress
|
|
|
|
BCCList []*MessageAddress
|
|
|
|
Header string
|
|
|
|
Attachments []*Attachment
|
|
|
|
LabelIDs []string
|
|
|
|
ExternalID string
|
2017-09-18 15:41:50 +03:00
|
|
|
}
|
|
|
|
|
2017-09-20 11:09:31 +03:00
|
|
|
func (msg *Message) Read(keyring openpgp.KeyRing, prompt openpgp.PromptFunction) (*openpgp.MessageDetails, error) {
|
|
|
|
switch msg.IsEncrypted {
|
|
|
|
case MessageUnencrypted:
|
|
|
|
return &openpgp.MessageDetails{
|
|
|
|
IsEncrypted: false,
|
|
|
|
IsSigned: false,
|
|
|
|
UnverifiedBody: strings.NewReader(msg.Body),
|
|
|
|
}, nil
|
|
|
|
default:
|
|
|
|
block, err := armor.Decode(strings.NewReader(msg.Body))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return openpgp.ReadMessage(block.Body, keyring, prompt, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type messageWriter struct {
|
|
|
|
plaintext io.WriteCloser
|
|
|
|
ciphertext io.WriteCloser
|
|
|
|
b *bytes.Buffer
|
|
|
|
msg *Message
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *messageWriter) Write(p []byte) (n int, err error) {
|
|
|
|
return w.plaintext.Write(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *messageWriter) Close() error {
|
|
|
|
if err := w.plaintext.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := w.ciphertext.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
w.msg.Body = w.b.String()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *Message) Encrypt(to []*openpgp.Entity, signed *openpgp.Entity) (plaintext io.WriteCloser, err error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
ciphertext, err := armor.Encode(&b, "PGP MESSAGE", nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
plaintext, err = openpgp.Encrypt(ciphertext, to, signed, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &messageWriter{
|
|
|
|
plaintext: plaintext,
|
|
|
|
ciphertext: ciphertext,
|
|
|
|
b: &b,
|
|
|
|
msg: msg,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 00:38:13 +02:00
|
|
|
type MessageFilter struct {
|
|
|
|
Page int
|
|
|
|
PageSize int
|
|
|
|
Limit int
|
|
|
|
|
|
|
|
Label string
|
|
|
|
Sort string
|
|
|
|
Asc bool
|
|
|
|
Begin int64
|
|
|
|
End int64
|
|
|
|
Keyword string
|
|
|
|
To string
|
|
|
|
From string
|
|
|
|
Subject string
|
|
|
|
Attachments *bool
|
|
|
|
Starred *bool
|
|
|
|
Unread *bool
|
|
|
|
Conversation string
|
|
|
|
Address string
|
|
|
|
ID []string
|
|
|
|
ExternalID string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) ListMessages(filter *MessageFilter) (total int, messages []*Message, err error) {
|
|
|
|
v := url.Values{}
|
|
|
|
if filter.Page != 0 {
|
|
|
|
v.Set("Page", strconv.Itoa(filter.Page))
|
|
|
|
}
|
|
|
|
if filter.PageSize != 0 {
|
|
|
|
v.Set("PageSize", strconv.Itoa(filter.PageSize))
|
|
|
|
}
|
|
|
|
if filter.Limit != 0 {
|
|
|
|
v.Set("Limit", strconv.Itoa(filter.Limit))
|
|
|
|
}
|
|
|
|
if filter.Label != "" {
|
|
|
|
v.Set("Label", filter.Label)
|
|
|
|
}
|
|
|
|
if filter.Sort != "" {
|
|
|
|
v.Set("Sort", filter.Sort)
|
|
|
|
}
|
|
|
|
if filter.Asc {
|
|
|
|
v.Set("Desc", "0")
|
|
|
|
}
|
|
|
|
if filter.Conversation != "" {
|
|
|
|
v.Set("Conversation", filter.Conversation)
|
|
|
|
}
|
|
|
|
if filter.Address != "" {
|
|
|
|
v.Set("Address", filter.Address)
|
|
|
|
}
|
|
|
|
if filter.ExternalID != "" {
|
|
|
|
v.Set("ExternalID", filter.ExternalID)
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := c.newRequest(http.MethodGet, "/messages?"+v.Encode(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Total int
|
|
|
|
Messages []*Message
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Total, respData.Messages, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type MessageCount struct {
|
|
|
|
LabelID string
|
|
|
|
Total int
|
|
|
|
Unread int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) CountMessages(address string) ([]*MessageCount, error) {
|
|
|
|
v := url.Values{}
|
|
|
|
if address != "" {
|
|
|
|
v.Set("Address", address)
|
|
|
|
}
|
|
|
|
req, err := c.newRequest(http.MethodGet, "/messages/count?"+v.Encode(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Counts []*MessageCount
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Counts, nil
|
|
|
|
}
|
|
|
|
|
2017-09-18 15:41:50 +03:00
|
|
|
func (c *Client) GetMessage(id string) (*Message, error) {
|
|
|
|
req, err := c.newRequest(http.MethodGet, "/messages/"+id, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Message *Message
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Message, nil
|
|
|
|
}
|
|
|
|
|
2017-09-20 11:09:31 +03:00
|
|
|
// CreateDraftMessage creates a new draft message. ToList, CCList, BCCList,
|
|
|
|
// Subject, Body and AddressID are required in msg.
|
2017-09-18 15:41:50 +03:00
|
|
|
func (c *Client) CreateDraftMessage(msg *Message, parentID string) (*Message, error) {
|
|
|
|
reqData := struct {
|
2017-09-19 15:54:47 +03:00
|
|
|
Message *Message
|
2017-09-18 15:41:50 +03:00
|
|
|
ParentID string `json:",omitempty"`
|
2017-09-19 15:54:47 +03:00
|
|
|
Action *int `json:",omitempty"`
|
2017-09-18 15:41:50 +03:00
|
|
|
}{msg, parentID, nil}
|
|
|
|
req, err := c.newJSONRequest(http.MethodPost, "/messages", &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Message *Message
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Message, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UpdateDraftMessage(msg *Message) (*Message, error) {
|
|
|
|
reqData := struct {
|
|
|
|
Message *Message
|
|
|
|
}{msg}
|
|
|
|
req, err := c.newJSONRequest(http.MethodPut, "/messages/"+msg.ID, &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Message *Message
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Message, nil
|
|
|
|
}
|
|
|
|
|
2018-01-12 20:28:54 +02:00
|
|
|
func (c *Client) doMessages(action string, ids []string) error {
|
|
|
|
reqData := struct {
|
|
|
|
IDs []string
|
|
|
|
}{ids}
|
|
|
|
req, err := c.newJSONRequest(http.MethodPut, "/messages/"+action, &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: the response contains one response per message
|
|
|
|
return c.doJSON(req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) MarkMessagesRead(ids []string) error {
|
|
|
|
return c.doMessages("read", ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) MarkMessagesUnread(ids []string) error {
|
|
|
|
return c.doMessages("unread", ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) DeleteMessages(ids []string) error {
|
|
|
|
return c.doMessages("delete", ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UndeleteMessages(ids []string) error {
|
|
|
|
return c.doMessages("undelete", ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) LabelMessages(labelID string, ids []string) error {
|
|
|
|
reqData := struct {
|
|
|
|
LabelID string
|
|
|
|
IDs []string
|
|
|
|
}{labelID, ids}
|
|
|
|
req, err := c.newJSONRequest(http.MethodPut, "/messages/label", &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: the response contains one response per message
|
|
|
|
return c.doJSON(req, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UnlabelMessages(labelID string, ids []string) error {
|
|
|
|
reqData := struct {
|
|
|
|
LabelID string
|
|
|
|
IDs []string
|
|
|
|
}{labelID, ids}
|
|
|
|
req, err := c.newJSONRequest(http.MethodPut, "/messages/unlabel", &reqData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: the response contains one response per message
|
|
|
|
return c.doJSON(req, nil)
|
|
|
|
}
|
|
|
|
|
2017-09-18 15:41:50 +03:00
|
|
|
type MessageKeyPacket struct {
|
2017-09-19 15:54:47 +03:00
|
|
|
ID string
|
2017-09-18 15:41:50 +03:00
|
|
|
KeyPackets string
|
|
|
|
}
|
|
|
|
|
|
|
|
type MessagePackageType int
|
|
|
|
|
2017-09-21 21:02:54 +03:00
|
|
|
const (
|
|
|
|
MessagePackageInternal MessagePackageType = 1
|
|
|
|
MessagePackageEncryptedOutside = 2
|
2017-09-23 20:20:47 +03:00
|
|
|
MessagePackageCleartext = 4
|
2017-09-21 21:02:54 +03:00
|
|
|
MessagePackageInlinePGP = 8
|
|
|
|
MessagePackagePGPMIME = 16
|
|
|
|
MessagePackageMIME = 32
|
|
|
|
)
|
|
|
|
|
2018-01-11 01:01:49 +02:00
|
|
|
// From https://github.com/ProtonMail/WebClient/blob/public/src/app/composer/services/encryptMessage.js
|
2017-09-23 20:20:47 +03:00
|
|
|
|
2017-09-18 15:41:50 +03:00
|
|
|
type MessagePackage struct {
|
2017-09-23 20:20:47 +03:00
|
|
|
Type MessagePackageType
|
|
|
|
|
2017-12-03 12:55:59 +02:00
|
|
|
BodyKeyPacket string
|
2017-09-23 20:20:47 +03:00
|
|
|
AttachmentKeyPackets map[string]string
|
2017-12-03 12:55:59 +02:00
|
|
|
Signature int
|
2017-09-18 15:41:50 +03:00
|
|
|
|
2017-09-23 20:20:47 +03:00
|
|
|
// Only if encrypted for outside
|
|
|
|
PasswordHint string
|
2017-12-03 12:55:59 +02:00
|
|
|
Auth interface{} // TODO
|
|
|
|
Token string
|
|
|
|
EncToken string
|
2017-09-18 15:41:50 +03:00
|
|
|
}
|
|
|
|
|
2017-09-23 20:20:47 +03:00
|
|
|
type MessagePackageSet struct {
|
2017-12-03 12:55:59 +02:00
|
|
|
Type MessagePackageType // OR of each Type
|
2017-09-23 20:20:47 +03:00
|
|
|
Addresses map[string]*MessagePackage
|
2017-12-03 12:55:59 +02:00
|
|
|
MIMEType string
|
2018-01-11 01:01:49 +02:00
|
|
|
Body string // Encrypted body data packet
|
2017-09-23 20:20:47 +03:00
|
|
|
|
|
|
|
// Only if cleartext is sent
|
2017-12-03 12:55:59 +02:00
|
|
|
BodyKey string
|
2017-09-23 20:20:47 +03:00
|
|
|
AttachmentKeys map[string]string
|
|
|
|
|
2017-12-03 12:55:59 +02:00
|
|
|
bodyKey *packet.EncryptedKey
|
2017-09-23 20:20:47 +03:00
|
|
|
attachmentKeys map[string]*packet.EncryptedKey
|
2018-01-11 01:01:49 +02:00
|
|
|
signature int
|
2017-09-23 20:20:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessagePackageSet(attachmentKeys map[string]*packet.EncryptedKey) *MessagePackageSet {
|
|
|
|
return &MessagePackageSet{
|
2017-12-03 12:55:59 +02:00
|
|
|
Addresses: make(map[string]*MessagePackage),
|
2017-09-23 20:20:47 +03:00
|
|
|
attachmentKeys: attachmentKeys,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type outgoingMessageWriter struct {
|
2017-12-03 12:55:59 +02:00
|
|
|
cleartext io.WriteCloser
|
2017-09-23 20:20:47 +03:00
|
|
|
ciphertext io.WriteCloser
|
2017-12-03 12:55:59 +02:00
|
|
|
encoded *bytes.Buffer
|
|
|
|
set *MessagePackageSet
|
2017-09-23 20:20:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *outgoingMessageWriter) Write(p []byte) (int, error) {
|
|
|
|
return w.cleartext.Write(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *outgoingMessageWriter) Close() error {
|
|
|
|
if err := w.cleartext.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := w.ciphertext.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-02 17:23:06 +02:00
|
|
|
w.set.Body = w.encoded.String()
|
|
|
|
w.encoded = nil
|
2017-09-23 20:20:47 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-03 12:55:59 +02:00
|
|
|
// Encrypt encrypts the data that will be written to the returned
|
2018-01-11 01:05:13 +02:00
|
|
|
// io.WriteCloser, and optionally signs it.
|
2017-12-03 12:55:59 +02:00
|
|
|
func (set *MessagePackageSet) Encrypt(mimeType string, signed *openpgp.Entity) (io.WriteCloser, error) {
|
2017-12-02 17:23:06 +02:00
|
|
|
set.MIMEType = mimeType
|
|
|
|
|
2017-09-23 20:20:47 +03:00
|
|
|
config := &packet.Config{}
|
|
|
|
|
2017-12-03 12:55:59 +02:00
|
|
|
key, err := generateUnencryptedKey(packet.CipherAES256, config)
|
|
|
|
if err != nil {
|
2017-09-23 20:20:47 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
2017-12-03 12:55:59 +02:00
|
|
|
set.bodyKey = key
|
2017-09-23 20:20:47 +03:00
|
|
|
|
2018-01-11 01:01:49 +02:00
|
|
|
var signer *packet.PrivateKey
|
|
|
|
if signed != nil {
|
|
|
|
signKey, ok := signingKey(signed, config.Now())
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("no valid signing keys")
|
|
|
|
}
|
|
|
|
signer = signKey.PrivateKey
|
|
|
|
if signer == nil {
|
|
|
|
return nil, errors.New("no private key in signing key")
|
|
|
|
}
|
|
|
|
if signer.Encrypted {
|
|
|
|
return nil, errors.New("signing key must be decrypted")
|
|
|
|
}
|
|
|
|
set.signature = 1
|
2017-09-23 20:20:47 +03:00
|
|
|
}
|
2017-09-18 15:41:50 +03:00
|
|
|
|
2018-01-11 01:01:49 +02:00
|
|
|
encoded := new(bytes.Buffer)
|
|
|
|
ciphertext := base64.NewEncoder(base64.StdEncoding, encoded)
|
2017-09-23 20:20:47 +03:00
|
|
|
|
2018-01-11 01:13:28 +02:00
|
|
|
cleartext, err := symetricallyEncrypt(ciphertext, key, signer, nil, config)
|
2017-09-23 20:20:47 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &outgoingMessageWriter{
|
2018-01-11 01:01:49 +02:00
|
|
|
cleartext: cleartext,
|
2017-09-23 20:20:47 +03:00
|
|
|
ciphertext: ciphertext,
|
2018-01-11 01:01:49 +02:00
|
|
|
encoded: encoded,
|
2017-12-03 12:55:59 +02:00
|
|
|
set: set,
|
2017-09-23 20:20:47 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (set *MessagePackageSet) AddCleartext(addr string) error {
|
2018-01-11 01:01:49 +02:00
|
|
|
set.Addresses[addr] = &MessagePackage{
|
|
|
|
Type: MessagePackageCleartext,
|
|
|
|
Signature: set.signature,
|
|
|
|
}
|
2017-12-02 17:23:06 +02:00
|
|
|
set.Type |= MessagePackageCleartext
|
2017-09-23 20:20:47 +03:00
|
|
|
|
|
|
|
if set.BodyKey == "" || set.AttachmentKeys == nil {
|
|
|
|
set.BodyKey = base64.StdEncoding.EncodeToString(set.bodyKey.Key)
|
|
|
|
|
|
|
|
set.AttachmentKeys = make(map[string]string, len(set.attachmentKeys))
|
|
|
|
for att, key := range set.attachmentKeys {
|
|
|
|
set.AttachmentKeys[att] = base64.StdEncoding.EncodeToString(key.Key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func serializeEncryptedKey(symKey *packet.EncryptedKey, pub *packet.PublicKey, config *packet.Config) (string, error) {
|
2017-12-03 11:20:45 +02:00
|
|
|
var encoded bytes.Buffer
|
|
|
|
ciphertext := base64.NewEncoder(base64.StdEncoding, &encoded)
|
2017-09-23 20:20:47 +03:00
|
|
|
|
2017-12-03 11:20:45 +02:00
|
|
|
err := packet.SerializeEncryptedKey(ciphertext, pub, symKey.CipherFunc, symKey.Key, config)
|
2017-09-23 20:20:47 +03:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2017-12-03 11:20:45 +02:00
|
|
|
ciphertext.Close()
|
|
|
|
|
|
|
|
return encoded.String(), nil
|
2017-09-23 20:20:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (set *MessagePackageSet) AddInternal(addr string, pub *openpgp.Entity) error {
|
|
|
|
config := &packet.Config{}
|
|
|
|
|
|
|
|
encKey, ok := encryptionKey(pub, config.Now())
|
|
|
|
if !ok {
|
|
|
|
return errors.New("cannot encrypt a message to key id " + strconv.FormatUint(pub.PrimaryKey.KeyId, 16) + " because it has no encryption keys")
|
|
|
|
}
|
|
|
|
|
|
|
|
bodyKey, err := serializeEncryptedKey(set.bodyKey, encKey.PublicKey, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
attachmentKeys := make(map[string]string, len(set.attachmentKeys))
|
|
|
|
for att, key := range set.attachmentKeys {
|
|
|
|
attKey, err := serializeEncryptedKey(key, encKey.PublicKey, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
attachmentKeys[att] = attKey
|
|
|
|
}
|
|
|
|
|
2017-12-02 17:23:06 +02:00
|
|
|
set.Type |= MessagePackageInternal
|
2017-09-23 20:20:47 +03:00
|
|
|
set.Addresses[addr] = &MessagePackage{
|
2017-12-03 12:55:59 +02:00
|
|
|
Type: MessagePackageInternal,
|
|
|
|
BodyKeyPacket: bodyKey,
|
2017-09-23 20:20:47 +03:00
|
|
|
AttachmentKeyPackets: attachmentKeys,
|
2018-01-11 01:01:49 +02:00
|
|
|
Signature: set.signature,
|
2017-09-23 20:20:47 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type OutgoingMessage struct {
|
|
|
|
ID string
|
2017-09-21 21:02:54 +03:00
|
|
|
|
|
|
|
// Only if message expires
|
2017-09-23 20:20:47 +03:00
|
|
|
ExpirationTime int // Duration in seconds
|
2017-09-21 21:02:54 +03:00
|
|
|
|
2017-09-23 20:20:47 +03:00
|
|
|
Packages []*MessagePackageSet
|
2017-09-18 15:41:50 +03:00
|
|
|
}
|
|
|
|
|
2017-09-23 20:20:47 +03:00
|
|
|
func (c *Client) SendMessage(msg *OutgoingMessage) (sent, parent *Message, err error) {
|
2017-12-02 17:23:06 +02:00
|
|
|
req, err := c.newJSONRequest(http.MethodPost, "/messages/send/"+msg.ID, msg)
|
2017-09-18 15:41:50 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
resp
|
|
|
|
Sent, Parent *Message
|
|
|
|
}
|
|
|
|
if err := c.doJSON(req, &respData); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respData.Sent, respData.Parent, nil
|
|
|
|
}
|