mirror of
https://github.com/ProtonMail/go-proton-api.git
synced 2025-12-23 15:47:52 -05:00
Rather than only fetching the total in on request and discarding all the data, re-use the first page of data and then collect more of them if the data set exceeds the page size. This patch also includes various fixes to the GPA server to mimic proton server behavior.
389 lines
7.0 KiB
Go
389 lines
7.0 KiB
Go
package proton
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
|
"github.com/bradenaw/juniper/xslices"
|
|
"github.com/emersion/go-vcard"
|
|
)
|
|
|
|
const (
|
|
FieldPMScheme = "X-PM-SCHEME"
|
|
FieldPMSign = "X-PM-SIGN"
|
|
FieldPMEncrypt = "X-PM-ENCRYPT"
|
|
FieldPMEncryptUntrusted = "X-PM-ENCRYPT-UNTRUSTED"
|
|
FieldPMMIMEType = "X-PM-MIMETYPE"
|
|
)
|
|
|
|
type Cards []*Card
|
|
|
|
func (c *Cards) Merge(kr *crypto.KeyRing) (vcard.Card, error) {
|
|
merged := newVCard()
|
|
|
|
for _, card := range *c {
|
|
dec, err := card.decode(kr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for k, fields := range dec {
|
|
for _, f := range fields {
|
|
merged.Add(k, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
return merged, nil
|
|
}
|
|
|
|
func (c *Cards) Get(cardType CardType) (*Card, bool) {
|
|
for _, card := range *c {
|
|
if card.Type == cardType {
|
|
return card, true
|
|
}
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
type Card struct {
|
|
Type CardType
|
|
Data string
|
|
Signature string
|
|
}
|
|
|
|
type CardType int
|
|
|
|
const (
|
|
CardTypeClear CardType = iota
|
|
CardTypeEncrypted
|
|
CardTypeSigned
|
|
)
|
|
|
|
func NewCard(kr *crypto.KeyRing, cardType CardType) (*Card, error) {
|
|
card := &Card{Type: cardType}
|
|
|
|
if err := card.encode(kr, newVCard()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return card, nil
|
|
}
|
|
|
|
func newVCard() vcard.Card {
|
|
card := make(vcard.Card)
|
|
|
|
card.AddValue(vcard.FieldVersion, "4.0")
|
|
|
|
return card
|
|
}
|
|
|
|
func (c Card) Get(kr *crypto.KeyRing, key string) ([]*vcard.Field, error) {
|
|
dec, err := c.decode(kr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return dec[key], nil
|
|
}
|
|
|
|
func (c *Card) Set(kr *crypto.KeyRing, key string, value *vcard.Field) error {
|
|
dec, err := c.decode(kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if field := dec.Get(key); field != nil {
|
|
field.Value = value.Value
|
|
field.Params = value.Params
|
|
field.Group = value.Group
|
|
|
|
return c.encode(kr, dec)
|
|
}
|
|
|
|
dec.Add(key, value)
|
|
|
|
return c.encode(kr, dec)
|
|
}
|
|
|
|
func (c *Card) Add(kr *crypto.KeyRing, key string, value *vcard.Field) error {
|
|
dec, err := c.decode(kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dec.Add(key, value)
|
|
|
|
return c.encode(kr, dec)
|
|
}
|
|
|
|
func (c *Card) ChangeType(kr *crypto.KeyRing, cardType CardType) error {
|
|
dec, err := c.decode(kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.Type = cardType
|
|
|
|
return c.encode(kr, dec)
|
|
}
|
|
|
|
// GetGroup returns a type to manipulate the group defined by the given key/value pair.
|
|
func (c Card) GetGroup(kr *crypto.KeyRing, groupKey, groupValue string) (CardGroup, error) {
|
|
group, err := c.getGroup(kr, groupKey, groupValue)
|
|
if err != nil {
|
|
return CardGroup{}, err
|
|
}
|
|
|
|
return CardGroup{Card: c, kr: kr, group: group}, nil
|
|
}
|
|
|
|
// DeleteGroup removes all values in the group defined by the given key/value pair.
|
|
func (c *Card) DeleteGroup(kr *crypto.KeyRing, groupKey, groupValue string) error {
|
|
group, err := c.getGroup(kr, groupKey, groupValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.deleteGroup(kr, group)
|
|
}
|
|
|
|
type CardGroup struct {
|
|
Card
|
|
|
|
kr *crypto.KeyRing
|
|
group string
|
|
}
|
|
|
|
// Get returns the values in the group with the given key.
|
|
func (g CardGroup) Get(key string) ([]string, error) {
|
|
dec, err := g.decode(g.kr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var fields []*vcard.Field
|
|
|
|
for _, field := range dec[key] {
|
|
if field.Group != g.group {
|
|
continue
|
|
}
|
|
|
|
fields = append(fields, field)
|
|
}
|
|
|
|
return xslices.Map(fields, func(field *vcard.Field) string {
|
|
return field.Value
|
|
}), nil
|
|
}
|
|
|
|
// Set sets the value in the group.
|
|
func (g *CardGroup) Set(key, value string, params vcard.Params) error {
|
|
dec, err := g.decode(g.kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, field := range dec[key] {
|
|
if field.Group != g.group {
|
|
continue
|
|
}
|
|
|
|
field.Value = value
|
|
|
|
return g.encode(g.kr, dec)
|
|
}
|
|
|
|
dec.Add(key, &vcard.Field{
|
|
Value: value,
|
|
Group: g.group,
|
|
Params: params,
|
|
})
|
|
|
|
return g.encode(g.kr, dec)
|
|
}
|
|
|
|
// Add adds a value to the group.
|
|
func (g *CardGroup) Add(key, value string, params vcard.Params) error {
|
|
dec, err := g.decode(g.kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dec.Add(key, &vcard.Field{
|
|
Value: value,
|
|
Group: g.group,
|
|
Params: params,
|
|
})
|
|
|
|
return g.encode(g.kr, dec)
|
|
}
|
|
|
|
// Remove removes the value in the group with the given key/value.
|
|
func (g *CardGroup) Remove(key, value string) error {
|
|
dec, err := g.decode(g.kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fields, ok := dec[key]
|
|
if !ok {
|
|
return errors.New("no such key")
|
|
}
|
|
|
|
var rest []*vcard.Field
|
|
|
|
for _, field := range fields {
|
|
if field.Group != g.group {
|
|
rest = append(rest, field)
|
|
} else if field.Value != value {
|
|
rest = append(rest, field)
|
|
}
|
|
}
|
|
|
|
if len(rest) > 0 {
|
|
dec[key] = rest
|
|
} else {
|
|
delete(dec, key)
|
|
}
|
|
|
|
return g.encode(g.kr, dec)
|
|
}
|
|
|
|
// RemoveAll removes all values in the group with the given key.
|
|
func (g *CardGroup) RemoveAll(key string) error {
|
|
dec, err := g.decode(g.kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fields, ok := dec[key]
|
|
if !ok {
|
|
return errors.New("no such key")
|
|
}
|
|
|
|
var rest []*vcard.Field
|
|
|
|
for _, field := range fields {
|
|
if field.Group != g.group {
|
|
rest = append(rest, field)
|
|
}
|
|
}
|
|
|
|
if len(rest) > 0 {
|
|
dec[key] = rest
|
|
} else {
|
|
delete(dec, key)
|
|
}
|
|
|
|
return g.encode(g.kr, dec)
|
|
}
|
|
|
|
func (c Card) getGroup(kr *crypto.KeyRing, groupKey, groupValue string) (string, error) {
|
|
fields, err := c.Get(kr, groupKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, field := range fields {
|
|
if field.Value != groupValue {
|
|
continue
|
|
}
|
|
|
|
return field.Group, nil
|
|
}
|
|
|
|
return "", errors.New("no such field")
|
|
}
|
|
|
|
func (c *Card) deleteGroup(kr *crypto.KeyRing, group string) error {
|
|
dec, err := c.decode(kr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for key, fields := range dec {
|
|
var rest []*vcard.Field
|
|
|
|
for _, field := range fields {
|
|
if field.Group != group {
|
|
rest = append(rest, field)
|
|
}
|
|
}
|
|
|
|
if len(rest) > 0 {
|
|
dec[key] = rest
|
|
} else {
|
|
delete(dec, key)
|
|
}
|
|
}
|
|
|
|
return c.encode(kr, dec)
|
|
}
|
|
|
|
func (c Card) decode(kr *crypto.KeyRing) (vcard.Card, error) {
|
|
if c.Type&CardTypeEncrypted != 0 {
|
|
enc, err := crypto.NewPGPMessageFromArmored(c.Data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dec, err := kr.Decrypt(enc, nil, crypto.GetUnixTime())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c.Data = dec.GetString()
|
|
}
|
|
|
|
if c.Type&CardTypeSigned != 0 {
|
|
sig, err := crypto.NewPGPSignatureFromArmored(c.Signature)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := kr.VerifyDetached(crypto.NewPlainMessageFromString(c.Data), sig, crypto.GetUnixTime()); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return vcard.NewDecoder(strings.NewReader(c.Data)).Decode()
|
|
}
|
|
|
|
func (c *Card) encode(kr *crypto.KeyRing, card vcard.Card) error {
|
|
buf := new(bytes.Buffer)
|
|
|
|
if err := vcard.NewEncoder(buf).Encode(card); err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.Type&CardTypeSigned != 0 {
|
|
sig, err := kr.SignDetached(crypto.NewPlainMessageFromString(buf.String()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.Signature, err = sig.GetArmored(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if c.Type&CardTypeEncrypted != 0 {
|
|
enc, err := kr.Encrypt(crypto.NewPlainMessageFromString(buf.String()), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.Data, err = enc.GetArmored(); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
c.Data = buf.String()
|
|
}
|
|
|
|
return nil
|
|
}
|