diff options
Diffstat (limited to 'vendor/github.com/bgentry')
-rw-r--r-- | vendor/github.com/bgentry/go-netrc/LICENSE | 20 | ||||
-rw-r--r-- | vendor/github.com/bgentry/go-netrc/netrc/netrc.go | 510 |
2 files changed, 530 insertions, 0 deletions
diff --git a/vendor/github.com/bgentry/go-netrc/LICENSE b/vendor/github.com/bgentry/go-netrc/LICENSE new file mode 100644 index 0000000..aade9a5 --- /dev/null +++ b/vendor/github.com/bgentry/go-netrc/LICENSE | |||
@@ -0,0 +1,20 @@ | |||
1 | Original version Copyright © 2010 Fazlul Shahriar <fshahriar@gmail.com>. Newer | ||
2 | portions Copyright © 2014 Blake Gentry <blakesgentry@gmail.com>. | ||
3 | |||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | of this software and associated documentation files (the "Software"), to deal | ||
6 | in the Software without restriction, including without limitation the rights | ||
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
8 | copies of the Software, and to permit persons to whom the Software is | ||
9 | furnished to do so, subject to the following conditions: | ||
10 | |||
11 | The above copyright notice and this permission notice shall be included in | ||
12 | all copies or substantial portions of the Software. | ||
13 | |||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
20 | THE SOFTWARE. | ||
diff --git a/vendor/github.com/bgentry/go-netrc/netrc/netrc.go b/vendor/github.com/bgentry/go-netrc/netrc/netrc.go new file mode 100644 index 0000000..ea49987 --- /dev/null +++ b/vendor/github.com/bgentry/go-netrc/netrc/netrc.go | |||
@@ -0,0 +1,510 @@ | |||
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 | } | ||