blob: 2cb077f5abefda58d0c375d3aac71b90f93cc5fe [file] [log] [blame]
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00001# this module is an OS/2 oriented replacement for the pwd standard
2# extension module.
3
4# written by Andrew MacIntyre, April 2001.
Andrew MacIntyre71d74e82003-07-10 12:52:54 +00005# updated July 2003, adding field accessor support
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00006
Tim Peters182b5ac2004-07-18 06:16:08 +00007# note that this implementation checks whether ":" or ";" as used as
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00008# the field separator character. Path conversions are are applied when
9# the database uses ":" as the field separator character.
10
Tim Peters182b5ac2004-07-18 06:16:08 +000011"""Replacement for pwd standard extension module, intended for use on
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000012OS/2 and similar systems which don't normally have an /etc/passwd file.
13
Tim Peters182b5ac2004-07-18 06:16:08 +000014The standard Unix password database is an ASCII text file with 7 fields
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000015per record (line), separated by a colon:
16 - user name (string)
17 - password (encrypted string, or "*" or "")
18 - user id (integer)
19 - group id (integer)
20 - description (usually user's name)
21 - home directory (path to user's home directory)
22 - shell (path to the user's login shell)
23
24(see the section 8.1 of the Python Library Reference)
25
Tim Peters182b5ac2004-07-18 06:16:08 +000026This implementation differs from the standard Unix implementation by
27allowing use of the platform's native path separator character - ';' on OS/2,
28DOS and MS-Windows - as the field separator in addition to the Unix
29standard ":". Additionally, when ":" is the separator path conversions
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000030are applied to deal with any munging of the drive letter reference.
31
Tim Peters182b5ac2004-07-18 06:16:08 +000032The module looks for the password database at the following locations
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000033(in order first to last):
34 - ${ETC_PASSWD} (or %ETC_PASSWD%)
35 - ${ETC}/passwd (or %ETC%/passwd)
36 - ${PYTHONHOME}/Etc/passwd (or %PYTHONHOME%/Etc/passwd)
37
38Classes
39-------
40
41None
42
43Functions
44---------
45
46getpwuid(uid) - return the record for user-id uid as a 7-tuple
47
48getpwnam(name) - return the record for user 'name' as a 7-tuple
49
50getpwall() - return a list of 7-tuples, each tuple being one record
51 (NOTE: the order is arbitrary)
52
53Attributes
54----------
55
56passwd_file - the path of the password database file
57
58"""
59
60import os
61
62# try and find the passwd file
63__passwd_path = []
Collin Winter04706892007-09-01 20:35:04 +000064if 'ETC_PASSWD' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000065 __passwd_path.append(os.environ['ETC_PASSWD'])
Collin Winter04706892007-09-01 20:35:04 +000066if 'ETC' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000067 __passwd_path.append('%s/passwd' % os.environ['ETC'])
Collin Winter04706892007-09-01 20:35:04 +000068if 'PYTHONHOME' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000069 __passwd_path.append('%s/Etc/passwd' % os.environ['PYTHONHOME'])
70
71passwd_file = None
72for __i in __passwd_path:
73 try:
74 __f = open(__i, 'r')
75 __f.close()
76 passwd_file = __i
77 break
78 except:
79 pass
80
81# path conversion handlers
82def __nullpathconv(path):
83 return path.replace(os.altsep, os.sep)
84
85def __unixpathconv(path):
86 # two known drive letter variations: "x;" and "$x"
87 if path[0] == '$':
88 conv = path[1] + ':' + path[2:]
89 elif path[1] == ';':
90 conv = path[0] + ':' + path[2:]
91 else:
92 conv = path
93 return conv.replace(os.altsep, os.sep)
94
95# decide what field separator we can try to use - Unix standard, with
96# the platform's path separator as an option. No special field conversion
Tim Peters182b5ac2004-07-18 06:16:08 +000097# handler is required when using the platform's path separator as field
98# separator, but are required for the home directory and shell fields when
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000099# using the standard Unix (":") field separator.
100__field_sep = {':': __unixpathconv}
101if os.pathsep:
102 if os.pathsep != ':':
103 __field_sep[os.pathsep] = __nullpathconv
104
105# helper routine to identify which separator character is in use
106def __get_field_sep(record):
107 fs = None
108 for c in __field_sep.keys():
109 # there should be 6 delimiter characters (for 7 fields)
110 if record.count(c) == 6:
111 fs = c
112 break
113 if fs:
114 return fs
115 else:
Collin Wintere45be282007-08-23 00:01:55 +0000116 raise KeyError('>> passwd database fields not delimited <<')
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000117
Andrew MacIntyre71d74e82003-07-10 12:52:54 +0000118# class to match the new record field name accessors.
119# the resulting object is intended to behave like a read-only tuple,
120# with each member also accessible by a field name.
121class Passwd:
122 def __init__(self, name, passwd, uid, gid, gecos, dir, shell):
123 self.__dict__['pw_name'] = name
124 self.__dict__['pw_passwd'] = passwd
125 self.__dict__['pw_uid'] = uid
126 self.__dict__['pw_gid'] = gid
127 self.__dict__['pw_gecos'] = gecos
128 self.__dict__['pw_dir'] = dir
129 self.__dict__['pw_shell'] = shell
130 self.__dict__['_record'] = (self.pw_name, self.pw_passwd,
131 self.pw_uid, self.pw_gid,
132 self.pw_gecos, self.pw_dir,
133 self.pw_shell)
134
135 def __len__(self):
136 return 7
137
138 def __getitem__(self, key):
139 return self._record[key]
140
141 def __setattr__(self, name, value):
142 raise AttributeError('attribute read-only: %s' % name)
143
144 def __repr__(self):
145 return str(self._record)
146
147 def __cmp__(self, other):
148 this = str(self._record)
149 if this == other:
150 return 0
151 elif this < other:
152 return -1
153 else:
154 return 1
155
156
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000157# read the whole file, parsing each entry into tuple form
158# with dictionaries to speed recall by UID or passwd name
159def __read_passwd_file():
160 if passwd_file:
161 passwd = open(passwd_file, 'r')
162 else:
Collin Wintere45be282007-08-23 00:01:55 +0000163 raise KeyError('>> no password database <<')
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000164 uidx = {}
165 namx = {}
166 sep = None
Collin Winter04706892007-09-01 20:35:04 +0000167 while True:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000168 entry = passwd.readline().strip()
169 if len(entry) > 6:
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000170 if sep is None:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000171 sep = __get_field_sep(entry)
172 fields = entry.split(sep)
173 for i in (2, 3):
174 fields[i] = int(fields[i])
175 for i in (5, 6):
176 fields[i] = __field_sep[sep](fields[i])
Andrew MacIntyre71d74e82003-07-10 12:52:54 +0000177 record = Passwd(*fields)
Collin Winter04706892007-09-01 20:35:04 +0000178 if fields[2] not in uidx:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000179 uidx[fields[2]] = record
Collin Winter04706892007-09-01 20:35:04 +0000180 if fields[0] not in namx:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000181 namx[fields[0]] = record
182 elif len(entry) > 0:
183 pass # skip empty or malformed records
184 else:
185 break
186 passwd.close()
187 if len(uidx) == 0:
188 raise KeyError
189 return (uidx, namx)
190
191# return the passwd database entry by UID
192def getpwuid(uid):
193 u, n = __read_passwd_file()
194 return u[uid]
195
196# return the passwd database entry by passwd name
197def getpwnam(name):
198 u, n = __read_passwd_file()
199 return n[name]
200
201# return all the passwd database entries
202def getpwall():
203 u, n = __read_passwd_file()
204 return n.values()
205
206# test harness
207if __name__ == '__main__':
208 getpwall()