(root)/
Python-3.11.7/
Lib/
test/
test_mailcap.py
       1  import copy
       2  import os
       3  import sys
       4  import test.support
       5  import unittest
       6  import warnings
       7  from test.support import os_helper
       8  from test.support import warnings_helper
       9  
      10  
      11  mailcap = warnings_helper.import_deprecated('mailcap')
      12  
      13  
      14  # Location of mailcap file
      15  MAILCAPFILE = test.support.findfile("mailcap.txt")
      16  
      17  # Dict to act as mock mailcap entry for this test
      18  # The keys and values should match the contents of MAILCAPFILE
      19  MAILCAPDICT = {
      20      'application/x-movie':
      21          [{'compose': 'moviemaker %s',
      22            'x11-bitmap': '"/usr/lib/Zmail/bitmaps/movie.xbm"',
      23            'description': '"Movie"',
      24            'view': 'movieplayer %s',
      25            'lineno': 4}],
      26      'application/*':
      27          [{'copiousoutput': '',
      28            'view': 'echo "This is \\"%t\\" but        is 50 \\% Greek to me" \\; cat %s',
      29            'lineno': 5}],
      30      'audio/basic':
      31          [{'edit': 'audiocompose %s',
      32            'compose': 'audiocompose %s',
      33            'description': '"An audio fragment"',
      34            'view': 'showaudio %s',
      35            'lineno': 6}],
      36      'video/mpeg':
      37          [{'view': 'mpeg_play %s', 'lineno': 13}],
      38      'application/postscript':
      39          [{'needsterminal': '', 'view': 'ps-to-terminal %s', 'lineno': 1},
      40           {'compose': 'idraw %s', 'view': 'ps-to-terminal %s', 'lineno': 2}],
      41      'application/x-dvi':
      42          [{'view': 'xdvi %s', 'lineno': 3}],
      43      'message/external-body':
      44          [{'composetyped': 'extcompose %s',
      45            'description': '"A reference to data stored in an external location"',
      46            'needsterminal': '',
      47            'view': 'showexternal %s %{access-type} %{name} %{site}     %{directory} %{mode} %{server}',
      48            'lineno': 10}],
      49      'text/richtext':
      50          [{'test': 'test "`echo     %{charset} | tr \'[A-Z]\' \'[a-z]\'`"  = iso-8859-8',
      51            'copiousoutput': '',
      52            'view': 'shownonascii iso-8859-8 -e richtext -p %s',
      53            'lineno': 11}],
      54      'image/x-xwindowdump':
      55          [{'view': 'display %s', 'lineno': 9}],
      56      'audio/*':
      57          [{'view': '/usr/local/bin/showaudio %t', 'lineno': 7}],
      58      'video/*':
      59          [{'view': 'animate %s', 'lineno': 12}],
      60      'application/frame':
      61          [{'print': '"cat %s | lp"', 'view': 'showframe %s', 'lineno': 0}],
      62      'image/rgb':
      63          [{'view': 'display %s', 'lineno': 8}]
      64  }
      65  
      66  # For backwards compatibility, readmailcapfile() and lookup() still support
      67  # the old version of mailcapdict without line numbers.
      68  MAILCAPDICT_DEPRECATED = copy.deepcopy(MAILCAPDICT)
      69  for entry_list in MAILCAPDICT_DEPRECATED.values():
      70      for entry in entry_list:
      71          entry.pop('lineno')
      72  
      73  
      74  class ESC[4;38;5;81mHelperFunctionTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      75  
      76      def test_listmailcapfiles(self):
      77          # The return value for listmailcapfiles() will vary by system.
      78          # So verify that listmailcapfiles() returns a list of strings that is of
      79          # non-zero length.
      80          mcfiles = mailcap.listmailcapfiles()
      81          self.assertIsInstance(mcfiles, list)
      82          for m in mcfiles:
      83              self.assertIsInstance(m, str)
      84          with os_helper.EnvironmentVarGuard() as env:
      85              # According to RFC 1524, if MAILCAPS env variable exists, use that
      86              # and only that.
      87              if "MAILCAPS" in env:
      88                  env_mailcaps = env["MAILCAPS"].split(os.pathsep)
      89              else:
      90                  env_mailcaps = ["/testdir1/.mailcap", "/testdir2/mailcap"]
      91                  env["MAILCAPS"] = os.pathsep.join(env_mailcaps)
      92                  mcfiles = mailcap.listmailcapfiles()
      93          self.assertEqual(env_mailcaps, mcfiles)
      94  
      95      def test_readmailcapfile(self):
      96          # Test readmailcapfile() using test file. It should match MAILCAPDICT.
      97          with open(MAILCAPFILE, 'r') as mcf:
      98              with self.assertWarns(DeprecationWarning):
      99                  d = mailcap.readmailcapfile(mcf)
     100          self.assertDictEqual(d, MAILCAPDICT_DEPRECATED)
     101  
     102      def test_lookup(self):
     103          # Test without key
     104          expected = [{'view': 'animate %s', 'lineno': 12},
     105                      {'view': 'mpeg_play %s', 'lineno': 13}]
     106          actual = mailcap.lookup(MAILCAPDICT, 'video/mpeg')
     107          self.assertListEqual(expected, actual)
     108  
     109          # Test with key
     110          key = 'compose'
     111          expected = [{'edit': 'audiocompose %s',
     112                       'compose': 'audiocompose %s',
     113                       'description': '"An audio fragment"',
     114                       'view': 'showaudio %s',
     115                       'lineno': 6}]
     116          actual = mailcap.lookup(MAILCAPDICT, 'audio/basic', key)
     117          self.assertListEqual(expected, actual)
     118  
     119          # Test on user-defined dicts without line numbers
     120          expected = [{'view': 'mpeg_play %s'}, {'view': 'animate %s'}]
     121          actual = mailcap.lookup(MAILCAPDICT_DEPRECATED, 'video/mpeg')
     122          self.assertListEqual(expected, actual)
     123  
     124      def test_subst(self):
     125          plist = ['id=1', 'number=2', 'total=3']
     126          # test case: ([field, MIMEtype, filename, plist=[]], <expected string>)
     127          test_cases = [
     128              (["", "audio/*", "foo.txt"], ""),
     129              (["echo foo", "audio/*", "foo.txt"], "echo foo"),
     130              (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"),
     131              (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"),
     132              (["echo \\%t", "audio/*", "foo.txt"], "echo %t"),
     133              (["echo foo", "audio/*", "foo.txt", plist], "echo foo"),
     134              (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3")
     135          ]
     136          for tc in test_cases:
     137              self.assertEqual(mailcap.subst(*tc[0]), tc[1])
     138  
     139  
     140  class ESC[4;38;5;81mGetcapsTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     141  
     142      def test_mock_getcaps(self):
     143          # Test mailcap.getcaps() using mock mailcap file in this dir.
     144          # Temporarily override any existing system mailcap file by pointing the
     145          # MAILCAPS environment variable to our mock file.
     146          with os_helper.EnvironmentVarGuard() as env:
     147              env["MAILCAPS"] = MAILCAPFILE
     148              caps = mailcap.getcaps()
     149              self.assertDictEqual(caps, MAILCAPDICT)
     150  
     151      def test_system_mailcap(self):
     152          # Test mailcap.getcaps() with mailcap file(s) on system, if any.
     153          caps = mailcap.getcaps()
     154          self.assertIsInstance(caps, dict)
     155          mailcapfiles = mailcap.listmailcapfiles()
     156          existingmcfiles = [mcf for mcf in mailcapfiles if os.path.exists(mcf)]
     157          if existingmcfiles:
     158              # At least 1 mailcap file exists, so test that.
     159              for (k, v) in caps.items():
     160                  self.assertIsInstance(k, str)
     161                  self.assertIsInstance(v, list)
     162                  for e in v:
     163                      self.assertIsInstance(e, dict)
     164          else:
     165              # No mailcap files on system. getcaps() should return empty dict.
     166              self.assertEqual({}, caps)
     167  
     168  
     169  class ESC[4;38;5;81mFindmatchTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     170  
     171      def test_findmatch(self):
     172  
     173          # default findmatch arguments
     174          c = MAILCAPDICT
     175          fname = "foo.txt"
     176          plist = ["access-type=default", "name=john", "site=python.org",
     177                   "directory=/tmp", "mode=foo", "server=bar"]
     178          audio_basic_entry = {
     179              'edit': 'audiocompose %s',
     180              'compose': 'audiocompose %s',
     181              'description': '"An audio fragment"',
     182              'view': 'showaudio %s',
     183              'lineno': 6
     184          }
     185          audio_entry = {"view": "/usr/local/bin/showaudio %t", 'lineno': 7}
     186          video_entry = {'view': 'animate %s', 'lineno': 12}
     187          message_entry = {
     188              'composetyped': 'extcompose %s',
     189              'description': '"A reference to data stored in an external location"', 'needsterminal': '',
     190              'view': 'showexternal %s %{access-type} %{name} %{site}     %{directory} %{mode} %{server}',
     191              'lineno': 10,
     192          }
     193  
     194          # test case: (findmatch args, findmatch keyword args, expected output)
     195          #   positional args: caps, MIMEtype
     196          #   keyword args: key="view", filename="/dev/null", plist=[]
     197          #   output: (command line, mailcap entry)
     198          cases = [
     199              ([{}, "video/mpeg"], {}, (None, None)),
     200              ([c, "foo/bar"], {}, (None, None)),
     201              ([c, "video/mpeg"], {}, ('animate /dev/null', video_entry)),
     202              ([c, "audio/basic", "edit"], {}, ("audiocompose /dev/null", audio_basic_entry)),
     203              ([c, "audio/basic", "compose"], {}, ("audiocompose /dev/null", audio_basic_entry)),
     204              ([c, "audio/basic", "description"], {}, ('"An audio fragment"', audio_basic_entry)),
     205              ([c, "audio/basic", "foobar"], {}, (None, None)),
     206              ([c, "video/*"], {"filename": fname}, ("animate %s" % fname, video_entry)),
     207              ([c, "audio/basic", "compose"],
     208               {"filename": fname},
     209               ("audiocompose %s" % fname, audio_basic_entry)),
     210              ([c, "audio/basic"],
     211               {"key": "description", "filename": fname},
     212               ('"An audio fragment"', audio_basic_entry)),
     213              ([c, "audio/wav"],
     214               {"filename": fname},
     215               ("/usr/local/bin/showaudio audio/wav", audio_entry)),
     216              ([c, "message/external-body"],
     217               {"plist": plist},
     218               ("showexternal /dev/null default john python.org     /tmp foo bar", message_entry))
     219          ]
     220          self._run_cases(cases)
     221  
     222      @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system")
     223      @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks")
     224      @unittest.skipUnless(
     225          test.support.has_subprocess_support,
     226          "'test' command needs process support."
     227      )
     228      def test_test(self):
     229          # findmatch() will automatically check any "test" conditions and skip
     230          # the entry if the check fails.
     231          caps = {"test/pass": [{"test": "test 1 -eq 1"}],
     232                  "test/fail": [{"test": "test 1 -eq 0"}]}
     233          # test case: (findmatch args, findmatch keyword args, expected output)
     234          #   positional args: caps, MIMEtype, key ("test")
     235          #   keyword args: N/A
     236          #   output: (command line, mailcap entry)
     237          cases = [
     238              # findmatch will return the mailcap entry for test/pass because it evaluates to true
     239              ([caps, "test/pass", "test"], {}, ("test 1 -eq 1", {"test": "test 1 -eq 1"})),
     240              # findmatch will return None because test/fail evaluates to false
     241              ([caps, "test/fail", "test"], {}, (None, None))
     242          ]
     243          self._run_cases(cases)
     244  
     245      def test_unsafe_mailcap_input(self):
     246          with self.assertWarnsRegex(mailcap.UnsafeMailcapInput,
     247                                     'Refusing to substitute parameter.*'
     248                                     'into a shell command'):
     249              unsafe_param = mailcap.subst("echo %{total}",
     250                                           "audio/wav",
     251                                           "foo.txt",
     252                                           ["total=*"])
     253              self.assertEqual(unsafe_param, None)
     254  
     255          with self.assertWarnsRegex(mailcap.UnsafeMailcapInput,
     256                                     'Refusing to substitute MIME type'
     257                                     '.*into a shell'):
     258              unsafe_mimetype = mailcap.subst("echo %t", "audio/*", "foo.txt")
     259              self.assertEqual(unsafe_mimetype, None)
     260  
     261          with self.assertWarnsRegex(mailcap.UnsafeMailcapInput,
     262                                     'Refusing to use mailcap with filename.*'
     263                                     'Use a safe temporary filename.'):
     264              unsafe_filename = mailcap.findmatch(MAILCAPDICT,
     265                                                  "audio/wav",
     266                                                  filename="foo*.txt")
     267              self.assertEqual(unsafe_filename, (None, None))
     268  
     269      def _run_cases(self, cases):
     270          for c in cases:
     271              self.assertEqual(mailcap.findmatch(*c[0], **c[1]), c[2])
     272  
     273  
     274  if __name__ == '__main__':
     275      unittest.main()