16 import os |
16 import os |
17 import re |
17 import re |
18 import sys |
18 import sys |
19 import gettext |
19 import gettext |
20 from encodings.idna import ToASCII, ToUnicode |
20 from encodings.idna import ToASCII, ToUnicode |
|
21 from getpass import getpass |
21 from shutil import rmtree |
22 from shutil import rmtree |
22 from subprocess import Popen, PIPE |
23 from subprocess import Popen, PIPE |
23 |
24 |
24 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net |
25 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net |
25 |
26 |
70 self.__chkenv() |
71 self.__chkenv() |
71 |
72 |
72 def __chkCfgFile(self): |
73 def __chkCfgFile(self): |
73 """Checks the configuration file, returns bool""" |
74 """Checks the configuration file, returns bool""" |
74 if not os.path.isfile(self.__cfgFileName): |
75 if not os.path.isfile(self.__cfgFileName): |
75 raise VMMException((_(u"The file »%s« does not exists.") % |
76 raise VMMException((_(u"The file '%s' does not exists.") % |
76 self.__cfgFileName, ERR.CONF_NOFILE)) |
77 self.__cfgFileName, ERR.CONF_NOFILE)) |
77 fstat = os.stat(self.__cfgFileName) |
78 fstat = os.stat(self.__cfgFileName) |
78 try: |
79 try: |
79 fmode = self.__getFileMode() |
80 fmode = self.__getFileMode() |
80 except: |
81 except: |
137 if len(localpart) > 64: |
138 if len(localpart) > 64: |
138 raise VMMException((_('The local part is too long'), |
139 raise VMMException((_('The local part is too long'), |
139 ERR.LOCALPART_TOO_LONG)) |
140 ERR.LOCALPART_TOO_LONG)) |
140 if re.compile(RE_LOCALPART).search(localpart): |
141 if re.compile(RE_LOCALPART).search(localpart): |
141 raise VMMException(( |
142 raise VMMException(( |
142 _(u'The local part »%s« contains invalid characters.') % |
143 _(u"The local part '%s' contains invalid characters.") % |
143 localpart, ERR.LOCALPART_INVALID)) |
144 localpart, ERR.LOCALPART_INVALID)) |
144 return localpart |
145 return localpart |
145 |
146 |
146 def idn2ascii(self, domainname): |
147 def idn2ascii(self, domainname): |
147 """Converts an idn domainname in punycode. |
148 """Converts an idn domainname in punycode. |
189 |
190 |
190 def __chkEmailAddress(self, address): |
191 def __chkEmailAddress(self, address): |
191 try: |
192 try: |
192 localpart, domain = address.split('@') |
193 localpart, domain = address.split('@') |
193 except ValueError: |
194 except ValueError: |
194 raise VMMException((_(u"Missing '@' sign in e-mail address »%s«.") % |
195 raise VMMException((_(u"Missing '@' sign in e-mail address '%s'.") % |
195 address, ERR.INVALID_ADDRESS)) |
196 address, ERR.INVALID_ADDRESS)) |
196 except AttributeError: |
197 except AttributeError: |
197 raise VMMException((_(u"»%s« looks not like an e-mail address.") % |
198 raise VMMException((_(u"'%s' looks not like an e-mail address.") % |
198 address, ERR.INVALID_ADDRESS)) |
199 address, ERR.INVALID_ADDRESS)) |
199 domain = self.__chkDomainname(domain) |
200 domain = self.__chkDomainname(domain) |
200 localpart = self.__chkLocalpart(localpart) |
201 localpart = self.__chkLocalpart(localpart) |
201 return '%s@%s' % (localpart, domain) |
202 return '%s@%s' % (localpart, domain) |
202 |
203 |
204 address = self.__chkEmailAddress(address) |
205 address = self.__chkEmailAddress(address) |
205 self.__dbConnect() |
206 self.__dbConnect() |
206 if not password is None: |
207 if not password is None: |
207 password = self.__pwhash(password) |
208 password = self.__pwhash(password) |
208 return Account(self.__dbh, address, password) |
209 return Account(self.__dbh, address, password) |
|
210 |
|
211 def _readpass(self): |
|
212 clear0 = '' |
|
213 clear1 = '1' |
|
214 while clear0 != clear1: |
|
215 while len(clear0) < 1: |
|
216 clear0 = getpass(prompt=_('Enter new password: ')) |
|
217 if len(clear0) < 1: |
|
218 sys.stderr.write('%s\n' |
|
219 % _('Sorry, empty passwords are not permitted')) |
|
220 clear1 = getpass(prompt=_('Retype new password: ')) |
|
221 if clear0 != clear1: |
|
222 clear0 = '' |
|
223 sys.stderr.write('%s\n' % _('Sorry, passwords do not match')) |
|
224 return clear0 |
209 |
225 |
210 def __getAlias(self, address, destination=None): |
226 def __getAlias(self, address, destination=None): |
211 address = self.__chkEmailAddress(address) |
227 address = self.__chkEmailAddress(address) |
212 if not destination is None: |
228 if not destination is None: |
213 if destination.count('@'): |
229 if destination.count('@'): |
408 """ |
424 """ |
409 try: |
425 try: |
410 if not section: |
426 if not section: |
411 self.__Cfg.configure(self.__cfgSections) |
427 self.__Cfg.configure(self.__cfgSections) |
412 elif section not in self.__cfgSections: |
428 elif section not in self.__cfgSections: |
413 raise VMMException((_(u"Invalid section: »%s«") % section, |
429 raise VMMException((_(u"Invalid section: '%s'") % section, |
414 ERR.INVALID_SECTION)) |
430 ERR.INVALID_SECTION)) |
415 else: |
431 else: |
416 self.__Cfg.configure([section]) |
432 self.__Cfg.configure([section]) |
417 except: |
433 except: |
418 raise |
434 raise |
422 dom.save() |
438 dom.save() |
423 self.__domdirmake(dom.getDir(), dom.getID()) |
439 self.__domdirmake(dom.getDir(), dom.getID()) |
424 |
440 |
425 def domain_transport(self, domainname, transport, force=None): |
441 def domain_transport(self, domainname, transport, force=None): |
426 if force is not None and force != 'force': |
442 if force is not None and force != 'force': |
427 raise VMMDomainException((_(u'Invalid argument: »%s«') % force, |
443 raise VMMDomainException((_(u"Invalid argument: '%s'") % force, |
428 ERR.INVALID_OPTION)) |
444 ERR.INVALID_OPTION)) |
429 dom = self.__getDomain(domainname, None) |
445 dom = self.__getDomain(domainname, None) |
430 if force is None: |
446 if force is None: |
431 dom.updateTransport(transport) |
447 dom.updateTransport(transport) |
432 else: |
448 else: |
433 dom.updateTransport(transport, force=True) |
449 dom.updateTransport(transport, force=True) |
434 |
450 |
435 def domain_delete(self, domainname, force=None): |
451 def domain_delete(self, domainname, force=None): |
436 if not force is None and force not in ['deluser','delalias','delall']: |
452 if not force is None and force not in ['deluser','delalias','delall']: |
437 raise VMMDomainException((_(u'Invalid argument: »%s«') % force, |
453 raise VMMDomainException((_(u"Invalid argument: '%s'") % force, |
438 ERR.INVALID_OPTION)) |
454 ERR.INVALID_OPTION)) |
439 dom = self.__getDomain(domainname) |
455 dom = self.__getDomain(domainname) |
440 gid = dom.getID() |
456 gid = dom.getID() |
441 domdir = dom.getDir() |
457 domdir = dom.getDir() |
442 if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall': |
458 if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall': |
461 if detailed is None: |
477 if detailed is None: |
462 return dominfo |
478 return dominfo |
463 elif detailed == 'detailed': |
479 elif detailed == 'detailed': |
464 return dominfo, dom.getAccounts(), dom.getAliases() |
480 return dominfo, dom.getAccounts(), dom.getAliases() |
465 else: |
481 else: |
466 raise VMMDomainException(('%s: »%s«' % (_('Invalid argument'), |
482 raise VMMDomainException(("%s: '%s'" % (_('Invalid argument'), |
467 detailed), ERR.INVALID_OPTION)) |
483 detailed), ERR.INVALID_OPTION)) |
468 |
484 |
469 def domain_list(self, pattern=None): |
485 def domain_list(self, pattern=None): |
470 from Domain import search |
486 from Domain import search |
471 like = False |
487 like = False |
479 elif pattern.endswith('%'): |
495 elif pattern.endswith('%'): |
480 domain = pattern[:-1] |
496 domain = pattern[:-1] |
481 re.compile(RE_DOMAIN_SRCH) |
497 re.compile(RE_DOMAIN_SRCH) |
482 if not re.match(RE_DOMAIN_SRCH, domain): |
498 if not re.match(RE_DOMAIN_SRCH, domain): |
483 raise VMMException(( |
499 raise VMMException(( |
484 _(u'The pattern »%s« contains invalid characters.') % |
500 _(u"The pattern '%s' contains invalid characters.") % |
485 pattern, ERR.DOMAIN_INVALID)) |
501 pattern, ERR.DOMAIN_INVALID)) |
486 else: |
502 else: |
487 pattern = self.__chkDomainname(pattern) |
503 pattern = self.__chkDomainname(pattern) |
488 self.__dbConnect() |
504 self.__dbConnect() |
489 return search(self.__dbh, pattern=pattern, like=like) |
505 return search(self.__dbh, pattern=pattern, like=like) |
490 |
506 |
491 def user_add(self, emailaddress, password): |
507 def user_add(self, emailaddress, password): |
492 acc = self.__getAccount(emailaddress, password) |
508 acc = self.__getAccount(emailaddress, password) |
|
509 if password is None: |
|
510 password = self._readpass() |
|
511 acc.setPassword(self.__pwhash(password)) |
493 acc.save(self.__Cfg.get('maildir', 'folder'), |
512 acc.save(self.__Cfg.get('maildir', 'folder'), |
494 self.__Cfg.getboolean('services', 'smtp'), |
513 self.__Cfg.getboolean('services', 'smtp'), |
495 self.__Cfg.getboolean('services', 'pop3'), |
514 self.__Cfg.getboolean('services', 'pop3'), |
496 self.__Cfg.getboolean('services', 'imap'), |
515 self.__Cfg.getboolean('services', 'imap'), |
497 self.__Cfg.getboolean('services', 'managesieve')) |
516 self.__Cfg.getboolean('services', 'managesieve')) |
529 self.__dbConnect() |
548 self.__dbConnect() |
530 return getAccountByID(uid, self.__dbh) |
549 return getAccountByID(uid, self.__dbh) |
531 |
550 |
532 def user_password(self, emailaddress, password): |
551 def user_password(self, emailaddress, password): |
533 acc = self.__getAccount(emailaddress) |
552 acc = self.__getAccount(emailaddress) |
|
553 if acc.getUID() == 0: |
|
554 raise VMMException((_("Account doesn't exists"),ERR.NO_SUCH_ACCOUNT)) |
|
555 if password is None: |
|
556 password = self._readpass() |
534 acc.modify('password', self.__pwhash(password)) |
557 acc.modify('password', self.__pwhash(password)) |
535 |
558 |
536 def user_name(self, emailaddress, name): |
559 def user_name(self, emailaddress, name): |
537 acc = self.__getAccount(emailaddress) |
560 acc = self.__getAccount(emailaddress) |
538 acc.modify('name', name) |
561 acc.modify('name', name) |