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