mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-05 01:40:04 +00:00
161 lines
3.4 KiB
Go
161 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
|
||
|
}
|