OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / golang.org / x / crypto / openpgp / packet / userid.go
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 }