netrc.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """An object-oriented interface to .netrc files."""
  2. # Module and documentation by Eric S. Raymond, 21 Dec 1998
  3. import os, stat, shlex
  4. if os.name == 'posix':
  5. import pwd
  6. __all__ = ["netrc", "NetrcParseError"]
  7. class NetrcParseError(Exception):
  8. """Exception raised on syntax errors in the .netrc file."""
  9. def __init__(self, msg, filename=None, lineno=None):
  10. self.filename = filename
  11. self.lineno = lineno
  12. self.msg = msg
  13. Exception.__init__(self, msg)
  14. def __str__(self):
  15. return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno)
  16. class netrc:
  17. def __init__(self, file=None):
  18. default_netrc = file is None
  19. if file is None:
  20. try:
  21. file = os.path.join(os.environ['HOME'], ".netrc")
  22. except KeyError:
  23. raise IOError("Could not find .netrc: $HOME is not set")
  24. self.hosts = {}
  25. self.macros = {}
  26. with open(file) as fp:
  27. self._parse(file, fp, default_netrc)
  28. def _parse(self, file, fp, default_netrc):
  29. lexer = shlex.shlex(fp)
  30. lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
  31. lexer.commenters = lexer.commenters.replace('#', '')
  32. while 1:
  33. # Look for a machine, default, or macdef top-level keyword
  34. toplevel = tt = lexer.get_token()
  35. if not tt:
  36. break
  37. elif tt[0] == '#':
  38. # seek to beginning of comment, in case reading the token put
  39. # us on a new line, and then skip the rest of the line.
  40. pos = len(tt) + 1
  41. lexer.instream.seek(-pos, 1)
  42. lexer.instream.readline()
  43. continue
  44. elif tt == 'machine':
  45. entryname = lexer.get_token()
  46. elif tt == 'default':
  47. entryname = 'default'
  48. elif tt == 'macdef': # Just skip to end of macdefs
  49. entryname = lexer.get_token()
  50. self.macros[entryname] = []
  51. lexer.whitespace = ' \t'
  52. while 1:
  53. line = lexer.instream.readline()
  54. if not line or line == '\012':
  55. lexer.whitespace = ' \t\r\n'
  56. break
  57. self.macros[entryname].append(line)
  58. continue
  59. else:
  60. raise NetrcParseError(
  61. "bad toplevel token %r" % tt, file, lexer.lineno)
  62. # We're looking at start of an entry for a named machine or default.
  63. login = ''
  64. account = password = None
  65. self.hosts[entryname] = {}
  66. while 1:
  67. tt = lexer.get_token()
  68. if (tt.startswith('#') or
  69. tt in {'', 'machine', 'default', 'macdef'}):
  70. if password:
  71. self.hosts[entryname] = (login, account, password)
  72. lexer.push_token(tt)
  73. break
  74. else:
  75. raise NetrcParseError(
  76. "malformed %s entry %s terminated by %s"
  77. % (toplevel, entryname, repr(tt)),
  78. file, lexer.lineno)
  79. elif tt == 'login' or tt == 'user':
  80. login = lexer.get_token()
  81. elif tt == 'account':
  82. account = lexer.get_token()
  83. elif tt == 'password':
  84. if os.name == 'posix' and default_netrc:
  85. prop = os.fstat(fp.fileno())
  86. if prop.st_uid != os.getuid():
  87. try:
  88. fowner = pwd.getpwuid(prop.st_uid)[0]
  89. except KeyError:
  90. fowner = 'uid %s' % prop.st_uid
  91. try:
  92. user = pwd.getpwuid(os.getuid())[0]
  93. except KeyError:
  94. user = 'uid %s' % os.getuid()
  95. raise NetrcParseError(
  96. ("~/.netrc file owner (%s) does not match"
  97. " current user (%s)") % (fowner, user),
  98. file, lexer.lineno)
  99. if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
  100. raise NetrcParseError(
  101. "~/.netrc access too permissive: access"
  102. " permissions must restrict access to only"
  103. " the owner", file, lexer.lineno)
  104. password = lexer.get_token()
  105. else:
  106. raise NetrcParseError("bad follower token %r" % tt,
  107. file, lexer.lineno)
  108. def authenticators(self, host):
  109. """Return a (user, account, password) tuple for given host."""
  110. if host in self.hosts:
  111. return self.hosts[host]
  112. elif 'default' in self.hosts:
  113. return self.hosts['default']
  114. else:
  115. return None
  116. def __repr__(self):
  117. """Dump the class data in the format of a .netrc file."""
  118. rep = ""
  119. for host in self.hosts.keys():
  120. attrs = self.hosts[host]
  121. rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n"
  122. if attrs[1]:
  123. rep = rep + "account " + repr(attrs[1])
  124. rep = rep + "\tpassword " + repr(attrs[2]) + "\n"
  125. for macro in self.macros.keys():
  126. rep = rep + "macdef " + macro + "\n"
  127. for line in self.macros[macro]:
  128. rep = rep + line
  129. rep = rep + "\n"
  130. return rep
  131. if __name__ == '__main__':
  132. print netrc()