|
1 # this module is an OS/2 oriented replacement for the pwd standard |
|
2 # extension module. |
|
3 |
|
4 # written by Andrew MacIntyre, April 2001. |
|
5 # updated July 2003, adding field accessor support |
|
6 |
|
7 # note that this implementation checks whether ":" or ";" as used as |
|
8 # the field separator character. Path conversions are are applied when |
|
9 # the database uses ":" as the field separator character. |
|
10 |
|
11 """Replacement for pwd standard extension module, intended for use on |
|
12 OS/2 and similar systems which don't normally have an /etc/passwd file. |
|
13 |
|
14 The standard Unix password database is an ASCII text file with 7 fields |
|
15 per 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 |
|
26 This implementation differs from the standard Unix implementation by |
|
27 allowing use of the platform's native path separator character - ';' on OS/2, |
|
28 DOS and MS-Windows - as the field separator in addition to the Unix |
|
29 standard ":". Additionally, when ":" is the separator path conversions |
|
30 are applied to deal with any munging of the drive letter reference. |
|
31 |
|
32 The module looks for the password database at the following locations |
|
33 (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 |
|
38 Classes |
|
39 ------- |
|
40 |
|
41 None |
|
42 |
|
43 Functions |
|
44 --------- |
|
45 |
|
46 getpwuid(uid) - return the record for user-id uid as a 7-tuple |
|
47 |
|
48 getpwnam(name) - return the record for user 'name' as a 7-tuple |
|
49 |
|
50 getpwall() - return a list of 7-tuples, each tuple being one record |
|
51 (NOTE: the order is arbitrary) |
|
52 |
|
53 Attributes |
|
54 ---------- |
|
55 |
|
56 passwd_file - the path of the password database file |
|
57 |
|
58 """ |
|
59 |
|
60 import os |
|
61 |
|
62 # try and find the passwd file |
|
63 __passwd_path = [] |
|
64 if os.environ.has_key('ETC_PASSWD'): |
|
65 __passwd_path.append(os.environ['ETC_PASSWD']) |
|
66 if os.environ.has_key('ETC'): |
|
67 __passwd_path.append('%s/passwd' % os.environ['ETC']) |
|
68 if os.environ.has_key('PYTHONHOME'): |
|
69 __passwd_path.append('%s/Etc/passwd' % os.environ['PYTHONHOME']) |
|
70 |
|
71 passwd_file = None |
|
72 for __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 |
|
82 def __nullpathconv(path): |
|
83 return path.replace(os.altsep, os.sep) |
|
84 |
|
85 def __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 |
|
97 # 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 |
|
99 # using the standard Unix (":") field separator. |
|
100 __field_sep = {':': __unixpathconv} |
|
101 if os.pathsep: |
|
102 if os.pathsep != ':': |
|
103 __field_sep[os.pathsep] = __nullpathconv |
|
104 |
|
105 # helper routine to identify which separator character is in use |
|
106 def __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: |
|
116 raise KeyError, '>> passwd database fields not delimited <<' |
|
117 |
|
118 # 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. |
|
121 class 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 |
|
157 # read the whole file, parsing each entry into tuple form |
|
158 # with dictionaries to speed recall by UID or passwd name |
|
159 def __read_passwd_file(): |
|
160 if passwd_file: |
|
161 passwd = open(passwd_file, 'r') |
|
162 else: |
|
163 raise KeyError, '>> no password database <<' |
|
164 uidx = {} |
|
165 namx = {} |
|
166 sep = None |
|
167 while 1: |
|
168 entry = passwd.readline().strip() |
|
169 if len(entry) > 6: |
|
170 if sep is None: |
|
171 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]) |
|
177 record = Passwd(*fields) |
|
178 if not uidx.has_key(fields[2]): |
|
179 uidx[fields[2]] = record |
|
180 if not namx.has_key(fields[0]): |
|
181 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 |
|
192 def getpwuid(uid): |
|
193 u, n = __read_passwd_file() |
|
194 return u[uid] |
|
195 |
|
196 # return the passwd database entry by passwd name |
|
197 def getpwnam(name): |
|
198 u, n = __read_passwd_file() |
|
199 return n[name] |
|
200 |
|
201 # return all the passwd database entries |
|
202 def getpwall(): |
|
203 u, n = __read_passwd_file() |
|
204 return n.values() |
|
205 |
|
206 # test harness |
|
207 if __name__ == '__main__': |
|
208 getpwall() |