Index: trunk/wmfmailadmin/wmfmailadmin.py |
— | — | @@ -5,9 +5,9 @@ |
6 | 6 | Written by Mark Bergsma <mark@wikimedia.org> |
7 | 7 | """ |
8 | 8 | |
9 | | -import sys, os |
| 9 | +import sys, os, sqlite3 |
10 | 10 | |
11 | | -dbname = 'user.db' |
| 11 | +dbname = '/var/vmail/user.db' |
12 | 12 | conn = None |
13 | 13 | |
14 | 14 | actions = { |
— | — | @@ -20,14 +20,14 @@ |
21 | 21 | longactions = {} |
22 | 22 | |
23 | 23 | fieldmappings = { |
24 | | - # Option: ( fieldname, description, default ) |
25 | | - 'e': ('email', "E-mail address", None), |
26 | | - 'p': ('password', "Password", '-'), |
27 | | - 'r': ('realname', "Real name", None), |
28 | | - 'i': ('id', "Id", None), |
29 | | - 'q': ('quota', "Quota", 2**30/1024), |
30 | | - 'a': ('active', "Active", True), |
31 | | - 'f': ('filter', "Filter", None) |
| 24 | + # Option: ( fieldname, description, default, explanation ) |
| 25 | + 'e': ('email', "E-mail address", None, None), |
| 26 | + 'p': ('password', "Password", '-', "Password hash or '-' for prompting"), |
| 27 | + 'r': ('realname', "Real name", None, None), |
| 28 | + 'i': ('id', "Id", None, None), |
| 29 | + 'q': ('quota', "Quota", 2**30/1024, None), |
| 30 | + 'a': ('active', "Active", True, None), |
| 31 | + 'f': ('filter', "Filter", None, None) |
32 | 32 | } |
33 | 33 | longmappings = {} |
34 | 34 | |
— | — | @@ -161,13 +161,13 @@ |
162 | 162 | |
163 | 163 | def password_input(fields): |
164 | 164 | """ |
165 | | - Checks if the password argument on the commandline was "-", |
| 165 | + Checks if the password argument on the commandline was "-" or empty, |
166 | 166 | and prompts for a password if that is the case |
167 | 167 | """ |
168 | 168 | |
169 | 169 | global supported_hash_algorithms |
170 | 170 | |
171 | | - if fields['password'] != '-': return |
| 171 | + if fields['password'] not in ('', '-'): return |
172 | 172 | |
173 | 173 | # Simply outsource to dovecotpw |
174 | 174 | pipe = os.popen('dovecotpw -s sha1', 'r') |
— | — | @@ -195,7 +195,6 @@ |
196 | 196 | Creates a connection to the database |
197 | 197 | """ |
198 | 198 | |
199 | | - import sqlite3 |
200 | 199 | global conn |
201 | 200 | |
202 | 201 | conn = sqlite3.connect(dbname) |
— | — | @@ -208,7 +207,7 @@ |
209 | 208 | |
210 | 209 | global actions, fieldmappings |
211 | 210 | |
212 | | - print "Usage:", sys.argv[0], "[ACTION] [FIELDS]" |
| 211 | + print "Usage:", sys.argv[0], "[ACTION] [FIELDS] [dbfile]" |
213 | 212 | |
214 | 213 | print "\nActions:" |
215 | 214 | for a, action in actions.iteritems(): |
— | — | @@ -216,7 +215,7 @@ |
217 | 216 | |
218 | 217 | print "\nFields:" |
219 | 218 | for f, field in fieldmappings.iteritems(): |
220 | | - print " -%s <...> --%-10s\t%s" % (f, field[0], field[1]) |
| 219 | + print " -%s <...> --%-10s\t%s" % (f, field[0], field[3] or field[1]) |
221 | 220 | |
222 | 221 | def parse_arguments(): |
223 | 222 | """ |
— | — | @@ -224,7 +223,7 @@ |
225 | 224 | """ |
226 | 225 | |
227 | 226 | import getopt |
228 | | - global actions, fieldmappings |
| 227 | + global actions, fieldmappings, dbname |
229 | 228 | |
230 | 229 | # Build option list |
231 | 230 | options = "".join(actions.keys() + [c+':' for c in fieldmappings.keys()]) + "h" |
— | — | @@ -238,6 +237,10 @@ |
239 | 238 | # Print help information and exit |
240 | 239 | print_usage() |
241 | 240 | sys.exit(2) |
| 241 | + |
| 242 | + # (First, optional) argument should be dbfile |
| 243 | + if len(args) > 0 and args[0] != "": |
| 244 | + dbname = args[0] |
242 | 245 | |
243 | 246 | # Parse options |
244 | 247 | action, fields = None, {} |
— | — | @@ -266,36 +269,51 @@ |
267 | 270 | print_usage() |
268 | 271 | sys.exit(2) |
269 | 272 | |
270 | | - return action, fields |
| 273 | + if action is None: |
| 274 | + print_usage() |
| 275 | + sys.exit(2) |
| 276 | + else: |
| 277 | + return action, fields |
271 | 278 | |
272 | 279 | def main(): |
273 | 280 | """ |
274 | 281 | Main function |
275 | 282 | """ |
276 | 283 | |
277 | | - global longactions, longmappings |
| 284 | + global longactions, longmappings, dbname |
278 | 285 | |
279 | 286 | longactions, longmappings = add_index(actions, 0), add_index(fieldmappings, 0) |
280 | 287 | action, fields = parse_arguments() |
281 | 288 | |
282 | 289 | if action is not None: |
283 | | - connect_db() |
| 290 | + try: |
| 291 | + connect_db() |
| 292 | + except sqlite3.OperationalError, e: |
| 293 | + print >> sys.stderr, "Can't open database file %s: %s" % (dbname, e.message) |
| 294 | + sys.exit(2) |
284 | 295 | |
285 | 296 | # Split e-mail address into localpart and domain fields |
286 | 297 | if 'email' in fields: split_email(fields) |
287 | 298 | |
288 | | - if action == 'list': |
289 | | - list_accounts(fields) |
290 | | - elif action == 'create': |
291 | | - create_account(fields) |
292 | | - print "Account added:" |
293 | | - list_accounts(fields) |
294 | | - elif action == 'delete': |
295 | | - delete_account(fields) |
296 | | - elif action == 'update': |
297 | | - update_account(fields) |
298 | | - print "Account updated:" |
299 | | - list_accounts(fields) |
| 299 | + try: |
| 300 | + if action == 'list': |
| 301 | + list_accounts(fields) |
| 302 | + elif action == 'create': |
| 303 | + create_account(fields) |
| 304 | + print "Account added:" |
| 305 | + list_accounts(fields) |
| 306 | + elif action == 'delete': |
| 307 | + delete_account(fields) |
| 308 | + elif action == 'update': |
| 309 | + update_account(fields) |
| 310 | + print "Account updated:" |
| 311 | + list_accounts(fields) |
| 312 | + except sqlite3.IntegrityError, e: |
| 313 | + print >> sys.stderr, "SQL integrity error. Account does already exist? (%s)" % e.message |
| 314 | + sys.exit(2) |
| 315 | + except Exception, e: |
| 316 | + print >> sys.stderr, "Error:", e.message |
| 317 | + sys.exit(2) |
300 | 318 | |
301 | 319 | if __name__ == "__main__": |
302 | 320 | main() |
\ No newline at end of file |