aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/bgentry
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/bgentry')
-rw-r--r--vendor/github.com/bgentry/go-netrc/LICENSE20
-rw-r--r--vendor/github.com/bgentry/go-netrc/netrc/netrc.go510
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 @@
1Original version Copyright © 2010 Fazlul Shahriar <fshahriar@gmail.com>. Newer
2portions Copyright © 2014 Blake Gentry <blakesgentry@gmail.com>.
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE 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 @@
1package netrc
2
3import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "os"
10 "strings"
11 "sync"
12 "unicode"
13 "unicode/utf8"
14)
15
16type tkType int
17
18const (
19 tkMachine tkType = iota
20 tkDefault
21 tkLogin
22 tkPassword
23 tkAccount
24 tkMacdef
25 tkComment
26 tkWhitespace
27)
28
29var 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
39type 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.
50func (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.
69func (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
89func (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
139func (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
162func (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
180func (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.
192type 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.
206func (m *Machine) IsDefault() bool {
207 return m.Name == ""
208}
209
210// UpdatePassword sets the password for the Machine m.
211func (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.
217func (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.
223func (m *Machine) UpdateAccount(newaccount string) {
224 m.Account = newaccount
225 updateTokenValue(m.accounttoken, newaccount)
226}
227
228func 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.
240type Macros map[string]string
241
242type 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.
251type Error struct {
252 LineNum int // Line number
253 Msg string // Error message
254}
255
256// Error returns a string representation of error e.
257func (e *Error) Error() string {
258 return fmt.Sprintf("line %d: %s", e.LineNum, e.Msg)
259}
260
261func (e *Error) BadDefaultOrder() bool {
262 return e.Msg == errBadDefaultOrder
263}
264
265const 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.
273func 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().
303func 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
335func 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
358func 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
370func 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().
480func 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.
495func 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.
504func 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}