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 self.hosts = {} 30 self.macros = {} 31 with open(file) as fp: 32 self._parse(file, fp) 33 34 def _parse(self, file, fp): 35 lexer = shlex.shlex(fp) 36 lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" 37 lexer.commenters = lexer.commenters.replace('#', '') 38 while 1: 39 # Look for a machine, default, or macdef top-level keyword 40 toplevel = tt = lexer.get_token() 41 if not tt: 42 break 43 elif tt[0] == '#': 44 # seek to beginning of comment, in case reading the token put 45 # us on a new line, and then skip the rest of the line. 46 pos = len(tt) + 1 47 lexer.instream.seek(-pos, 1) 48 lexer.instream.readline() 49 continue 50 elif tt == 'machine': 51 entryname = lexer.get_token() 52 elif tt == 'default': 53 entryname = 'default' 54 elif tt == 'macdef': # Just skip to end of macdefs 55 entryname = lexer.get_token() 56 self.macros[entryname] = [] 57 lexer.whitespace = ' \t' 58 while 1: 59 line = lexer.instream.readline() 60 if not line or line == '\012': 61 lexer.whitespace = ' \t\r\n' 62 break 63 self.macros[entryname].append(line) 64 continue 65 else: 66 raise NetrcParseError( 67 "bad toplevel token %r" % tt, file, lexer.lineno) 68 69 # We're looking at start of an entry for a named machine or default. 70 login = '' 71 account = password = None 72 self.hosts[entryname] = {} 73 while 1: 74 tt = lexer.get_token() 75 if (tt.startswith('#') or 76 tt in {'', 'machine', 'default', 'macdef'}): 77 if password: 78 self.hosts[entryname] = (login, account, password) 79 lexer.push_token(tt) 80 break 81 else: 82 raise NetrcParseError( 83 "malformed %s entry %s terminated by %s" 84 % (toplevel, entryname, repr(tt)), 85 file, lexer.lineno) 86 elif tt == 'login' or tt == 'user': 87 login = lexer.get_token() 88 elif tt == 'account': 89 account = lexer.get_token() 90 elif tt == 'password': 91 password = lexer.get_token() 92 else: 93 raise NetrcParseError("bad follower token %r" % tt, 94 file, lexer.lineno) 95 96 def authenticators(self, host): 97 """Return a (user, account, password) tuple for given host.""" 98 if host in self.hosts: 99 return self.hosts[host] 100 elif 'default' in self.hosts: 101 return self.hosts['default'] 102 else: 103 return None 104 105 def __repr__(self): 106 """Dump the class data in the format of a .netrc file.""" 107 rep = "" 108 for host in self.hosts.keys(): 109 attrs = self.hosts[host] 110 rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n" 111 if attrs[1]: 112 rep = rep + "account " + repr(attrs[1]) 113 rep = rep + "\tpassword " + repr(attrs[2]) + "\n" 114 for macro in self.macros.keys(): 115 rep = rep + "macdef " + macro + "\n" 116 for line in self.macros[macro]: 117 rep = rep + line 118 rep = rep + "\n" 119 return rep 120 121 if __name__ == '__main__': 122 print netrc() 123