Convert from BoltDB to Maildir storage for emails
This commit is contained in:
131
internal/imap/uidlist/header.go
Normal file
131
internal/imap/uidlist/header.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package uidlist
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderTemplate = "%d V%d N%d G%s"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
Version uint64
|
||||
UIDValidity uint64
|
||||
NextUID uint64
|
||||
GUID string
|
||||
Extra map[string][]string
|
||||
}
|
||||
|
||||
func setUint64(s string, out *uint64) error {
|
||||
num, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*out = uint64(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateHeader produces a valid verison 3 dovecot-uidlist header line, using
|
||||
// the time for UIDValidity and a random string for the GUID.
|
||||
func GenerateHeader() (Header, error) {
|
||||
// A GUID is a random 128-bit number
|
||||
guidBytes := make([]byte, 16)
|
||||
_, err := rand.Read(guidBytes)
|
||||
if err != nil {
|
||||
return Header{}, err
|
||||
}
|
||||
|
||||
// ...hex-encoded
|
||||
guidString := hex.EncodeToString(guidBytes)
|
||||
|
||||
return Header{
|
||||
Version: uint64(3),
|
||||
UIDValidity: uint64(time.Now().Unix()),
|
||||
NextUID: uint64(1),
|
||||
GUID: guidString,
|
||||
Extra: make(map[string][]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseHeader breaks a dovecot-uidlist header line into its constituent parts
|
||||
func ParseHeader(line string) (Header, error) {
|
||||
var seenUIDValidity bool
|
||||
var seenNextUID bool
|
||||
var seenGUID bool
|
||||
|
||||
out := Header{}
|
||||
parts := strings.Split(strings.TrimSpace(line), " ")
|
||||
if len(parts) < 4 {
|
||||
return out, errors.New("Short header")
|
||||
}
|
||||
|
||||
if err := setUint64(parts[0], &out.Version); err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
out.Extra = make(map[string][]string)
|
||||
for _, part := range parts[1:] {
|
||||
part = strings.TrimSpace(part)
|
||||
|
||||
if len(part) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := part[0:1]
|
||||
value := part[1:]
|
||||
switch key {
|
||||
case "V":
|
||||
if seenUIDValidity {
|
||||
return out, errors.New("UIDVALIDITY specified twice")
|
||||
}
|
||||
seenUIDValidity = true
|
||||
|
||||
if err := setUint64(value, &out.UIDValidity); err != nil {
|
||||
return out, err
|
||||
}
|
||||
case "N":
|
||||
if seenNextUID {
|
||||
return out, errors.New("NEXTUID specified twice")
|
||||
}
|
||||
seenNextUID = true
|
||||
|
||||
if err := setUint64(value, &out.NextUID); err != nil {
|
||||
return out, err
|
||||
}
|
||||
case "G":
|
||||
if seenGUID {
|
||||
return out, errors.New("GUID specified twice")
|
||||
}
|
||||
seenGUID = true
|
||||
|
||||
out.GUID = value
|
||||
default:
|
||||
out.Extra[key] = append(out.Extra[key], value)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h Header) String() string {
|
||||
var sb strings.Builder
|
||||
|
||||
fmt.Fprintf(
|
||||
&sb,
|
||||
HeaderTemplate,
|
||||
h.Version, h.UIDValidity, h.NextUID, h.GUID,
|
||||
)
|
||||
|
||||
for k, v := range h.Extra {
|
||||
fmt.Fprintf(&sb, " %s%s", k, v)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
Reference in New Issue
Block a user