chiark
/
gitweb
/
~mdw
/
stgit
/ blobdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
|
commitdiff
|
tree
raw
|
inline
| side by side
Let the caller supply the diff text to diffstat()
[stgit]
/
stgit
/
commands
/
mail.py
diff --git
a/stgit/commands/mail.py
b/stgit/commands/mail.py
index 151a40804c67cc9aa1620d18e037f265819c491e..7d19ecacd239cff0f3b0a39f4e936cebaef3a7ed 100644
(file)
--- a/
stgit/commands/mail.py
+++ b/
stgit/commands/mail.py
@@
-15,12
+15,13
@@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
-import sys, os, re, time, datetime, s
mtplib
+import sys, os, re, time, datetime, s
ocket, smtplib, getpass
import email, email.Utils, email.Header
from optparse import OptionParser, make_option
from stgit.commands.common import *
from stgit.utils import *
import email, email.Utils, email.Header
from optparse import OptionParser, make_option
from stgit.commands.common import *
from stgit.utils import *
+from stgit.out import *
from stgit import stack, git, version, templates
from stgit.config import config
from stgit import stack, git, version, templates
from stgit.config import config
@@
-28,12
+29,15
@@
from stgit.config import config
help = 'send a patch or series of patches by e-mail'
usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
help = 'send a patch or series of patches by e-mail'
usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
-Send a patch or a range of patches by e-mail using the 'smtpserver'
-configuration option. The From address and the e-mail format are
-generated from the template file passed as argument to '--template'
-(defaulting to '.git/patchmail.tmpl' or
+Send a patch or a range of patches by e-mail using the SMTP server
+specified by the 'stgit.smtpserver' configuration option, or the
+'--smtp-server' command line option. The From address and the e-mail
+format are generated from the template file passed as argument to
+'--template' (defaulting to '.git/patchmail.tmpl' or
'~/.stgit/templates/patchmail.tmpl' or
'~/.stgit/templates/patchmail.tmpl' or
-'/usr/share/stgit/templates/patchmail.tmpl').
+'/usr/share/stgit/templates/patchmail.tmpl'). A patch can be sent as
+attachment using the --attach option in which case the 'mailattch.tmpl'
+template will be used instead of 'patchmail.tmpl'.
The To/Cc/Bcc addresses can either be added to the template file or
passed via the corresponding command line options. They can be e-mail
The To/Cc/Bcc addresses can either be added to the template file or
passed via the corresponding command line options. They can be e-mail
@@
-53,31
+57,40
@@
replies to a different e-mail by using the '--refid' option.
SMTP authentication is also possible with '--smtp-user' and
'--smtp-password' options, also available as configuration settings:
SMTP authentication is also possible with '--smtp-user' and
'--smtp-password' options, also available as configuration settings:
-'smtpuser' and 'smtppassword'.
+'smtpuser' and 'smtppassword'. TLS encryption can be enabled by
+'--smtp-tls' option and 'smtptls' setting.
-The patch e-mail template accepts the following variables:
+The following variables are accepted by both the preamble and the
+patch e-mail templates:
- %(patch)s - patch name
- %(sender)s - 'sender' or 'authname <authemail>' as per the config file
- %(shortdescr)s - the first line of the patch description
- %(longdescr)s - the rest of the patch description, after the first line
- %(diff)s - unified diff of the patch
%(diffstat)s - diff statistics
%(diffstat)s - diff statistics
- %(version)s - ' version' string passed on the command line (or empty)
- %(prefix)s - 'prefix ' string passed on the command line
+ %(number)s - empty if only one patch is sent or ' patchnr/totalnr'
%(patchnr)s - patch number
%(patchnr)s - patch number
+ %(sender)s - 'sender' or 'authname <authemail>' as per the config file
%(totalnr)s - total number of patches to be sent
%(totalnr)s - total number of patches to be sent
- %(number)s - empty if only one patch is sent or ' patchnr/totalnr'
- %(fromauth)s - 'From: author\\n\\n' if different from sender
- %(authname)s - author's name
- %(authemail)s - author's email
+ %(version)s - ' version' string passed on the command line (or empty)
+
+In addition to the common variables, the preamble e-mail template
+accepts the following:
+
+ %(shortlog)s - first line of each patch description, listed by author
+
+In addition to the common variables, the patch e-mail template accepts
+the following:
+
%(authdate)s - patch creation date
%(authdate)s - patch creation date
- %(commname)s - committer's name
+ %(authemail)s - author's email
+ %(authname)s - author's name
%(commemail)s - committer's e-mail
%(commemail)s - committer's e-mail
+ %(commname)s - committer's name
+ %(diff)s - unified diff of the patch
+ %(fromauth)s - 'From: author\\n\\n' if different from sender
+ %(longdescr)s - the rest of the patch description, after the first line
+ %(patch)s - patch name
+ %(prefix)s - 'prefix ' string passed on the command line
+ %(shortdescr)s - the first line of the patch description"""
-For the preamble e-mail template, only the %(sender)s, %(version)s,
-%(patchnr)s, %(totalnr)s and %(number)s variables are supported."""
-
+directory = DirectoryHasRepository()
options = [make_option('-a', '--all',
help = 'e-mail all the applied patches',
action = 'store_true'),
options = [make_option('-a', '--all',
help = 'e-mail all the applied patches',
action = 'store_true'),
@@
-96,6
+109,12
@@
options = [make_option('-a', '--all',
make_option('--noreply',
help = 'do not send subsequent messages as replies',
action = 'store_true'),
make_option('--noreply',
help = 'do not send subsequent messages as replies',
action = 'store_true'),
+ make_option('--unrelated',
+ help = 'send patches without sequence numbering',
+ action = 'store_true'),
+ make_option('--attach',
+ help = 'send a patch as attachment',
+ action = 'store_true'),
make_option('-v', '--version', metavar = 'VERSION',
help = 'add VERSION to the [PATCH ...] prefix'),
make_option('--prefix', metavar = 'PREFIX',
make_option('-v', '--version', metavar = 'VERSION',
help = 'add VERSION to the [PATCH ...] prefix'),
make_option('--prefix', metavar = 'PREFIX',
@@
-114,15
+133,21
@@
options = [make_option('-a', '--all',
help = 'sleep for SECONDS between e-mails sending'),
make_option('--refid',
help = 'use REFID as the reference id'),
help = 'sleep for SECONDS between e-mails sending'),
make_option('--refid',
help = 'use REFID as the reference id'),
+ make_option('--smtp-server', metavar = 'HOST[:PORT]',
+ help = 'SMTP server to use for sending mail'),
make_option('-u', '--smtp-user', metavar = 'USER',
help = 'username for SMTP authentication'),
make_option('-p', '--smtp-password', metavar = 'PASSWORD',
help = 'username for SMTP authentication'),
make_option('-u', '--smtp-user', metavar = 'USER',
help = 'username for SMTP authentication'),
make_option('-p', '--smtp-password', metavar = 'PASSWORD',
help = 'username for SMTP authentication'),
+ make_option('-T', '--smtp-tls',
+ help = 'use SMTP with TLS encryption',
+ action = 'store_true'),
make_option('-b', '--branch',
help = 'use BRANCH instead of the default one'),
make_option('-m', '--mbox',
help = 'generate an mbox file instead of sending',
make_option('-b', '--branch',
help = 'use BRANCH instead of the default one'),
make_option('-m', '--mbox',
help = 'generate an mbox file instead of sending',
- action = 'store_true')]
+ action = 'store_true')
+ ] + make_diff_opts_option()
def __get_sender():
def __get_sender():
@@
-160,7
+185,7
@@
def __parse_addresses(msg):
return (from_addr_list[0], to_addr_list)
def __send_message(smtpserver, from_addr, to_addr_list, msg, sleep,
return (from_addr_list[0], to_addr_list)
def __send_message(smtpserver, from_addr, to_addr_list, msg, sleep,
- smtpuser, smtppassword):
+ smtpuser, smtppassword
, use_tls
):
"""Send the message using the given SMTP server
"""
try:
"""Send the message using the given SMTP server
"""
try:
@@
-172,9
+197,16
@@
def __send_message(smtpserver, from_addr, to_addr_list, msg, sleep,
try:
if smtpuser and smtppassword:
s.ehlo()
try:
if smtpuser and smtppassword:
s.ehlo()
+ if use_tls:
+ if not hasattr(socket, 'ssl'):
+ raise CmdException, "cannot use TLS - no SSL support in Python"
+ s.starttls()
+ s.ehlo()
s.login(smtpuser, smtppassword)
s.login(smtpuser, smtppassword)
- s.sendmail(from_addr, to_addr_list, msg)
+ result = s.sendmail(from_addr, to_addr_list, msg)
+ if len(result):
+ print "mail server refused delivery for the following recipients: %s" % result
# give recipients a chance of receiving patches in the correct order
time.sleep(sleep)
except Exception, err:
# give recipients a chance of receiving patches in the correct order
time.sleep(sleep)
except Exception, err:
@@
-206,6
+238,7
@@
def __build_address_headers(msg, options, extra_cc = []):
to_addr = ', '.join(options.to)
if options.cc:
cc_addr = ', '.join(options.cc + extra_cc)
to_addr = ', '.join(options.to)
if options.cc:
cc_addr = ', '.join(options.cc + extra_cc)
+ cc_addr = ', '.join(options.cc + extra_cc)
elif extra_cc:
cc_addr = ', '.join(extra_cc)
if options.bcc:
elif extra_cc:
cc_addr = ', '.join(extra_cc)
if options.bcc:
@@
-223,7
+256,7
@@
def __get_signers_list(msg):
"""
addr_list = []
"""
addr_list = []
- r = re.compile('^(signed-off-by|acked-by):\s+(.+)$', re.I)
+ r = re.compile('^(signed-off-by|acked-by
|cc
):\s+(.+)$', re.I)
for line in msg.split('\n'):
m = r.match(line)
if m:
for line in msg.split('\n'):
m = r.match(line)
if m:
@@
-238,6
+271,8
@@
def __build_extra_headers(msg, msg_id, ref_id = None):
msg['Date'] = email.Utils.formatdate(localtime = True)
msg['Message-ID'] = msg_id
if ref_id:
msg['Date'] = email.Utils.formatdate(localtime = True)
msg['Message-ID'] = msg_id
if ref_id:
+ # make sure the ref id has the angle brackets
+ ref_id = '<%s>' % ref_id.strip(' \t\n<>')
msg['In-Reply-To'] = ref_id
msg['References'] = ref_id
msg['User-Agent'] = 'StGIT/%s' % version.version
msg['In-Reply-To'] = ref_id
msg['References'] = ref_id
msg['User-Agent'] = 'StGIT/%s' % version.version
@@
-262,7
+297,11
@@
def __encode_message(msg):
msg.replace_header(header, new_val)
# encode the body and set the MIME and encoding headers
msg.replace_header(header, new_val)
# encode the body and set the MIME and encoding headers
- msg.set_charset(charset)
+ if msg.is_multipart():
+ for p in msg.get_payload():
+ p.set_charset(charset)
+ else:
+ msg.set_charset(charset)
def __edit_message(msg):
fname = '.stgitmail.txt'
def __edit_message(msg):
fname = '.stgitmail.txt'
@@
-281,7
+320,7
@@
def __edit_message(msg):
return msg
return msg
-def __build_cover(tmpl,
total_nr
, msg_id, options):
+def __build_cover(tmpl,
patches
, msg_id, options):
"""Build the cover message (series description) to be sent via SMTP
"""
sender = __get_sender()
"""Build the cover message (series description) to be sent via SMTP
"""
sender = __get_sender()
@@
-294,11
+333,15
@@
def __build_cover(tmpl, total_nr, msg_id, options):
if options.prefix:
prefix_str = options.prefix + ' '
else:
if options.prefix:
prefix_str = options.prefix + ' '
else:
- prefix_str = ''
+ confprefix = config.get('stgit.mail.prefix')
+ if confprefix:
+ prefix_str = confprefix + ' '
+ else:
+ prefix_str = ''
- total_nr_str = str(
total_nr
)
+ total_nr_str = str(
len(patches)
)
patch_nr_str = '0'.zfill(len(total_nr_str))
patch_nr_str = '0'.zfill(len(total_nr_str))
- if
total_nr
> 1:
+ if
len(patches)
> 1:
number_str = ' %s/%s' % (patch_nr_str, total_nr_str)
else:
number_str = ''
number_str = ' %s/%s' % (patch_nr_str, total_nr_str)
else:
number_str = ''
@@
-314,7
+357,12
@@
def __build_cover(tmpl, total_nr, msg_id, options):
'prefix': prefix_str,
'patchnr': patch_nr_str,
'totalnr': total_nr_str,
'prefix': prefix_str,
'patchnr': patch_nr_str,
'totalnr': total_nr_str,
- 'number': number_str}
+ 'number': number_str,
+ 'shortlog': stack.shortlog(crt_series.get_patch(p)
+ for p in patches),
+ 'diffstat': git.diffstat(git.diff(
+ rev1 = git_id(crt_series, '%s//bottom' % patches[0]),
+ rev2 = git_id(crt_series, '%s//top' % patches[-1])))}
try:
msg_string = tmpl % tmpl_dict
try:
msg_string = tmpl % tmpl_dict
@@
-372,15
+420,22
@@
def __build_message(tmpl, patch, patch_nr, total_nr, msg_id, ref_id, options):
if options.prefix:
prefix_str = options.prefix + ' '
else:
if options.prefix:
prefix_str = options.prefix + ' '
else:
- prefix_str = ''
-
+ confprefix = config.get('stgit.mail.prefix')
+ if confprefix:
+ prefix_str = confprefix + ' '
+ else:
+ prefix_str = ''
+
total_nr_str = str(total_nr)
patch_nr_str = str(patch_nr).zfill(len(total_nr_str))
total_nr_str = str(total_nr)
patch_nr_str = str(patch_nr).zfill(len(total_nr_str))
- if total_nr > 1:
+ if
not options.unrelated and
total_nr > 1:
number_str = ' %s/%s' % (patch_nr_str, total_nr_str)
else:
number_str = ''
number_str = ' %s/%s' % (patch_nr_str, total_nr_str)
else:
number_str = ''
+ diff = git.diff(rev1 = git_id(crt_series, '%s//bottom' % patch),
+ rev2 = git_id(crt_series, '%s//top' % patch),
+ diff_flags = options.diff_flags)
tmpl_dict = {'patch': patch,
'sender': sender,
# for backward template compatibility
tmpl_dict = {'patch': patch,
'sender': sender,
# for backward template compatibility
@@
-389,10
+444,8
@@
def __build_message(tmpl, patch, patch_nr, total_nr, msg_id, ref_id, options):
'longdescr': long_descr,
# for backward template compatibility
'endofheaders': '',
'longdescr': long_descr,
# for backward template compatibility
'endofheaders': '',
- 'diff': git.diff(rev1 = git_id('%s//bottom' % patch),
- rev2 = git_id('%s//top' % patch)),
- 'diffstat': git.diffstat(rev1 = git_id('%s//bottom'%patch),
- rev2 = git_id('%s//top' % patch)),
+ 'diff': diff,
+ 'diffstat': git.diffstat(diff),
# for backward template compatibility
'date': '',
'version': version_str,
# for backward template compatibility
'date': '',
'version': version_str,
@@
-444,7
+497,7
@@
def func(parser, options, args):
"""Send the patches by e-mail using the patchmail.tmpl file as
a template
"""
"""Send the patches by e-mail using the patchmail.tmpl file as
a template
"""
- smtpserver = config.get('stgit.smtpserver')
+ smtpserver =
options.smtp_server or
config.get('stgit.smtpserver')
applied = crt_series.get_applied()
applied = crt_series.get_applied()
@@
-458,25
+511,34
@@
def func(parser, options, args):
smtppassword = options.smtp_password or config.get('stgit.smtppassword')
smtpuser = options.smtp_user or config.get('stgit.smtpuser')
smtppassword = options.smtp_password or config.get('stgit.smtppassword')
smtpuser = options.smtp_user or config.get('stgit.smtpuser')
+ smtpusetls = options.smtp_tls or config.get('stgit.smtptls') == 'yes'
if (smtppassword and not smtpuser):
raise CmdException, 'SMTP password supplied, username needed'
if (smtppassword and not smtpuser):
raise CmdException, 'SMTP password supplied, username needed'
+ if (smtpusetls and not smtpuser):
+ raise CmdException, 'SMTP over TLS requested, username needed'
if (smtpuser and not smtppassword):
if (smtpuser and not smtppassword):
- raise CmdException, 'SMTP username supplied, password needed'
+ smtppassword = getpass.getpass("Please enter SMTP password: ")
total_nr = len(patches)
if total_nr == 0:
raise CmdException, 'No patches to send'
total_nr = len(patches)
if total_nr == 0:
raise CmdException, 'No patches to send'
- if options.noreply:
- ref_id = None
- else:
+ if options.refid:
+ if options.noreply or options.unrelated:
+ raise CmdException, \
+ '--refid option not allowed with --noreply or --unrelated'
ref_id = options.refid
ref_id = options.refid
+ else:
+ ref_id = None
sleep = options.sleep or config.getint('stgit.smtpdelay')
# send the cover message (if any)
if options.cover or options.edit_cover:
sleep = options.sleep or config.getint('stgit.smtpdelay')
# send the cover message (if any)
if options.cover or options.edit_cover:
+ if options.unrelated:
+ raise CmdException, 'cover sending not allowed with --unrelated'
+
# find the template file
if options.cover:
tmpl = file(options.cover).read()
# find the template file
if options.cover:
tmpl = file(options.cover).read()
@@
-486,7
+548,7
@@
def func(parser, options, args):
raise CmdException, 'No cover message template file found'
msg_id = email.Utils.make_msgid('stgit')
raise CmdException, 'No cover message template file found'
msg_id = email.Utils.make_msgid('stgit')
- msg = __build_cover(tmpl,
total_nr
, msg_id, options)
+ msg = __build_cover(tmpl,
patches
, msg_id, options)
from_addr, to_addr_list = __parse_addresses(msg)
msg_string = msg.as_string(options.mbox)
from_addr, to_addr_list = __parse_addresses(msg)
msg_string = msg.as_string(options.mbox)
@@
-496,19
+558,21
@@
def func(parser, options, args):
ref_id = msg_id
if options.mbox:
ref_id = msg_id
if options.mbox:
- print msg_string
+ out.stdout_raw(msg_string + '\n')
else:
else:
- print 'Sending the cover message...',
- sys.stdout.flush()
+ out.start('Sending the cover message')
__send_message(smtpserver, from_addr, to_addr_list, msg_string,
__send_message(smtpserver, from_addr, to_addr_list, msg_string,
- sleep, smtpuser, smtppassword)
- print 'done'
+ sleep, smtpuser, smtppassword
, smtpusetls
)
+ out.done()
# send the patches
if options.template:
tmpl = file(options.template).read()
else:
# send the patches
if options.template:
tmpl = file(options.template).read()
else:
- tmpl = templates.get_template('patchmail.tmpl')
+ if options.attach:
+ tmpl = templates.get_template('mailattch.tmpl')
+ else:
+ tmpl = templates.get_template('patchmail.tmpl')
if not tmpl:
raise CmdException, 'No e-mail template file found'
if not tmpl:
raise CmdException, 'No e-mail template file found'
@@
-521,14
+585,13
@@
def func(parser, options, args):
msg_string = msg.as_string(options.mbox)
# subsequent e-mails are seen as replies to the first one
msg_string = msg.as_string(options.mbox)
# subsequent e-mails are seen as replies to the first one
- if not options.noreply and not ref_id:
+ if not options.noreply and not
options.unrelated and not
ref_id:
ref_id = msg_id
if options.mbox:
ref_id = msg_id
if options.mbox:
- print msg_string
+ out.stdout_raw(msg_string + '\n')
else:
else:
- print 'Sending patch "%s"...' % p,
- sys.stdout.flush()
+ out.start('Sending patch "%s"' % p)
__send_message(smtpserver, from_addr, to_addr_list, msg_string,
__send_message(smtpserver, from_addr, to_addr_list, msg_string,
- sleep, smtpuser, smtppassword)
- print 'done'
+ sleep, smtpuser, smtppassword
, smtpusetls
)
+ out.done()