113 exec_ok(val) |
114 exec_ok(val) |
114 except VMMException, e: |
115 except VMMException, e: |
115 code = e.code() |
116 code = e.code() |
116 if code is ERR.NO_SUCH_BINARY: |
117 if code is ERR.NO_SUCH_BINARY: |
117 raise VMMException(_(u'“%(binary)s” doesn\'t exist.\n\ |
118 raise VMMException(_(u'“%(binary)s” doesn\'t exist.\n\ |
118 (vmm.cfg: section "bin", option "%(option)s")') %{'binary': val,'option': opt}, |
119 (vmm.cfg: section "bin", option "%(option)s")') % |
|
120 {'binary': val, 'option': opt}, |
119 ERR.NO_SUCH_BINARY) |
121 ERR.NO_SUCH_BINARY) |
120 elif code is ERR.NOT_EXECUTABLE: |
122 elif code is ERR.NOT_EXECUTABLE: |
121 raise VMMException(_(u'“%(binary)s” is not executable.\n\ |
123 raise VMMException(_(u'“%(binary)s” is not executable.\ |
122 (vmm.cfg: section "bin", option "%(option)s")') %{'binary': val,'option': opt}, |
124 \n(vmm.cfg: section "bin", option "%(option)s")') % |
|
125 {'binary': val, 'option': opt}, |
123 ERR.NOT_EXECUTABLE) |
126 ERR.NOT_EXECUTABLE) |
124 else: |
127 else: |
125 raise |
128 raise |
126 |
129 |
127 def __dbConnect(self): |
130 def __dbConnect(self): |
160 accountExists = staticmethod(accountExists) |
163 accountExists = staticmethod(accountExists) |
161 |
164 |
162 def aliasExists(dbh, address): |
165 def aliasExists(dbh, address): |
163 sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\ |
166 sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\ |
164 domain_name WHERE domainname = '%s') AND address = '%s'" % ( |
167 domain_name WHERE domainname = '%s') AND address = '%s'" % ( |
165 address._domainname, address._localpart) |
168 address._domainname, address._localpart) |
166 return Handler._exists(dbh, sql) |
169 return Handler._exists(dbh, sql) |
167 aliasExists = staticmethod(aliasExists) |
170 aliasExists = staticmethod(aliasExists) |
168 |
171 |
169 def relocatedExists(dbh, address): |
172 def relocatedExists(dbh, address): |
170 sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\ |
173 sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\ |
171 domain_name WHERE domainname = '%s') AND address = '%s'" % ( |
174 domain_name WHERE domainname = '%s') AND address = '%s'" % ( |
172 address._domainname, address._localpart) |
175 address._domainname, address._localpart) |
173 return Handler._exists(dbh, sql) |
176 return Handler._exists(dbh, sql) |
174 relocatedExists = staticmethod(relocatedExists) |
177 relocatedExists = staticmethod(relocatedExists) |
175 |
|
176 |
178 |
177 def __getAccount(self, address, password=None): |
179 def __getAccount(self, address, password=None): |
178 self.__dbConnect() |
180 self.__dbConnect() |
179 address = EmailAddress(address) |
181 address = EmailAddress(address) |
180 if not password is None: |
182 if not password is None: |
186 address = EmailAddress(address) |
188 address = EmailAddress(address) |
187 if destination is not None: |
189 if destination is not None: |
188 destination = EmailAddress(destination) |
190 destination = EmailAddress(destination) |
189 return Alias(self.__dbh, address, destination) |
191 return Alias(self.__dbh, address, destination) |
190 |
192 |
191 def __getRelocated(self,address, destination=None): |
193 def __getRelocated(self, address, destination=None): |
192 self.__dbConnect() |
194 self.__dbConnect() |
193 address = EmailAddress(address) |
195 address = EmailAddress(address) |
194 if destination is not None: |
196 if destination is not None: |
195 destination = EmailAddress(destination) |
197 destination = EmailAddress(destination) |
196 return Relocated(self.__dbh, address, destination) |
198 return Relocated(self.__dbh, address, destination) |
268 oldpwd = os.getcwd() |
271 oldpwd = os.getcwd() |
269 os.chdir(domdir) |
272 os.chdir(domdir) |
270 |
273 |
271 maildir = self._Cfg.dget('maildir.name') |
274 maildir = self._Cfg.dget('maildir.name') |
272 folders = [maildir] |
275 folders = [maildir] |
|
276 append = folders.append |
273 for folder in self._Cfg.dget('maildir.folders').split(':'): |
277 for folder in self._Cfg.dget('maildir.folders').split(':'): |
274 folder = folder.strip() |
278 folder = folder.strip() |
275 if len(folder) and not folder.count('..')\ |
279 if len(folder) and not folder.count('..'): |
276 and re.match(RE_MBOX_NAMES, folder): |
280 if re.match(RE_MBOX_NAMES, folder): |
277 folders.append('%s/.%s' % (maildir, folder)) |
281 append('%s/.%s' % (maildir, folder)) |
|
282 else: |
|
283 self.__warnings.append(_('Skipped mailbox folder: %r') % |
|
284 folder) |
|
285 else: |
|
286 self.__warnings.append(_('Skipped mailbox folder: %r') % |
|
287 folder) |
|
288 |
278 subdirs = ['cur', 'new', 'tmp'] |
289 subdirs = ['cur', 'new', 'tmp'] |
279 mode = self._Cfg.dget('account.directory_mode') |
290 mode = self._Cfg.dget('account.directory_mode') |
280 |
291 |
281 self.__makedir('%s' % uid, mode, uid, gid) |
292 self.__makedir('%s' % uid, mode, uid, gid) |
282 os.chdir('%s' % uid) |
293 os.chdir('%s' % uid) |
283 for folder in folders: |
294 for folder in folders: |
284 self.__makedir(folder, mode, uid, gid) |
295 self.__makedir(folder, mode, uid, gid) |
285 for subdir in subdirs: |
296 for subdir in subdirs: |
286 self.__makedir(os.path.join(folder, subdir), mode, uid, gid) |
297 self.__makedir(os.path.join(folder, subdir), mode, uid, gid) |
287 self.__subscribeFL([f.replace(maildir+'/.', '') for f in folders[1:]], |
298 self.__subscribe((f.replace(maildir + '/.', '') for f in folders[1:]), |
288 uid, gid) |
299 uid, gid) |
289 os.chdir(oldpwd) |
300 os.chdir(oldpwd) |
290 |
301 |
291 def __userDirDelete(self, domdir, uid, gid): |
302 def __userDirDelete(self, domdir, uid, gid): |
292 if uid > 0 and gid > 0: |
303 if uid > 0 and gid > 0: |
293 userdir = '%s' % uid |
304 userdir = '%s' % uid |
294 if userdir.count('..') or domdir.count('..'): |
305 if userdir.count('..') or domdir.count('..'): |
295 raise VMMException(_(u'Found ".." in home directory path.'), |
306 raise VMMException(_(u'Found ".." in home directory path.'), |
296 ERR.FOUND_DOTS_IN_PATH) |
307 ERR.FOUND_DOTS_IN_PATH) |
297 if os.path.isdir(domdir): |
308 if os.path.isdir(domdir): |
298 os.chdir(domdir) |
309 os.chdir(domdir) |
299 if os.path.isdir(userdir): |
310 if os.path.isdir(userdir): |
300 mdstat = os.stat(userdir) |
311 mdstat = os.stat(userdir) |
301 if (mdstat.st_uid, mdstat.st_gid) != (uid, gid): |
312 if (mdstat.st_uid, mdstat.st_gid) != (uid, gid): |
302 raise VMMException( |
313 raise VMMException(_( |
303 _(u'Detected owner/group mismatch in home directory.'), |
314 u'Detected owner/group mismatch in home directory.'), |
304 ERR.MAILDIR_PERM_MISMATCH) |
315 ERR.MAILDIR_PERM_MISMATCH) |
305 rmtree(userdir, ignore_errors=True) |
316 rmtree(userdir, ignore_errors=True) |
306 else: |
317 else: |
307 raise VMMException(_(u"No such directory: %s") % |
318 raise VMMException(_(u"No such directory: %s") % |
308 os.path.join(domdir, userdir), ERR.NO_SUCH_DIRECTORY) |
319 os.path.join(domdir, userdir), ERR.NO_SUCH_DIRECTORY) |
309 |
320 |
310 def __domDirDelete(self, domdir, gid): |
321 def __domDirDelete(self, domdir, gid): |
311 if gid > 0: |
322 if gid > 0: |
312 if not self.__isdir(domdir): |
323 if not self.__isdir(domdir): |
313 return |
324 return |
314 basedir = self._Cfg.dget('misc.base_directory') |
325 basedir = self._Cfg.dget('misc.base_directory') |
315 domdirdirs = domdir.replace(basedir+'/', '').split('/') |
326 domdirdirs = domdir.replace(basedir + '/', '').split('/') |
316 domdirparent = os.path.join(basedir, domdirdirs[0]) |
327 domdirparent = os.path.join(basedir, domdirdirs[0]) |
317 if basedir.count('..') or domdir.count('..'): |
328 if basedir.count('..') or domdir.count('..'): |
318 raise VMMException(_(u'Found ".." in domain directory path.'), |
329 raise VMMException(_(u'Found ".." in domain directory path.'), |
319 ERR.FOUND_DOTS_IN_PATH) |
330 ERR.FOUND_DOTS_IN_PATH) |
320 if os.path.isdir(domdirparent): |
331 if os.path.isdir(domdirparent): |
376 return '{%s}%s' % (self._scheme, self.__pwMD5(password, user)) |
387 return '{%s}%s' % (self._scheme, self.__pwMD5(password, user)) |
377 elif self._scheme == 'MD4': |
388 elif self._scheme == 'MD4': |
378 return '{%s}%s' % (self._scheme, self.__pwMD4(password)) |
389 return '{%s}%s' % (self._scheme, self.__pwMD4(password)) |
379 elif self._scheme in ['SMD5', 'SSHA', 'CRAM-MD5', 'HMAC-MD5', |
390 elif self._scheme in ['SMD5', 'SSHA', 'CRAM-MD5', 'HMAC-MD5', |
380 'LANMAN', 'NTLM', 'RPA']: |
391 'LANMAN', 'NTLM', 'RPA']: |
381 return Popen([self._Cfg.dget('bin.dovecotpw'), '-s', |
392 return Popen([self._Cfg.dget('bin.dovecotpw'), |
382 self._scheme,'-p',password],stdout=PIPE).communicate()[0][:-1] |
393 '-s', self._scheme, '-p', password], |
|
394 stdout=PIPE).communicate()[0][:-1] |
383 else: |
395 else: |
384 return '{%s}%s' % (self._scheme, password) |
396 return '{%s}%s' % (self._scheme, password) |
385 |
397 |
386 def hasWarnings(self): |
398 def hasWarnings(self): |
387 """Checks if warnings are present, returns bool.""" |
399 """Checks if warnings are present, returns bool.""" |
411 dom.updateTransport(transport) |
423 dom.updateTransport(transport) |
412 else: |
424 else: |
413 dom.updateTransport(transport, force=True) |
425 dom.updateTransport(transport, force=True) |
414 |
426 |
415 def domainDelete(self, domainname, force=None): |
427 def domainDelete(self, domainname, force=None): |
416 if not force is None and force not in ['deluser','delalias','delall']: |
428 if not force is None and force not in ['deluser', 'delalias', |
417 raise VMMDomainException(_(u"Invalid argument: “%s”") % force, |
429 'delall']: |
418 ERR.INVALID_OPTION) |
430 raise VMMDomainException(_(u'Invalid argument: “%s”') % |
|
431 force, ERR.INVALID_OPTION) |
419 dom = self.__getDomain(domainname) |
432 dom = self.__getDomain(domainname) |
420 gid = dom.getID() |
433 gid = dom.getID() |
421 domdir = dom.getDir() |
434 domdir = dom.getDir() |
422 if self._Cfg.dget('domain.force_deletion') or force == 'delall': |
435 if self._Cfg.dget('domain.force_deletion') or force == 'delall': |
423 dom.delete(True, True) |
436 dom.delete(True, True) |
430 if self._Cfg.dget('domain.delete_directory'): |
443 if self._Cfg.dget('domain.delete_directory'): |
431 self.__domDirDelete(domdir, gid) |
444 self.__domDirDelete(domdir, gid) |
432 |
445 |
433 def domainInfo(self, domainname, details=None): |
446 def domainInfo(self, domainname, details=None): |
434 if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full', |
447 if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full', |
435 'relocated', 'detailed']: |
448 'relocated']: |
436 raise VMMException(_(u'Invalid argument: “%s”') % details, |
449 raise VMMException(_(u'Invalid argument: “%s”') % details, |
437 ERR.INVALID_AGUMENT) |
450 ERR.INVALID_AGUMENT) |
438 if details == 'detailed': |
|
439 details = 'full' |
|
440 self.__warnings.append(_(u'\ |
|
441 The keyword “detailed” is deprecated and will be removed in a future release.\n\ |
|
442 Please use the keyword “full” to get full details.')) |
|
443 dom = self.__getDomain(domainname) |
451 dom = self.__getDomain(domainname) |
444 dominfo = dom.getInfo() |
452 dominfo = dom.getInfo() |
445 if dominfo['domainname'].startswith('xn--'): |
453 if dominfo['domainname'].startswith('xn--'): |
446 dominfo['domainname'] += ' (%s)' % ace2idna(dominfo['domainname']) |
454 dominfo['domainname'] += ' (%s)' % ace2idna(dominfo['domainname']) |
447 if details is None: |
455 if details is None: |
532 alias.save(long(self._postconf.read('virtual_alias_expansion_limit'))) |
540 alias.save(long(self._postconf.read('virtual_alias_expansion_limit'))) |
533 gid = self.__getDomain(alias._dest._domainname).getID() |
541 gid = self.__getDomain(alias._dest._domainname).getID() |
534 if gid > 0 and (not Handler.accountExists(self.__dbh, alias._dest) and |
542 if gid > 0 and (not Handler.accountExists(self.__dbh, alias._dest) and |
535 not Handler.aliasExists(self.__dbh, alias._dest)): |
543 not Handler.aliasExists(self.__dbh, alias._dest)): |
536 self.__warnings.append( |
544 self.__warnings.append( |
537 _(u"The destination account/alias “%s” doesn't exist.") % |
545 _(u"The destination account/alias “%s” doesn't exist.") % |
538 alias._dest) |
546 alias._dest) |
539 |
547 |
540 def userDelete(self, emailaddress, force=None): |
548 def userDelete(self, emailaddress, force=None): |
541 if force not in [None, 'delalias']: |
549 if force not in [None, 'delalias']: |
542 raise VMMException(_(u"Invalid argument: “%s”") % force, |
550 raise VMMException(_(u"Invalid argument: “%s”") % force, |
591 if password is None or (isinstance(password, basestring) and |
600 if password is None or (isinstance(password, basestring) and |
592 not len(password)): |
601 not len(password)): |
593 raise ValueError('could not accept password: %r' % password) |
602 raise ValueError('could not accept password: %r' % password) |
594 acc = self.__getAccount(emailaddress) |
603 acc = self.__getAccount(emailaddress) |
595 if acc.getUID() == 0: |
604 if acc.getUID() == 0: |
596 raise VMMException(_(u"Account doesn't exist"), ERR.NO_SUCH_ACCOUNT) |
605 raise VMMException(_(u"Account doesn't exist"), |
|
606 ERR.NO_SUCH_ACCOUNT) |
597 acc.modify('password', self.__pwhash(password, user=emailaddress)) |
607 acc.modify('password', self.__pwhash(password, user=emailaddress)) |
598 |
608 |
599 def userName(self, emailaddress, name): |
609 def userName(self, emailaddress, name): |
600 acc = self.__getAccount(emailaddress) |
610 acc = self.__getAccount(emailaddress) |
601 acc.modify('name', name) |
611 acc.modify('name', name) |