320e9b6057
ci-via-github-actions
160 lines
3.4 KiB
Go
160 lines
3.4 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package packet
|
|
|
|
import (
|
|
"io"
|
|
"io/ioutil"
|
|
"strings"
|
|
)
|
|
|
|
// UserId contains text that is intended to represent the name and email
|
|
// address of the key holder. See RFC 4880, section 5.11. By convention, this
|
|
// takes the form "Full Name (Comment) <email@example.com>"
|
|
type UserId struct {
|
|
Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below.
|
|
|
|
Name, Comment, Email string
|
|
}
|
|
|
|
func hasInvalidCharacters(s string) bool {
|
|
for _, c := range s {
|
|
switch c {
|
|
case '(', ')', '<', '>', 0:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NewUserId returns a UserId or nil if any of the arguments contain invalid
|
|
// characters. The invalid characters are '\x00', '(', ')', '<' and '>'
|
|
func NewUserId(name, comment, email string) *UserId {
|
|
// RFC 4880 doesn't deal with the structure of userid strings; the
|
|
// name, comment and email form is just a convention. However, there's
|
|
// no convention about escaping the metacharacters and GPG just refuses
|
|
// to create user ids where, say, the name contains a '('. We mirror
|
|
// this behaviour.
|
|
|
|
if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) {
|
|
return nil
|
|
}
|
|
|
|
uid := new(UserId)
|
|
uid.Name, uid.Comment, uid.Email = name, comment, email
|
|
uid.Id = name
|
|
if len(comment) > 0 {
|
|
if len(uid.Id) > 0 {
|
|
uid.Id += " "
|
|
}
|
|
uid.Id += "("
|
|
uid.Id += comment
|
|
uid.Id += ")"
|
|
}
|
|
if len(email) > 0 {
|
|
if len(uid.Id) > 0 {
|
|
uid.Id += " "
|
|
}
|
|
uid.Id += "<"
|
|
uid.Id += email
|
|
uid.Id += ">"
|
|
}
|
|
return uid
|
|
}
|
|
|
|
func (uid *UserId) parse(r io.Reader) (err error) {
|
|
// RFC 4880, section 5.11
|
|
b, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
uid.Id = string(b)
|
|
uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id)
|
|
return
|
|
}
|
|
|
|
// Serialize marshals uid to w in the form of an OpenPGP packet, including
|
|
// header.
|
|
func (uid *UserId) Serialize(w io.Writer) error {
|
|
err := serializeHeader(w, packetTypeUserId, len(uid.Id))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write([]byte(uid.Id))
|
|
return err
|
|
}
|
|
|
|
// parseUserId extracts the name, comment and email from a user id string that
|
|
// is formatted as "Full Name (Comment) <email@example.com>".
|
|
func parseUserId(id string) (name, comment, email string) {
|
|
var n, c, e struct {
|
|
start, end int
|
|
}
|
|
var state int
|
|
|
|
for offset, rune := range id {
|
|
switch state {
|
|
case 0:
|
|
// Entering name
|
|
n.start = offset
|
|
state = 1
|
|
fallthrough
|
|
case 1:
|
|
// In name
|
|
if rune == '(' {
|
|
state = 2
|
|
n.end = offset
|
|
} else if rune == '<' {
|
|
state = 5
|
|
n.end = offset
|
|
}
|
|
case 2:
|
|
// Entering comment
|
|
c.start = offset
|
|
state = 3
|
|
fallthrough
|
|
case 3:
|
|
// In comment
|
|
if rune == ')' {
|
|
state = 4
|
|
c.end = offset
|
|
}
|
|
case 4:
|
|
// Between comment and email
|
|
if rune == '<' {
|
|
state = 5
|
|
}
|
|
case 5:
|
|
// Entering email
|
|
e.start = offset
|
|
state = 6
|
|
fallthrough
|
|
case 6:
|
|
// In email
|
|
if rune == '>' {
|
|
state = 7
|
|
e.end = offset
|
|
}
|
|
default:
|
|
// After email
|
|
}
|
|
}
|
|
switch state {
|
|
case 1:
|
|
// ended in the name
|
|
n.end = len(id)
|
|
case 3:
|
|
// ended in comment
|
|
c.end = len(id)
|
|
case 6:
|
|
// ended in email
|
|
e.end = len(id)
|
|
}
|
|
|
|
name = strings.TrimSpace(id[n.start:n.end])
|
|
comment = strings.TrimSpace(id[c.start:c.end])
|
|
email = strings.TrimSpace(id[e.start:e.end])
|
|
return
|
|
}
|