protonmail: add some messages + public keys endpoints
This commit is contained in:
parent
26817a27c7
commit
c6b479b01a
|
@ -8,10 +8,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/emersion/hydroxide/protonmail"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"golang.org/x/crypto/nacl/secretbox"
|
"golang.org/x/crypto/nacl/secretbox"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
|
|
||||||
|
"github.com/emersion/hydroxide/protonmail"
|
||||||
)
|
)
|
||||||
|
|
||||||
const authFile = "auth.json"
|
const authFile = "auth.json"
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
package protonmail
|
package protonmail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
)
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
ID string
|
ID string
|
||||||
Version int
|
Version int
|
||||||
|
@ -8,3 +17,55 @@ type Key struct {
|
||||||
Fingerprint string
|
Fingerprint string
|
||||||
Activation interface{} // TODO
|
Activation interface{} // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecipientType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RecipientInternal RecipientType = 1
|
||||||
|
RecipientExternal = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type PublicKeyResp struct {
|
||||||
|
RecipientType RecipientType
|
||||||
|
MIMEType string
|
||||||
|
Keys []*PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicKey struct {
|
||||||
|
Send int
|
||||||
|
PublicKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pub *PublicKey) Entity() (*openpgp.Entity, error) {
|
||||||
|
keyRing, err := openpgp.ReadArmoredKeyRing(strings.NewReader(pub.PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(keyRing) == 0 {
|
||||||
|
return nil, errors.New("public key is empty")
|
||||||
|
}
|
||||||
|
return keyRing[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKeys retrieves public keys for a user. The first key in
|
||||||
|
// PublicKeyResp.Keys can be used for sending.
|
||||||
|
func (c *Client) GetPublicKeys(email string) (*PublicKeyResp, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("Email", email)
|
||||||
|
// TODO: Fingerprint
|
||||||
|
|
||||||
|
req, err := c.newRequest(http.MethodGet, "/keys?"+v.Encode(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var respData struct {
|
||||||
|
resp
|
||||||
|
*PublicKeyResp
|
||||||
|
}
|
||||||
|
if err := c.doJSON(req, &respData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return respData.PublicKeyResp, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package protonmail
|
package protonmail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageType int
|
type MessageType int
|
||||||
|
@ -15,6 +21,20 @@ const (
|
||||||
|
|
||||||
type MessageEncryption int
|
type MessageEncryption int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MessageUnencrypted MessageEncryption = iota
|
||||||
|
MessageEncryptedInternal
|
||||||
|
MessageEncryptedExternal
|
||||||
|
MessageEncryptedOutside
|
||||||
|
_
|
||||||
|
_
|
||||||
|
_
|
||||||
|
MessageEncryptedInlinePGP
|
||||||
|
MessageEncryptedPGPMIME
|
||||||
|
_
|
||||||
|
_
|
||||||
|
)
|
||||||
|
|
||||||
type MessageAddress struct {
|
type MessageAddress struct {
|
||||||
Address string
|
Address string
|
||||||
Name string
|
Name string
|
||||||
|
@ -48,6 +68,66 @@ type Message struct {
|
||||||
ExternalID string
|
ExternalID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetMessage(id string) (*Message, error) {
|
func (c *Client) GetMessage(id string) (*Message, error) {
|
||||||
req, err := c.newRequest(http.MethodGet, "/messages/"+id, nil)
|
req, err := c.newRequest(http.MethodGet, "/messages/"+id, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,6 +145,8 @@ func (c *Client) GetMessage(id string) (*Message, error) {
|
||||||
return respData.Message, nil
|
return respData.Message, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateDraftMessage creates a new draft message. ToList, CCList, BCCList,
|
||||||
|
// Subject, Body and AddressID are required in msg.
|
||||||
func (c *Client) CreateDraftMessage(msg *Message, parentID string) (*Message, error) {
|
func (c *Client) CreateDraftMessage(msg *Message, parentID string) (*Message, error) {
|
||||||
reqData := struct {
|
reqData := struct {
|
||||||
Message *Message
|
Message *Message
|
||||||
|
|
Loading…
Reference in New Issue