# Copyright (C) 1998 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software 
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


"""Mixin class with message delivery routines."""


import string, os, sys
import operator
import errno
import mm_cfg
import Errors
import Utils


# Note that the text templates for the various messages have been moved into
# the templates directory.

# We could abstract these two better...
class Deliverer:
    def AddNonStandardHeaders(self, headers, zaplist=None):
        # Add headers like Precedence: and other useful headers.  None of
        # these are standard and finding information on some of them are
        # fairly difficult.  Some are just common practice, and we'll add more 
        # here as they become necessary.  A good place to look is
        # http://www.dsv.su.se/~jpalme/ietf/jp-ietf-home.html
        #
        # None of these headers are added if they already exist
        d = {}
        if zaplist is None:
            zaplist = headers
        for h in zaplist:
            i = string.find(h, ':')
            key = string.lower(h[:6])
            d[key] = key
        if not d.has_key('x-mailman-version'):
            headers.append('X-Mailman-Version: %s\n' % mm_cfg.VERSION)
        # semi-controversial: some don't want this included at all, others
        # want the value to be `list'
        if not d.has_key('precedence'):
            headers.append('Precedence: bulk\n')
        # Other list related non-standard headers.  Defined in:
        #
        # Grant Neufeld and Joshua D. Baer: The Use of URLs as Meta-Syntax for
        # Core Mail List Commands and their Transport through Message Header
        # fields, draft-baer-listspec-01.txt, September 1997.
        #
        # Referenced in
        #
        # http://www.dsv.su.se/~jpalme/ietf/mail-attributes.html
        if not d.has_key('list-id'):
            headers.append('List-Id: %s\n' % self.GetListIdentifier())
        # These currently seem like overkill.  Maybe add them in later when
        # the draft gets closer to a standard
        # List-Subscribe
        # List-Unsubscribe
        # List-Owner
        # List-Help
        # List-Post
        # List-Archive
        # List-Software
        # X-Listserver

    # This method assumes the sender is list-admin if you don't give one.
    def SendTextToUser(self, subject, text, recipient, sender=None,
                       add_headers=None):
        if add_headers is None:
            add_headers = []
        if not sender:
            sender = self.GetAdminEmail()
        self.AddNonStandardHeaders(add_headers)
        Utils.SendTextToUser(subject, text, recipient, sender,
                             add_headers=add_headers)

    def DeliverToUser(self, msg, recipient):
        # This method assumes the sender is the one given by the message.
        add_headers = []
        self.AddNonStandardHeaders(add_headers, msg.headers)
        add_headers.append('Errors-To: %s\n' % self.GetAdminEmail())
        Utils.DeliverToUser(msg, recipient, add_headers=add_headers)

    def QuotePeriods(self, text):
	return string.join(string.split(text, '\n.\n'), '\n .\n')

    #
    # This function is used to deliver messages
    # that are sent to <listname>-admin.  The sender
    # of the Mailman-originating message is the admin address
    # of the list -- otherwise, if both incoming sender and the
    # list's admin address(es) were non-local, many MTAs
    # wouldn't accept the message as it would be considered
    # improper relaying.
    # The recipients arg is a list of the current list
    # owners.  We don't mess with adding list specific
    # headers on body modifications so that the address
    # will work just like a normal forwarding address
    #
    def DeliverToOwner(self, msg, recipients):
        if not (len(recipients)):
            return
        cmd = "%s %s" % (mm_cfg.PYTHON,
                         os.path.join(mm_cfg.SCRIPTS_DIR, "deliver"))
        cmdproc = os.popen(cmd, 'w')

        cmdproc.write("%d\n" % self.num_spawns)
        cmdproc.write("%s\n" % self.GetAdminEmail())
        for r in recipients:
            # Mustn't send blank lines before end of recipients:
            if not r: continue
            cmdproc.write(r + "\n")
        cmdproc.write('\n')
        cmdproc.write(string.join(msg.headers, '') + "\n")
	cmdproc.write(self.QuotePeriods(msg.body))
	status = cmdproc.close()
        if status:
            self.LogMsg('deliverer',
                        'Non-zero exit status: %d\nCommand: %s'
                        (status >> 8), cmd)

    def DeliverToList(self, msg, recipients, 
                      header="", footer="", remove_to=0, tmpfile_prefix = ""):
	if not(len(recipients)):
	    return
        # Massage the headers.
        if remove_to:
            del msg['to']
            del msg['x-to']
	    msg.headers.append('To: %s\n' % self.GetListEmail())
	if self.reply_goes_to_list:
            del msg['reply-to']
            msg.headers.append('Reply-To: %s\n' % self.GetListEmail())
        # these can be used to surreptitiously gather membership info
        del msg['return-receipt-to']
        del msg['disposition-notification-to']
        del msg['x-confirm-reading-to']
        # get rid of duplicate fields
        del msg['sender']
        del msg['errors-to']
	msg.headers.append('Sender: %s\n' % self.GetAdminEmail())
	msg.headers.append('Errors-To: %s\n' % self.GetAdminEmail())
        self.AddNonStandardHeaders(msg.headers)
	msg.headers.append('X-BeenThere: %s\n' % self.GetListEmail())

        cmd = "%s %s" % (mm_cfg.PYTHON,
                         os.path.join(mm_cfg.SCRIPTS_DIR, "deliver"))
        cmdproc = os.popen(cmd, 'w')

        cmdproc.write("%d\n" % self.num_spawns)
        cmdproc.write("%s\n" % self.GetAdminEmail())
        for r in recipients:
            # Mustn't send blank lines before end of recipients:
            if not r: continue
            cmdproc.write(r + "\n")
        cmdproc.write("\n")             # Empty line for end of recipients.
        cmdproc.write(string.join(msg.headers, '') + "\n")
	if header:                      # The *body* header:
	    cmdproc.write(header + "\n")
	cmdproc.write(self.QuotePeriods(msg.body))
	if footer:
	    cmdproc.write(footer)

        status = cmdproc.close()
        if status:
            self.LogMsg('deliverer', 
                        'Non-zero exit status: %d\nCommand: %s',
                        (status >> 8), cmd)

    def SendPostAck(self, msg, sender):
	subject = msg.getheader('subject')
	if not subject:
	    subject = '[none]'
        else:
            sp = self.subject_prefix
            if (len(subject) > len(sp)
                and subject[0:len(sp)] == sp):
                # Trim off subject prefix
                subject = subject[len(sp):]
        # get the text from the template
        body = Utils.maketext(
            'postack.txt',
            {'subject'     : subject,
             'listname'    : self.real_name,
             'listinfo_url': self.GetAbsoluteScriptURL('listinfo'),
             })
	self.SendTextToUser('%s post acknowlegement' % self.real_name,
                            body, sender)

    def CreateSubscribeAck(self, name, password):
	if self.welcome_msg:
	    welcome = Utils.wrap(self.welcome_msg) + '\n'
	else:
	    welcome = ''
        if self.umbrella_list:
            umbrella = Utils.wrap(
                "(Since this is a list of mailing lists, administrative"
                " notices like the password reminder will be sent to"
                " your membership administrative address, %s.)\n"
                % self.GetMemberAdminEmail(name))
        else:
            umbrella = ''
        # get the text from the template
        body = Utils.maketext(
            'subscribeack.txt',
            {'real_name'   : self.real_name,
             'host_name'   : self.host_name,
             'welcome'     : welcome,
             'umbrella'    : umbrella,
             'emailaddr'   : self.GetListEmail(),
             'listinfo_url': self.GetAbsoluteScriptURL('listinfo'),
             'optionsurl'  : self.GetAbsoluteOptionsURL(name),
             'password'    : password,
             })
        return body


    def SendSubscribeAck(self, name, password, digest):
        if not self.send_welcome_msg:
	    return
	if digest:
	    digest_mode = '(Digest mode)'
	else:
	    digest_mode = ''
        self.SendTextToUser(subject = 'Welcome To "%s"! %s' % (self.real_name, 
							       digest_mode),
			    recipient = self.GetMemberAdminEmail(name),
			    text = self.CreateSubscribeAck(name, password))

    def SendUnsubscribeAck(self, name):
	self.SendTextToUser(subject = 'Unsubscribed from "%s"\n' % 
			               self.real_name,
			    recipient = self.GetMemberAdminEmail(name),
			    text = Utils.wrap(self.goodbye_msg))

    def MailUserPassword(self, user):
        listfullname = '%s@%s' % (self.real_name, self.host_name)
        ok = 1
        # find the lowercased version of the user's address
        user = self.FindUser(user)
        if user and self.passwords.has_key(user):
            cpuser = self.GetUserSubscribedAddress(user)
            recipient = self.GetMemberAdminEmail(cpuser)
            subj = '%s mailing list reminder\n' % listfullname
            # get the text from the template
            text = Utils.maketext(
                'userpass.txt',
                {'user'       : cpuser,
                 'listname'   : self.real_name,
                 'password'   : self.passwords[user],
                 'options_url': self.GetAbsoluteOptionsURL(user),
                 'requestaddr': self.GetRequestEmail(),
                 'adminaddr'  : self.GetAdminEmail(),
                 })
        else:
            ok = 0
            recipient = self.GetAdminEmail()
            subj = '%s user %s missing password!\n' % (listfullname, user)
            text = Utils.maketext(
                'nopass.txt',
                {'username'     : `user`,
                 'internal_name': self._internal_name,
                 })
	self.SendTextToUser(subject = subj,
			    recipient = recipient,
                            text = text,
                            add_headers=["Errors-To: %s"
                                         % self.GetAdminEmail(),
                                         "X-No-Archive: yes",
                                         "Precedence: bulk"])
        if not ok:
             raise Errors.MMBadUserError
