Imported Upstream version 2.4.1
[scm/test.git] / vendor / github.com / bgentry / go-netrc / netrc / netrc.go
1 package netrc
2
3 import (
4         "bufio"
5         "bytes"
6         "fmt"
7         "io"
8         "io/ioutil"
9         "os"
10         "strings"
11         "sync"
12         "unicode"
13         "unicode/utf8"
14 )
15
16 type tkType int
17
18 const (
19         tkMachine tkType = iota
20         tkDefault
21         tkLogin
22         tkPassword
23         tkAccount
24         tkMacdef
25         tkComment
26         tkWhitespace
27 )
28
29 var keywords = map[string]tkType{
30         "machine":  tkMachine,
31         "default":  tkDefault,
32         "login":    tkLogin,
33         "password": tkPassword,
34         "account":  tkAccount,
35         "macdef":   tkMacdef,
36         "#":        tkComment,
37 }
38
39 type Netrc struct {
40         tokens     []*token
41         machines   []*Machine
42         macros     Macros
43         updateLock sync.Mutex
44 }
45
46 // FindMachine returns the Machine in n named by name. If a machine named by
47 // name exists, it is returned. If no Machine with name name is found and there
48 // is a ``default'' machine, the ``default'' machine is returned. Otherwise, nil
49 // is returned.
50 func (n *Netrc) FindMachine(name string) (m *Machine) {
51         // TODO(bgentry): not safe for concurrency
52         var def *Machine
53         for _, m = range n.machines {
54                 if m.Name == name {
55                         return m
56                 }
57                 if m.IsDefault() {
58                         def = m
59                 }
60         }
61         if def == nil {
62                 return nil
63         }
64         return def
65 }
66
67 // MarshalText implements the encoding.TextMarshaler interface to encode a
68 // Netrc into text format.
69 func (n *Netrc) MarshalText() (text []byte, err error) {
70         // TODO(bgentry): not safe for concurrency
71         for i := range n.tokens {
72                 switch n.tokens[i].kind {
73                 case tkComment, tkDefault, tkWhitespace: // always append these types
74                         text = append(text, n.tokens[i].rawkind...)
75                 default:
76                         if n.tokens[i].value != "" { // skip empty-value tokens
77                                 text = append(text, n.tokens[i].rawkind...)
78                         }
79                 }
80                 if n.tokens[i].kind == tkMacdef {
81                         text = append(text, ' ')
82                         text = append(text, n.tokens[i].macroName...)
83                 }
84                 text = append(text, n.tokens[i].rawvalue...)
85         }
86         return
87 }
88
89 func (n *Netrc) NewMachine(name, login, password, account string) *Machine {
90         n.updateLock.Lock()
91         defer n.updateLock.Unlock()
92
93         prefix := "\n"
94         if len(n.tokens) == 0 {
95                 prefix = ""
96         }
97         m := &Machine{
98                 Name:     name,
99                 Login:    login,
100                 Password: password,
101                 Account:  account,
102
103                 nametoken: &token{
104                         kind:     tkMachine,
105                         rawkind:  []byte(prefix + "machine"),
106                         value:    name,
107                         rawvalue: []byte(" " + name),
108                 },
109                 logintoken: &token{
110                         kind:     tkLogin,
111                         rawkind:  []byte("\n\tlogin"),
112                         value:    login,
113                         rawvalue: []byte(" " + login),
114                 },
115                 passtoken: &token{
116                         kind:     tkPassword,
117                         rawkind:  []byte("\n\tpassword"),
118                         value:    password,
119                         rawvalue: []byte(" " + password),
120                 },
121                 accounttoken: &token{
122                         kind:     tkAccount,
123                         rawkind:  []byte("\n\taccount"),
124                         value:    account,
125                         rawvalue: []byte(" " + account),
126                 },
127         }
128         n.insertMachineTokensBeforeDefault(m)
129         for i := range n.machines {
130                 if n.machines[i].IsDefault() {
131                         n.machines = append(append(n.machines[:i], m), n.machines[i:]...)
132                         return m
133                 }
134         }
135         n.machines = append(n.machines, m)
136         return m
137 }
138
139 func (n *Netrc) insertMachineTokensBeforeDefault(m *Machine) {
140         newtokens := []*token{m.nametoken}
141         if m.logintoken.value != "" {
142                 newtokens = append(newtokens, m.logintoken)
143         }
144         if m.passtoken.value != "" {
145                 newtokens = append(newtokens, m.passtoken)
146         }
147         if m.accounttoken.value != "" {
148                 newtokens = append(newtokens, m.accounttoken)
149         }
150         for i := range n.tokens {
151                 if n.tokens[i].kind == tkDefault {
152                         // found the default, now insert tokens before it
153                         n.tokens = append(n.tokens[:i], append(newtokens, n.tokens[i:]...)...)
154                         return
155                 }
156         }
157         // didn't find a default, just add the newtokens to the end
158         n.tokens = append(n.tokens, newtokens...)
159         return
160 }
161
162 func (n *Netrc) RemoveMachine(name string) {
163         n.updateLock.Lock()
164         defer n.updateLock.Unlock()
165
166         for i := range n.machines {
167                 if n.machines[i] != nil && n.machines[i].Name == name {
168                         m := n.machines[i]
169                         for _, t := range []*token{
170                                 m.nametoken, m.logintoken, m.passtoken, m.accounttoken,
171                         } {
172                                 n.removeToken(t)
173                         }
174                         n.machines = append(n.machines[:i], n.machines[i+1:]...)
175                         return
176                 }
177         }
178 }
179
180 func (n *Netrc) removeToken(t *token) {
181         if t != nil {
182                 for i := range n.tokens {
183                         if n.tokens[i] == t {
184                                 n.tokens = append(n.tokens[:i], n.tokens[i+1:]...)
185                                 return
186                         }
187                 }
188         }
189 }
190
191 // Machine contains information about a remote machine.
192 type Machine struct {
193         Name     string
194         Login    string
195         Password string
196         Account  string
197
198         nametoken    *token
199         logintoken   *token
200         passtoken    *token
201         accounttoken *token
202 }
203
204 // IsDefault returns true if the machine is a "default" token, denoted by an
205 // empty name.
206 func (m *Machine) IsDefault() bool {
207         return m.Name == ""
208 }
209
210 // UpdatePassword sets the password for the Machine m.
211 func (m *Machine) UpdatePassword(newpass string) {
212         m.Password = newpass
213         updateTokenValue(m.passtoken, newpass)
214 }
215
216 // UpdateLogin sets the login for the Machine m.
217 func (m *Machine) UpdateLogin(newlogin string) {
218         m.Login = newlogin
219         updateTokenValue(m.logintoken, newlogin)
220 }
221
222 // UpdateAccount sets the login for the Machine m.
223 func (m *Machine) UpdateAccount(newaccount string) {
224         m.Account = newaccount
225         updateTokenValue(m.accounttoken, newaccount)
226 }
227
228 func updateTokenValue(t *token, value string) {
229         oldvalue := t.value
230         t.value = value
231         newraw := make([]byte, len(t.rawvalue))
232         copy(newraw, t.rawvalue)
233         t.rawvalue = append(
234                 bytes.TrimSuffix(newraw, []byte(oldvalue)),
235                 []byte(value)...,
236         )
237 }
238
239 // Macros contains all the macro definitions in a netrc file.
240 type Macros map[string]string
241
242 type token struct {
243         kind      tkType
244         macroName string
245         value     string
246         rawkind   []byte
247         rawvalue  []byte
248 }
249
250 // Error represents a netrc file parse error.
251 type Error struct {
252         LineNum int    // Line number
253         Msg     string // Error message
254 }
255
256 // Error returns a string representation of error e.
257 func (e *Error) Error() string {
258         return fmt.Sprintf("line %d: %s", e.LineNum, e.Msg)
259 }
260
261 func (e *Error) BadDefaultOrder() bool {
262         return e.Msg == errBadDefaultOrder
263 }
264
265 const errBadDefaultOrder = "default token must appear after all machine tokens"
266
267 // scanLinesKeepPrefix is a split function for a Scanner that returns each line
268 // of text. The returned token may include newlines if they are before the
269 // first non-space character. The returned line may be empty. The end-of-line
270 // marker is one optional carriage return followed by one mandatory newline. In
271 // regular expression notation, it is `\r?\n`. The last non-empty line of
272 // input will be returned even if it has no newline.
273 func scanLinesKeepPrefix(data []byte, atEOF bool) (advance int, token []byte, err error) {
274         if atEOF && len(data) == 0 {
275                 return 0, nil, nil
276         }
277         // Skip leading spaces.
278         start := 0
279         for width := 0; start < len(data); start += width {
280                 var r rune
281                 r, width = utf8.DecodeRune(data[start:])
282                 if !unicode.IsSpace(r) {
283                         break
284                 }
285         }
286         if i := bytes.IndexByte(data[start:], '\n'); i >= 0 {
287                 // We have a full newline-terminated line.
288                 return start + i, data[0 : start+i], nil
289         }
290         // If we're at EOF, we have a final, non-terminated line. Return it.
291         if atEOF {
292                 return len(data), data, nil
293         }
294         // Request more data.
295         return 0, nil, nil
296 }
297
298 // scanWordsKeepPrefix is a split function for a Scanner that returns each
299 // space-separated word of text, with prefixing spaces included. It will never
300 // return an empty string. The definition of space is set by unicode.IsSpace.
301 //
302 // Adapted from bufio.ScanWords().
303 func scanTokensKeepPrefix(data []byte, atEOF bool) (advance int, token []byte, err error) {
304         // Skip leading spaces.
305         start := 0
306         for width := 0; start < len(data); start += width {
307                 var r rune
308                 r, width = utf8.DecodeRune(data[start:])
309                 if !unicode.IsSpace(r) {
310                         break
311                 }
312         }
313         if atEOF && len(data) == 0 || start == len(data) {
314                 return len(data), data, nil
315         }
316         if len(data) > start && data[start] == '#' {
317                 return scanLinesKeepPrefix(data, atEOF)
318         }
319         // Scan until space, marking end of word.
320         for width, i := 0, start; i < len(data); i += width {
321                 var r rune
322                 r, width = utf8.DecodeRune(data[i:])
323                 if unicode.IsSpace(r) {
324                         return i, data[:i], nil
325                 }
326         }
327         // If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
328         if atEOF && len(data) > start {
329                 return len(data), data, nil
330         }
331         // Request more data.
332         return 0, nil, nil
333 }
334
335 func newToken(rawb []byte) (*token, error) {
336         _, tkind, err := bufio.ScanWords(rawb, true)
337         if err != nil {
338                 return nil, err
339         }
340         var ok bool
341         t := token{rawkind: rawb}
342         t.kind, ok = keywords[string(tkind)]
343         if !ok {
344                 trimmed := strings.TrimSpace(string(tkind))
345                 if trimmed == "" {
346                         t.kind = tkWhitespace // whitespace-only, should happen only at EOF
347                         return &t, nil
348                 }
349                 if strings.HasPrefix(trimmed, "#") {
350                         t.kind = tkComment // this is a comment
351                         return &t, nil
352                 }
353                 return &t, fmt.Errorf("keyword expected; got " + string(tkind))
354         }
355         return &t, nil
356 }
357
358 func scanValue(scanner *bufio.Scanner, pos int) ([]byte, string, int, error) {
359         if scanner.Scan() {
360                 raw := scanner.Bytes()
361                 pos += bytes.Count(raw, []byte{'\n'})
362                 return raw, strings.TrimSpace(string(raw)), pos, nil
363         }
364         if err := scanner.Err(); err != nil {
365                 return nil, "", pos, &Error{pos, err.Error()}
366         }
367         return nil, "", pos, nil
368 }
369
370 func parse(r io.Reader, pos int) (*Netrc, error) {
371         b, err := ioutil.ReadAll(r)
372         if err != nil {
373                 return nil, err
374         }
375
376         nrc := Netrc{machines: make([]*Machine, 0, 20), macros: make(Macros, 10)}
377
378         defaultSeen := false
379         var currentMacro *token
380         var m *Machine
381         var t *token
382         scanner := bufio.NewScanner(bytes.NewReader(b))
383         scanner.Split(scanTokensKeepPrefix)
384
385         for scanner.Scan() {
386                 rawb := scanner.Bytes()
387                 if len(rawb) == 0 {
388                         break
389                 }
390                 pos += bytes.Count(rawb, []byte{'\n'})
391                 t, err = newToken(rawb)
392                 if err != nil {
393                         if currentMacro == nil {
394                                 return nil, &Error{pos, err.Error()}
395                         }
396                         currentMacro.rawvalue = append(currentMacro.rawvalue, rawb...)
397                         continue
398                 }
399
400                 if currentMacro != nil && bytes.Contains(rawb, []byte{'\n', '\n'}) {
401                         // if macro rawvalue + rawb would contain \n\n, then macro def is over
402                         currentMacro.value = strings.TrimLeft(string(currentMacro.rawvalue), "\r\n")
403                         nrc.macros[currentMacro.macroName] = currentMacro.value
404                         currentMacro = nil
405                 }
406
407                 switch t.kind {
408                 case tkMacdef:
409                         if _, t.macroName, pos, err = scanValue(scanner, pos); err != nil {
410                                 return nil, &Error{pos, err.Error()}
411                         }
412                         currentMacro = t
413                 case tkDefault:
414                         if defaultSeen {
415                                 return nil, &Error{pos, "multiple default token"}
416                         }
417                         if m != nil {
418                                 nrc.machines, m = append(nrc.machines, m), nil
419                         }
420                         m = new(Machine)
421                         m.Name = ""
422                         defaultSeen = true
423                 case tkMachine:
424                         if defaultSeen {
425                                 return nil, &Error{pos, errBadDefaultOrder}
426                         }
427                         if m != nil {
428                                 nrc.machines, m = append(nrc.machines, m), nil
429                         }
430                         m = new(Machine)
431                         if t.rawvalue, m.Name, pos, err = scanValue(scanner, pos); err != nil {
432                                 return nil, &Error{pos, err.Error()}
433                         }
434                         t.value = m.Name
435                         m.nametoken = t
436                 case tkLogin:
437                         if m == nil || m.Login != "" {
438                                 return nil, &Error{pos, "unexpected token login "}
439                         }
440                         if t.rawvalue, m.Login, pos, err = scanValue(scanner, pos); err != nil {
441                                 return nil, &Error{pos, err.Error()}
442                         }
443                         t.value = m.Login
444                         m.logintoken = t
445                 case tkPassword:
446                         if m == nil || m.Password != "" {
447                                 return nil, &Error{pos, "unexpected token password"}
448                         }
449                         if t.rawvalue, m.Password, pos, err = scanValue(scanner, pos); err != nil {
450                                 return nil, &Error{pos, err.Error()}
451                         }
452                         t.value = m.Password
453                         m.passtoken = t
454                 case tkAccount:
455                         if m == nil || m.Account != "" {
456                                 return nil, &Error{pos, "unexpected token account"}
457                         }
458                         if t.rawvalue, m.Account, pos, err = scanValue(scanner, pos); err != nil {
459                                 return nil, &Error{pos, err.Error()}
460                         }
461                         t.value = m.Account
462                         m.accounttoken = t
463                 }
464
465                 nrc.tokens = append(nrc.tokens, t)
466         }
467
468         if err := scanner.Err(); err != nil {
469                 return nil, err
470         }
471
472         if m != nil {
473                 nrc.machines, m = append(nrc.machines, m), nil
474         }
475         return &nrc, nil
476 }
477
478 // ParseFile opens the file at filename and then passes its io.Reader to
479 // Parse().
480 func ParseFile(filename string) (*Netrc, error) {
481         fd, err := os.Open(filename)
482         if err != nil {
483                 return nil, err
484         }
485         defer fd.Close()
486         return Parse(fd)
487 }
488
489 // Parse parses from the the Reader r as a netrc file and returns the set of
490 // machine information and macros defined in it. The ``default'' machine,
491 // which is intended to be used when no machine name matches, is identified
492 // by an empty machine name. There can be only one ``default'' machine.
493 //
494 // If there is a parsing error, an Error is returned.
495 func Parse(r io.Reader) (*Netrc, error) {
496         return parse(r, 1)
497 }
498
499 // FindMachine parses the netrc file identified by filename and returns the
500 // Machine named by name. If a problem occurs parsing the file at filename, an
501 // error is returned. If a machine named by name exists, it is returned. If no
502 // Machine with name name is found and there is a ``default'' machine, the
503 // ``default'' machine is returned. Otherwise, nil is returned.
504 func FindMachine(filename, name string) (m *Machine, err error) {
505         n, err := ParseFile(filename)
506         if err != nil {
507                 return nil, err
508         }
509         return n.FindMachine(name), nil
510 }