def _setValue(self, mlist, property, val, doc): # If we're changing the list's preferred language, change the I18N # context as well if property == 'preferred_language': i18n.set_language(val) doc.set_language(val) GUIBase._setValue(self, mlist, property, val, doc)
def __refuse(self, request, recip, comment, origmsg=None, lang=None): # As this message is going to the requestor, try to set the language # to his/her language choice, if they are a member. Otherwise use the # list's preferred language. realname = self.real_name if lang is None: lang = self.getMemberLanguage(recip) text = Utils.maketext( 'refuse.txt', {'listname' : realname, 'request' : request, 'reason' : comment, 'adminaddr': self.GetOwnerEmail(), }, lang=lang, mlist=self) otrans = i18n.get_translation() i18n.set_language(lang) try: # add in original message, but not wrap/filled if origmsg: text = NL.join( [text, '---------- ' + _('Original Message') + ' ----------', str(origmsg) ]) subject = _('Request to mailing list %(realname)s rejected') finally: i18n.set_translation(otrans) msg = Message.UserNotification(recip, self.GetOwnerEmail(), subject, text, lang) msg.send(self)
def subscription_prompt(mlist, doc, cookie, userdesc): email = userdesc.address password = userdesc.password digest = userdesc.digest lang = userdesc.language name = Utils.uncanonstr(userdesc.fullname, lang) i18n.set_language(lang) doc.set_language(lang) title = _('Anmeldung abschließen') doc.SetTitle(title) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0) table.AddRow([Bold(FontAttr(title, size='+2'))]) listname = mlist.real_name # This is the normal, no-confirmation required results text. # # We do things this way so we don't have to reformat this paragraph, which # would mess up translations. If you modify this text for other reasons, # please refill the paragraph, and clean up the logic. result = _("<br>Um die Anmeldung der Email-Adresse <strong>" + email + "</strong> " + """<br>zur Mailingliste <strong>%(listname)s</strong> abzuschließen, klicken Sie bitte unten auf "Anmeldung bestätigen". """) + '<p><br>' table.AddRow([result]) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([SubmitButton('submit', 'Anmeldung bestätigen')]) form.AddItem(table) doc.AddItem(form)
def html_foot(self): # avoid i18n side-effects mlist = self.maillist otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) # Convenience def quotetime(s): return html_quote(i18n.ctime(s), self.lang) try: d = {"lastdate": quotetime(self.lastdate), "archivedate": quotetime(self.archivedate), "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "version": self.version, "listname": html_quote(mlist.real_name, self.lang), } i = {"thread": _("thread"), "subject": _("subject"), "author": _("author"), "date": _("date") } finally: i18n.set_translation(otrans) for t in i.keys(): cap = t[0].upper() + t[1:] if self.type == cap: d["%s_ref" % (t)] = "" else: d["%s_ref" % (t)] = ('<a href="%s.html#start">[ %s ]</a>' % (t, i[t])) return quick_maketext( 'archidxfoot.html', d, mlist=mlist)
def decode_headers(self): """MIME-decode headers. If the email, subject, or author attributes contain non-ASCII characters using the encoded-word syntax of RFC 2047, decoded versions of those attributes are placed in the self.decoded (a dictionary). If the list's charset differs from the header charset, an attempt is made to decode the headers as Unicode. If that fails, they are left undecoded. """ author = self.decode_charset(self.author) subject = self.decode_charset(self.subject) if author: self.decoded['author'] = author email = self.decode_charset(self.email) if email: self.decoded['email'] = email if subject: if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: otrans = i18n.get_translation() try: i18n.set_language(self._lang) atmark = unicode(_(' at '), Utils.GetCharSet(self._lang)) subject = re.sub(r'([-+,.\w]+)@([-+.\w]+)', '\g<1>' + atmark + '\g<2>', subject) finally: i18n.set_translation(otrans) self.decoded['subject'] = subject self.decoded['stripped'] = self.strip_subject(subject or self.subject)
def sendProbe(self, member, msg): listname = self.real_name # Put together the substitution dictionary. d = { "listname": listname, "address": member, "optionsurl": self.GetOptionsURL(member, absolute=True), "owneraddr": self.GetOwnerEmail(), } text = Utils.maketext("probe.txt", d, lang=self.getMemberLanguage(member), mlist=self) # Calculate the VERP'd sender address for bounce processing of the # probe message. token = self.pend_new(Pending.PROBE_BOUNCE, member, msg) probedict = {"bounces": self.internal_name() + "-bounces", "token": token} probeaddr = "%s@%s" % ((mm_cfg.VERP_PROBE_FORMAT % probedict), self.host_name) # Calculate the Subject header, in the member's preferred language ulang = self.getMemberLanguage(member) otrans = i18n.get_translation() i18n.set_language(ulang) try: subject = _("%(listname)s mailing list probe message") finally: i18n.set_translation(otrans) outer = Message.UserNotification(member, probeaddr, subject, lang=ulang) outer.set_type("multipart/mixed") text = MIMEText(text, _charset=Utils.GetCharSet(ulang)) outer.attach(text) outer.attach(MIMEMessage(msg)) # Turn off further VERP'ing in the final delivery step. We set # probe_token for the OutgoingRunner to more easily handling local # rejects of probe messages. outer.send(self, envsender=probeaddr, verp=False, probe_token=token)
def unsubscription_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who is unsubscribing. op, addr = mlist.pend_confirm(cookie, expunge=False) lang = mlist.getMemberLanguage(addr) i18n.set_language(lang) doc.set_language(lang) op, addr = mlist.ProcessConfirmation(cookie) except Errors.NotAMemberError: bad_confirmation(doc, _('''Invalid confirmation string. It is possible that you are attempting to confirm a request for an address that has already been unsubscribed.''')) else: # The response listname = mlist.real_name title = _('Unsubscription request confirmed') listinfourl = mlist.GetScriptURL('listinfo', absolute=1) doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem(_("""\ You have successfully unsubscribed from the %(listname)s mailing list. You can now <a href="%(listinfourl)s">visit the list's main information page</a>.""")) mlist.Save() finally: mlist.Unlock()
def html_foot(self): # avoid i18n side-effects mlist = self.maillist otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) # Convenience def quotetime(s): return html_quote(i18n.ctime(s), self.lang) try: d = { "lastdate": quotetime(self.lastdate), "archivedate": quotetime(self.archivedate), "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "version": self.version, "listname": html_quote(mlist.real_name, self.lang), } i = { "thread": _("thread"), "subject": _("subject"), "author": _("author"), "date": _("date") } finally: i18n.set_translation(otrans) for t in i.keys(): cap = t[0].upper() + t[1:] if self.type == cap: d["%s_ref" % (t)] = "" else: d["%s_ref" % (t)] = ('<a href="%s.html#start">[ %s ]</a>' % (t, i[t])) return quick_maketext('archidxfoot.html', d, mlist=mlist)
def __refuse(self, request, recip, comment, origmsg=None, lang=None): # As this message is going to the requestor, try to set the language # to his/her language choice, if they are a member. Otherwise use the # list's preferred language. realname = self.real_name if lang is None: lang = self.getMemberLanguage(recip) text = Utils.maketext('refuse.txt', { 'listname': realname, 'request': request, 'reason': comment, 'adminaddr': self.GetOwnerEmail(), }, lang=lang, mlist=self) otrans = i18n.get_translation() i18n.set_language(lang) try: # add in original message, but not wrap/filled if origmsg: text = NL.join([ text, '---------- ' + _('Original Message') + ' ----------', str(origmsg) ]) subject = _('Request to mailing list %(realname)s rejected') finally: i18n.set_translation(otrans) msg = Message.UserNotification(recip, self.GetOwnerEmail(), subject, text, lang) msg.send(self)
def SendUnsubscribeAck(self, addr, lang): realname = self.real_name i18n.set_language(lang) msg = Message.UserNotification( self.GetMemberAdminEmail(addr), self.GetBouncesEmail(), _('You have been unsubscribed from the %(realname)s mailing list'), Utils.wrap(self.goodbye_msg), lang) msg.send(self, verp=mm_cfg.VERP_PERSONALIZED_DELIVERIES)
def addrchange_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who is unsubscribing. op, oldaddr, newaddr, globally = mlist.pend_confirm(cookie, expunge=False) lang = mlist.getMemberLanguage(oldaddr) i18n.set_language(lang) doc.set_language(lang) op, oldaddr, newaddr = mlist.ProcessConfirmation(cookie) except Errors.NotAMemberError: bad_confirmation( doc, _('''Invalid confirmation string. It is possible that you are attempting to confirm a request for an address that has already been unsubscribed.''')) except Errors.MembershipIsBanned: owneraddr = mlist.GetOwnerEmail() realname = mlist.real_name doc.addError( _("""%(newaddr)s is banned from subscribing to the %(realname)s list. If you think this restriction is erroneous, please contact the list owners at %(owneraddr)s.""")) except Errors.MMAlreadyAMember: realname = mlist.real_name bad_confirmation( doc, _("""%(newaddr)s is already a member of the %(realname)s list. It is possible that you are attempting to confirm a request for an address that has already been subscribed.""")) else: # The response listname = mlist.real_name title = _('Change of address request confirmed') optionsurl = mlist.GetOptionsURL(newaddr, absolute=1) doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem( _("""\ You have successfully changed your address on the %(listname)s mailing list from <b>%(oldaddr)s</b> to <b>%(newaddr)s</b>. You can now <a href="%(optionsurl)s">proceed to your membership login page</a>.""")) mlist.Save() finally: mlist.Unlock()
def addrchange_prompt(mlist, doc, cookie, oldaddr, newaddr, globally): title = _('Confirm change of address request') doc.SetTitle(title) lang = mlist.getMemberLanguage(oldaddr) i18n.set_language(lang) doc.set_language(lang) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0, width='100%') table.AddRow([Center(Bold(FontAttr(title, size='+1')))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) listname = mlist.real_name fullname = mlist.getMemberName(oldaddr) if fullname is None: fullname = _('<em>Not available</em>') else: fullname = Utils.websafe(Utils.uncanonstr(fullname, lang)) if globally: globallys = _('globally') else: globallys = '' table.AddRow([ _("""Your confirmation is required in order to complete the change of address request for the mailing list <em>%(listname)s</em>. You are currently subscribed with <ul><li><b>Real name:</b> %(fullname)s <li><b>Old email address:</b> %(oldaddr)s </ul> and you have requested to %(globallys)s change your email address to <ul><li><b>New email address:</b> %(newaddr)s </ul> Hit the <em>Change address</em> button below to complete the confirmation process. <p>Or hit <em>Cancel and discard</em> to cancel this change of address request.""") + '<p><hr>' ]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([ SubmitButton('submit', _('Change address')), SubmitButton('cancel', _('Cancel and discard')) ]) form.AddItem(table) doc.AddItem(form)
def subscription_cancel(mlist, doc, cookie): mlist.Lock() try: # Discard this cookie userdesc = mlist.pend_confirm(cookie)[1] finally: mlist.Unlock() lang = userdesc.language i18n.set_language(lang) doc.set_language(lang) doc.AddItem(_('You have canceled your subscription request.'))
def main(): cgidata = cgi.FieldStorage() language = cgidata.getvalue('language') parts = Utils.GetPathPieces() i18n.set_language(language) openidreg_overview(language) if (cgidata.has_key("action") and cgidata.has_key("listname") and cgidata.has_key("reg-pw") and cgidata.has_key("reg-email")): if (cgidata["action"].value == "display"): result = test(cgidata["listname"].value, cgidata["reg-pw"].value, cgidata["reg-email"].value) display_page(result) else: print "<B> Fields are blank, please enter correct Username , Password and choose your list </B>" FormatOpenIDLogin()
def main(): # Trick out pygettext since we want to mark template_data as translatable, # but we don't want to actually translate it here. def _(s): return s template_data = ( ('listinfo.html', _('General list information page')), ('subscribe.html', _('Subscribe results page')), ('options.html', _('User specific options page')), ('subscribeack.txt', _('Welcome email text file')), ('masthead.txt', _('Digest masthead')), ('postheld.txt', _('User notice of held post')), ('approve.txt', _('User notice of held subscription')), ('refuse.txt', _('Notice of post refused by moderator')), ('invite.txt', _('Invitation to join list')), ('verify.txt', _('Request to confirm subscription')), ('unsub.txt', _('Request to confirm unsubscription')), ('nomoretoday.txt', _('User notice of autoresponse limit')), ('postack.txt', _('User post acknowledgement')), ('disabled.txt', _('Subscription disabled by bounce warning')), ('admlogin.html', _('Admin/moderator login page')), ('private.html', _('Private archive login page')), ('userpass.txt', _('On demand password reminder')), ) _ = i18n._ doc = Document() # Set up the system default language i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) parts = Utils.GetPathPieces() if not parts: doc.AddItem(Header(2, _("List name is required."))) print doc.Format() return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>'))) # Send this with a 404 status. print 'Status: 404 Not Found' print doc.Format() syslog('error', 'edithtml: No such list "%s": %s', listname, e) return
def SendSubscribeAck(self, name, password, digest, text=""): pluser = self.getMemberLanguage(name) # Need to set this here to get the proper l10n of the Subject: i18n.set_language(pluser) if self.welcome_msg: welcome = Utils.wrap(self.welcome_msg) + "\n" else: welcome = "" if self.umbrella_list: addr = self.GetMemberAdminEmail(name) umbrella = Utils.wrap( _( """\ Note: Since this is a list of mailing lists, administrative notices like the password reminder will be sent to your membership administrative address, %(addr)s.""" ) ) else: umbrella = "" # get the text from the template text += Utils.maketext( "subscribeack.txt", { "real_name": self.real_name, "host_name": self.host_name, "welcome": welcome, "umbrella": umbrella, "emailaddr": self.GetListEmail(), "listinfo_url": self.GetScriptURL("listinfo", absolute=True), "optionsurl": self.GetOptionsURL(name, absolute=True), "password": password, "user": self.getMemberCPAddress(name), }, lang=pluser, mlist=self, ) if digest: digmode = _(" (Digest mode)") else: digmode = "" realname = self.real_name msg = Message.UserNotification( self.GetMemberAdminEmail(name), self.GetRequestEmail(), _('Welcome to the "%(realname)s" mailing list%(digmode)s'), text, pluser, ) msg["X-No-Archive"] = "yes" msg.send(self, verp=mm_cfg.VERP_PERSONALIZED_DELIVERIES)
def as_html(self): """Different attributes that can be used when generating a html file from a template for the archives.""" d = self.__dict__.copy() # avoid i18n side-effects otrans = i18n.get_translation() i18n.set_language(self._lang) try: d["prev"], d["prev_wsubj"] = self._get_prev() d["next"], d["next_wsubj"] = self._get_next() d["email_html"] = self.quote(self.email) d["title"] = self.quote(self.subject) d["subject_html"] = self.quote(self.subject) # TK: These two _url variables are used to compose a response # from the archive web page. So, ... # Possibly remove since not used?? d["subject_url"] = url_quote('Re: ' + self.subject) d["in_reply_to_url"] = url_quote(self._message_id) if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: # Point the mailto url back to the list author = re.sub('@', _(' at '), self.author) emailurl = self._mlist.GetListEmail() else: author = self.author emailurl = self.email d["author_html"] = self.quote(author) d["email_url"] = url_quote(emailurl) d["datestr_html"] = self.quote(i18n.ctime(int(self.date))) d["body"] = self._get_body() d['listurl'] = self._mlist.GetScriptURL('listinfo', absolute=1) d['listname'] = self._mlist.real_name d['encoding'] = '' try: # This should work after a couple of emails has been send and the thread # address has been set, but to not get an error in the beginning the error # is caught and an already existing address is provided. Is for dlists. d['thread_addr'] = self.thread_addr d['thread_addr_at'] = re.sub('@', _(' at '), self.thread_addr) except: d['thread_addr'] = d["email_url"] d['thread_addr_at'] = d["email_url"] finally: i18n.set_translation(otrans) charset = Utils.GetCharSet(self._lang) d["encoding"] = html_charset % charset self._add_decoded(d) return quick_maketext( 'article.html', d, lang=self._lang, mlist=self._mlist)
def _dispose(self, mlist, msg, msgdata): # Support clobber_date, i.e. setting the date in the archive to the # received date, not the (potentially bogus) Date: header of the # original message. clobber = 0 originaldate = msg.get('date') receivedtime = formatdate(msgdata['received_time']) if not originaldate: clobber = 1 elif mm_cfg.ARCHIVER_CLOBBER_DATE_POLICY == 1: clobber = 1 elif mm_cfg.ARCHIVER_CLOBBER_DATE_POLICY == 2: # what's the timestamp on the original message? tup = parsedate_tz(originaldate) now = time.time() try: if not tup: clobber = 1 elif abs(now - mktime_tz(tup)) > \ mm_cfg.ARCHIVER_ALLOWABLE_SANE_DATE_SKEW: clobber = 1 except (ValueError, OverflowError): # The likely cause of this is that the year in the Date: field # is horribly incorrect, e.g. (from SF bug # 571634): # Date: Tue, 18 Jun 0102 05:12:09 +0500 # Obviously clobber such dates. clobber = 1 if clobber: del msg['date'] del msg['x-original-date'] msg['Date'] = receivedtime if originaldate: msg['X-Original-Date'] = originaldate # Always put an indication of when we received the message. msg['X-List-Received-Date'] = receivedtime # Now try to get the list lock try: mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT) except LockFile.TimeOutError: # oh well, try again later return 1 try: # Archiving should be done in the list's preferred language, not # the sender's language. i18n.set_language(mlist.preferred_language) mlist.ArchiveMail(msg) mlist.Save() finally: mlist.Unlock()
def addrchange_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who is unsubscribing. op, oldaddr, newaddr, globally = mlist.pend_confirm( cookie, expunge=False) lang = mlist.getMemberLanguage(oldaddr) i18n.set_language(lang) doc.set_language(lang) op, oldaddr, newaddr = mlist.ProcessConfirmation(cookie) except Errors.NotAMemberError: bad_confirmation(doc, _('''Invalid confirmation string. It is possible that you are attempting to confirm a request for an address that has already been unsubscribed.''')) except Errors.MembershipIsBanned: owneraddr = mlist.GetOwnerEmail() realname = mlist.real_name doc.addError(_("""%(newaddr)s is banned from subscribing to the %(realname)s list. If you think this restriction is erroneous, please contact the list owners at %(owneraddr)s.""")) except Errors.MMAlreadyAMember: realname = mlist.real_name bad_confirmation(doc, _("""%(newaddr)s is already a member of the %(realname)s list. It is possible that you are attempting to confirm a request for an address that has already been subscribed.""")) else: # The response listname = mlist.real_name title = _('Change of address request confirmed') optionsurl = mlist.GetOptionsURL(newaddr, absolute=1) doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem(_("""\ You have successfully changed your address on the %(listname)s mailing list from <b>%(oldaddr)s</b> to <b>%(newaddr)s</b>. You can now <a href="%(optionsurl)s">proceed to your membership login page</a>.""")) mlist.Save() finally: mlist.Unlock()
def addrchange_prompt(mlist, doc, cookie, oldaddr, newaddr, globally): title = _('Confirm change of address request') doc.SetTitle(title) lang = mlist.getMemberLanguage(oldaddr) i18n.set_language(lang) doc.set_language(lang) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0, width='100%') table.AddRow([Center(Bold(FontAttr(title, size='+1')))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) listname = mlist.real_name fullname = mlist.getMemberName(oldaddr) if fullname is None: fullname = _('<em>Not available</em>') else: fullname = Utils.websafe(Utils.uncanonstr(fullname, lang)) if globally: globallys = _('globally') else: globallys = '' table.AddRow([_("""Your confirmation is required in order to complete the change of address request for the mailing list <em>%(listname)s</em>. You are currently subscribed with <ul><li><b>Real name:</b> %(fullname)s <li><b>Old email address:</b> %(oldaddr)s </ul> and you have requested to %(globallys)s change your email address to <ul><li><b>New email address:</b> %(newaddr)s </ul> Hit the <em>Change address</em> button below to complete the confirmation process. <p>Or hit <em>Cancel and discard</em> to cancel this change of address request.""") + '<p><hr>']) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([SubmitButton('submit', _('Change address')), SubmitButton('cancel', _('Cancel and discard'))]) form.AddItem(table) doc.AddItem(form)
def SendHostileSubscriptionNotice(self, listname, address): # Some one was invited to one list but tried to confirm to a different # list. We inform both list owners of the bogosity, but be careful # not to reveal too much information. selfname = self.internal_name() syslog("mischief", "%s was invited to %s but confirmed to %s", address, listname, selfname) # First send a notice to the attacked list msg = Message.OwnerNotification( self, _("Hostile subscription attempt detected"), Utils.wrap( _( """%(address)s was invited to a different mailing list, but in a deliberate malicious attempt they tried to confirm the invitation to your list. We just thought you'd like to know. No further action by you is required.""" ) ), ) msg.send(self) # Now send a notice to the invitee list try: # Avoid import loops from Mailman.MailList import MailList mlist = MailList(listname, lock=False) except Errors.MMListError: # Oh well return otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) try: msg = Message.OwnerNotification( mlist, _("Hostile subscription attempt detected"), Utils.wrap( _( """You invited %(address)s to your list, but in a deliberate malicious attempt, they tried to confirm the invitation to a different list. We just thought you'd like to know. No further action by you is required.""" ) ), ) msg.send(mlist) finally: i18n.set_translation(otrans)
def _onefile(self, msg, msgdata): # Do some common sanity checking on the message metadata. It's got to # be destined for a particular mailing list. This switchboard is used # to shunt off badly formatted messages. We don't want to just trash # them because they may be fixable with human intervention. Just get # them out of our site though. # # Find out which mailing list this message is destined for. listname = msgdata.get('listname') if not listname: listname = mm_cfg.MAILMAN_SITE_LIST mlist = self._open_list(listname) if not mlist: syslog('error', 'Dequeuing message destined for missing list: %s', listname) self._shunt.enqueue(msg, msgdata) return # Now process this message, keeping track of any subprocesses that may # have been spawned. We'll reap those later. # # We also want to set up the language context for this message. The # context will be the preferred language for the user if a member of # the list, or the list's preferred language. However, we must take # special care to reset the defaults, otherwise subsequent messages # may be translated incorrectly. BAW: I'm not sure I like this # approach, but I can't think of anything better right now. otranslation = i18n.get_translation() sender = msg.get_sender() if mlist: lang = mlist.getMemberLanguage(sender) else: lang = mm_cfg.DEFAULT_SERVER_LANGUAGE i18n.set_language(lang) msgdata['lang'] = lang try: keepqueued = self._dispose(mlist, msg, msgdata) finally: i18n.set_translation(otranslation) # Keep tabs on any child processes that got spawned. kids = msgdata.get('_kids') if kids: self._kids.update(kids) if keepqueued: self._switchboard.enqueue(msg, msgdata)
def heldmsg_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who posted the message. subject = 'n/a' op, id = mlist.pend_confirm(cookie) ign, sender, msgsubject, ign, ign, ign = mlist.GetRecord(id) lang = mlist.getMemberLanguage(sender) subject = Utils.websafe( Utils.oneline(msgsubject, Utils.GetCharSet(lang))) i18n.set_language(lang) doc.set_language(lang) # Discard the message mlist.HandleRequest(id, mm_cfg.DISCARD, _('Sender discarded message via web.')) except (Errors.LostHeldMessage, KeyError): bad_confirmation( doc, _('''The held message with the Subject: header <em>%(subject)s</em> could not be found. The most likely reason for this is that the list moderator has already approved or rejected the message. You were not able to cancel it in time.''')) else: # The response listname = mlist.real_name title = _('Posted message canceled') doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem( _('''\ You have successfully canceled the posting of your message with the Subject: header <em>%(subject)s</em> to the mailing list %(listname)s.''')) mlist.Save() finally: mlist.Unlock()
def html_TOC(self): mlist = self.maillist listname = mlist.internal_name() mbox = os.path.join(mlist.archive_dir() + '.mbox', listname + '.mbox') d = { "listname": mlist.real_name, "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "fullarch": '../%s.mbox/%s.mbox' % (listname, listname), "size": sizeof(mbox, mlist.preferred_language), 'meta': '', } # Avoid i18n side-effects otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) try: if not self.archives: d["noarchive_msg"] = _( '<P>Currently, there are no archives. </P>') d["archive_listing_start"] = "" d["archive_listing_end"] = "" d["archive_listing"] = "" else: d["noarchive_msg"] = "" d["archive_listing_start"] = quick_maketext( 'archliststart.html', lang=mlist.preferred_language, mlist=mlist) d["archive_listing_end"] = quick_maketext('archlistend.html', mlist=mlist) accum = [] for a in self.archives: accum.append(self.html_TOC_entry(a)) d["archive_listing"] = EMPTYSTRING.join(accum) finally: i18n.set_translation(otrans) # The TOC is always in the charset of the list's preferred language d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language) # The site can disable public access to the mbox file. if mm_cfg.PUBLIC_MBOX: template = 'archtoc.html' else: template = 'archtocnombox.html' return quick_maketext(template, d, mlist=mlist)
def html_TOC(self): mlist = self.maillist listname = mlist.internal_name() mbox = os.path.join(mlist.archive_dir()+'.mbox', listname+'.mbox') d = {"listname": mlist.real_name, "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "fullarch": '../%s.mbox/%s.mbox' % (listname, listname), "size": sizeof(mbox, mlist.preferred_language), 'meta': '', } # Avoid i18n side-effects otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) try: if not self.archives: d["noarchive_msg"] = _( '<P>Currently, there are no archives. </P>') d["archive_listing_start"] = "" d["archive_listing_end"] = "" d["archive_listing"] = "" else: d["noarchive_msg"] = "" d["archive_listing_start"] = quick_maketext( 'archliststart.html', lang=mlist.preferred_language, mlist=mlist) d["archive_listing_end"] = quick_maketext( 'archlistend.html', mlist=mlist) accum = [] for a in self.archives: accum.append(self.html_TOC_entry(a)) d["archive_listing"] = EMPTYSTRING.join(accum) finally: i18n.set_translation(otrans) # The TOC is always in the charset of the list's preferred language d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language) # The site can disable public access to the mbox file. if mm_cfg.PUBLIC_MBOX: template = 'archtoc.html' else: template = 'archtocnombox.html' return quick_maketext(template, d, mlist=mlist)
def HoldSubscription(self, addr, fullname, password, digest, lang): # Assure that the database is open for writing self.__opendb() # Get the next unique id id = self.__nextid() # Save the information to the request database. for held subscription # entries, each record in the database will be one of the following # format: # # the time the subscription request was received # the subscriber's address # the subscriber's selected password (TBD: is this safe???) # the digest flag # the user's preferred language data = time.time(), addr, fullname, password, digest, lang self.__db[id] = (SUBSCRIPTION, data) # # TBD: this really shouldn't go here but I'm not sure where else is # appropriate. syslog('vette', '%s: held subscription request from %s', self.internal_name(), addr) # Possibly notify the administrator in default list language if self.admin_immed_notify: ## cpanel patch: is the str() handling still needed? i18n.set_language(self.preferred_language) realname = str(self.real_name) subject = _( 'New subscription request to list %(realname)s from %(addr)s') text = Utils.maketext( 'subauth.txt', { 'username': addr, 'listname': self.real_name, 'hostname': self.host_name, 'admindb_url': self.GetScriptURL('admindb', absolute=1), }, mlist=self) # This message should appear to come from the <list>-owner so as # to avoid any useless bounce processing. owneraddr = self.GetOwnerEmail() msg = Message.UserNotification(owneraddr, owneraddr, subject, text, self.preferred_language) msg.send(self, **{'tomoderators': 1}) # Restore the user's preferred language. i18n.set_language(lang)
def SendSubscribeAck(self, name, password, digest, text=''): pluser = self.getMemberLanguage(name) # Need to set this here to get the proper l10n of the Subject: i18n.set_language(pluser) if self.welcome_msg: welcome = Utils.wrap(self.welcome_msg) + '\n' else: welcome = '' if self.umbrella_list: addr = self.GetMemberAdminEmail(name) umbrella = Utils.wrap( _('''\ Note: Since this is a list of mailing lists, administrative notices like the password reminder will be sent to your membership administrative address, %(addr)s.''')) else: umbrella = '' # get the text from the template text += Utils.maketext( 'subscribeack.txt', { 'real_name': self.real_name, 'host_name': self.host_name, 'welcome': welcome, 'umbrella': umbrella, 'emailaddr': self.GetListEmail(), 'listinfo_url': self.GetScriptURL('listinfo', absolute=True), 'optionsurl': self.GetOptionsURL(name, absolute=True), 'password': password, 'user': self.getMemberCPAddress(name), }, lang=pluser, mlist=self) if digest: digmode = _(' (Digest mode)') else: digmode = '' realname = self.real_name msg = Message.UserNotification( self.GetMemberAdminEmail(name), self.GetRequestEmail(), _('Welcome to the "%(realname)s" mailing list%(digmode)s'), text, pluser) msg['X-No-Archive'] = 'yes' msg.send(self, verp=mm_cfg.VERP_PERSONALIZED_DELIVERIES)
def HoldSubscription(self, addr, fullname, password, digest, lang): # Assure that the database is open for writing self.__opendb() # Get the next unique id id = self.__nextid() # Save the information to the request database. for held subscription # entries, each record in the database will be one of the following # format: # # the time the subscription request was received # the subscriber's address # the subscriber's selected password (TBD: is this safe???) # the digest flag # the user's preferred language data = time.time(), addr, fullname, password, digest, lang self.__db[id] = (SUBSCRIPTION, data) # # TBD: this really shouldn't go here but I'm not sure where else is # appropriate. syslog('vette', '%s: held subscription request from %s', self.internal_name(), addr) # Possibly notify the administrator in default list language if self.admin_immed_notify: ## cpanel patch: is the str() handling still needed? i18n.set_language(self.preferred_language) realname = str(self.real_name) subject = _( 'New subscription request to list %(realname)s from %(addr)s') text = Utils.maketext( 'subauth.txt', {'username' : addr, 'listname' : self.real_name, 'hostname' : self.host_name, 'admindb_url': self.GetScriptURL('admindb', absolute=1), }, mlist=self) # This message should appear to come from the <list>-owner so as # to avoid any useless bounce processing. owneraddr = self.GetOwnerEmail() msg = Message.UserNotification(owneraddr, owneraddr, subject, text, self.preferred_language) msg.send(self, **{'tomoderators': 1}) # Restore the user's preferred language. i18n.set_language(lang)
def sendProbe(self, member, msg): listname = self.real_name # Put together the substitution dictionary. d = { 'listname': listname, 'address': member, 'optionsurl': self.GetOptionsURL(member, absolute=True), 'owneraddr': self.GetOwnerEmail(), } text = Utils.maketext('probe.txt', d, lang=self.getMemberLanguage(member), mlist=self) # Calculate the VERP'd sender address for bounce processing of the # probe message. token = self.pend_new(Pending.PROBE_BOUNCE, member, msg) probedict = { 'bounces': self.internal_name() + '-bounces', 'token': token, } probeaddr = '%s@%s' % ( (mm_cfg.VERP_PROBE_FORMAT % probedict), self.host_name) # Calculate the Subject header, in the member's preferred language ulang = self.getMemberLanguage(member) otrans = i18n.get_translation() i18n.set_language(ulang) try: subject = _('%(listname)s mailing list probe message') finally: i18n.set_translation(otrans) outer = Message.UserNotification(member, probeaddr, subject, lang=ulang) outer.set_type('multipart/mixed') text = MIMEText(text, _charset=Utils.GetCharSet(ulang)) outer.attach(text) outer.attach(MIMEMessage(msg)) # Turn off further VERP'ing in the final delivery step. We set # probe_token for the OutgoingRunner to more easily handling local # rejects of probe messages. outer.send(self, envsender=probeaddr, verp=False, probe_token=token)
def heldmsg_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who posted the message. subject = 'n/a' op, id = mlist.pend_confirm(cookie) ign, sender, msgsubject, ign, ign, ign = mlist.GetRecord(id) lang = mlist.getMemberLanguage(sender) subject = Utils.websafe(Utils.oneline(msgsubject, Utils.GetCharSet(lang))) i18n.set_language(lang) doc.set_language(lang) # Discard the message mlist.HandleRequest(id, mm_cfg.DISCARD, _('Sender discarded message via web.')) except (Errors.LostHeldMessage, KeyError): bad_confirmation(doc, _('''The held message with the Subject: header <em>%(subject)s</em> could not be found. The most likely reason for this is that the list moderator has already approved or rejected the message. You were not able to cancel it in time.''')) else: # The response listname = mlist.real_name title = _('Posted message canceled') doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem(_('''\ You have successfully canceled the posting of your message with the Subject: header <em>%(subject)s</em> to the mailing list %(listname)s.''')) mlist.Save() finally: mlist.Unlock()
def as_html(self): d = self.__dict__.copy() # avoid i18n side-effects otrans = i18n.get_translation() i18n.set_language(self._lang) try: d["prev"], d["prev_wsubj"] = self._get_prev() d["next"], d["next_wsubj"] = self._get_next() d["email_html"] = self.quote(self.email) d["title"] = self.quote(self.subject) d["subject_html"] = self.quote(self.subject) d["message_id"] = self.quote(self._message_id) # TK: These two _url variables are used to compose a response # from the archive web page. So, ... d["subject_url"] = url_quote('Re: ' + self.subject) d["in_reply_to_url"] = url_quote(self._message_id) if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: # Point the mailto url back to the list author = re.sub('@', _(' at '), self.author) emailurl = self._mlist.GetListEmail() else: author = self.author emailurl = self.email d["author_html"] = self.quote(author) d["email_url"] = url_quote(emailurl) d["datestr_html"] = self.quote(i18n.ctime(int(self.date))) d["body"] = self._get_body() d['listurl'] = self._mlist.GetScriptURL('listinfo', absolute=1) d['listname'] = self._mlist.real_name d['encoding'] = '' finally: i18n.set_translation(otrans) charset = Utils.GetCharSet(self._lang) d["encoding"] = html_charset % charset self._add_decoded(d) return quick_maketext('article.html', d, lang=self._lang, mlist=self._mlist)
def html_head(self): # avoid i18n side-effects mlist = self.maillist otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) # Convenience def quotetime(s): return html_quote(i18n.ctime(s), self.lang) try: d = { "listname": html_quote(mlist.real_name, self.lang), "archtype": self.type, "archive": self.volNameToDesc(self.archive), "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "firstdate": quotetime(self.firstdate), "lastdate": quotetime(self.lastdate), "size": self.size, } i = { "thread": _("thread"), "subject": _("subject"), "author": _("author"), "date": _("date"), } finally: i18n.set_translation(otrans) for t in i.keys(): cap = t[0].upper() + t[1:] if self.type == cap: d["%s_ref" % (t)] = "" d["archtype"] = i[t] else: d["%s_ref" % (t)] = ('<a href="%s.html#start">[ %s ]</a>' % (t, i[t])) if self.charset: d["encoding"] = html_charset % self.charset else: d["encoding"] = "" return quick_maketext('archidxhead.html', d, mlist=mlist)
def as_html(self): d = self.__dict__.copy() # avoid i18n side-effects otrans = i18n.get_translation() i18n.set_language(self._lang) try: d["prev"], d["prev_wsubj"] = self._get_prev() d["next"], d["next_wsubj"] = self._get_next() d["email_html"] = self.quote(self.email) d["title"] = self.quote(self.subject) d["subject_html"] = self.quote(self.subject) d["message_id"] = self.quote(self._message_id) # TK: These two _url variables are used to compose a response # from the archive web page. So, ... d["subject_url"] = url_quote('Re: ' + self.subject) d["in_reply_to_url"] = url_quote(self._message_id) if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: # Point the mailto url back to the list author = re.sub('@', _(' at '), self.author) emailurl = self._mlist.GetListEmail() else: author = self.author emailurl = self.email d["author_html"] = self.quote(author) d["email_url"] = url_quote(emailurl) d["datestr_html"] = self.quote(i18n.ctime(int(self.date))) d["body"] = self._get_body() d['listurl'] = self._mlist.GetScriptURL('listinfo', absolute=1) d['listname'] = self._mlist.real_name d['encoding'] = '' finally: i18n.set_translation(otrans) charset = Utils.GetCharSet(self._lang) d["encoding"] = html_charset % charset self._add_decoded(d) return quick_maketext( 'article.html', d, lang=self._lang, mlist=self._mlist)
def html_head(self): # avoid i18n side-effects mlist = self.maillist otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) # Convenience def quotetime(s): return html_quote(i18n.ctime(s), self.lang) try: d = {"listname": html_quote(mlist.real_name, self.lang), "archtype": self.type, "archive": self.volNameToDesc(self.archive), "listinfo": mlist.GetScriptURL('listinfo', absolute=1), "firstdate": quotetime(self.firstdate), "lastdate": quotetime(self.lastdate), "size": self.size, } i = {"thread": _("thread"), "subject": _("subject"), "author": _("author"), "date": _("date"), } finally: i18n.set_translation(otrans) for t in i.keys(): cap = t[0].upper() + t[1:] if self.type == cap: d["%s_ref" % (t)] = "" d["archtype"] = i[t] else: d["%s_ref" % (t)] = ('<a href="%s.html#start">[ %s ]</a>' % (t, i[t])) if self.charset: d["encoding"] = html_charset % self.charset else: d["encoding"] = "" return quick_maketext( 'archidxhead.html', d, mlist=mlist)
def unsubscription_prompt(mlist, doc, cookie, addr): title = _('Confirm unsubscription request') doc.SetTitle(title) lang = mlist.getMemberLanguage(addr) i18n.set_language(lang) doc.set_language(lang) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0, width='100%') table.AddRow([Center(Bold(FontAttr(title, size='+1')))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) listname = mlist.real_name fullname = mlist.getMemberName(addr) if fullname is None: fullname = _('<em>Not available</em>') else: fullname = Utils.uncanonstr(fullname, lang) table.AddRow([_("""Your confirmation is required in order to complete the unsubscription request from the mailing list <em>%(listname)s</em>. You are currently subscribed with <ul><li><b>Real name:</b> %(fullname)s <li><b>Email address:</b> %(addr)s </ul> Hit the <em>Unsubscribe</em> button below to complete the confirmation process. <p>Or hit <em>Cancel and discard</em> to cancel this unsubscription request.""") + '<p><hr>']) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([SubmitButton('submit', _('Unsubscribe')), SubmitButton('cancel', _('Cancel and discard'))]) form.AddItem(table) doc.AddItem(form)
def reenable_confirm(mlist, doc, cookie): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) mlist.Lock() try: try: # Do this in two steps so we can get the preferred language for # the user who is unsubscribing. op, listname, addr = mlist.pend_confirm(cookie, expunge=False) lang = mlist.getMemberLanguage(addr) i18n.set_language(lang) doc.set_language(lang) op, addr = mlist.ProcessConfirmation(cookie) except Errors.NotAMemberError: bad_confirmation( doc, _('''Invalid confirmation string. It is possible that you are attempting to confirm a request for an address that has already been unsubscribed.''')) else: # The response listname = mlist.real_name title = _('Membership re-enabled.') optionsurl = mlist.GetOptionsURL(addr, absolute=1) doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem( _("""\ You have successfully re-enabled your membership in the %(listname)s mailing list. You can now <a href="%(optionsurl)s">visit your member options page</a>. """)) mlist.Save() finally: mlist.Unlock()
def main(): # Trick out pygettext since we want to mark template_data as translatable, # but we don't want to actually translate it here. def _(s): return s template_data = ( ('listinfo.html', _('General list information page')), ('subscribe.html', _('Subscribe results page')), ('options.html', _('User specific options page')), ('subscribeack.txt', _('Welcome email text file')), ('masthead.txt', _('Digest masthead')), ) _ = i18n._ doc = Document() # Set up the system default language i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) parts = Utils.GetPathPieces() if not parts: doc.AddItem(Header(2, _("List name is required."))) print doc.Format() return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>'))) # Send this with a 404 status. print 'Status: 404 Not Found' print doc.Format() syslog('error', 'No such list "%s": %s', listname, e) return
def send_digests(mlist, mboxfp): # Set the digest volume and time if mlist.digest_last_sent_at: bump = False # See if we should bump the digest volume number timetup = time.localtime(mlist.digest_last_sent_at) now = time.localtime(time.time()) freq = mlist.digest_volume_frequency if freq == 0 and timetup[0] < now[0]: # Yearly bump = True elif freq == 1 and timetup[1] <> now[1]: # Monthly, but we take a cheap way to calculate this. We assume # that the clock isn't going to be reset backwards. bump = True elif freq == 2 and (timetup[1] % 4 <> now[1] % 4): # Quarterly, same caveat bump = True elif freq == 3: # Once again, take a cheap way of calculating this weeknum_last = int(time.strftime('%W', timetup)) weeknum_now = int(time.strftime('%W', now)) if weeknum_now > weeknum_last or timetup[0] > now[0]: bump = True elif freq == 4 and timetup[7] <> now[7]: # Daily bump = True if bump: mlist.bump_digest_volume() mlist.digest_last_sent_at = time.time() # Wrapper around actually digest crafter to set up the language context # properly. All digests are translated to the list's preferred language. otranslation = i18n.get_translation() i18n.set_language(mlist.preferred_language) try: send_i18n_digests(mlist, mboxfp) finally: i18n.set_translation(otranslation)
def as_text(self): d = self.__dict__.copy() # We need to guarantee a valid From_ line, even if there are # bososities in the headers. if not d.get('fromdate', '').strip(): d['fromdate'] = time.ctime(time.time()) if not d.get('email', '').strip(): d['email'] = '*****@*****.**' if not d.get('datestr', '').strip(): d['datestr'] = time.ctime(time.time()) # headers = ['From %(email)s %(fromdate)s', 'From: %(email)s (%(author)s)', 'Date: %(datestr)s', 'Subject: %(subject)s'] if d['_in_reply_to']: headers.append('In-Reply-To: %(_in_reply_to)s') if d['_references']: headers.append('References: %(_references)s') if d['_message_id']: headers.append('Message-ID: %(_message_id)s') body = EMPTYSTRING.join(self.body) cset = Utils.GetCharSet(self._lang) # Coerce the body to Unicode and replace any invalid characters. if not isinstance(body, types.UnicodeType): body = unicode(body, cset, 'replace') if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: otrans = i18n.get_translation() try: i18n.set_language(self._lang) atmark = unicode(_(' at '), cset) body = re.sub(r'([-+,.\w]+)@([-+.\w]+)', '\g<1>' + atmark + '\g<2>', body) finally: i18n.set_translation(otrans) # Return body to character set of article. body = body.encode(cset, 'replace') return NL.join(headers) % d + '\n\n' + body + '\n'
def as_text(self): d = self.__dict__.copy() # We need to guarantee a valid From_ line, even if there are # bososities in the headers. if not d.get('fromdate', '').strip(): d['fromdate'] = time.ctime(time.time()) if not d.get('email', '').strip(): d['email'] = '*****@*****.**' if not d.get('datestr', '').strip(): d['datestr'] = time.ctime(time.time()) # headers = [ 'From %(email)s %(fromdate)s', 'From: %(email)s (%(author)s)', 'Date: %(datestr)s', 'Subject: %(subject)s' ] if d['_in_reply_to']: headers.append('In-Reply-To: %(_in_reply_to)s') if d['_references']: headers.append('References: %(_references)s') if d['_message_id']: headers.append('Message-ID: %(_message_id)s') body = EMPTYSTRING.join(self.body) cset = Utils.GetCharSet(self._lang) # Coerce the body to Unicode and replace any invalid characters. if not isinstance(body, types.UnicodeType): body = unicode(body, cset, 'replace') if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: otrans = i18n.get_translation() try: i18n.set_language(self._lang) atmark = unicode(_(' at '), cset) body = re.sub(r'([-+,.\w]+)@([-+.\w]+)', '\g<1>' + atmark + '\g<2>', body) finally: i18n.set_translation(otrans) # Return body to character set of article. body = body.encode(cset, 'replace') return NL.join(headers) % d + '\n\n' + body + '\n'
def main(): parts = Utils.GetPathPieces() if not parts: listinfo_overview() return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError as e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) # Send this with a 404 status. print('Status: 404 Not Found') listinfo_overview(_('No such list <em>%(safelistname)s</em>')) syslog('error', 'listinfo: No such list "%s": %s', listname, e) return # See if the user want to see this page in other language cgidata = cgi.FieldStorage() try: language = cgidata.getfirst('language') except TypeError: # Someone crafted a POST with a bad Content-Type:. doc = Document() doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('Invalid options to CGI script.'))) # Send this with a 400 status. print('Status: 400 Bad Request') print(doc.Format()) return if not Utils.IsLanguage(language): language = mlist.preferred_language i18n.set_language(language) list_listinfo(mlist, language)
def SendHostileSubscriptionNotice(self, listname, address): # Some one was invited to one list but tried to confirm to a different # list. We inform both list owners of the bogosity, but be careful # not to reveal too much information. selfname = self.internal_name() syslog('mischief', '%s was invited to %s but confirmed to %s', address, listname, selfname) # First send a notice to the attacked list msg = Message.OwnerNotification( self, _('Hostile subscription attempt detected'), Utils.wrap( _("""%(address)s was invited to a different mailing list, but in a deliberate malicious attempt they tried to confirm the invitation to your list. We just thought you'd like to know. No further action by you is required."""))) msg.send(self) # Now send a notice to the invitee list try: # Avoid import loops from Mailman.MailList import MailList mlist = MailList(listname, lock=False) except Errors.MMListError: # Oh well return otrans = i18n.get_translation() i18n.set_language(mlist.preferred_language) try: msg = Message.OwnerNotification( mlist, _('Hostile subscription attempt detected'), Utils.wrap( _("""You invited %(address)s to your list, but in a deliberate malicious attempt, they tried to confirm the invitation to a different list. We just thought you'd like to know. No further action by you is required."""))) msg.send(mlist) finally: i18n.set_translation(otrans)
from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog from Mailman import DlistUtils # Added to support dlists from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from urlparse import urlparse import time import Cookie import cgitb import sys # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) def main(): parts = Utils.GetPathPieces() if not parts: listinfo_overview() return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: # Avoid cross-site scripting attacks
def subscription_confirm(mlist, doc, cookie, cgidata): # See the comment in admin.py about the need for the signal # handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) listname = mlist.real_name mlist.Lock() try: try: # Some pending values may be overridden in the form. email of # course is hardcoded. ;) lang = cgidata.getvalue('language') if not Utils.IsLanguage(lang): lang = mlist.preferred_language i18n.set_language(lang) doc.set_language(lang) if cgidata.has_key('digests'): try: digest = int(cgidata.getvalue('digests')) except ValueError: digest = None else: digest = None userdesc = mlist.pend_confirm(cookie, expunge=False)[1] fullname = cgidata.getvalue('realname', None) if fullname is not None: fullname = Utils.canonstr(fullname, lang) overrides = UserDesc(fullname=fullname, digest=digest, lang=lang) userdesc += overrides op, addr, pw, digest, lang = mlist.ProcessConfirmation( cookie, userdesc) except Errors.MMNeedApproval: title = _('Awaiting moderator approval') doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem(_("""\ You have successfully confirmed your subscription request to the mailing list %(listname)s, however final approval is required from the list moderator before you will be subscribed. Your request has been forwarded to the list moderator, and you will be notified of the moderator's decision.""")) except Errors.NotAMemberError: bad_confirmation(doc, _('''Invalid confirmation string. It is possible that you are attempting to confirm a request for an address that has already been unsubscribed.''')) except Errors.MMAlreadyAMember: doc.addError(_("You are already a member of this mailing list!")) except Errors.MembershipIsBanned: owneraddr = mlist.GetOwnerEmail() doc.addError(_("""You are currently banned from subscribing to this list. If you think this restriction is erroneous, please contact the list owners at %(owneraddr)s.""")) except Errors.HostileSubscriptionError: doc.addError(_("""\ You were not invited to this mailing list. The invitation has been discarded, and both list administrators have been alerted.""")) else: # Use the user's preferred language i18n.set_language(lang) doc.set_language(lang) # The response listname = mlist.real_name title = _('Subscription request confirmed') optionsurl = mlist.GetOptionsURL(addr, absolute=1) doc.SetTitle(title) doc.AddItem(Header(3, Bold(FontAttr(title, size='+2')))) doc.AddItem(_('''\ You have successfully confirmed your subscription request for "%(addr)s" to the %(listname)s mailing list. A separate confirmation message will be sent to your email address, along with your password, and other useful information and links. <p>You can now <a href="%(optionsurl)s">proceed to your membership login page</a>.''')) mlist.Save() finally: mlist.Unlock()
def heldmsg_prompt(mlist, doc, cookie, id): title = _('Cancel held message posting') doc.SetTitle(title) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0, width='100%') table.AddRow([Center(Bold(FontAttr(title, size='+1')))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) # Blarg. The list must be locked in order to interact with the ListAdmin # database, even for read-only. See the comment in admin.py about the # need for the signal handler. def sigterm_handler(signum, frame, mlist=mlist): mlist.Unlock() sys.exit(0) # Get the record, but watch for KeyErrors which mean the admin has already # disposed of this message. mlist.Lock() try: try: data = mlist.GetRecord(id) except KeyError: data = None finally: mlist.Unlock() if data is None: bad_confirmation(doc, _("""The held message you were referred to has already been handled by the list administrator.""")) return # Unpack the data and present the confirmation message ign, sender, msgsubject, givenreason, ign, ign = data # Now set the language to the sender's preferred. lang = mlist.getMemberLanguage(sender) i18n.set_language(lang) doc.set_language(lang) subject = Utils.websafe(Utils.oneline(msgsubject, Utils.GetCharSet(lang))) reason = Utils.websafe(_(givenreason)) listname = mlist.real_name table.AddRow([_('''Your confirmation is required in order to cancel the posting of your message to the mailing list <em>%(listname)s</em>: <ul><li><b>Sender:</b> %(sender)s <li><b>Subject:</b> %(subject)s <li><b>Reason:</b> %(reason)s </ul> Hit the <em>Cancel posting</em> button to discard the posting. <p>Or hit the <em>Continue awaiting approval</em> button to continue to allow the list moderator to approve or reject the message.''') + '<p><hr>']) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([SubmitButton('submit', _('Cancel posting')), SubmitButton('cancel', _('Continue awaiting approval'))]) form.AddItem(table) doc.AddItem(form)
def reenable_prompt(mlist, doc, cookie, list, member): title = _('Re-enable mailing list membership') doc.SetTitle(title) form = Form(mlist.GetScriptURL('confirm', 1)) table = Table(border=0, width='100%') table.AddRow([Center(Bold(FontAttr(title, size='+1')))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) lang = mlist.getMemberLanguage(member) i18n.set_language(lang) doc.set_language(lang) realname = mlist.real_name info = mlist.getBounceInfo(member) if not info: listinfourl = mlist.GetScriptURL('listinfo', absolute=1) # They've already be unsubscribed table.AddRow([_("""We're sorry, but you have already been unsubscribed from this mailing list. To re-subscribe, please visit the <a href="%(listinfourl)s">list information page</a>.""")]) return date = time.strftime('%A, %B %d, %Y', time.localtime(time.mktime(info.date + (0,)*6))) daysleft = int(info.noticesleft * mlist.bounce_you_are_disabled_warnings_interval / mm_cfg.days(1)) # BAW: for consistency this should be changed to 'fullname' or the above # 'fullname's should be changed to 'username'. Don't want to muck with # the i18n catalogs though. username = mlist.getMemberName(member) if username is None: username = _('<em>not available</em>') else: username = Utils.websafe(Utils.uncanonstr(username, lang)) table.AddRow([_("""Your membership in the %(realname)s mailing list is currently disabled due to excessive bounces. Your confirmation is required in order to re-enable delivery to your address. We have the following information on file: <ul><li><b>Member address:</b> %(member)s <li><b>Member name:</b> %(username)s <li><b>Last bounce received on:</b> %(date)s <li><b>Approximate number of days before you are permanently removed from this list:</b> %(daysleft)s </ul> Hit the <em>Re-enable membership</em> button to resume receiving postings from the mailing list. Or hit the <em>Cancel</em> button to defer re-enabling your membership. """)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([Hidden('cookie', cookie)]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) table.AddRow([SubmitButton('submit', _('Re-enable membership')), SubmitButton('cancel', _('Cancel'))]) form.AddItem(table) doc.AddItem(form)
from Mailman import mm_cfg from Mailman import Utils from Mailman import MailList from Mailman import Message from Mailman import Errors from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog from Mailman.Utils import sha_new from Mailman.UserDesc import UserDesc from Mailman.tornado import template # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) def auto_version (resource): """Intended to be invoked from inside tornado templates, given a resource names such as /static/ctl.js this will return something like /static/ctl.v201211010141551.css to version it. For this to work we need a working url rewrite rule in apache or equivalent to strip out the timestamp. It is assumed that static resources that need to be versioned are available at the first level inside the PREFIX Directory""" if mm_cfg.SSO_ENVIRONMENT == mm_cfg.SSO_DEV: return resource absname = os.path.abspath(os.path.join(mm_cfg.VAR_PREFIX, resource[1:]))
if not os.getuid(): os.setregid(gid, gid) os.setreuid(uid, uid) signal.signal(signal.SIGHUP, signal.SIG_IGN) if ( os.getuid() is not uid ) or ( os.getgid() is not gid): sys.exit(0) opts, args = getopt.getopt(sys.argv[1:], 'f') for o, a in opts: if o == '-f' and os.fork(): sys.exit(0) i18n.set_language('fr') mysql = connectDB() lock = Lock() #------------------------------------------------------------------------------- # server # server = FastXMLRPCServer((SRV_HOST, SRV_PORT), BasicAuthXMLRPCRequestHandler) # index.php server.register_function(get_lists) server.register_function(subscribe) server.register_function(unsubscribe) # members.php server.register_function(get_members) # trombi.php
def __init__(self, message=None, sequence=0, keepHeaders=[], lang=mm_cfg.DEFAULT_SERVER_LANGUAGE, mlist=None): self.__super_init(message, sequence, keepHeaders) self.prev = None self.next = None # Trim Re: from the subject line i = 0 while i != -1: result = REpat.match(self.subject) if result: i = result.end(0) self.subject = self.subject[i:] else: i = -1 # Useful to keep around self._lang = lang self._mlist = mlist if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS: # Avoid i18n side-effects. Note that the language for this # article (for this list) could be different from the site-wide # preferred language, so we need to ensure no side-effects will # occur. Think what happens when executing bin/arch. otrans = i18n.get_translation() try: i18n.set_language(lang) if self.author == self.email: self.author = self.email = re.sub('@', _(' at '), self.email) else: self.email = re.sub('@', _(' at '), self.email) finally: i18n.set_translation(otrans) # Snag the content-* headers. RFC 1521 states that their values are # case insensitive. ctype = message.get('Content-Type', 'text/plain') cenc = message.get('Content-Transfer-Encoding', '') self.ctype = ctype.lower() self.cenc = cenc.lower() self.decoded = {} cset = Utils.GetCharSet(mlist.preferred_language) cset_out = Charset(cset).output_charset or cset if isinstance(cset_out, unicode): # email 3.0.1 (python 2.4) doesn't like unicode cset_out = cset_out.encode('us-ascii') charset = message.get_content_charset(cset_out) if charset: charset = charset.lower().strip() if charset[0] == '"' and charset[-1] == '"': charset = charset[1:-1] if charset[0] == "'" and charset[-1] == "'": charset = charset[1:-1] try: body = message.get_payload(decode=True) except binascii.Error: body = None if body and charset != Utils.GetCharSet(self._lang): # decode body try: body = unicode(body, charset) except (UnicodeError, LookupError): body = None if body: self.body = [l + "\n" for l in body.splitlines()] self.decode_headers()
return s.replace('\000', ' ') def sizeof(filename, lang): try: size = os.path.getsize(filename) except OSError, e: # ENOENT can happen if the .mbox file was moved away or deleted, and # an explicit mbox file name was given to bin/arch. if e.errno <> errno.ENOENT: raise return _('size not available') if size < 1000: # Avoid i18n side-effects otrans = i18n.get_translation() try: i18n.set_language(lang) out = _(' %(size)i bytes ') finally: i18n.set_translation(otrans) return out elif size < 1000000: return ' %d KB ' % (size / 1000) # GB?? :-) return ' %d MB ' % (size / 1000000) html_charset = '<META http-equiv="Content-Type" ' \ 'content="text/html; charset=%s">' def CGIescape(arg, lang=None):
from Mailman import Message from Mailman import i18n from Mailman.Handlers.Moderate import ModeratedMemberPost from Mailman.ListAdmin import HELDMSG from Mailman.ListAdmin import readMessage from Mailman.Cgi import Auth from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog EMPTYSTRING = '' NL = '\n' # Set up i18n. Until we know which list is being requested, we use the # server's default. _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) EXCERPT_HEIGHT = 10 EXCERPT_WIDTH = 76 def helds_by_sender(mlist): heldmsgs = mlist.GetHeldMessageIds() bysender = {} for id in heldmsgs: sender = mlist.GetRecord(id)[1] bysender.setdefault(sender, []).append(id) return bysender
import cgi import sys import errno import shutil from Mailman import mm_cfg from Mailman import Utils from Mailman import MailList from Mailman import Errors from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) def main(): doc = Document() doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) cgidata = cgi.FieldStorage() try: cgidata.getfirst('password', '') except TypeError: # Someone crafted a POST with a bad Content-Type:. doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('Invalid options to CGI script.'))) # Send this with a 400 status. print 'Status: 400 Bad Request'
def main(): # Trick out pygettext since we want to mark template_data as translatable, # but we don't want to actually translate it here. def _(s): return s template_data = ( ('listinfo.html', _('General list information page')), ('subscribe.html', _('Subscribe results page')), ('options.html', _('User specific options page')), ('subscribeack.txt', _('Welcome email text file')), ('masthead.txt', _('Digest masthead')), ('postheld.txt', _('User notice of held post')), ('approve.txt', _('User notice of held subscription')), ('refuse.txt', _('Notice of post refused by moderator')), ('invite.txt', _('Invitation to join list')), ('verify.txt', _('Request to confirm subscription')), ('unsub.txt', _('Request to confirm unsubscription')), ('nomoretoday.txt', _('User notice of autoresponse limit')), ('postack.txt', _('User post acknowledgement')), ('disabled.txt', _('Subscription disabled by bounce warning')), ('admlogin.html', _('Admin/moderator login page')), ('private.html', _('Private archive login page')), ('userpass.txt', _('On demand password reminder')), ) _ = i18n._ doc = Document() # Set up the system default language i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) parts = Utils.GetPathPieces() if not parts: doc.AddItem(Header(2, _("List name is required."))) print(doc.Format()) return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError as e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>'))) # Send this with a 404 status. print('Status: 404 Not Found') print(doc.Format()) syslog('error', 'edithtml: No such list "%s": %s', listname, e) return # Now that we have a valid list, set the language to its default i18n.set_language(mlist.preferred_language) doc.set_language(mlist.preferred_language) # Must be authenticated to get any farther cgidata = cgi.FieldStorage() try: cgidata.getfirst('adminpw', '') except TypeError: # Someone crafted a POST with a bad Content-Type:. doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('Invalid options to CGI script.'))) # Send this with a 400 status. print('Status: 400 Bad Request') print(doc.Format()) return # CSRF check safe_params = ['VARHELP', 'adminpw', 'admlogin'] params = list(cgidata.keys()) if set(params) - set(safe_params): csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) else: csrf_checked = True # if password is present, void cookie to force password authentication. if cgidata.getfirst('adminpw'): os.environ['HTTP_COOKIE'] = '' csrf_checked = True # Editing the html for a list is limited to the list admin and site admin. if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin), cgidata.getfirst('adminpw', '')): if 'admlogin' in cgidata: # This is a re-authorization attempt msg = Bold(FontSize('+1', _('Authorization failed.'))).Format() remote = os.environ.get( 'HTTP_FORWARDED_FOR', os.environ.get( 'HTTP_X_FORWARDED_FOR', os.environ.get('REMOTE_ADDR', 'unidentified origin'))) syslog('security', 'Authorization failed (edithtml): list=%s: remote=%s', listname, remote) else: msg = '' Auth.loginpage(mlist, 'admin', msg=msg) return realname = mlist.real_name if len(parts) > 1: template_name = parts[1] for (template, info) in template_data: if template == template_name: template_info = _(info) doc.SetTitle( _('%(realname)s -- Edit html for %(template_info)s')) break else: # Avoid cross-site scripting attacks safetemplatename = Utils.websafe(template_name) doc.SetTitle(_('Edit HTML : Error')) doc.AddItem(Header(2, _("%(safetemplatename)s: Invalid template"))) doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format()) return else: doc.SetTitle(_('%(realname)s -- HTML Page Editing')) doc.AddItem(Header(1, _('%(realname)s -- HTML Page Editing'))) doc.AddItem(Header(2, _('Select page to edit:'))) template_list = UnorderedList() for (template, info) in template_data: l = Link(mlist.GetScriptURL('edithtml') + '/' + template, _(info)) template_list.AddItem(l) doc.AddItem(FontSize("+2", template_list)) doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format()) return try: if list(cgidata.keys()): if csrf_checked: ChangeHTML(mlist, cgidata, template_name, doc) else: doc.addError( _('The form lifetime has expired. (request forgery check)') ) FormatHTML(mlist, doc, template_name, template_info) finally: doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format())