Files
proton-bridge/pkg/pmapi/addresses.go

233 lines
5.0 KiB
Go

// Copyright (c) 2020 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 (
"errors"
"fmt"
"strings"
pmcrypto "github.com/ProtonMail/gopenpgp/crypto"
)
// Address statuses.
const (
DisabledAddress = iota
EnabledAddress
)
// Address receive values.
const (
CannotReceive = iota
CanReceive
)
// Address HasKeys values.
const (
MissingKeys = iota
KeysPresent
)
// Address types.
const (
_ = iota // Skip first.
OriginalAddress
AliasAddress
CustomAddress
PremiumAddress
)
// Address Send values.
const (
NoSendAddress = iota
MainSendAddress
SecondarySendAddress
)
// Address represents a user's address.
type Address struct {
ID string
DomainID string
Email string
Send int
Receive int
Status int
Order int `json:",omitempty"`
Type int
DisplayName string
Signature string
MemberID string `json:",omitempty"`
MemberName string `json:",omitempty"`
HasKeys int
Keys PMKeys
}
// AddressList is a list of addresses.
type AddressList []*Address
type AddressesRes struct {
Res
Addresses AddressList
}
// KeyRing returns the (possibly unlocked) PMKeys KeyRing.
func (a *Address) KeyRing() *pmcrypto.KeyRing {
return a.Keys.KeyRing
}
// ByID returns an address by id. Returns nil if no address is found.
func (l AddressList) ByID(id string) *Address {
for _, addr := range l {
if addr.ID == id {
return addr
}
}
return nil
}
func (l AddressList) ActiveEmails() (addresses []string) {
for _, a := range l {
if a.Receive == CanReceive {
addresses = append(addresses, a.Email)
}
}
return
}
// Main gets the main address.
func (l AddressList) Main() *Address {
for _, addr := range l {
if addr.Order == 1 {
return addr
}
}
return nil
}
// ByEmail gets an address by email. Returns nil if no address is found.
func (l AddressList) ByEmail(email string) *Address {
email = SanitizeEmail(email)
for _, addr := range l {
if strings.EqualFold(addr.Email, email) {
return addr
}
}
return nil
}
func SanitizeEmail(email string) string {
splitAt := strings.Split(email, "@")
if len(splitAt) != 2 {
return email
}
splitPlus := strings.Split(splitAt[0], "+")
email = splitPlus[0] + "@" + splitAt[1]
return email
}
func ConstructAddress(headerEmail string, addressEmail string) string {
splitAtHeader := strings.Split(headerEmail, "@")
if len(splitAtHeader) != 2 {
return addressEmail
}
splitPlus := strings.Split(splitAtHeader[0], "+")
if len(splitPlus) != 2 {
return addressEmail
}
splitAtAddress := strings.Split(addressEmail, "@")
if len(splitAtAddress) != 2 {
return addressEmail
}
return splitAtAddress[0] + "+" + splitPlus[1] + "@" + splitAtAddress[1]
}
// GetAddresses requests all of current user addresses (without pagination).
func (c *client) GetAddresses() (addresses AddressList, err error) {
req, err := c.NewRequest("GET", "/addresses", nil)
if err != nil {
return
}
var res AddressesRes
if err = c.DoJSON(req, &res); err != nil {
return
}
return res.Addresses, res.Err()
}
func (c *client) ReorderAddresses(addressIDs []string) (err error) {
var reqBody struct {
AddressIDs []string
}
reqBody.AddressIDs = addressIDs
req, err := c.NewJSONRequest("PUT", "/addresses/order", reqBody)
if err != nil {
return
}
var addContactsRes AddContactsResponse
if err = c.DoJSON(req, &addContactsRes); err != nil {
return
}
_, err = c.UpdateUser()
return
}
// Addresses returns the addresses stored in the client object itself rather than fetching from the API.
func (c *client) Addresses() AddressList {
return c.addresses
}
// UnlockAddresses unlocks all keys for all addresses of current user.
func (c *client) UnlockAddresses(passphrase []byte) (err error) {
for _, a := range c.addresses {
if a.HasKeys == MissingKeys {
continue
}
// Unlock the address token using the UserKey, use the unlocked token to unlock the keyring.
if err = a.Keys.unlockKeyRing(c.kr, passphrase, c.keyLocker); err != nil {
err = fmt.Errorf("pmapi: cannot unlock private key of address %v: %v", a.Email, err)
return
}
}
return
}
func (c *client) KeyRingForAddressID(addrID string) (*pmcrypto.KeyRing, error) {
if addr := c.addresses.ByID(addrID); addr != nil {
return addr.KeyRing(), nil
}
if addr := c.addresses.Main(); addr != nil {
return addr.KeyRing(), nil
}
return nil, errors.New("no such address ID")
}