Redbrick User management tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2250 lines
59 KiB

  1. # -*- coding: iso8859-15 -*-
  2. #-----------------------------------------------------------------------------#
  3. # MODULE DESCRIPTION #
  4. #-----------------------------------------------------------------------------#
  5. """RedBrick command line user administration interface."""
  6. # System modules
  7. import atexit
  8. import getopt
  9. import getpass
  10. import grp
  11. import os
  12. import pprint
  13. import pwd
  14. import re
  15. import readline
  16. import sys
  17. # RedBrick modules
  18. from rbaccount import *
  19. from rbuserdb import *
  20. #-----------------------------------------------------------------------------#
  21. # DATA #
  22. #-----------------------------------------------------------------------------#
  23. __version__ = '$Revision: 1.17 $'
  24. __author__ = 'Cillian Sharkey'
  25. # Command name -> (command description, optional arguments)
  26. #
  27. cmds = {
  28. 'add': ('Add new user', '[username]'),
  29. 'renew': ('Renew user', '[username]'),
  30. 'update': ('Update user', '[username]'),
  31. 'altmail': ('Change Alternate Email', '[username]'),
  32. 'activate': ('Re-Enable a club/soc account', '[username]'),
  33. 'delete': ('Delete user', '[username]'),
  34. 'resetpw': ('Set new random password and mail it to user', '[username]'),
  35. 'setshell': ('Set user\'s shell', '[username [shell]]'),
  36. 'resetsh': ('Reset user\'s shell', '[username]'),
  37. 'rename': ('Rename user', '[username]'),
  38. 'convert': ('Change user to a different usertype', '[username]'),
  39. 'disuser': ('Disuser a user', '[username [new username]]'),
  40. 'reuser': ('Re-user a user', '[username]'),
  41. 'show': ('Show user details', '[username]'),
  42. 'info': ('Show shorter user details', '[username]'),
  43. 'freename': ('Check if a username is free', '[username]'),
  44. 'search': ('Search user and dcu databases', '[username]'),
  45. 'pre_sync': ('Dump LDAP tree for use by sync before new tree is loaded', ''),
  46. 'sync': ('Synchronise accounts with userdb (for RRS)', '[rrs-logfile [presync-file]]'),
  47. 'sync_dcu_info': ('Interactive update of userdb using dcu database info', ''),
  48. 'list_users': ('List all usernames', ''),
  49. 'list_unavailable': ('List all usernames that are unavailable', ''),
  50. 'list_newbies': ('List all paid newbies', ''),
  51. 'list_renewals': ('List all paid renewals (non-newbie)', ''),
  52. 'list_unpaid': ('List all non-renewed users', ''),
  53. 'list_unpaid_normal': ('List all normal non-renewed users', ''),
  54. 'list_unpaid_reset': ('List all normal non-renewed users with reset shells', ''),
  55. 'list_unpaid_grace': ('List all grace non-renewed users', ''),
  56. 'newyear': ('Prepare database for start of new academic year', ''),
  57. 'unpaid_warn': ('Warn (mail) all non-renewed users', ''),
  58. 'unpaid_disable': ('Disable all normal non-renewed users', ''),
  59. 'unpaid_delete': ('Delete all grace non-renewed users', ''),
  60. 'checkdb': ('Check database for inconsistencies', ''),
  61. 'stats': ('Show database and account statistics', ''),
  62. 'create_uidNumber': ('Create uidNumber text file with next free uidNumber', ''),
  63. }
  64. # Command groups
  65. #
  66. cmds_single_user = ('add', 'delete', 'renew', 'update', 'altmail', 'activate', 'rename', 'convert')
  67. cmds_single_account = ('resetpw', 'resetsh', 'disuser', 'reuser', 'setshell')
  68. cmds_single_user_info = ('show', 'info', 'freename')
  69. cmds_interactive_batch = ('search', 'sync', 'sync_dcu_info')
  70. cmds_batch = ('newyear', 'unpaid_warn', 'unpaid_disable', 'unpaid_delete')
  71. cmds_batch_info = ('pre_sync', 'list_users', 'list_unavailable', 'list_newbies', 'list_renewals', 'list_unpaid', 'list_unpaid_normal', 'list_unpaid_reset', 'list_unpaid_grace')
  72. cmds_misc = ('checkdb', 'stats', 'create_uidNumber')
  73. # Command group descriptions
  74. #
  75. cmds_group_desc = (
  76. (cmds_single_user, 'Single user commands'),
  77. (cmds_single_account, 'Single account commands'),
  78. (cmds_single_user_info, 'Single user information commands'),
  79. (cmds_interactive_batch,'Interactive batch commands'),
  80. (cmds_batch, 'Batch commands'),
  81. (cmds_batch_info, 'Batch information commands'),
  82. (cmds_misc, 'Miscellaneous commands')
  83. )
  84. # All commands
  85. #
  86. cmds_all = cmds.keys()
  87. # Command option -> (optional argument, option description, commands that use option)
  88. #
  89. cmds_opts = (
  90. ('h', '', 'Display this usage', cmds_all),
  91. ('T', '', 'Test mode, show what would be done', cmds_all),
  92. ('d', '', 'Perform database operations only', cmds_single_user),
  93. ('a', '', 'Perform unix account operations only', cmds_single_user),
  94. ('u', 'username', 'Unix username of who updated this user', cmds_single_user + ('disuser', 'reuser')),
  95. ('f', '', 'Set newbie (fresher) to true', ('add', 'update')),
  96. ('F', '', 'Opposite of -f', ('add', 'update')),
  97. ('m', '', 'Send account details to user\'s alternate email address', ('add', 'renew', 'rename', 'resetpw')),
  98. ('M', '', 'Opposite of -m', ('add', 'renew', 'rename', 'resetpw')),
  99. ('o', '', 'Override warning errors', cmds_all),
  100. ('p', '', 'Set new random password', ('add', 'renew')),
  101. ('P', '', 'Opposite of -p', ('add', 'renew')),
  102. ('t', 'usertype', 'Type of account', ('add', 'renew', 'update', 'convert')),
  103. ('n', 'name', 'Real name or account description', ('add', 'renew', 'update', 'search')),
  104. ('e', 'email', 'Alternative email address', ('add', 'renew', 'update')),
  105. ('i', 'id', 'Student/Staff ID', ('add', 'renew', 'update', 'search')),
  106. ('c', 'course', 'DCU course (abbreviation)', ('add', 'renew', 'update')),
  107. ('y', 'year', 'DCU year', ('add', 'renew', 'update')),
  108. ('s', 'years', 'paid Number of years paid (subscription)', ('add', 'renew', 'update')),
  109. ('b', 'birthday', 'Birthday (format YYYY-MM-DD)', ('add', 'renew', 'update')),
  110. ('q', '', 'Quiet mode', ('reuser',))
  111. )
  112. input_instructions = '\033[1mRETURN\033[0m: use [default] given \033[1mTAB\033[0m: answer completion \033[1mEOF\033[0m: give empty answer\n'
  113. # Global variables.
  114. #
  115. opt = RBOpt()
  116. udb = acc = None # Initialised later in main()
  117. header_mesg = None
  118. #-----------------------------------------------------------------------------#
  119. # MAIN #
  120. #-----------------------------------------------------------------------------#
  121. def main():
  122. """Program entry function."""
  123. atexit.register(shutdown)
  124. if len(sys.argv) > 1 and sys.argv[1][0] != '-':
  125. opt.mode = sys.argv.pop(1)
  126. try:
  127. opts, args = getopt.getopt(sys.argv[1:], 'b:c:e:i:n:s:t:u:y:adfFhmMopPqT')
  128. except getopt.GetoptError, e:
  129. print e
  130. usage()
  131. sys.exit(1)
  132. for o, a in opts:
  133. if o == '-h':
  134. opt.help = 1
  135. usage()
  136. sys.exit(0)
  137. elif o == '-T':
  138. opt.test = 1
  139. elif o == '-d':
  140. opt.dbonly = 1
  141. opt.aconly = 0
  142. elif o == '-a':
  143. opt.aconly = 1
  144. opt.dbonly = 0
  145. elif o == '-u':
  146. opt.updatedby = a
  147. elif o == '-f':
  148. opt.newbie = 1
  149. elif o == '-F':
  150. opt.newbie = 0
  151. elif o == '-m':
  152. opt.mailuser = 1
  153. elif o == '-M':
  154. opt.mailuser = 0
  155. elif o == '-o':
  156. opt.override = 1
  157. elif o == '-p':
  158. opt.setpasswd = 1
  159. elif o == '-P':
  160. opt.setpasswd = 0
  161. elif o == '-t':
  162. opt.usertype = a
  163. elif o == '-n':
  164. opt.cn = a
  165. elif o == '-e':
  166. opt.altmail = a
  167. elif o == '-i':
  168. opt.id = a
  169. elif o == '-c':
  170. opt.course = a
  171. elif o == '-y':
  172. opt.year = a
  173. elif o == '-s':
  174. opt.yearsPaid = a
  175. elif o == '-b':
  176. opt.birthday = a
  177. elif o == '-q':
  178. opt.quiet = 1
  179. if not cmds.has_key(opt.mode):
  180. usage()
  181. sys.exit(1)
  182. global udb, acc
  183. udb = RBUserDB()
  184. try:
  185. udb.connect()
  186. except ldap.LDAPError, e:
  187. error(e, 'Could not connect to user database')
  188. # not reached
  189. except KeyboardInterrupt:
  190. print
  191. sys.exit(1)
  192. # not reached
  193. acc = RBAccount()
  194. # Optional additional parameters after command line options.
  195. opt.args = args
  196. try:
  197. # Call function for specific mode.
  198. eval(opt.mode + "()")
  199. except KeyboardInterrupt:
  200. print
  201. sys.exit(1)
  202. # not reached
  203. except RBError, e:
  204. rberror(e)
  205. # not reached
  206. except ldap.LDAPError, e:
  207. error(e)
  208. # not reached
  209. sys.exit(0)
  210. def shutdown():
  211. """Cleanup function registered with atexit."""
  212. if udb: udb.close()
  213. def usage():
  214. """Print command line usage and options."""
  215. if opt.mode and not cmds.has_key(opt.mode):
  216. print "Unknown command '%s'" % opt.mode
  217. opt.mode = None
  218. if not opt.mode:
  219. print "Usage: useradm command [options]"
  220. if opt.help:
  221. for grp in cmds_group_desc:
  222. print "%s:" % (grp[1])
  223. for cmd in grp[0]:
  224. print " %-20s %s" % (cmd, cmds[cmd][0])
  225. print "'useradm command -h' for more info on a command's options & usage."
  226. else:
  227. print "'useradm -h' for more info on available commands"
  228. else:
  229. print cmds[opt.mode][0]
  230. print "Usage: useradm", opt.mode, "[options]", cmds[opt.mode][1]
  231. for i in cmds_opts:
  232. if opt.mode in i[3]:
  233. print " -%s %-15s%s" % (i[0], i[1], i[2])
  234. #=============================================================================#
  235. # MAIN FUNCTIONS #
  236. #=============================================================================#
  237. #-----------------------------------------------------------------------------#
  238. # SINGLE USER COMMANDS #
  239. #-----------------------------------------------------------------------------#
  240. def add():
  241. """Add a new user."""
  242. usr = RBUser()
  243. get_usertype(usr)
  244. get_freeusername(usr)
  245. oldusertype = usr.usertype
  246. while 1:
  247. try:
  248. get_id(usr)
  249. udb.get_userinfo_new(usr, override = 1)
  250. except RBError, e:
  251. if not rberror(e, opt.id == None):
  252. break
  253. usr.id = None
  254. else:
  255. break
  256. udb.get_userdefaults_new(usr)
  257. # If we get info from the DCU databases, show the user details and any
  258. # differences to previous data (in this case it's just the initial
  259. # usertype entered at first) and offer to edit these with a default of
  260. # no so we can hit return and quickly add a user without verifying each
  261. # attribute.
  262. #
  263. if usr.cn:
  264. udb.show(usr)
  265. print
  266. if oldusertype != usr.usertype:
  267. print 'NOTICE: Given usertype is different to one determined by DCU database! '
  268. print
  269. edit_details = yesno('Details of user to be added are shown above. Edit user details?', 0)
  270. else:
  271. edit_details = 1
  272. if edit_details:
  273. get_usertype(usr)
  274. get_newbie(usr)
  275. get_name(usr)
  276. get_email(usr)
  277. get_course(usr)
  278. get_year(usr)
  279. get_years_paid(usr)
  280. get_birthday(usr)
  281. get_createaccount(usr)
  282. get_setpasswd(usr)
  283. get_mailuser(usr)
  284. get_updatedby(usr)
  285. # End of user interaction, set options for override & test mode.
  286. #
  287. udb.setopt(opt)
  288. acc.setopt(opt)
  289. if opt.setpasswd:
  290. usr.passwd = rbconfig.gen_passwd()
  291. if not opt.aconly:
  292. print "User added: %s %s (%s)" % (usr.usertype, usr.uid, usr.cn)
  293. udb.add(usr)
  294. if not opt.dbonly:
  295. print "Account created: %s %s password: %s" % (usr.usertype, usr.uid, usr.passwd)
  296. acc.add(usr)
  297. else:
  298. # If not creating a Unix account but setting a new password is
  299. # required, do that now.
  300. #
  301. if opt.setpasswd:
  302. print "Account password set for %s password: %s" % (usr.uid, usr.passwd)
  303. #acc.setpasswd(usr.uid, usr.passwd)
  304. if opt.mailuser:
  305. print "User mailed:", usr.altmail
  306. mailuser(usr)
  307. def delete():
  308. """Delete user."""
  309. usr = RBUser()
  310. get_username(usr)
  311. udb.get_user_byname(usr)
  312. # End of user interaction, set options for override & test mode.
  313. udb.setopt(opt)
  314. acc.setopt(opt)
  315. if not opt.aconly:
  316. print 'User deleted:', usr.uid
  317. udb.delete(usr)
  318. if not opt.dbonly:
  319. print 'Account deleted:', usr.uid
  320. acc.delete(usr)
  321. def renew():
  322. """Renew user."""
  323. usr = RBUser()
  324. curusr = RBUser()
  325. get_username(usr)
  326. try:
  327. udb.get_userinfo_renew(usr, curusr, override=1)
  328. except RBError, e:
  329. if rberror(e, opt.uid == None):
  330. return
  331. try:
  332. udb.check_unpaid(curusr)
  333. except RBError, e:
  334. if rberror(e, opt.uid == None):
  335. return
  336. udb.get_userdefaults_renew(usr)
  337. udb.show_diff(usr, curusr)
  338. print
  339. if curusr.usertype != usr.usertype:
  340. print 'NOTICE: A new usertype was determined by DCU database!'
  341. print
  342. edit_details = yesno(
  343. 'New details of user to be renewed are shown above with any differences\n' \
  344. 'from current values. Edit user details?', 0)
  345. if edit_details:
  346. while 1:
  347. get_id(usr)
  348. try:
  349. # If id was changed, need to get updated user info.
  350. #
  351. udb.get_userinfo_renew(usr, override = 1)
  352. # XXX: check id not already in use
  353. except RBError, e:
  354. if not rberror(e, opt.id == None):
  355. break
  356. else:
  357. break
  358. if curusr.id != usr.id:
  359. udb.show_diff(usr, curusr)
  360. print
  361. get_usertype(usr)
  362. get_newbie(usr)
  363. get_name(usr, (curusr.cn,))
  364. get_email(usr, (curusr.altmail,))
  365. get_course(usr, (curusr.course,))
  366. get_year(usr, (curusr.year,))
  367. get_years_paid(usr)
  368. get_birthday(usr)
  369. get_setpasswd(usr)
  370. get_mailuser(usr)
  371. get_updatedby(usr)
  372. # End of user interaction, set options for override & test mode.
  373. udb.setopt(opt)
  374. acc.setopt(opt)
  375. if not opt.aconly:
  376. print 'User renewed:', usr.uid
  377. udb.renew(usr)
  378. if opt.setpasswd:
  379. usr.passwd = rbconfig.gen_passwd()
  380. print "Account password reset: %s password: %s" % (usr.uid, usr.passwd)
  381. udb.set_passwd(usr)
  382. if curusr.usertype != usr.usertype:
  383. if not opt.aconly:
  384. print 'User converted: %s -> %s' % (usr.uid, usr.usertype)
  385. udb.convert(curusr, usr)
  386. if not opt.dbonly:
  387. print 'Account converted: %s -> %s' % (usr.uid, usr.usertype)
  388. acc.convert(curusr, usr)
  389. if udb.reset_shell(usr):
  390. print 'Account shell reset for', usr.uid, '(%s)' % usr.loginShell
  391. if opt.mailuser:
  392. print "User mailed:", usr.altmail
  393. mailuser(usr)
  394. def update():
  395. """Update user."""
  396. # Update mode only works on database.
  397. opt.dbonly = 1
  398. opt.aconly = 0
  399. usr = RBUser()
  400. get_username(usr)
  401. udb.get_user_byname(usr)
  402. get_newbie(usr)
  403. defid = usr.id
  404. while 1:
  405. try:
  406. get_id(usr)
  407. newusr = RBUser(id = usr.id)
  408. if usr.id != None:
  409. udb.get_dcu_byid(newusr)
  410. except RBError, e:
  411. if not rberror(e, opt.id == None):
  412. break
  413. usr.id = defid
  414. else:
  415. break
  416. get_name(usr, (newusr.cn,))
  417. get_email(usr, (newusr.altmail,))
  418. get_course(usr, (newusr.course,))
  419. get_year(usr, (newusr.year,))
  420. get_years_paid(usr)
  421. get_birthday(usr)
  422. get_updatedby(usr)
  423. # End of user interaction, set options for override & test mode.
  424. udb.setopt(opt)
  425. print "User updated:", usr.uid
  426. udb.update(usr)
  427. def altmail():
  428. """Update user."""
  429. # Update mode only works on database.
  430. opt.dbonly = 1
  431. opt.aconly = 0
  432. usr = RBUser()
  433. get_username(usr)
  434. udb.get_user_byname(usr)
  435. # get_newbie(usr)
  436. defid = usr.id
  437. while 1:
  438. try:
  439. get_id(usr)
  440. newusr = RBUser(id = usr.id)
  441. if usr.id != None:
  442. udb.get_dcu_byid(newusr)
  443. except RBError, e:
  444. if not rberror(e, opt.id == None):
  445. break
  446. usr.id = defid
  447. else:
  448. break
  449. get_email(usr, (newusr.altmail,))
  450. get_updatedby(usr)
  451. # End of user interaction, set options for override & test mode.
  452. udb.setopt(opt)
  453. print "User updated:", usr.uid
  454. udb.update(usr)
  455. def activate():
  456. """Update user."""
  457. # Update mode only works on database.
  458. # opt.dbonly = 1
  459. # opt.aconly = 0
  460. print " "
  461. print "To confirm society committee details check:"
  462. print "https://www.dcu.ie/portal/index.php3?club_soc_registration_function=8"
  463. print "Continuing will mail a new password for this account,"
  464. print "and set the shell to /usr/local/shells/zsh"
  465. print " "
  466. usr = RBUser()
  467. get_username(usr)
  468. udb.get_user_byname(usr)
  469. # if we're activating them they're hardly newbies
  470. # get_newbie(usr)
  471. defid = usr.id
  472. while 1:
  473. try:
  474. get_id(usr)
  475. newusr = RBUser(id = usr.id)
  476. if usr.id != None:
  477. udb.get_dcu_byid(newusr)
  478. except RBError, e:
  479. if not rberror(e, opt.id == None):
  480. break
  481. usr.id = defid
  482. else:
  483. break
  484. get_email(usr, (newusr.altmail,))
  485. # everyone likes zsh
  486. # get_shell(usr)
  487. usr.loginShell = "/usr/local/shells/zsh"
  488. # Don't bother asking, assume we want to do it
  489. # get_setpasswd(usr)
  490. # Again, we always want to do this.
  491. # get_mailuser(usr)
  492. get_updatedby(usr)
  493. usr.passwd = rbconfig.gen_passwd()
  494. print "Account password reset: %s password: %s" % (usr.uid, usr.passwd)
  495. udb.set_passwd(usr)
  496. print "User mailed:", usr.altmail
  497. mailuser(usr)
  498. # End of user interaction, set options for override & test mode.
  499. udb.setopt(opt)
  500. acc.setopt(opt)
  501. udb.update(usr)
  502. print 'Account email & shell set for', usr.uid, '(%s)' % usr.loginShell
  503. udb.set_shell(usr)
  504. def rename():
  505. """Rename user."""
  506. usr = RBUser()
  507. newusr = RBUser()
  508. get_username(usr)
  509. get_freeusername(newusr)
  510. get_updatedby(usr)
  511. # End of user interaction, set options for override & test mode.
  512. udb.setopt(opt)
  513. acc.setopt(opt)
  514. print 'User renamed: %s -> %s' % (usr.uid, newusr.uid)
  515. udb.rename(usr, newusr)
  516. print 'Account renamed: %s -> %s' % (usr.uid, newusr.uid)
  517. acc.rename(usr, newusr)
  518. def convert():
  519. """Convert user."""
  520. usr = RBUser()
  521. newusr = RBUser()
  522. get_username(usr)
  523. get_convert_usertype(newusr)
  524. get_updatedby(usr)
  525. # End of user interaction, set options for override & test mode.
  526. udb.setopt(opt)
  527. acc.setopt(opt)
  528. if not opt.aconly:
  529. print 'User converted: %s -> %s' % (usr.uid, newusr.usertype)
  530. udb.convert(usr, newusr)
  531. if not opt.dbonly:
  532. print 'Account converted: %s -> %s' % (usr.uid, newusr.usertype)
  533. acc.convert(usr, newusr)
  534. #-----------------------------------------------------------------------------#
  535. # SINGLE ACCOUNT COMMANDS #
  536. #-----------------------------------------------------------------------------#
  537. def resetpw():
  538. """Set new random password and mail it to user."""
  539. usr = RBUser()
  540. get_username(usr)
  541. udb.get_user_byname(usr)
  542. usr.passwd = rbconfig.gen_passwd()
  543. check_paid(usr)
  544. get_mailuser(usr)
  545. # End of user interaction, set options for override & test mode.
  546. udb.setopt(opt)
  547. #print "Account password reset: %s password: %s" % (usr.uid, usr.passwd)
  548. print "Account password reset: %s " % (usr.uid)
  549. udb.set_passwd(usr)
  550. if opt.mailuser:
  551. print "User mailed:", usr.altmail
  552. mailuser(usr)
  553. def resetsh():
  554. """Reset user's shell."""
  555. usr = RBUser()
  556. get_username(usr)
  557. udb.get_user_byname(usr)
  558. check_paid(usr)
  559. # End of user interaction, set options for override & test mode.
  560. udb.setopt(opt)
  561. acc.setopt(opt)
  562. if udb.reset_shell(usr):
  563. print 'Account shell reset for', usr.uid, '(%s)' % usr.loginShell
  564. else:
  565. print 'Account', usr.uid, 'already had valid shell, no action performed.'
  566. def disuser():
  567. """Disuser a user."""
  568. raise RBFatalError("NOT IMPLEMENTED YET")
  569. usr = RBUser()
  570. get_username(usr)
  571. get_disuser_period(usr)
  572. get_disuser_message(usr)
  573. get_updatedby(usr)
  574. # End of user interaction, set options for override & test mode.
  575. udb.setopt(opt)
  576. acc.setopt(opt)
  577. acc.disuser(usr.uid, usr.disuser_period)
  578. def reuser():
  579. """Re-user a user."""
  580. raise RBFatalError("NOT IMPLEMENTED YET")
  581. usr = RBUser()
  582. get_username(usr)
  583. get_updatedby(usr)
  584. # End of user interaction, set options for override & test mode.
  585. udb.setopt(opt)
  586. acc.setopt(opt)
  587. def setshell():
  588. """Set user's shell."""
  589. usr = RBUser()
  590. get_username(usr)
  591. udb.get_user_byname(usr)
  592. check_paid(usr)
  593. get_shell(usr)
  594. # End of user interaction, set options for override & test mode.
  595. udb.setopt(opt)
  596. acc.setopt(opt)
  597. print 'Account shell set for', usr.uid, '(%s)' % usr.loginShell
  598. udb.set_shell(usr)
  599. #-----------------------------------------------------------------------------#
  600. # SINGLE USER INFORMATION COMMANDS #
  601. #-----------------------------------------------------------------------------#
  602. def show():
  603. """Show user's database and account details."""
  604. usr = RBUser()
  605. get_username(usr, check_user_exists = 0)
  606. # End of user interaction, set options for override & test mode.
  607. udb.setopt(opt)
  608. udb.get_user_byname(usr)
  609. print header('User Information')
  610. udb.show(usr)
  611. print header('Account Information')
  612. acc.show(usr)
  613. def info():
  614. """Show user's database and account details."""
  615. usr = RBUser()
  616. get_username(usr, check_user_exists = 0)
  617. # End of user interaction, set options for override & test mode.
  618. udb.setopt(opt)
  619. udb.get_user_byname(usr)
  620. print header('User Information')
  621. udb.info(usr)
  622. def freename():
  623. """Check if a username is free."""
  624. usr = RBUser()
  625. if get_freeusername(usr):
  626. print "Username '%s' is free." % (usr.uid)
  627. #-----------------------------------------------------------------------------#
  628. # BATCH INTERACTIVE COMMANDS #
  629. #-----------------------------------------------------------------------------#
  630. def search():
  631. """Search user and/or DCU databases."""
  632. raise RBFatalError("NOT IMPLEMENTED YET")
  633. pager = os.environ.get('PAGER', 'more')
  634. username = None
  635. if len(opt.args) > 0:
  636. username = opt.args.pop(0)
  637. if not username and not opt.id and not opt.cn:
  638. username = ask('Enter username to search user database', optional = 1)
  639. if not username:
  640. opt.id = ask('Enter DCU Id number to search user and DCU databases', optional = 1)
  641. if not opt.id:
  642. opt.cn = ask('Enter name to search user and DCU databases', optional = 1)
  643. if username:
  644. res = udb.search_users_byusername(username)
  645. fd = os.popen(pager, 'w')
  646. print >> fd, "User database search for username '%s' - %d match%s\n" % (username, len(res), len(res) != 1 and 'es' or '')
  647. show_search_results(res, fd)
  648. fd.close()
  649. elif opt.id or opt.cn:
  650. fd = os.popen(pager, 'w')
  651. if opt.id:
  652. res = udb.search_users_byid(opt.id)
  653. print >> fd, "User database search for id '%s' - %d match%s\n" % (opt.id, len(res), len(res) != 1 and 'es' or '')
  654. else:
  655. res = udb.search_users_byname(opt.cn)
  656. print >> fd, "User database search for name '%s' - %d match%s\n" % (opt.cn, len(res), len(res) != 1 and 'es' or '')
  657. show_search_results(res, fd)
  658. print >> fd
  659. if opt.id:
  660. res = udb.search_dcu_byid(opt.id)
  661. print >> fd, "DCU database search for id '%s' - %d match%s\n" % (opt.id, len(res), len(res) != 1 and 'es' or '')
  662. else:
  663. res = udb.search_dcu_byname(opt.cn)
  664. print >> fd, "DCU database search for name '%s' - %d match%s\n" % (opt.cn, len(res), len(res) != 1 and 'es' or '')
  665. show_search_results(res, fd)
  666. fd.close()
  667. else:
  668. raise RBFatalError('No search term given!')
  669. def show_search_results(res, fd):
  670. """Actual routine to display search results on given output steam."""
  671. if res:
  672. print >> fd, '%-*s %-*s %-8s %-30s %-6s %-4s %s' % (rbconfig.maxlen_uname, 'username', rbconfig.maxlen_group, 'usertype', 'id', 'name', 'course', 'year', 'email')
  673. print >> fd, '%s %s %s %s %s %s %s' % ('-' * rbconfig.maxlen_uname, '-' * rbconfig.maxlen_group, '-' * 8, '-' * 30, '-' * 6, '-' * 4, '-' * 30)
  674. for username, usertype, id, name, course, year, email in res:
  675. print >> fd, "%-*s %-*s %-8s %-30.30s %-6.6s %-4.4s %s" % (rbconfig.maxlen_uname, username or '-', rbconfig.maxlen_group, usertype or '-', id != None and id or '-', name, course or '-', year or '-', email)
  676. def pre_sync():
  677. """Dump current LDAP information to a file for use by sync().
  678. This step is performed before the new LDAP accounts tree is loaded so
  679. that a bare minimum copy of the old tree is available."""
  680. get_pre_sync()
  681. print 'Dumping...'
  682. fd = open(opt.presync, 'w')
  683. print >> fd, 'global old_ldap\nold_ldap = ',
  684. pprint.pprint(udb.list_pre_sync(), fd)
  685. fd.close()
  686. def sync():
  687. """Synchronise accounts (i.e. no changes are made to userdb) after an
  688. offline update to user database with RRS. Needs rrs.log to process
  689. renames and password resets for existing accounts.
  690. Procedure:
  691. 1. Process all renames, only applicable to existing accounts. Taken from
  692. rrs.log. Keep a mapping of username renewals. Note there may be
  693. multiple rename entries.
  694. 2. Process all conversions, only applicable to existing accounts.
  695. Detected by comparing Unix group and usertype in database.
  696. 3. Process all deletions, only applicable to existing accounts.
  697. Detected by checking for unix accounts missing a database entry. All
  698. accounts should be renamed all ready so the only accounts left with
  699. missing database entries must have been deleted. Confirmation is
  700. required for each deletion, however.
  701. 4. Process all adds, only applicable to new users. Detected by user
  702. database entry being a newbie, paid and having no unix account.
  703. Password is generated on-the-fly and mailed.
  704. 5. Process all renewals, only applicable to existing users. Detected by
  705. user database entry being a non-newbie, paid and having an existing
  706. unix account. Reset account shell if needed. Password is reset
  707. on-the-fly and mailed if flagged in rrs.log. The flag in the logfile
  708. will be marked on the original username so the rename map is used to
  709. map this to the current username.
  710. """
  711. get_rrslog()
  712. get_pre_sync()
  713. # Load in old_ldap dictionary.
  714. #
  715. execfile(opt.presync)
  716. # XXX: Set override by default ?
  717. # Set options for override & test mode.
  718. udb.setopt(opt)
  719. acc.setopt(opt)
  720. # Build user_rename maps.
  721. user_convert = {}
  722. user_rename = {}
  723. user_rename_reverse = {}
  724. user_rename_stages = {}
  725. reset_password = {}
  726. # Open log file to build map of renamed usernames, usernames flagged
  727. # for a new password and usernames that were converted.
  728. #
  729. fd = open(opt.rrslog, 'r')
  730. for line in fd.readlines():
  731. tlog = line.rstrip().split(':')
  732. # We ignore renames of new accounts as we just go by the final
  733. # entry in the database.
  734. #
  735. if tlog[4] == 'rename-existing':
  736. olduid = tlog[5]
  737. newuid = tlog[6]
  738. # Remove old user rename mapping and add new one unless
  739. # it points back to the original username.
  740. #
  741. user_rename_reverse[newuid] = user_rename_reverse.pop(olduid, olduid)
  742. if user_rename_reverse[newuid] == newuid:
  743. user_rename_reverse.pop(newuid)
  744. # If this user was flagged for new password and/or a
  745. # conversion, remove the old user mapping and add the
  746. # new one.
  747. #
  748. if user_convert.has_key(olduid):
  749. user_convert[tlog[6]] = user_convert.pop(tlog[5])
  750. if reset_password.has_key(tlog[5]):
  751. reset_password[tlog[6]] = reset_password.pop(tlog[5])
  752. elif tlog[4] == 'convert':
  753. # User was converted, so we flag it. Don't care what
  754. # they're converted to, we check that later.
  755. #
  756. user_convert[tlog[5]] = 1
  757. elif tlog[4] == 'renew':
  758. # tlog[7] indicates whether a new password is required
  759. # or not. We take the last value of this in the log
  760. # file as the final decision.
  761. #
  762. reset_password[tlog[5]] = int(tlog[7])
  763. fd.close()
  764. # Now build olduid -> newuid map from the reverse one.
  765. #
  766. for newuid, olduid in user_rename_reverse.items():
  767. user_rename[olduid] = newuid
  768. if opt.test:
  769. print 'rrs.log username maps'
  770. print
  771. print 'RENAME'
  772. print
  773. for k, v in user_rename.items():
  774. print k, '->', v
  775. print
  776. print 'CONVERT'
  777. print
  778. for k in user_convert.keys():
  779. print k
  780. print
  781. print 'RESETPW'
  782. print
  783. for k, v in reset_password.items():
  784. if v: print k
  785. print
  786. #-------------#
  787. # sync_rename #
  788. #-------------#
  789. print '===> start sync_rename'
  790. pause()
  791. for olduid, newuid in user_rename.items():
  792. oldusr = RBUser(uid = olduid, homeDirectory = old_ldap[olduid]['homeDirectory'])
  793. newusr = RBUser(uid = newuid)
  794. udb.get_user_byname(newusr)
  795. try:
  796. acc.check_account_byname(oldusr)
  797. except RBFatalError:
  798. # Old account doesn't exist, must be renamed already.
  799. if opt.test:
  800. print 'SKIPPED: account rename: %s -> %s' % (olduid, newuid)
  801. else:
  802. print 'Account renamed: %s -> %s' % (olduid, newuid)
  803. acc.rename(oldusr, newusr)
  804. #pause()
  805. #--------------#
  806. # sync_convert #
  807. #--------------#
  808. print '\n===> start sync_convert'
  809. pause()
  810. for newuid in user_convert.keys():
  811. olduid = user_rename_reverse.get(newuid, newuid)
  812. if not old_ldap.has_key(olduid):
  813. print 'WARNING: Existing non newbie user', newuid, 'not in previous copy of ldap tree!'
  814. continue
  815. oldusr = RBUser(uid = olduid, homeDirectory = old_ldap[olduid]['homeDirectory'], usertype = old_ldap[olduid]['usertype'])
  816. newusr = RBUser(uid = newuid)
  817. udb.get_user_byname(newusr)
  818. # If old and new usertypes are the same, they were temporarily
  819. # or accidentally converted to a different usertype then
  820. # converted back.
  821. #
  822. if oldusr.usertype == newusr.usertype:
  823. continue
  824. try:
  825. acc.check_account_byname(oldusr)
  826. except RBFatalError:
  827. # Old account doesn't exist, must be converted already.
  828. if opt.test:
  829. print 'SKIPPED: account convert: %s: %s -> %s' % (oldusr.uid, oldusr.usertype, newusr.usertype)
  830. else:
  831. print 'Account converted: %s: %s -> %s' % (oldusr.uid, oldusr.usertype, newusr.usertype)
  832. acc.convert(oldusr, newusr)
  833. #pause()
  834. #-------------#
  835. # sync_delete #
  836. #-------------#
  837. #print '\n===> start sync_delete'
  838. #pause()
  839. #for pw in pwd.getpwall():
  840. # try:
  841. # udb.check_user_byname(pw[0])
  842. # except RBError:
  843. # # User doesn't exist in database, ask to delete it.
  844. # #
  845. # if yesno("Delete account %s" % pw[0]):
  846. # print 'Account deleted: %s' % pw[0]
  847. # acc.delete(pw[0])
  848. # else:
  849. # # User exists in database, do nothing!
  850. # pass
  851. #----------#
  852. # sync_add #
  853. #----------#
  854. print '\n===> start sync_add'
  855. pause()
  856. for username in udb.list_newbies():
  857. usr = RBUser(uid = username)
  858. udb.get_user_byname(usr)
  859. try:
  860. acc.check_account_byname(usr)
  861. except RBFatalError:
  862. usr.passwd = rbconfig.gen_passwd()
  863. # print 'Account password set for %s password: %s' % (usr.uid, usr.passwd)
  864. udb.set_passwd(usr)
  865. print "Account created: %s %s" % (usr.usertype, usr.uid)
  866. acc.add(usr)
  867. print "User mailed:", usr.altmail
  868. mailuser(usr)
  869. #pause()
  870. else:
  871. # New account exists, must be created already.
  872. if opt.test:
  873. print 'SKIPPED: account create:', usr.usertype, usr.uid
  874. #------------#
  875. # sync_renew #
  876. #------------#
  877. print '\n===> start sync_renew'
  878. pause()
  879. if not os.path.isdir('renewal_mailed'):
  880. os.mkdir('renewal_mailed')
  881. for newuid in udb.list_paid_non_newbies():
  882. action = 0
  883. olduid = user_rename_reverse.get(newuid, newuid)
  884. if not old_ldap.has_key(olduid):
  885. print 'WARNING: Existing non newbie user', newuid, 'not in previous copy of ldap tree!'
  886. continue
  887. newusr = RBUser(uid = newuid)
  888. udb.get_user_byname(newusr)
  889. try:
  890. acc.check_account_byname(newusr)
  891. except RBFatalError:
  892. # Accounts should be renamed & converted by now, so we
  893. # should never get here!
  894. #
  895. print "SKIPPED: User", newuid, "missing account. Earlier rename/conversion not completed?"
  896. continue
  897. if not udb.valid_shell(newusr.loginShell):
  898. newusr.loginShell = udb.get_backup_shell(olduid)
  899. print 'Account shell reset for:', newuid, '(%s)' % newusr.loginShell
  900. udb.set_shell(newusr)
  901. action = 1
  902. if not os.path.exists('renewal_mailed/%s' % newusr.uid):
  903. # Set a new password if they need one.
  904. #
  905. if reset_password.get(newuid):
  906. newusr.passwd = rbconfig.gen_passwd()
  907. print 'Account password reset for %s password: %s' % (newuid, newusr.passwd)
  908. udb.set_passwd(newusr)
  909. action = 1
  910. # Send a mail to people who renewed. All renewals should have
  911. # an entry in reset_password i.e. 0 or 1.
  912. #
  913. if reset_password.has_key(newuid):
  914. print 'User mailed:', newusr.uid, '(%s)' % newusr.altmail
  915. mailuser(newusr)
  916. action = 1
  917. # Flag this user as mailed so we don't do it again if
  918. # sync is rerun.
  919. #
  920. if not opt.test:
  921. open('renewal_mailed/%s' % newusr.uid, 'w').close()
  922. elif opt.test:
  923. print 'SKIPPED: User mailed:', newusr.uid
  924. #if action:
  925. # pause()
  926. print
  927. print 'sync completed.'
  928. def sync_dcu_info():
  929. """Interactive update of user database using dcu database information."""
  930. raise RBFatalError("NOT IMPLEMENTED YET")
  931. print 'Comparing user and DCU databases. NOTE: please be patient'
  932. print 'this takes some time...\n'
  933. # Set options for override & test mode.
  934. udb.setopt(opt)
  935. acc.setopt(opt)
  936. #-----------------------------------------------------------------------------#
  937. # BATCH INFORMATION COMMANDS #
  938. #-----------------------------------------------------------------------------#
  939. def list_users():
  940. """List all usernames."""
  941. for username in udb.list_users():
  942. print username
  943. def list_newbies():
  944. """List all paid newbies."""
  945. for username in udb.list_paid_newbies():
  946. print username
  947. def list_renewals():
  948. """List all paid renewals (non-newbie)."""
  949. for username in udb.list_paid_non_newbies():
  950. print username
  951. def list_unavailable():
  952. """List all usernames that are taken."""
  953. for username in udb.list_reserved_all():
  954. print username
  955. def list_unpaid():
  956. """Print list of all non-renewed users."""
  957. for username in udb.list_unpaid():
  958. print username
  959. def list_unpaid_normal():
  960. """Print list of all normal non-renewed users."""
  961. for username in udb.list_unpaid_normal():
  962. print username
  963. def list_unpaid_reset():
  964. """Print list of all normal non-renewed users with reset shells (i.e. not expired)."""
  965. for username in udb.list_unpaid_reset():
  966. print username
  967. def list_unpaid_grace():
  968. """Print list of all grace non-renewed users."""
  969. for username in udb.list_unpaid_grace():
  970. print username
  971. #-----------------------------------------------------------------------------#
  972. # BATCH COMMANDS #
  973. #-----------------------------------------------------------------------------#
  974. def newyear():
  975. """Prepare database for start of new academic year."""
  976. raise RBFatalError("NOT IMPLEMENTED YET")
  977. # Set options for override & test mode.
  978. udb.setopt(opt)
  979. acc.setopt(opt)
  980. print 'Prepared database for start of new academic year'
  981. udb.newyear()
  982. def unpaid_warn():
  983. """Mail a reminder/warning message to all non-renewed users."""
  984. # Set options for override & test mode.
  985. udb.setopt(opt)
  986. acc.setopt(opt)
  987. for username in udb.list_unpaid():
  988. usr = RBUser(uid = username)
  989. udb.get_user_byname(usr)
  990. print "Warned user:", username
  991. mail_unpaid(usr)
  992. def unpaid_disable():
  993. """Disable all normal non-renewed users."""
  994. # Set options for override & test mode.
  995. udb.setopt(opt)
  996. acc.setopt(opt)
  997. for username in udb.list_unpaid_reset():
  998. print "Account disabled:", username
  999. udb.set_shell(RBUser(uid = username, loginShell = rbconfig.shell_expired))
  1000. def unpaid_delete():
  1001. """Delete all grace non-renewed users."""
  1002. # Set options for override & test mode.
  1003. udb.setopt(opt)
  1004. acc.setopt(opt)
  1005. for username in udb.list_unpaid_grace():
  1006. usr = RBUser(uid = username)
  1007. udb.get_user_byname(usr)
  1008. print 'User deleted:', username
  1009. udb.delete(usr)
  1010. print 'Account deleted:', username
  1011. acc.delete(usr)
  1012. #-----------------------------------------------------------------------------#
  1013. # MISCELLANEOUS COMMANDS #
  1014. #-----------------------------------------------------------------------------#
  1015. def checkdb():
  1016. """Check database for inconsistencies."""
  1017. uidNumbers = {}
  1018. re_mail = re.compile(r'.+@.*dcu\.ie', re.I)
  1019. set_header('User database problems')
  1020. unpaid_valid_shells = 0
  1021. reserved = udb.dict_reserved_desc()
  1022. for uid in udb.list_users():
  1023. usr = RBUser(uid=uid)
  1024. udb.get_user_byname(usr)
  1025. desc = reserved.get(uid)
  1026. if desc:
  1027. show_header()
  1028. print '%-*s is reserved: %s' % (rbconfig.maxlen_uname, uid, desc)
  1029. if not uidNumbers.has_key(usr.uidNumber):
  1030. uidNumbers[usr.uidNumber] = [uid]
  1031. else:
  1032. uidNumbers[usr.uidNumber].append(uid)
  1033. if usr.usertype == 'member':
  1034. try:
  1035. udb.get_student_byid(usr)
  1036. except RBWarningError:
  1037. show_header()
  1038. print '%-*s is a member without a valid DCU student id: %s' % (rbconfig.maxlen_uname, uid, usr.id)
  1039. if usr.yearsPaid != None:
  1040. if not -1 <= usr.yearsPaid <= 5:
  1041. show_header()
  1042. print '%-*s has bogus yearsPaid: %s' % (rbconfig.maxlen_uname, uid, usr.yearsPaid)
  1043. if usr.newbie and usr.yearsPaid < 1:
  1044. show_header()
  1045. print '%-*s is a newbie but is unpaid (yearsPaid = %s)' % (rbconfig.maxlen_uname, uid, usr.yearsPaid)
  1046. if usr.yearsPaid < 1 and udb.valid_shell(usr.loginShell):
  1047. unpaid_valid_shells += 1
  1048. if usr.yearsPaid > 0 and not udb.valid_shell(usr.loginShell):
  1049. show_header()
  1050. print '%-*s is paid but has an invalid shell: %s' % (rbconfig.maxlen_uname, uid, usr.loginShell)
  1051. if usr.yearsPaid < -1:
  1052. print '%-*s has should have been deleted: %s' % (rbconfig.maxlen_uname, uid, usr.yearsPaid)
  1053. if usr.yearsPaid == None and usr.usertype in ('member', 'associat', 'staff'):
  1054. show_header()
  1055. print '%-*s is missing a yearsPaid attribute' % (rbconfig.maxlen_uname, uid)
  1056. if usr.id == None and usr.usertype in ('member', 'associat', 'staff'):
  1057. show_header()
  1058. print '%-*s is missing a DCU ID number' % (rbconfig.maxlen_uname, uid)
  1059. for dir, desc in (usr.homeDirectory, 'home'), (rbconfig.gen_webtree(uid), 'webtree'):
  1060. if not os.path.exists(dir) or not os.path.isdir(dir):
  1061. show_header()
  1062. print '%-*s is missing %s directory: %s' % (rbconfig.maxlen_uname, uid, desc, dir)
  1063. else:
  1064. stat = os.stat(dir)
  1065. if (stat.st_uid, stat.st_gid) != (usr.uidNumber, usr.gidNumber):
  1066. show_header()
  1067. print '%-*s has wrong %s ownership' % (rbconfig.maxlen_uname, uid, desc)
  1068. if stat.st_mode & 0020:
  1069. show_header()
  1070. print '%-*s has group writeable %s' % (rbconfig.maxlen_uname, uid, desc)
  1071. if stat.st_mode & 0002:
  1072. show_header()
  1073. print '%-*s has WORLD writeable %s' % (rbconfig.maxlen_uname, uid, desc)
  1074. try:
  1075. grp = udb.get_group_byid(usr.gidNumber)
  1076. except RBFatalError:
  1077. grp = '#%d' % usr.gidNumber
  1078. show_header()
  1079. print '%-*s has unknown gidNumber: %d' % (rbconfig.maxlen_uname, uid, usr.gidNumber)
  1080. if usr.usertype in ('member', 'staff', 'committe') and \
  1081. (usr.altmail.lower().find('%s@redbrick.dcu.ie' % usr.uid) != -1 or \
  1082. not re.search(re_mail, usr.altmail)):
  1083. show_header()
  1084. print "%-*s is a %s without a DCU altmail address: %s" % (rbconfig.maxlen_uname, uid, usr.usertype, usr.altmail)
  1085. # commented by receive, it makes stuff crash
  1086. # if not usr.userPassword[7].isalnum() and not usr.userPassword[7] in '/.':
  1087. # show_header()
  1088. # print '%-*s has a disabled password: %s' % (rbconfig.maxlen_uname, uid, usr.userPassword)
  1089. if usr.usertype != 'redbrick':
  1090. if grp != usr.usertype:
  1091. show_header()
  1092. print '%-*s has different group [%s] and usertype [%s]' % (rbconfig.maxlen_uname, uid, grp, usr.usertype)
  1093. if usr.homeDirectory != rbconfig.gen_homedir(uid, usr.usertype):
  1094. show_header()
  1095. print '%-*s has wrong home directory [%s] for usertype [%s]' % (rbconfig.maxlen_uname, uid, usr.homeDirectory, usr.usertype)
  1096. if unpaid_valid_shells > 0:
  1097. show_header()
  1098. print
  1099. print "There are %d shifty gits on redbrick. Unpaid users with valid" % unpaid_valid_shells
  1100. print "login shells, that is. Go get 'em."
  1101. set_header('Duplicate uidNumbers')
  1102. for uidNumber, uids in uidNumbers.items():
  1103. if len(uids) > 1:
  1104. show_header()
  1105. print '%d is shared by: %s' % (uidNumber, ', '.join(uids))
  1106. def stats():
  1107. """Show database and account statistics."""
  1108. print header('User database stats')
  1109. udb.stats()
  1110. def create_uidNumber():
  1111. """Fine next available uidNumber and write it out to uidNumber text file."""
  1112. n = udb.uidNumber_findmax() + 1
  1113. print 'Next available uidNumber:', n
  1114. fd = open(rbconfig.file_uidNumber, 'w')
  1115. fd.write('%s\n' % n)
  1116. fd.close()
  1117. #-----------------------------------------------------------------------------#
  1118. # USER INPUT FUNCTIONS #
  1119. #-----------------------------------------------------------------------------#
  1120. def ask(prompt, default = None, optional = 0, hints = None):
  1121. """Ask a question using given prompt and return user's answer.
  1122. A default answer maybe provided which is returned if no answer is given
  1123. (i.e. user hits RETURN).
  1124. If optional is false (the default) an answer is required. So if no
  1125. default answer is given and the user presses RETURN, the question will
  1126. be repeated.
  1127. If optional is true, the first answer given is returned. If a default
  1128. is provided an empty answer may be given by sending EOF (typically
  1129. Control-D). If no default is provided an empty answer may be given by
  1130. pressing RETURN.
  1131. If there is more than one default answer these can be provided in the
  1132. form of "hints" which the user can cycle through by hitting TAB. The
  1133. default answer is also available by pressing TAB.
  1134. """
  1135. def complete(text, state):
  1136. """Completion function used by readline module for ask().
  1137. If no text typed in yet, offer all hints. Otherwise offer only
  1138. hints that begin with the given text.
  1139. """
  1140. if not text and len(hints) > state:
  1141. return str(hints[state])
  1142. else:
  1143. tmp = [i for i in hints if i.startswith(text)]
  1144. if len(tmp) > state:
  1145. return str(tmp[state])
  1146. return None
  1147. global input_instructions
  1148. if input_instructions:
  1149. print input_instructions
  1150. input_instructions = None
  1151. if hints == None:
  1152. hints = []
  1153. if default == None:
  1154. defans = 'no default'
  1155. else:
  1156. defans = default
  1157. hints = [i for i in hints if i != None]
  1158. num_hints = len(hints)
  1159. if default != None:
  1160. if default not in hints:
  1161. hints.insert(0, default)
  1162. else:
  1163. num_hints -= 1
  1164. prompt = '%s\n%s%s[%s] >> ' % (prompt, optional and '(optional) ' or '', num_hints and '(hints) ' or '', defans)
  1165. readline.parse_and_bind('tab: menu-complete')
  1166. readline.set_completer(complete)
  1167. ans = None
  1168. while ans == None or ans == '':
  1169. try:
  1170. ans = raw_input(prompt)
  1171. except EOFError:
  1172. print
  1173. ans = None
  1174. else:
  1175. if not ans:
  1176. ans = default
  1177. print
  1178. if optional:
  1179. break
  1180. return ans
  1181. def yesno(prompt, default = None):
  1182. """Prompt for confirmation to a question. Returns boolean."""
  1183. global input_instructions
  1184. if input_instructions:
  1185. print input_instructions
  1186. input_instructions = None
  1187. if default == None:
  1188. defans = 'no default'
  1189. else:
  1190. if default: defans = 'yes'
  1191. else: defans = 'no'
  1192. prompt = '%s\n[%s] (Y/N) ? ' % (prompt, defans)
  1193. ans = None
  1194. while 1:
  1195. try:
  1196. ans = raw_input(prompt)
  1197. except EOFError:
  1198. print
  1199. ans = None
  1200. else:
  1201. print
  1202. if not ans and not default == None:
  1203. return default
  1204. if ans:
  1205. if re.search(r'^[yY]', ans):
  1206. return 1
  1207. elif re.search(r'^[nN]', ans):
  1208. return 0
  1209. def pause():
  1210. """Prompt for user input to continue."""
  1211. print 'Press RETURN to continue...'
  1212. try:
  1213. raw_input()
  1214. except EOFError:
  1215. pass
  1216. #-----------------------------------------------------------------------------#
  1217. # MISCELLANEOUS FUNCTIONS #
  1218. #-----------------------------------------------------------------------------#
  1219. def header(mesg):
  1220. """Return a simple header string for given message."""
  1221. return '\n' + mesg + '\n' + '=' * len(mesg)
  1222. def set_header(mesg):
  1223. """Set the heading for the next section."""
  1224. global header_mesg
  1225. header_mesg = header(mesg)
  1226. def show_header():
  1227. """Display the heading for the current section as
  1228. set by set_header(). Will only print header once."""
  1229. global header_mesg
  1230. if header_mesg:
  1231. print header_mesg
  1232. header_mesg = None
  1233. #-----------------------------------------------------------------------------#
  1234. # USER MAILING FUNCTIONS #
  1235. #-----------------------------------------------------------------------------#
  1236. def mailuser(usr):
  1237. """Mail user's account details to their alternate email address."""
  1238. fd = sendmail_open()
  1239. fd.write(
  1240. """From: Redbrick Admin Team <admins@redbrick.dcu.ie>
  1241. Subject: Welcome to Redbrick! - Your Account Details
  1242. To: %s
  1243. Reply-To: admin-request@redbrick.dcu.ie
  1244. """ % usr.altmail)
  1245. if usr.newbie:
  1246. fd.write("Welcome to Redbrick, the DCU Networking Society! Thank you for joining.")
  1247. else:
  1248. fd.write("Welcome back to Redbrick, the DCU Networking Society! Thank you for renewing.")
  1249. fd.write("\n\nYour Redbrick Account details are:\n\n")
  1250. fd.write('%21s: %s\n' % ('username', usr.uid))
  1251. if usr.passwd:
  1252. fd.write('%21s: %s\n\n' % ('password', usr.passwd))
  1253. fd.write('%21s: %s\n' % ('account type', usr.usertype))
  1254. fd.write('%21s: %s\n' % ('name', usr.cn))
  1255. if usr.id != None:
  1256. fd.write('%21s: %s\n' % ('id number', usr.id))
  1257. if usr.course:
  1258. fd.write('%21s: %s\n' % ('course', usr.course))
  1259. if usr.year != None:
  1260. fd.write('%21s: %s\n' % ('year', usr.year))
  1261. fd.write(
  1262. """
  1263. -------------------------------------------------------------------------------
  1264. your Redbrick webpage: http://www.redbrick.dcu.ie/~%s
  1265. your Redbrick email: %s@redbrick.dcu.ie
  1266. You can find out more about our services at:
  1267. http://www.redbrick.dcu.ie/about/welcome
  1268. """ % (usr.uid, usr.uid))
  1269. fd.write(
  1270. """
  1271. We recommend that you change your password as soon as you login.
  1272. Problems with your password or wish to change your username? Contact:
  1273. admin-request@redbrick.dcu.ie
  1274. Problems using Redbrick in general or not sure what to do? Contact:
  1275. helpdesk-request@redbrick.dcu.ie
  1276. Have fun!
  1277. - Redbrick Admin Team
  1278. """)
  1279. sendmail_close(fd)
  1280. def mail_unpaid(usr):
  1281. """Mail a warning to a non-renewed user."""
  1282. fd = sendmail_open()
  1283. fd.write(
  1284. """From: Redbrick Admin Team <admins@redbrick.dcu.ie>
  1285. Subject: Time to renew your Redbrick account! - Note our Bank A/C Details have changed.
  1286. To: %s@redbrick.dcu.ie
  1287. """ % usr.uid)
  1288. if usr.altmail.lower().find('%s@redbrick.dcu.ie' % usr.uid) == -1:
  1289. print >> fd, 'Cc:', usr.altmail
  1290. fd.write(
  1291. """Reply-To: accounts@redbrick.dcu.ie
  1292. Hey there,
  1293. It's that time again to renew your Redbrick account!
  1294. Membership prices, as set by the SLC, are as follows:
  1295. Members EUR 4
  1296. Associates EUR 8
  1297. Staff EUR 8
  1298. Guests EUR 10
  1299. Note: if you have left DCU, you need to apply for associate membership.
  1300. You can pay in person, by lodging money into our account, electronic bank
  1301. transfer, or even PayPal! All the details you need are here:
  1302. !!NOTE!!
  1303. PayPal doesn't allow the addition of Payment notes, Please email
  1304. accounts@redbrick.dcu.ie if you pay by PayPal
  1305. !!NOTE!!
  1306. http://www.redbrick.dcu.ie/help/joining/
  1307. Our bank details are:
  1308. a/c name: DCU Redbrick Society
  1309. IBAN: IE59BOFI90675027999600
  1310. BIC: BOFIIE2D
  1311. a/c number: 27999600
  1312. sort code: 90 - 67 - 50
  1313. Please Note!
  1314. ------------""")
  1315. ######Change the message below every year######
  1316. if usr.yearsPaid == 0:
  1317. fd.write(
  1318. """
  1319. If you do not renew by the 30th November 2016, your account will be disabled.
  1320. Your account will remain on the system for a grace period of a year - you
  1321. just won't be able to login. So don't worry, it won't be deleted any time
  1322. soon! You can renew at any time during the year.
  1323. """)
  1324. else:
  1325. fd.write(
  1326. """
  1327. If you do not renew within the following month, your account WILL BE
  1328. DELETED at the start of the new year. This is because you were not
  1329. recorded as having paid for last year and as such are nearing the end of
  1330. your one year 'grace' period to renew. Please make sure to renew as soon
  1331. as possible otherwise please contact us at: accounts@redbrick.dcu.ie.
  1332. """)
  1333. fd.write(
  1334. """
  1335. If in fact you have renewed and have received this email in error, it is
  1336. important you let us know. Just reply to this email and tell us how and
  1337. when you renewed and we'll sort it out.
  1338. For your information, your current Redbrick account details are:
  1339. username: %s
  1340. account type: %s
  1341. name: %s
  1342. alternative email: %s
  1343. """ % (usr.uid, usr.usertype, usr.cn, usr.altmail))
  1344. if usr.id != None:
  1345. fd.write('%21s: %s\n' % ('id number', usr.id))
  1346. if usr.course:
  1347. fd.write('%21s: %s\n' % ('course', usr.course))
  1348. if usr.year != None:
  1349. fd.write('%21s: %s\n' % ('year', usr.year))
  1350. fd.write(
  1351. """
  1352. If any of the above details are wrong, please correct them when you
  1353. renew!
  1354. - Redbrick Admin Team
  1355. """)
  1356. sendmail_close(fd)
  1357. def mail_committee(subject, body):
  1358. """Email committee with given subject and message body."""
  1359. fd = sendmail_open()
  1360. fd.write(
  1361. """From: Redbrick Admin Team <admins@redbrick.dcu.ie>
  1362. Subject: %s
  1363. To: committee@redbrick.dcu.ie
  1364. %s
  1365. """ % (subject, body))
  1366. sendmail_close(fd)
  1367. def sendmail_open():
  1368. """Return file descriptor to write email message to."""
  1369. if opt.test:
  1370. print >> sys.stderr, header('Email message that would be sent')
  1371. return sys.stderr
  1372. else:
  1373. return os.popen('%s -t -i' % rbconfig.command_sendmail, 'w')
  1374. def sendmail_close(fd):
  1375. """Close sendmail file descriptor."""
  1376. if not opt.test:
  1377. fd.close()
  1378. #-----------------------------------------------------------------------------#
  1379. # GET USER DATA FUNCTIONS #
  1380. #-----------------------------------------------------------------------------#
  1381. def get_username(usr, check_user_exists = 1):
  1382. """Get an existing username."""
  1383. if len(opt.args) > 0 and opt.args[0]:
  1384. usr.uid = opt.uid = opt.args.pop(0)
  1385. interact = 0
  1386. else:
  1387. interact = 1
  1388. while 1:
  1389. if interact:
  1390. usr.uid = ask('Enter username')
  1391. try:
  1392. udb.check_username(usr.uid)
  1393. if check_user_exists:
  1394. print "Checking user exists"
  1395. tmpusr = RBUser(uid = usr.uid)
  1396. udb.get_user_byname(tmpusr)
  1397. udb.check_user_byname(usr.uid)
  1398. acc.check_account_byname(tmpusr)
  1399. except RBError, e:
  1400. if not rberror(e, interact):
  1401. break
  1402. else:
  1403. break
  1404. if not interact:
  1405. break
  1406. def get_freeusername(usr):
  1407. """Get a new (free) username."""
  1408. if len(opt.args) > 0 and opt.args[0]:
  1409. usr.uid = opt.uid = opt.args.pop(0)
  1410. interact = 0
  1411. else:
  1412. interact = 1
  1413. while 1:
  1414. if interact:
  1415. usr.uid = ask('Enter new username')
  1416. try:
  1417. udb.check_username(usr.uid)
  1418. udb.check_userfree(usr.uid)
  1419. except RBError, e:
  1420. if not rberror(e, interact):
  1421. break
  1422. else:
  1423. break
  1424. if not interact:
  1425. return 0
  1426. return 1
  1427. def get_usertype(usr):
  1428. """Get usertype."""
  1429. usr.oldusertype = usr.usertype
  1430. if opt.usertype:
  1431. usr.usertype = opt.usertype
  1432. interact = 0
  1433. else:
  1434. interact = 1
  1435. print "Usertype must be specified. List of valid usertypes:\n"
  1436. for i in rbconfig.usertypes_list:
  1437. if opt.mode != 'renew' or i in rbconfig.usertypes_paying:
  1438. print " %-12s %s" % (i, rbconfig.usertypes[i])
  1439. print
  1440. defans = usr.usertype or 'member'
  1441. while 1:
  1442. if interact:
  1443. usr.usertype = ask('Enter usertype', defans, hints = [i for i in rbconfig.usertypes_list if opt.mode != 'renew' or i in rbconfig.usertypes_paying])
  1444. try:
  1445. if opt.mode == 'renew':
  1446. udb.check_renewal_usertype(usr.usertype)
  1447. else:
  1448. udb.check_usertype(usr.usertype)
  1449. except RBError, e:
  1450. if not rberror(e, interact):
  1451. break
  1452. else:
  1453. break
  1454. def get_convert_usertype(usr):
  1455. """Get usertype to convert to."""
  1456. if opt.usertype:
  1457. usr.usertype = opt.usertype
  1458. interact = 0
  1459. else:
  1460. interact = 1
  1461. print "Conversion usertype must be specified. List of valid usertypes:\n"
  1462. for i in rbconfig.usertypes_list:
  1463. print " %-12s %s" % (i, rbconfig.usertypes[i])
  1464. print "\nSpecial committee positions (usertype is 'committe'):\n"
  1465. for i, j in rbconfig.convert_usertypes.items():
  1466. print " %-12s %s" % (i, j)
  1467. print
  1468. while 1:
  1469. if interact:
  1470. usr.usertype = ask('Enter conversion usertype', hints = list(rbconfig.usertypes_list) + rbconfig.convert_usertypes.keys())
  1471. try:
  1472. udb.check_convert_usertype(usr.usertype)
  1473. except RBError, e:
  1474. if not rberror(e, interact):
  1475. break
  1476. else:
  1477. break
  1478. def get_id(usr):
  1479. """Get DCU ID."""
  1480. if usr.usertype not in rbconfig.usertypes_dcu and opt.mode != 'update':
  1481. return
  1482. if opt.id != None:
  1483. usr.id = opt.id
  1484. interact = 0
  1485. else:
  1486. interact = 1
  1487. defans = usr.id
  1488. while 1:
  1489. if interact:
  1490. usr.id = ask('Enter student/staff id', defans, optional = opt.mode == 'update' or usr.usertype == 'committe')
  1491. try:
  1492. if usr.id:
  1493. usr.id = int(usr.id)
  1494. udb.check_id(usr)
  1495. except (ValueError, RBError), e:
  1496. if not rberror(e, interact):
  1497. break
  1498. else:
  1499. break
  1500. def get_name(usr, hints = None):
  1501. """Get name (or account description)."""
  1502. if opt.cn:
  1503. usr.cn = opt.cn
  1504. interact = 0
  1505. else:
  1506. interact = 1
  1507. defans = usr.cn
  1508. while 1:
  1509. if interact:
  1510. usr.cn = ask("Enter name (or account description)", defans, hints = hints)
  1511. try:
  1512. udb.check_name(usr)
  1513. except RBError, e:
  1514. if not rberror(e, interact):
  1515. break
  1516. else:
  1517. break
  1518. def get_mailuser(usr):
  1519. """Ask wheter to mail user their details."""
  1520. if opt.mailuser != None:
  1521. return
  1522. if not usr.usertype == 'reserved':
  1523. # By default mail them.
  1524. opt.mailuser = 1
  1525. # If only adding database entry, don't mail.
  1526. if opt.mode == 'add' and opt.dbonly:
  1527. opt.mailuser = 0
  1528. opt.mailuser = yesno('Mail account details to user', opt.mailuser)
  1529. else:
  1530. opt.mailuser = 0
  1531. def get_createaccount(usr):
  1532. """Ask if account should be created."""
  1533. if opt.dbonly != None and opt.aconly != None:
  1534. return
  1535. if not yesno('Create account', 1):
  1536. opt.dbonly = 1
  1537. opt.aconly = 0
  1538. def get_setpasswd(usr):
  1539. """Ask if new random password should be set."""
  1540. if opt.setpasswd != None:
  1541. return
  1542. # XXX
  1543. #if opt.dbonly != None:
  1544. # opt.setpasswd = not opt.dbonly
  1545. # return
  1546. if opt.mode == 'renew':
  1547. opt.setpasswd = 0
  1548. else:
  1549. opt.setpasswd = 1
  1550. opt.setpasswd = yesno('Set new random password', opt.setpasswd)
  1551. def get_newbie(usr):
  1552. """Get newbie boolean."""
  1553. if opt.newbie != None:
  1554. usr.newbie = opt.newbie
  1555. return
  1556. usr.newbie = yesno('Flag as a new user', usr.newbie)
  1557. def get_years_paid(usr):
  1558. """Get years paid."""
  1559. if not usr.usertype in rbconfig.usertypes_paying and opt.mode != 'update':
  1560. return
  1561. if opt.yearsPaid != None:
  1562. usr.yearsPaid = opt.yearsPaid
  1563. interact = 0
  1564. else:
  1565. interact = 1
  1566. if opt.mode == 'add' and usr.yearsPaid == None:
  1567. usr.yearsPaid = 1
  1568. defans = usr.yearsPaid
  1569. while 1:
  1570. if interact:
  1571. usr.yearsPaid = ask('Enter number of years paid', defans, optional = opt.mode == 'update' or usr.usertype in ('committe', 'guest'))
  1572. try:
  1573. if usr.yearsPaid:
  1574. usr.yearsPaid = int(usr.yearsPaid)
  1575. udb.check_years_paid(usr)
  1576. except (ValueError, RBError), e:
  1577. if not rberror(e, interact):
  1578. break
  1579. else:
  1580. break
  1581. def get_course(usr, hints = None):
  1582. """Get DCU course."""
  1583. if usr.usertype not in ('member', 'committee') and opt.mode != 'update':
  1584. return
  1585. if opt.course:
  1586. usr.course = opt.course
  1587. return
  1588. usr.course = ask('Enter course', usr.course, optional = opt.mode == 'update' or usr.usertype == 'committe', hints = hints)
  1589. def get_year(usr, hints = None):
  1590. """Get DCU year."""
  1591. if usr.usertype not in ('member', 'committee') and opt.mode != 'update':
  1592. return
  1593. if opt.year != None:
  1594. usr.year = opt.year
  1595. return
  1596. usr.year = ask('Enter year', usr.year, optional = opt.mode == 'update' or usr.usertype == 'committe', hints = hints)
  1597. def get_email(usr, hints = None):
  1598. """Get alternative email address."""
  1599. if opt.altmail:
  1600. usr.altmail = opt.altmail
  1601. interact = 0
  1602. else:
  1603. interact = 1
  1604. defans = usr.altmail
  1605. while 1:
  1606. if interact:
  1607. usr.altmail = ask('Enter email', defans, hints = hints)
  1608. try:
  1609. udb.check_email(usr)
  1610. except RBError, e:
  1611. if not rberror(e, interact):
  1612. break
  1613. else:
  1614. break
  1615. def get_updatedby(usr):
  1616. """Get username of who is performing the action.
  1617. Uses LOGNAME environment variable by default unless it's 'root' in
  1618. which case no default is provided. There is no actual restriction on
  1619. using 'root', although its use (or any other generic username) is
  1620. strongly not recommended.
  1621. """
  1622. if opt.updatedby:
  1623. usr.updatedby = opt.updatedby
  1624. interact = 0
  1625. else:
  1626. interact = 1
  1627. usr.updatedby = os.environ.get('LOGNAME') or os.environ.get('SU_FROM')
  1628. defans = usr.updatedby
  1629. while 1:
  1630. if interact:
  1631. usr.updatedby = ask('Enter who updated this user (give Unix username)', defans)
  1632. try:
  1633. udb.check_updatedby(usr.updatedby)
  1634. except RBError, e:
  1635. if not rberror(e, interact):
  1636. break
  1637. else:
  1638. break
  1639. def get_birthday(usr):
  1640. """Get (optional) birthday."""
  1641. if not usr.usertype in rbconfig.usertypes_paying:
  1642. return
  1643. if opt.birthday != None:
  1644. usr.birthday = opt.birthday or None
  1645. interact = 0
  1646. else:
  1647. interact = 1
  1648. defans = usr.birthday
  1649. while 1:
  1650. if interact:
  1651. usr.birthday = ask("Enter birthday as 'YYYY-MM-DD'", defans, optional = 1)
  1652. try:
  1653. udb.check_birthday(usr)
  1654. except RBError, e:
  1655. if not rberror(e, interact):
  1656. break
  1657. else:
  1658. break
  1659. def get_disuser_period(usr):
  1660. """Get (optional) period of disuserment."""
  1661. if len(opt.args) > 0:
  1662. usr.disuser_period = opt.args[0]
  1663. interact = 0
  1664. else:
  1665. interact = 1
  1666. while 1:
  1667. if interact:
  1668. usr.disuser_period = ask("If the account is to be automatically re-enabled, enter a valid at(1) timespec,\ne.g: '5pm', '12am + 2 weeks', 'now + 1 month' (see at man page).", optional = 1)
  1669. try:
  1670. udb.check_disuser_period(usr)
  1671. except RBError, e:
  1672. if not rberror(e, interact):
  1673. break
  1674. else:
  1675. break
  1676. def get_disuser_message(usr):
  1677. """Get message to display when disusered user tries to log in."""
  1678. file = "%s/%s" % (rbconfig.dir_daft, usr.uid)
  1679. editor = os.environ.get('EDITOR', os.environ.get('VISUAL', 'vi'))
  1680. while 1:
  1681. if not os.path.isfile(file):
  1682. fd = open(file, "w")
  1683. fd.write("The contents of this file will be displayed when %s logs in.\nThe reason for disuserment should be placed here.\n" % (usr.uid))
  1684. fd.close()
  1685. mtime = os.path.getmtime(file)
  1686. os.system("%s %s" % (acc.shquote(editor), acc.shquote(file)))
  1687. if not os.path.isfile(file) or not os.path.getsize(file) or mtime == os.path.getmtime(file):
  1688. if not rberror(RBWarningError('Unchanged disuser message file detected'), 1):
  1689. break
  1690. else:
  1691. break
  1692. os.chmod(file, 0644)
  1693. def get_rrslog():
  1694. """Get name of RRS log file."""
  1695. if len(opt.args) > 0 and opt.args[0]:
  1696. opt.rrslog = opt.args.pop(0)
  1697. interact = 0
  1698. else:
  1699. interact = 1
  1700. while 1:
  1701. if interact:
  1702. opt.rrslog = ask('Enter name of RRS logfile', rbconfig.file_rrslog)
  1703. try:
  1704. open(opt.rrslog, 'r').close()
  1705. except IOError, e:
  1706. if not rberror(e, interact):
  1707. break
  1708. else:
  1709. break
  1710. def get_pre_sync():
  1711. """Get name of pre_sync file."""
  1712. if len(opt.args) > 0 and opt.args[0]:
  1713. opt.presync = opt.args.pop(0)
  1714. interact = 0
  1715. else:
  1716. interact = 1
  1717. while 1:
  1718. if interact:
  1719. opt.presync = ask('Enter name of pre_sync file', rbconfig.file_pre_sync)
  1720. try:
  1721. open(opt.presync, 'r').close()
  1722. except IOError, e:
  1723. if not rberror(e, interact):
  1724. break
  1725. else:
  1726. break
  1727. def get_shell(usr):
  1728. """Get user shell."""
  1729. if len(opt.args) > 0 and opt.args[0]:
  1730. usr.loginShell = opt.args.pop(0)
  1731. interact = 0
  1732. else:
  1733. interact = 1
  1734. defans = usr.loginShell
  1735. # XXX: gross hack to make dizer happy. preloads /etc/shells so we can
  1736. # pass it as hints below
  1737. #
  1738. udb.valid_shell('fuzz')
  1739. while 1:
  1740. if interact:
  1741. usr.loginShell = ask('Enter shell', defans, hints = [defans] + udb.valid_shells.keys())
  1742. try:
  1743. # XXX: valid_shell should raise an exception?
  1744. if not udb.valid_shell(usr.loginShell):
  1745. raise RBWarningError('Not a valid shell')
  1746. except RBError, e:
  1747. if not rberror(e, interact):
  1748. break
  1749. else:
  1750. break
  1751. def check_paid(usr):
  1752. if usr.yearsPaid != None and usr.yearsPaid < 1 and not yesno('WARNING: This user has not renewed, continue?', 0):
  1753. raise RBFatalError('Aborting, user has not paid.')
  1754. #-----------------------------------------------------------------------------#
  1755. # ERROR HANDLING #
  1756. #-----------------------------------------------------------------------------#
  1757. def rberror(e, interactive = 0):
  1758. """rberror(e[, interactive]) -> status
  1759. Handle (mostly) RBError exceptions.
  1760. Interactive: If e is a RBWarningError, prompt to override this error.
  1761. If overridden, return false. Otherwise and for all other errors,
  1762. return true.
  1763. Not interactive: If e is a RBWarningError and the override option was
  1764. set on the command line, return false. Otherwise and for all other
  1765. errors, exit the program.
  1766. """
  1767. res = None
  1768. if not isinstance(e, RBError):
  1769. print "FATAL:",
  1770. print e
  1771. if not isinstance(e, RBWarningError):
  1772. if interactive:
  1773. print
  1774. return 1
  1775. else:
  1776. if interactive:
  1777. print
  1778. if yesno('Ignore this error?'):
  1779. opt.override = 1
  1780. return 0
  1781. else:
  1782. return 1
  1783. elif opt.override:
  1784. print "[IGNORED]\n"
  1785. return 0
  1786. # If we reach here we're not in interactive mode and the override
  1787. # option wasn't set, so all errors result in program exit.
  1788. #
  1789. print
  1790. sys.exit(1)
  1791. def error(e, mesg = None):
  1792. """error(e[, mesg])
  1793. Handle general exceptions: prints the 'FATAL:' prefix, optional
  1794. message followed by the exception message. Exits program.
  1795. """
  1796. print "FATAL: ",
  1797. if mesg:
  1798. print mesg
  1799. print e
  1800. print
  1801. sys.exit(1)
  1802. #-----------------------------------------------------------------------------#
  1803. # If module is called as script, run main() #
  1804. #-----------------------------------------------------------------------------#
  1805. if __name__ == "__main__":
  1806. main()