126 lines
5.4 KiB
Go
126 lines
5.4 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
)
|
|
|
|
// CreatePGPIdentity adds a new PGP identity for a mailbox — a mailbox may hold
|
|
// several at once (see esrv_mailbox_pgp_identities in schema.go).
|
|
func (d *DB) CreatePGPIdentity(mailboxID int64, label, email, fingerprint, publicKeyArmor, privateKeyArmor string) (int64, error) {
|
|
res, err := d.Exec(`
|
|
INSERT INTO esrv_mailbox_pgp_identities (mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
`, mailboxID, label, email, fingerprint, publicKeyArmor, privateKeyArmor)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.LastInsertId()
|
|
}
|
|
|
|
// ListPGPIdentities returns a mailbox's PGP identities, most recent first.
|
|
func (d *DB) ListPGPIdentities(mailboxID int64) ([]MailboxPGPIdentity, error) {
|
|
rows, err := d.Query(`SELECT id, mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor, created_at
|
|
FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? ORDER BY created_at DESC, id DESC`, mailboxID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []MailboxPGPIdentity
|
|
for rows.Next() {
|
|
var id MailboxPGPIdentity
|
|
if err := rows.Scan(&id.ID, &id.MailboxID, &id.Label, &id.Email, &id.Fingerprint, &id.PublicKeyArmor, &id.PrivateKeyArmor, &id.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, id)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// GetPGPIdentity returns nil, nil if no such identity exists for this mailbox —
|
|
// scoped to mailboxID so one mailbox owner can't reach another's identity by
|
|
// guessing its ID.
|
|
func (d *DB) GetPGPIdentity(mailboxID, identityID int64) (*MailboxPGPIdentity, error) {
|
|
row := d.QueryRow(`SELECT id, mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor, created_at
|
|
FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? AND id = ?`, mailboxID, identityID)
|
|
var id MailboxPGPIdentity
|
|
if err := row.Scan(&id.ID, &id.MailboxID, &id.Label, &id.Email, &id.Fingerprint, &id.PublicKeyArmor, &id.PrivateKeyArmor, &id.CreatedAt); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &id, nil
|
|
}
|
|
|
|
// DeletePGPIdentity removes one identity, scoped to mailboxID.
|
|
func (d *DB) DeletePGPIdentity(mailboxID, identityID int64) error {
|
|
_, err := d.Exec(`DELETE FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? AND id = ?`, mailboxID, identityID)
|
|
return err
|
|
}
|
|
|
|
// UpsertPGPContact adds a contact's PGP public key, replacing any existing key
|
|
// already on file for that email (e.g. after the contact rotates their key).
|
|
func (d *DB) UpsertPGPContact(mailboxID int64, email, label, fingerprint, publicKeyArmor string) error {
|
|
_, err := d.Exec(`
|
|
INSERT INTO esrv_mailbox_pgp_contacts (mailbox_id, email, label, fingerprint, public_key_armor)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
ON CONFLICT(mailbox_id, email) DO UPDATE SET label = excluded.label, fingerprint = excluded.fingerprint, public_key_armor = excluded.public_key_armor
|
|
`, mailboxID, email, label, fingerprint, publicKeyArmor)
|
|
return err
|
|
}
|
|
|
|
// GetPGPContact returns nil, nil if no key is on file for that email.
|
|
func (d *DB) GetPGPContact(mailboxID int64, email string) (*MailboxPGPContact, error) {
|
|
row := d.QueryRow(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND email = ?`, mailboxID, email)
|
|
var c MailboxPGPContact
|
|
if err := row.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
// GetPGPContactByID returns nil, nil if no such contact exists for this mailbox —
|
|
// scoped to mailboxID so one mailbox owner can't reach another's contact by guessing
|
|
// its ID. Used by compose's recipient-key picker, which selects contacts by ID
|
|
// rather than matching a To/Cc/Bcc address against GetPGPContact's stored email.
|
|
func (d *DB) GetPGPContactByID(mailboxID, contactID int64) (*MailboxPGPContact, error) {
|
|
row := d.QueryRow(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND id = ?`, mailboxID, contactID)
|
|
var c MailboxPGPContact
|
|
if err := row.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
// ListPGPContacts returns a mailbox's collected contact keys, alphabetical by email.
|
|
func (d *DB) ListPGPContacts(mailboxID int64) ([]MailboxPGPContact, error) {
|
|
rows, err := d.Query(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? ORDER BY email`, mailboxID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []MailboxPGPContact
|
|
for rows.Next() {
|
|
var c MailboxPGPContact
|
|
if err := rows.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, c)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// DeletePGPContact removes one contact key, scoped to mailboxID so one mailbox
|
|
// owner can't delete another's contact by guessing its ID.
|
|
func (d *DB) DeletePGPContact(mailboxID, contactID int64) error {
|
|
_, err := d.Exec(`DELETE FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND id = ?`, mailboxID, contactID)
|
|
return err
|
|
}
|