--- MailList.py Thu Feb 10 20:57:53 2000 +++ /home/gerrit/MailList.py Thu Feb 10 20:54:18 2000 @@ -60,6 +60,19 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin, Archiver, Digester, SecurityManager, Bouncer, GatewayManager): + """MailList([name [,lock]]) -> instance + + This class represents a mailinglist. The constructor takes zero, one + or two arguments: if 'name' is given, the mailinglist with the name + 'name' is loaded. If the mailinglist isn't found, the exception + Errors.MMUnknownListError is raised. If the second argument is true, + the list is locked. This is true by default. If you are going to write + to the list, this should be true. It's a good habit to unlock the list + when you're ready, using the Unlock() method, in case something goes + wrong or your program runs a long time. You don't need to lock if you + are going to read list data only. + """ + def __init__(self, name=None, lock=1): if name and name not in Utils.list_names(): raise Errors.MMUnknownListError, 'list not found: %s' % name @@ -82,15 +95,27 @@ pass def GetMembers(self): - """returns a list of the members. (all lowercase)""" + """GetMembers() -> list + + Returns a list of all subscribed non-digest members, all in lowercase. + """ + return self.members.keys() def GetDigestMembers(self): - """returns a list of digest members. (all lowercase)""" + """GetDigestMembers() -> list + + Returns a list of all subscribed digest members, all in lowercase. + """ + return self.digest_members.keys() def GetDeliveryMembers(self): - """returns a list of the members with username case preserved.""" + """GetDeliveryMembers() -> list + + Returns a list of the non-digest members with username case preserved. + """ + res = [] for k, v in self.members.items(): if type(v) is StringType: @@ -100,7 +125,11 @@ return res def GetDigestDeliveryMembers(self): - """returns a list of the members with username case preserved.""" + """GetDigestDeliveryMembers() -> list + + Returns a list of the digest members with username case preserved. + """ + res = [] for k,v in self.digest_members.items(): if type(v) is StringType: @@ -110,9 +139,11 @@ return res def __AddMember(self, addr, digest): - """adds the appropriate data to the internal members dict. + """__AddMember(addr, digest) -> None - If the username has upercase letters in it, then the value + Adds the appropriate data to the internal members dict. + + If the username has uppercase letters in it, then the value in the members dict is the case preserved address, otherwise, the value is 0. """ @@ -128,18 +159,25 @@ self.members[string.lower(addr)] = addr def GetAdminEmail(self): + """GetAdminEmail() -> string + + Returns the email address (a string) of the list owner. + """ + return '%s-admin@%s' % (self._internal_name, self.host_name) def GetMemberAdminEmail(self, member): - """Usually the member addr, but modified for umbrella lists. + """GetMemberAdminEmail(member) + + Usually the member address, but modified for umbrella lists. Umbrella lists have other mailing lists as members, and so admin stuff like confirmation requests and passwords must not be sent to the member addresses - the sublists - but rather to the administrators of the sublists. This routine picks the right address, considering regular member address to be their own administrative addresses. - """ + if not self.umbrella_list: return member else: @@ -147,8 +185,11 @@ return "%s%s@%s" % (acct, self.umbrella_member_suffix, host) def GetUserSubscribedAddress(self, member): - """Return the member's case preserved address. + """GetUserSubscribedAddress(member) -> string + + Returns the member's case preserved address. """ + member = string.lower(member) cpuser = self.members.get(member) if type(cpuser) == IntType: @@ -163,24 +204,50 @@ return None def GetUserCanonicalAddress(self, member): - """Return the member's address lower cased.""" + """GetUserCanonicalAddress(member) -> string + + Return the member's address lower cased. + """ + cpuser = self.GetUserSubscribedAddress(member) if cpuser is not None: return string.lower(cpuser) return None def GetRequestEmail(self): + """GetRequestEmail(self) -> string + + Returns the address where users can send administrative requests. + """ + return '%s-request@%s' % (self._internal_name, self.host_name) def GetListEmail(self): + """GetListEmail() -> string + + Returns the email address of the mailinglist. + """ + return '%s@%s' % (self._internal_name, self.host_name) def GetListIdentifier(self): - """Return the unique (RFC draft-chandhok-listid-02) identifier.""" + """GetListIdentifier() -> string + + Returns the unique (RFC draft-chandhok-listid-02) identifier. + """ + return ("%s <%s.%s>" % (self.description, self._internal_name, self.host_name)) def GetScriptURL(self, scriptname, relative=0): + """GetScriptURL(scriptname[, relative]) -> string + + Returns the URL of the 'scriptname' page of the mailing list. If + the optional argument 'relative' is true (it's false by default), + the URL is relative to the directory in the PATH_INFO environment + variable. + """ + if relative: prefix = '../' * Utils.GetNestingLevel() if not prefix: @@ -197,6 +264,15 @@ self.internal_name()) def GetOptionsURL(self, addr, obscure=0, relative=0): + """GetOptionsURL(addr[ ,obscure][, relative]]) -> string + + Returns the URL of the options page of the email address 'addr'. + If 'obscure' is set, the email address is obscured using + Utils.ObscureEmail(addr). If 'relative' is set, the URL is + relative to the directory in the PATH_INFO environment + variable. + """ + addr = string.lower(addr) url = self.GetScriptURL('options', relative) if obscure: @@ -208,10 +284,22 @@ GetAbsoluteScriptURL = GetScriptURL def GetRelativeScriptURL(self, scriptname): + """GetRelativeScriptURL(scriptname) -> string + + Returns the URL of the 'scriptname' script, relative to the + directory in the PATH_INFO environment variable. + """ + return self.GetScriptURL(scriptname, relative=1) def GetUserOption(self, user, option): - """Return user's setting for option, defaulting to 0 if no settings.""" + """GetUserOption(user, option) -> boolean + + Returns wheter 'option' is set for the options of 'user'. If + 'user' is not a member of the mailing list, 0 is returned. + Note that 'user' is not a string, but an integer from the + mm_cfg module. + """ user = self.GetUserCanonicalAddress(user) if option == mm_cfg.Digests: return self.digest_members.has_key(user) @@ -220,6 +308,13 @@ return not not self.user_options[user] & option def SetUserOption(self, user, option, value, save_list=1): + """SetUserOption(user, option, value[, save_list]) -> None + + Sets the 'option' option of the member 'user' to 'value'. 'value' + is an integer from the mm_cfg module. Is 'save_list' is true (it is + by default), the list configuration is saved afterwards. + """ + user = self.GetUserCanonicalAddress(user) if not self.user_options.has_key(user): self.user_options[user] = 0 @@ -255,12 +350,12 @@ # self.passwords is, of course the password in plain text. def FindUser(self, email): - """Return the lowercased version of the subscribed email address. + """FindUser(email) -> string or None - If email is not subscribed, either as a regular member or digest - member, None is returned. If they are subscribed, the return value is - guaranteed to be lowercased. + If 'email' is a member of this list, the lowercased email address + is returned. Otherwise, None is returned. """ + # shortcut lcuser = self.GetUserCanonicalAddress(email) if lcuser is not None: @@ -274,7 +369,11 @@ return string.lower(matches[0]) def InitTempVars(self, name, lock): - """Set transient variables of this and inherited classes.""" + """InitTempVars(name, lock) -> None + + Set transient variables of this and inherited classes. + """ + self.__createlock_p = lock self.__lock = LockFile.LockFile( os.path.join(mm_cfg.LOCK_DIR, name or '') + '.lock', @@ -291,7 +390,13 @@ ListAdmin.InitTempVars(self) def InitVars(self, name=None, admin='', crypted_password=''): - """Assign default values - some will be overriden by stored state.""" + """InitVars([name][, admin][, crypted_password]) -> None + + Initialized the mailing list information. This should not + be used directly (FIXME: truth?), use the .Create method + to create a new list instead. + """ + # Non-configurable list info if name: self._internal_name = name @@ -363,6 +468,10 @@ self.msg_footer = mm_cfg.DEFAULT_MSG_FOOTER def GetConfigInfo(self): + """GetConfigInfo() -> dictionairy + + Returns the information about the list configuration options. + """ config_info = {} config_info['digest'] = Digester.GetConfigInfo(self) config_info['archive'] = Archiver.GetConfigInfo(self) @@ -739,6 +848,14 @@ return config_info def Create(self, name, admin, crypted_password): + '''Create(name, admin, crypted_password) -> None + + Creates a mailinglist called 'name', with 'admin' as the list + owner. If the list already exists, ValueError is raised. The + third argument is the _encrypted_ password (a password can be + encrypted using the Crypt.crypt() function). + ''' + if name in Utils.list_names(): raise ValueError, 'List %s already exists.' % name Utils.ValidateEmail(admin) @@ -766,6 +883,11 @@ os.umask(ou) def Save(self): + """Save() -> None + + Saves the current configuration of the list. + """ + # If more than one client is manipulating the database at once, we're # pretty hosed. That's a good reason to make this a daemon not a # program.