github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/openpgp/packet/userid.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package packet 6 7 import ( 8 "io" 9 "io/ioutil" 10 "strings" 11 ) 12 13 // UserId contains text that is intended to represent the name and email 14 // address of the key holder. See RFC 4880, section 5.11. By convention, this 15 // takes the form "Full Name (Comment) <email@example.com>" 16 type UserId struct { 17 Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below. 18 19 Name, Comment, Email string 20 } 21 22 func hasInvalidCharacters(s string) bool { 23 for _, c := range s { 24 switch c { 25 case '(', ')', '<', '>', 0: 26 return true 27 } 28 } 29 return false 30 } 31 32 // NewUserId returns a UserId or nil if any of the arguments contain invalid 33 // characters. The invalid characters are '\x00', '(', ')', '<' and '>' 34 func NewUserId(name, comment, email string) *UserId { 35 // RFC 4880 doesn't deal with the structure of userid strings; the 36 // name, comment and email form is just a convention. However, there's 37 // no convention about escaping the metacharacters and GPG just refuses 38 // to create user ids where, say, the name contains a '('. We mirror 39 // this behaviour. 40 41 if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { 42 return nil 43 } 44 45 uid := new(UserId) 46 uid.Name, uid.Comment, uid.Email = name, comment, email 47 uid.Id = name 48 if len(comment) > 0 { 49 if len(uid.Id) > 0 { 50 uid.Id += " " 51 } 52 uid.Id += "(" 53 uid.Id += comment 54 uid.Id += ")" 55 } 56 if len(email) > 0 { 57 if len(uid.Id) > 0 { 58 uid.Id += " " 59 } 60 uid.Id += "<" 61 uid.Id += email 62 uid.Id += ">" 63 } 64 return uid 65 } 66 67 func (uid *UserId) parse(r io.Reader) (err error) { 68 // RFC 4880, section 5.11 69 b, err := ioutil.ReadAll(r) 70 if err != nil { 71 return 72 } 73 uid.Id = string(b) 74 uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) 75 return 76 } 77 78 // Serialize marshals uid to w in the form of an OpenPGP packet, including 79 // header. 80 func (uid *UserId) Serialize(w io.Writer) error { 81 err := serializeHeader(w, packetTypeUserId, len(uid.Id)) 82 if err != nil { 83 return err 84 } 85 _, err = w.Write([]byte(uid.Id)) 86 return err 87 } 88 89 // parseUserId extracts the name, comment and email from a user id string that 90 // is formatted as "Full Name (Comment) <email@example.com>". 91 func parseUserId(id string) (name, comment, email string) { 92 var n, c, e struct { 93 start, end int 94 } 95 var state int 96 97 for offset, rune := range id { 98 switch state { 99 case 0: 100 // Entering name 101 n.start = offset 102 state = 1 103 fallthrough 104 case 1: 105 // In name 106 if rune == '(' { 107 state = 2 108 n.end = offset 109 } else if rune == '<' { 110 state = 5 111 n.end = offset 112 } 113 case 2: 114 // Entering comment 115 c.start = offset 116 state = 3 117 fallthrough 118 case 3: 119 // In comment 120 if rune == ')' { 121 state = 4 122 c.end = offset 123 } 124 case 4: 125 // Between comment and email 126 if rune == '<' { 127 state = 5 128 } 129 case 5: 130 // Entering email 131 e.start = offset 132 state = 6 133 fallthrough 134 case 6: 135 // In email 136 if rune == '>' { 137 state = 7 138 e.end = offset 139 } 140 default: 141 // After email 142 } 143 } 144 switch state { 145 case 1: 146 // ended in the name 147 n.end = len(id) 148 case 3: 149 // ended in comment 150 c.end = len(id) 151 case 6: 152 // ended in email 153 e.end = len(id) 154 } 155 156 name = strings.TrimSpace(id[n.start:n.end]) 157 comment = strings.TrimSpace(id[c.start:c.end]) 158 email = strings.TrimSpace(id[e.start:e.end]) 159 return 160 }