1*cda5da8dSAndroid Build Coastguard Worker"""An object-oriented interface to .netrc files.""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Worker# Module and documentation by Eric S. Raymond, 21 Dec 1998 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Workerimport os, shlex, stat 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker__all__ = ["netrc", "NetrcParseError"] 8*cda5da8dSAndroid Build Coastguard Worker 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Workerclass NetrcParseError(Exception): 11*cda5da8dSAndroid Build Coastguard Worker """Exception raised on syntax errors in the .netrc file.""" 12*cda5da8dSAndroid Build Coastguard Worker def __init__(self, msg, filename=None, lineno=None): 13*cda5da8dSAndroid Build Coastguard Worker self.filename = filename 14*cda5da8dSAndroid Build Coastguard Worker self.lineno = lineno 15*cda5da8dSAndroid Build Coastguard Worker self.msg = msg 16*cda5da8dSAndroid Build Coastguard Worker Exception.__init__(self, msg) 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard Worker def __str__(self): 19*cda5da8dSAndroid Build Coastguard Worker return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker 22*cda5da8dSAndroid Build Coastguard Workerclass _netrclex: 23*cda5da8dSAndroid Build Coastguard Worker def __init__(self, fp): 24*cda5da8dSAndroid Build Coastguard Worker self.lineno = 1 25*cda5da8dSAndroid Build Coastguard Worker self.instream = fp 26*cda5da8dSAndroid Build Coastguard Worker self.whitespace = "\n\t\r " 27*cda5da8dSAndroid Build Coastguard Worker self.pushback = [] 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard Worker def _read_char(self): 30*cda5da8dSAndroid Build Coastguard Worker ch = self.instream.read(1) 31*cda5da8dSAndroid Build Coastguard Worker if ch == "\n": 32*cda5da8dSAndroid Build Coastguard Worker self.lineno += 1 33*cda5da8dSAndroid Build Coastguard Worker return ch 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard Worker def get_token(self): 36*cda5da8dSAndroid Build Coastguard Worker if self.pushback: 37*cda5da8dSAndroid Build Coastguard Worker return self.pushback.pop(0) 38*cda5da8dSAndroid Build Coastguard Worker token = "" 39*cda5da8dSAndroid Build Coastguard Worker fiter = iter(self._read_char, "") 40*cda5da8dSAndroid Build Coastguard Worker for ch in fiter: 41*cda5da8dSAndroid Build Coastguard Worker if ch in self.whitespace: 42*cda5da8dSAndroid Build Coastguard Worker continue 43*cda5da8dSAndroid Build Coastguard Worker if ch == '"': 44*cda5da8dSAndroid Build Coastguard Worker for ch in fiter: 45*cda5da8dSAndroid Build Coastguard Worker if ch == '"': 46*cda5da8dSAndroid Build Coastguard Worker return token 47*cda5da8dSAndroid Build Coastguard Worker elif ch == "\\": 48*cda5da8dSAndroid Build Coastguard Worker ch = self._read_char() 49*cda5da8dSAndroid Build Coastguard Worker token += ch 50*cda5da8dSAndroid Build Coastguard Worker else: 51*cda5da8dSAndroid Build Coastguard Worker if ch == "\\": 52*cda5da8dSAndroid Build Coastguard Worker ch = self._read_char() 53*cda5da8dSAndroid Build Coastguard Worker token += ch 54*cda5da8dSAndroid Build Coastguard Worker for ch in fiter: 55*cda5da8dSAndroid Build Coastguard Worker if ch in self.whitespace: 56*cda5da8dSAndroid Build Coastguard Worker return token 57*cda5da8dSAndroid Build Coastguard Worker elif ch == "\\": 58*cda5da8dSAndroid Build Coastguard Worker ch = self._read_char() 59*cda5da8dSAndroid Build Coastguard Worker token += ch 60*cda5da8dSAndroid Build Coastguard Worker return token 61*cda5da8dSAndroid Build Coastguard Worker 62*cda5da8dSAndroid Build Coastguard Worker def push_token(self, token): 63*cda5da8dSAndroid Build Coastguard Worker self.pushback.append(token) 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker 66*cda5da8dSAndroid Build Coastguard Workerclass netrc: 67*cda5da8dSAndroid Build Coastguard Worker def __init__(self, file=None): 68*cda5da8dSAndroid Build Coastguard Worker default_netrc = file is None 69*cda5da8dSAndroid Build Coastguard Worker if file is None: 70*cda5da8dSAndroid Build Coastguard Worker file = os.path.join(os.path.expanduser("~"), ".netrc") 71*cda5da8dSAndroid Build Coastguard Worker self.hosts = {} 72*cda5da8dSAndroid Build Coastguard Worker self.macros = {} 73*cda5da8dSAndroid Build Coastguard Worker try: 74*cda5da8dSAndroid Build Coastguard Worker with open(file, encoding="utf-8") as fp: 75*cda5da8dSAndroid Build Coastguard Worker self._parse(file, fp, default_netrc) 76*cda5da8dSAndroid Build Coastguard Worker except UnicodeDecodeError: 77*cda5da8dSAndroid Build Coastguard Worker with open(file, encoding="locale") as fp: 78*cda5da8dSAndroid Build Coastguard Worker self._parse(file, fp, default_netrc) 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker def _parse(self, file, fp, default_netrc): 81*cda5da8dSAndroid Build Coastguard Worker lexer = _netrclex(fp) 82*cda5da8dSAndroid Build Coastguard Worker while 1: 83*cda5da8dSAndroid Build Coastguard Worker # Look for a machine, default, or macdef top-level keyword 84*cda5da8dSAndroid Build Coastguard Worker saved_lineno = lexer.lineno 85*cda5da8dSAndroid Build Coastguard Worker toplevel = tt = lexer.get_token() 86*cda5da8dSAndroid Build Coastguard Worker if not tt: 87*cda5da8dSAndroid Build Coastguard Worker break 88*cda5da8dSAndroid Build Coastguard Worker elif tt[0] == '#': 89*cda5da8dSAndroid Build Coastguard Worker if lexer.lineno == saved_lineno and len(tt) == 1: 90*cda5da8dSAndroid Build Coastguard Worker lexer.instream.readline() 91*cda5da8dSAndroid Build Coastguard Worker continue 92*cda5da8dSAndroid Build Coastguard Worker elif tt == 'machine': 93*cda5da8dSAndroid Build Coastguard Worker entryname = lexer.get_token() 94*cda5da8dSAndroid Build Coastguard Worker elif tt == 'default': 95*cda5da8dSAndroid Build Coastguard Worker entryname = 'default' 96*cda5da8dSAndroid Build Coastguard Worker elif tt == 'macdef': 97*cda5da8dSAndroid Build Coastguard Worker entryname = lexer.get_token() 98*cda5da8dSAndroid Build Coastguard Worker self.macros[entryname] = [] 99*cda5da8dSAndroid Build Coastguard Worker while 1: 100*cda5da8dSAndroid Build Coastguard Worker line = lexer.instream.readline() 101*cda5da8dSAndroid Build Coastguard Worker if not line: 102*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError( 103*cda5da8dSAndroid Build Coastguard Worker "Macro definition missing null line terminator.", 104*cda5da8dSAndroid Build Coastguard Worker file, lexer.lineno) 105*cda5da8dSAndroid Build Coastguard Worker if line == '\n': 106*cda5da8dSAndroid Build Coastguard Worker # a macro definition finished with consecutive new-line 107*cda5da8dSAndroid Build Coastguard Worker # characters. The first \n is encountered by the 108*cda5da8dSAndroid Build Coastguard Worker # readline() method and this is the second \n. 109*cda5da8dSAndroid Build Coastguard Worker break 110*cda5da8dSAndroid Build Coastguard Worker self.macros[entryname].append(line) 111*cda5da8dSAndroid Build Coastguard Worker continue 112*cda5da8dSAndroid Build Coastguard Worker else: 113*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError( 114*cda5da8dSAndroid Build Coastguard Worker "bad toplevel token %r" % tt, file, lexer.lineno) 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Worker if not entryname: 117*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError("missing %r name" % tt, file, lexer.lineno) 118*cda5da8dSAndroid Build Coastguard Worker 119*cda5da8dSAndroid Build Coastguard Worker # We're looking at start of an entry for a named machine or default. 120*cda5da8dSAndroid Build Coastguard Worker login = account = password = '' 121*cda5da8dSAndroid Build Coastguard Worker self.hosts[entryname] = {} 122*cda5da8dSAndroid Build Coastguard Worker while 1: 123*cda5da8dSAndroid Build Coastguard Worker prev_lineno = lexer.lineno 124*cda5da8dSAndroid Build Coastguard Worker tt = lexer.get_token() 125*cda5da8dSAndroid Build Coastguard Worker if tt.startswith('#'): 126*cda5da8dSAndroid Build Coastguard Worker if lexer.lineno == prev_lineno: 127*cda5da8dSAndroid Build Coastguard Worker lexer.instream.readline() 128*cda5da8dSAndroid Build Coastguard Worker continue 129*cda5da8dSAndroid Build Coastguard Worker if tt in {'', 'machine', 'default', 'macdef'}: 130*cda5da8dSAndroid Build Coastguard Worker self.hosts[entryname] = (login, account, password) 131*cda5da8dSAndroid Build Coastguard Worker lexer.push_token(tt) 132*cda5da8dSAndroid Build Coastguard Worker break 133*cda5da8dSAndroid Build Coastguard Worker elif tt == 'login' or tt == 'user': 134*cda5da8dSAndroid Build Coastguard Worker login = lexer.get_token() 135*cda5da8dSAndroid Build Coastguard Worker elif tt == 'account': 136*cda5da8dSAndroid Build Coastguard Worker account = lexer.get_token() 137*cda5da8dSAndroid Build Coastguard Worker elif tt == 'password': 138*cda5da8dSAndroid Build Coastguard Worker password = lexer.get_token() 139*cda5da8dSAndroid Build Coastguard Worker else: 140*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError("bad follower token %r" % tt, 141*cda5da8dSAndroid Build Coastguard Worker file, lexer.lineno) 142*cda5da8dSAndroid Build Coastguard Worker self._security_check(fp, default_netrc, self.hosts[entryname][0]) 143*cda5da8dSAndroid Build Coastguard Worker 144*cda5da8dSAndroid Build Coastguard Worker def _security_check(self, fp, default_netrc, login): 145*cda5da8dSAndroid Build Coastguard Worker if os.name == 'posix' and default_netrc and login != "anonymous": 146*cda5da8dSAndroid Build Coastguard Worker prop = os.fstat(fp.fileno()) 147*cda5da8dSAndroid Build Coastguard Worker if prop.st_uid != os.getuid(): 148*cda5da8dSAndroid Build Coastguard Worker import pwd 149*cda5da8dSAndroid Build Coastguard Worker try: 150*cda5da8dSAndroid Build Coastguard Worker fowner = pwd.getpwuid(prop.st_uid)[0] 151*cda5da8dSAndroid Build Coastguard Worker except KeyError: 152*cda5da8dSAndroid Build Coastguard Worker fowner = 'uid %s' % prop.st_uid 153*cda5da8dSAndroid Build Coastguard Worker try: 154*cda5da8dSAndroid Build Coastguard Worker user = pwd.getpwuid(os.getuid())[0] 155*cda5da8dSAndroid Build Coastguard Worker except KeyError: 156*cda5da8dSAndroid Build Coastguard Worker user = 'uid %s' % os.getuid() 157*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError( 158*cda5da8dSAndroid Build Coastguard Worker (f"~/.netrc file owner ({fowner}, {user}) does not match" 159*cda5da8dSAndroid Build Coastguard Worker " current user")) 160*cda5da8dSAndroid Build Coastguard Worker if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): 161*cda5da8dSAndroid Build Coastguard Worker raise NetrcParseError( 162*cda5da8dSAndroid Build Coastguard Worker "~/.netrc access too permissive: access" 163*cda5da8dSAndroid Build Coastguard Worker " permissions must restrict access to only" 164*cda5da8dSAndroid Build Coastguard Worker " the owner") 165*cda5da8dSAndroid Build Coastguard Worker 166*cda5da8dSAndroid Build Coastguard Worker def authenticators(self, host): 167*cda5da8dSAndroid Build Coastguard Worker """Return a (user, account, password) tuple for given host.""" 168*cda5da8dSAndroid Build Coastguard Worker if host in self.hosts: 169*cda5da8dSAndroid Build Coastguard Worker return self.hosts[host] 170*cda5da8dSAndroid Build Coastguard Worker elif 'default' in self.hosts: 171*cda5da8dSAndroid Build Coastguard Worker return self.hosts['default'] 172*cda5da8dSAndroid Build Coastguard Worker else: 173*cda5da8dSAndroid Build Coastguard Worker return None 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 176*cda5da8dSAndroid Build Coastguard Worker """Dump the class data in the format of a .netrc file.""" 177*cda5da8dSAndroid Build Coastguard Worker rep = "" 178*cda5da8dSAndroid Build Coastguard Worker for host in self.hosts.keys(): 179*cda5da8dSAndroid Build Coastguard Worker attrs = self.hosts[host] 180*cda5da8dSAndroid Build Coastguard Worker rep += f"machine {host}\n\tlogin {attrs[0]}\n" 181*cda5da8dSAndroid Build Coastguard Worker if attrs[1]: 182*cda5da8dSAndroid Build Coastguard Worker rep += f"\taccount {attrs[1]}\n" 183*cda5da8dSAndroid Build Coastguard Worker rep += f"\tpassword {attrs[2]}\n" 184*cda5da8dSAndroid Build Coastguard Worker for macro in self.macros.keys(): 185*cda5da8dSAndroid Build Coastguard Worker rep += f"macdef {macro}\n" 186*cda5da8dSAndroid Build Coastguard Worker for line in self.macros[macro]: 187*cda5da8dSAndroid Build Coastguard Worker rep += line 188*cda5da8dSAndroid Build Coastguard Worker rep += "\n" 189*cda5da8dSAndroid Build Coastguard Worker return rep 190*cda5da8dSAndroid Build Coastguard Worker 191*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 192*cda5da8dSAndroid Build Coastguard Worker print(netrc()) 193