aboutsummaryrefslogblamecommitdiffstats
path: root/bin/add_members
blob: bb1c067809e74502523bfb3485041372377e9a23 (plain) (tree)
1
2
3
4
5
6
7
8
9

           
                                                               




                                                                
 



                                                                 
 
                                                                   
                                                             
                                                                                






















                                                                               
           

                                                                 










                                                                            



                                                                       




















                                                                        
                        
                         
                          



                                 

          
            







                          
                            




















                                                                   




                              
                                            






                               
                                                       






                                                               




                                                             

                                                           


                                                                             








                                                                               

                                                                      
 




                                                
                                                

                                                            
                                                           
                                                   
                                             









                                            

                           
                  




                                                    

                                                     





                                            
                                                                         





                                             
                                                                          

                                       
 



                                       

                                                                 



                                           
                                                  


















                                                 
                                          



                                                   
                                                                   

                    
                                                                   




                                                                 



                                                      


                                         







                                    
#! @PYTHON@
#
# Copyright (C) 1998-2016 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# argv[1] should be the name of the list.
# argv[2] should be the list of non-digested users.
# argv[3] should be the list of digested users.

# Make sure that the list of email addresses doesn't contain any comments,
# like majordomo may throw in.  For now, you just have to remove them manually.

"""Add members to a list from the command line.

Usage:
    add_members [options] listname

Options:

    --regular-members-file=file
    -r file
        A file containing addresses of the members to be added, one
        address per line.  This list of people become non-digest
        members.  If file is `-', read addresses from stdin.  Note that
        -n/--non-digest-members-file are deprecated synonyms for this option.

    --digest-members-file=file
    -d file
        Similar to above, but these people become digest members.

    --welcome-msg=<y|n>
    -w <y|n>
        Set whether or not to send the list members a welcome message,
        overriding whatever the list's `send_welcome_msg' setting is.

    --admin-notify=<y|n>
    -a <y|n>
        Set whether or not to send the list administrators a notification on
        the success/failure of these subscriptions, overriding whatever the
        list's `admin_notify_mchanges' setting is.

    --nomail
    -n
        Set the newly added members mail delivery to disabled by admin.

    --help
    -h
        Print this help message and exit.

    listname
        The name of the Mailman list you are adding members to.  It must
        already exist.

You must supply at least one of -r and -d options.  At most one of the
files can be `-'.
"""

import sys
import os
import getopt
from cStringIO import StringIO

import paths
# Import this /after/ paths so that the sys.path is properly hacked
from email.Utils import parseaddr

from Mailman import i18n
from Mailman import Utils
from Mailman import mm_cfg
from Mailman import Errors
from Mailman import Message
from Mailman import MailList
from Mailman import MemberAdaptor

_ = i18n._
C_ = i18n.C_



def usage(status, msg=''):
    if status:
        fd = sys.stderr
    else:
        fd = sys.stdout
    print >> fd, C_(__doc__)
    if msg:
        print >> fd, msg
    sys.exit(status)



def readfile(filename):
    if filename == '-':
        fp = sys.stdin
        closep = 0
    else:
        fp = open(filename)
        closep = 1
    # strip all the lines of whitespace and discard blank lines
    lines = filter(None, [line.strip() for line in fp.readlines()])
    if closep:
        fp.close()
    return lines



class Tee:
    def __init__(self, outfp):
        self.__outfp = outfp

    def write(self, msg):
        sys.stdout.write(i18n.tolocale(msg))
        self.__outfp.write(msg)


class UserDesc: pass



def addall(mlist, members, digest, ack, outfp, nomail):
    tee = Tee(outfp)
    for member in members:
        userdesc = UserDesc()
        userdesc.fullname, userdesc.address = parseaddr(member)
        userdesc.digest = digest

        try:
            mlist.ApprovedAddMember(userdesc,
                                    ack=ack,
                                    admin_notif=False,
                                    whence='bin/add_members',
                                   )
        except Errors.MMAlreadyAMember:
            print >> tee, _('Already a member: %(member)s')
        except Errors.MembershipIsBanned, pattern:
            print >> tee, ('%s:' %
                           member), _('Banned address (matched %(pattern)s)')
        except Errors.MMBadEmailError:
            if userdesc.address == '':
                print >> tee, _('Bad/Invalid email address: blank line')
            else:
                print >> tee, _('Bad/Invalid email address: %(member)s')
        except Errors.MMHostileAddress:
            print >> tee, _('Hostile address (illegal characters): %(member)s')
        else:
            print >> tee, _('Subscribed: %(member)s')
            if nomail:
                mlist.setDeliveryStatus(member, MemberAdaptor.BYADMIN)



def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   'a:r:d:w:nh',
                                   ['admin-notify=',
                                    'regular-members-file=',
                                    'digest-members-file=',
                                    'welcome-msg=',
                                    'nomail',
                                    'help'])
    except getopt.error, msg:
        usage(1, msg)

    if len(args) <> 1:
        usage(1)

    listname = args[0].lower().strip()
    nfile = None
    dfile = None
    send_welcome_msg = None
    admin_notif = None
    nomail = False
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        elif opt in ('-d', '--digest-members-file'):
            dfile = arg
        elif opt in ('-r', '--regular-members-file'):
            nfile = arg
        elif opt in ('-w', '--welcome-msg'):
            if arg.lower()[0] == 'y':
                send_welcome_msg = 1
            elif arg.lower()[0] == 'n':
                send_welcome_msg = 0
            else:
                usage(1, C_('Bad argument to -w/--welcome-msg: %(arg)s'))
        elif opt in ('-a', '--admin-notify'):
            if arg.lower()[0] == 'y':
                admin_notif = 1
            elif arg.lower()[0] == 'n':
                admin_notif = 0
            else:
                usage(1, C_('Bad argument to -a/--admin-notify: %(arg)s'))
        elif opt in ('-n', '--nomail'):
            nomail = True

    if dfile is None and nfile is None:
        usage(1)

    if dfile == "-" and nfile == "-":
        usage(1, C_('Cannot read both digest and normal members '
                    'from standard input.'))

    try:
        mlist = MailList.MailList(listname)
    except Errors.MMUnknownListError:
        usage(1, C_('No such list: %(listname)s'))

    # Set up defaults
    if send_welcome_msg is None:
        send_welcome_msg = mlist.send_welcome_msg
    if admin_notif is None:
        admin_notif = mlist.admin_notify_mchanges

    otrans = i18n.get_translation()
    # Read the regular and digest member files
    try:
        dmembers = []
        if dfile:
            dmembers = readfile(dfile)

        nmembers = []
        if nfile:
            nmembers = readfile(nfile)

        if not dmembers and not nmembers:
            usage(0, C_('Nothing to do.'))

        s = StringIO()
        i18n.set_language(mlist.preferred_language)
        if nmembers:
            addall(mlist, nmembers, 0, send_welcome_msg, s, nomail)

        if dmembers:
            addall(mlist, dmembers, 1, send_welcome_msg, s, nomail)

        if admin_notif:
            realname = mlist.real_name
            subject = _('%(realname)s subscription notification')
            msg = Message.UserNotification(
                mlist.owner,
                Utils.get_site_email(mlist.host_name),
                subject,
                s.getvalue(),
                mlist.preferred_language)
            msg.send(mlist)

        mlist.Save()
    finally:
        mlist.Unlock()
        i18n.set_translation(otrans)


if __name__ == '__main__':
    main()