Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | """An object-oriented interface to .netrc files.""" |
2 | ||
3 | # Module and documentation by Eric S. Raymond, 21 Dec 1998 | |
4 | ||
5 | import os, shlex | |
6 | ||
7 | __all__ = ["netrc", "NetrcParseError"] | |
8 | ||
9 | ||
10 | class NetrcParseError(Exception): | |
11 | """Exception raised on syntax errors in the .netrc file.""" | |
12 | def __init__(self, msg, filename=None, lineno=None): | |
13 | self.filename = filename | |
14 | self.lineno = lineno | |
15 | self.msg = msg | |
16 | Exception.__init__(self, msg) | |
17 | ||
18 | def __str__(self): | |
19 | return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) | |
20 | ||
21 | ||
22 | class netrc: | |
23 | def __init__(self, file=None): | |
24 | if file is None: | |
25 | try: | |
26 | file = os.path.join(os.environ['HOME'], ".netrc") | |
27 | except KeyError: | |
28 | raise IOError("Could not find .netrc: $HOME is not set") | |
29 | fp = open(file) | |
30 | self.hosts = {} | |
31 | self.macros = {} | |
32 | lexer = shlex.shlex(fp) | |
33 | lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" | |
34 | while 1: | |
35 | # Look for a machine, default, or macdef top-level keyword | |
36 | toplevel = tt = lexer.get_token() | |
37 | if not tt: | |
38 | break | |
39 | elif tt == 'machine': | |
40 | entryname = lexer.get_token() | |
41 | elif tt == 'default': | |
42 | entryname = 'default' | |
43 | elif tt == 'macdef': # Just skip to end of macdefs | |
44 | entryname = lexer.get_token() | |
45 | self.macros[entryname] = [] | |
46 | lexer.whitespace = ' \t' | |
47 | while 1: | |
48 | line = lexer.instream.readline() | |
49 | if not line or line == '\012': | |
50 | lexer.whitespace = ' \t\r\n' | |
51 | break | |
52 | self.macros[entryname].append(line) | |
53 | continue | |
54 | else: | |
55 | raise NetrcParseError( | |
56 | "bad toplevel token %r" % tt, file, lexer.lineno) | |
57 | ||
58 | # We're looking at start of an entry for a named machine or default. | |
59 | login = '' | |
60 | account = password = None | |
61 | self.hosts[entryname] = {} | |
62 | while 1: | |
63 | tt = lexer.get_token() | |
64 | if (tt=='' or tt == 'machine' or | |
65 | tt == 'default' or tt =='macdef'): | |
66 | if password: | |
67 | self.hosts[entryname] = (login, account, password) | |
68 | lexer.push_token(tt) | |
69 | break | |
70 | else: | |
71 | raise NetrcParseError( | |
72 | "malformed %s entry %s terminated by %s" | |
73 | % (toplevel, entryname, repr(tt)), | |
74 | file, lexer.lineno) | |
75 | elif tt == 'login' or tt == 'user': | |
76 | login = lexer.get_token() | |
77 | elif tt == 'account': | |
78 | account = lexer.get_token() | |
79 | elif tt == 'password': | |
80 | password = lexer.get_token() | |
81 | else: | |
82 | raise NetrcParseError("bad follower token %r" % tt, | |
83 | file, lexer.lineno) | |
84 | ||
85 | def authenticators(self, host): | |
86 | """Return a (user, account, password) tuple for given host.""" | |
87 | if host in self.hosts: | |
88 | return self.hosts[host] | |
89 | elif 'default' in self.hosts: | |
90 | return self.hosts['default'] | |
91 | else: | |
92 | return None | |
93 | ||
94 | def __repr__(self): | |
95 | """Dump the class data in the format of a .netrc file.""" | |
96 | rep = "" | |
97 | for host in self.hosts.keys(): | |
98 | attrs = self.hosts[host] | |
99 | rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n" | |
100 | if attrs[1]: | |
101 | rep = rep + "account " + repr(attrs[1]) | |
102 | rep = rep + "\tpassword " + repr(attrs[2]) + "\n" | |
103 | for macro in self.macros.keys(): | |
104 | rep = rep + "macdef " + macro + "\n" | |
105 | for line in self.macros[macro]: | |
106 | rep = rep + line | |
107 | rep = rep + "\n" | |
108 | return rep | |
109 | ||
110 | if __name__ == '__main__': | |
111 | print netrc() |