> I like your patch, but it doesn't apply anymore (apologies it took so
> long to review) - would you please re-base it against the current
> master? thanks!

My apologies as well. I missed this message. I've rebased the
patch. I haven't looked closely at the code recently, but the merge
was clean enough, it runs cleanly with my mua (gnome-gmail)
and does not introduce new test failures.
From 6066867522181da0efdc0968e35af4a7df856dca Mon Sep 17 00:00:00 2001
From: David Steele <dste...@gmail.com>
Date: Sat, 6 Jul 2019 15:45:21 -0400
Subject: [PATCH] Add support for a custom mua command

The "mua" option allowed a custom mua command to be defined, but it
was only permitted for the list of 5 supported muas. This patch allows
an arbitrary mua command string to be defined.

The defined mua is set to default, and is added to the list of available
MUAs.

This also adds a 'mua-version' option, which specifies the mua
command-line argument for 'no action', which is used in utils.mua_exists(),
as an 'are you there?" check. This only applies to custom muas. The option
defaults to "--version".

As a side effect, the overloaded definition of 'mua' in utils, where
it could either be a string or a Mua object, depending on user options,
is eliminated.
---
 bin/reportbug        |  14 +++---
 conf/reportbug.conf  |   9 +++-
 man/reportbug.1      |   9 +++-
 man/reportbug.conf.5 |  12 ++++-
 reportbug/utils.py   | 111 ++++++++++++++++++-------------------------
 test/test_utils.py   |  29 ++++++-----
 6 files changed, 96 insertions(+), 88 deletions(-)

diff --git a/bin/reportbug b/bin/reportbug
index 2baca08..766b590 100755
--- a/bin/reportbug
+++ b/bin/reportbug
@@ -800,7 +800,8 @@ def main():
                     editor='', offline=False, verify=True, check_uid=True,
                     testmode=False, attachments=[], keyid='', body=None,
                     bodyfile=None, smtptls=False, smtpuser='', smtppasswd='',
-                    paranoid=False, mbox_reader_cmd=None)
+                    paranoid=False, mbox_reader_cmd=None,
+                    mua_version="--version")
 
     # Convention: consider `option.foo' names read-only; they always contain
     # the original value as determined by the cascade of command-line options
@@ -868,6 +869,8 @@ def main():
                       dest='bugnumber', help='specify a bug number to look for')
     parser.add_option('--mua', dest='mua',
                       help='send the report using the specified mail user agent')
+    parser.add_option('--mua-version', dest='mua_version',
+                      help='an mua option that results in no action (to verify mua is available)')
     parser.add_option('--mta', dest='mta', help='send the report using the '
                                                 'specified mail transport agent')
     parser.add_option('--list-cc', action='append', dest='listcc',
@@ -1062,17 +1065,14 @@ def main():
             ewrite("The directory %s does not exist; exiting.\n" % options.draftpath)
             sys.exit(1)
 
-    if options.mua and not options.template:
-        if not utils.mua_is_supported(options.mua):
-            ewrite("Specified mail user agent is not supported; exiting.\n")
-            sys.exit(1)
+    if isinstance(options.mua, str):
+        options.mua = utils.mua_create(options.mua, options.mua_version)
 
+    if options.mua and not options.template:
         if not utils.mua_exists(options.mua):
             ewrite("Selected mail user agent cannot be found; exiting.\n")
             sys.exit(1)
 
-        options.mua = utils.mua_name(options.mua)
-
     # try to import the specified UI, but only if template
     # is not set (it's useful only in 'text' UI).
     if options.interface and not options.template:
diff --git a/conf/reportbug.conf b/conf/reportbug.conf
index f4f67b2..c69e89e 100644
--- a/conf/reportbug.conf
+++ b/conf/reportbug.conf
@@ -19,9 +19,16 @@ submit
 # mh
 # nmh
 
-# You can also use 'mua'; it takes an argument like that to --mua
+# You can also use 'mua'; it takes an argument like that to --mua.
+# '%s' is replaced with the path to the message file. It is appended if
+# not present.
 # mua 'mutt'
 
+# If you use the 'mua' option, you can also specify an mua option that
+# causes the mua to exit without action. This is used to verify that the
+# mua is present and available. It defaults to "--version".
+# mua_version '--version'
+
 # Additional headers to add:
 # header "X-Debbugs-CC: debian...@lists.debian.org"
 # header "X-Silly-Header: I haven't edited my /etc/reportbug.conf"
diff --git a/man/reportbug.1 b/man/reportbug.1
index 6bf2d2d..da0b656 100644
--- a/man/reportbug.1
+++ b/man/reportbug.1
@@ -294,7 +294,14 @@ Specify an alternate \fIMTA\fP, instead of \fB/usr/sbin/sendmail\fP
 .B \-\-mua=MUA
 Instead of spawning an editor to revise the bug report, use the
 specified \fIMUA\fP (mail user agent) to edit and send
-it. \fB--mutt\fP and \fB--nmh\fP options are processed.
+it. The string \fI%s\fP is replaced with the file containing the
+message to send. It is appended to the command if not present.
+.TP
+.B \-\-mua-version=OPTION
+If the \fB--mua\fP option is used, specify the command-line MUA option
+for returning with no action. This is used by reportbug to determine
+that the MUA is installed and available. If not specified, this
+defaults to "--version".
 .TP
 .B \-n, \-\-mh, \-\-nmh
 Instead of spawning an editor to revise the bug report, use the
diff --git a/man/reportbug.conf.5 b/man/reportbug.conf.5
index 5ea17f2..8836314 100644
--- a/man/reportbug.conf.5
+++ b/man/reportbug.conf.5
@@ -171,10 +171,20 @@ with the default:
 .TP
 .B mua
 The mail user agent to use (the default is empty, to use an internal
-mailer). Example:
+mailer). The string "%s" is replaced with the path to the file containing
+the message to be sent. If not present, the path is set as the last
+argument. Example:
 
 \fBmua\fP \fI'mutt \-H'\fP
 
+.TP
+.B mua_version
+If the \fBmua\fP option is used, this specifies a MUA command-line
+option that causes the MUA to return without action. This is used to
+verify that the MUA is installed and available. Example:
+
+\fBmua_version\fP \fI'\-h'\fP
+
 .TP
 .B mutt
 Use the \fBmutt\fP mailer.
diff --git a/reportbug/utils.py b/reportbug/utils.py
index 545504d..645af96 100644
--- a/reportbug/utils.py
+++ b/reportbug/utils.py
@@ -843,21 +843,19 @@ USERFILE = os.path.expanduser('~/.reportbugrc')
 FILES = ('/etc/reportbug.conf', USERFILE)
 
 CONFIG_ARGS = (
-    'sendto', 'severity', 'mua', 'mta', 'email', 'realname', 'bts', 'verify',
-    'replyto', 'http_proxy', 'smtphost', 'editor', 'debconf', 'justification',
-    'sign', 'nocc', 'nocompress', 'dontquery', 'noconf', 'mirrors', 'keyid',
-    'headers', 'interface', 'template', 'mode', 'check_available', 'query_src',
-    'printonly', 'offline', 'check_uid', 'smtptls', 'smtpuser', 'smtppasswd',
-    'paranoid', 'mbox_reader_cmd', 'max_attachment_size', 'listccme', 'outfile')
+    'sendto', 'severity', 'mua', 'mua_version', 'mta', 'email', 'realname',
+    'bts', 'verify', 'replyto', 'http_proxy', 'smtphost', 'editor', 'debconf',
+    'justification', 'sign', 'nocc', 'nocompress', 'dontquery', 'noconf',
+    'mirrors', 'keyid', 'headers', 'interface', 'template', 'mode',
+    'check_available', 'query_src', 'printonly', 'offline', 'check_uid',
+    'smtptls', 'smtpuser', 'smtppasswd', 'paranoid', 'mbox_reader_cmd',
+    'max_attachment_size', 'listccme', 'outfile')
 
 
 class Mua:
-    command = ""
-    name = ""
-
-    def __init__(self, command):
+    def __init__(self, name, command):
         self.command = command
-        self.name = command.split()[0]
+        self.name = name
 
     def send(self, filename):
         mua = self.command
@@ -870,9 +868,8 @@ class Mua:
 
 
 class Gnus(Mua):
-    name = "gnus"
-
     def __init__(self):
+        self.name = "gnus"
         pass
 
     def send(self, filename):
@@ -886,67 +883,56 @@ class Gnus(Mua):
 
 
 MUA = {
-    'mutt': Mua('mutt -H'),
-    'mh': Mua('/usr/bin/mh/comp -use -file'),
+    'mutt': Mua('mutt', 'mutt -H'),
+    'mh': Mua('mh', '/usr/bin/mh/comp -use -file'),
+    'nmh': Mua('nmh', '/usr/bin/mh/comp -use -file'),
     'gnus': Gnus(),
-    'claws-mail': Mua('claws-mail --compose-from-file'),
+    'claws-mail': Mua('claws-mail', 'claws-mail --compose-from-file'),
 }
-MUA['nmh'] = MUA['mh']
+
 
 # TODO: convert them to class methods
 MUAVERSION = {
-    MUA['mutt']: 'mutt -v',
-    MUA['mh']: '/usr/bin/mh/comp -use -file',
-    MUA['gnus']: 'emacs --version',
-    MUA['claws-mail']: 'claws-mail --version',
+    'mutt': 'mutt -v',
+    'mh': '/usr/bin/mh/comp -use -file',
+    'nmh': '/usr/bin/mh/comp -use -file',
+    'gnus': 'emacs --version',
+    'claws-mail': 'claws-mail --version',
 }
 
 
-def mua_is_supported(mua):
-    # check if the mua is supported by reportbug
-    if mua == 'mh' or mua == MUA['mh']:
-        mua_tmp = 'mh'
-    elif mua == 'nmh' or mua == MUA['nmh']:
-        mua_tmp = 'mh'
-    elif mua == 'gnus' or mua == MUA['gnus']:
-        mua_tmp = 'gnus'
-    elif mua == 'mutt' or mua == MUA['mutt']:
-        mua_tmp = 'mutt'
-    elif mua == 'claws-mail' or mua == MUA['claws-mail']:
-        mua_tmp = 'claws-mail'
-    else:
-        mua_tmp = mua
-    if mua_tmp not in MUA:
-        return False
+def mua_create(muacmd, verstr):
+    global MUA, MUAVERSION
+
+    if muacmd in MUA:
+        mua = MUA[muacmd]
     else:
-        return True
+        muaname = muacmd.split(' ')[0]
+        mua = Mua(muaname, muacmd)
+        MUA[muaname] = mua
+        MUAVERSION[muaname] = "{0} {1}".format(muaname, verstr)
+
+    return mua
+
+
+def mua_version_cmd(mua):
+    return MUAVERSION[mua]
 
 
 def mua_exists(mua):
-    # check if the mua is available on the system
-    if mua == 'mh' or mua == MUA['mh']:
-        mua_tmp = MUA['mh']
-    elif mua == 'nmh' or mua == MUA['nmh']:
-        mua_tmp = MUA['mh']
-    elif mua == 'gnus' or mua == MUA['gnus']:
-        mua_tmp = MUA['gnus']
-    elif mua == 'mutt' or mua == MUA['mutt']:
-        mua_tmp = MUA['mutt']
-    elif mua == 'claws-mail' or mua == MUA['claws-mail']:
-        mua_tmp = MUA['claws-mail']
-    else:
-        mua_tmp = MUA[mua]
     output = '/dev/null'
+    muaname = mua.get_name()
     if os.path.exists(output):
         try:
-            returnvalue = subprocess.call(MUAVERSION[mua_tmp],
-                                          stdout=open(output, 'w', errors='backslashreplace'),
+            returnvalue = subprocess.call(mua_version_cmd(muaname),
+                                          stdout=open(output, 'w'),
                                           stderr=subprocess.STDOUT,
+                                          errors='backslashreplace',
                                           shell=True)
         except (IOError, OSError):
-            returnvalue = subprocess.call(MUAVERSION[mua_tmp], shell=True)
+            returnvalue = subprocess.call(mua_version_cmd(muaname), shell=True)
     else:
-        returnvalue = subprocess.call(MUAVERSION[mua_tmp], shell=True)
+        returnvalue = subprocess.call(mua_version_cmd(muaname), shell=True)
     # 127 is the shell standard return value to indicate a 'command not found' result
     if returnvalue == 127:
         return False
@@ -954,14 +940,6 @@ def mua_exists(mua):
         return True
 
 
-def mua_name(mua):
-    # in case the user specifies only the mua name in --mua, returns the default options
-    if mua in MUA:
-        return MUA[mua]
-    else:
-        return mua
-
-
 def first_run():
     return not os.path.exists(USERFILE)
 
@@ -1001,9 +979,10 @@ def parse_config_files():
                 elif token in ('printonly', 'template', 'offline'):
                     args[token] = True
                 elif token in ('email', 'realname', 'replyto', 'http_proxy',
-                               'smtphost', 'editor', 'mua', 'mta', 'smtpuser',
-                               'smtppasswd', 'justification', 'keyid',
-                               'mbox_reader_cmd', 'outfile'):
+                               'smtphost', 'editor', 'mua', 'mua_version',
+                               'mta', 'smtpuser', 'smtppasswd',
+                               'justification', 'keyid', 'mbox_reader_cmd',
+                               'outfile'):
                     bit = lex.get_token()
                     args[token] = bit
                 elif token in ('no-smtptls', 'smtptls'):
diff --git a/test/test_utils.py b/test/test_utils.py
index bcd997c..1a276c2 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -352,26 +352,31 @@ class TestSystemInformation(unittest.TestCase):
 
 
 class TestMua(unittest.TestCase):
-    def test_mua_is_supported(self):
+    def test_mua_exists(self):
 
-        for mua in ('mh', 'nmh', 'gnus', 'mutt', 'claws-mail'):
-            self.assertTrue(utils.mua_is_supported(mua))
+        for muaname in ('mh', 'nmh', 'gnus', 'mutt', 'claws-mail'):
+            if not utils.mua_exists(utils.MUA[muaname]):
+                self.fail("%s MUA program not available" % muaname)
 
-        self.assertFalse(utils.mua_is_supported('mua-of-my-dreams'))
+    def test_mua_custom_exists(self):
+        mymua = utils.mua_create('ls -l', '-h')
 
-    def test_mua_exists(self):
+        self.assertTrue(utils.mua_exists(mymua))
 
-        for mua in ('mh', 'nmh', 'gnus', 'mutt', 'claws-mail'):
-            if not utils.mua_exists(mua):
-                self.fail("%s MUA program not available" % mua)
+        del utils.MUA['ls']
+        del utils.MUAVERSION['ls']
 
-    def test_mua_name(self):
+    def test_mua_version(self):
+        self.assertEqual(utils.mua_version_cmd('mutt'), 'mutt -v')
 
-        for mua in ('mh', 'nmh', 'gnus', 'mutt', 'claws-mail'):
-            self.assertIsInstance(utils.mua_name(mua), utils.Mua)
+    def test_mua_set_custom(self):
+        utils.mua_create('mua-of-my-dreams --send', '-h')
 
-        self.assertEqual(utils.mua_name('mua-of-my-dreams'), 'mua-of-my-dreams')
+        self.assertEqual(utils.mua_version_cmd('mua-of-my-dreams'),
+                                               'mua-of-my-dreams -h')
 
+        del utils.MUA['mua-of-my-dreams']
+        del utils.MUAVERSION['mua-of-my-dreams']
 
 class TestBugreportBody(unittest.TestCase):
     def test_get_dependency_info(self):
-- 
2.20.1

Reply via email to