Subject: Test spam mail (GTUBE)
Message-ID: <GTUBE1.1010101@example.net>
Date: Wed, 23 Jul 2003 23:30:00 +0200
From: Sender <sender@example.net>
To: Recipient <recipient@example.net>
Precedence: junk
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
headers: hello-world
This is the GTUBE, the
Generic
Test for
Unsolicited
Bulk
Email
If your spam filter supports it, the GTUBE provides a test by which you
can verify that the filter is installed correctly and is detecting incoming
spam. You can send yourself a test mail containing the following string of
characters (in upper case and with no white spaces and line breaks):
XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
You should send this test mail from an account outside of your network.
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
Cell In[2], line 1
----> 1 mail = mailparser.parse_from_file("gtube_with_headers_header.txt")
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:76, in parse_from_file(fp)
66 def parse_from_file(fp):
67 """
68 Parsing email from file.
69
(...) 74 Instance of MailParser with raw email parsed
75 """
---> 76 return MailParser.from_file(fp)
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:187, in MailParser.from_file(cls, fp, is_outlook)
184 log.debug("Removing temp converted Outlook email {!r}".format(fp))
185 os.remove(fp)
--> 187 return cls(message)
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:134, in MailParser.__init__(self, message)
132 self._message = message
133 log.debug("All headers of emails: {}".format(", ".join(message.keys())))
--> 134 self.parse()
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:493, in MailParser.parse(self)
490 self._text_not_managed.append(payload)
492 # Parsed object mail with all parts
--> 493 self._mail = self._make_mail()
495 # Parsed object mail with mains parts
496 self._mail_partial = self._make_mail(complete=False)
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:299, in MailParser._make_mail(self, complete)
297 for i in keys:
298 log.debug("Getting header or part {!r}".format(i))
--> 299 value = getattr(self, i)
300 if value:
301 mail[i] = value
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:647, in MailParser.headers(self)
645 d = {}
646 for i in self.message.keys():
--> 647 d[i] = getattr(self, i)
648 return d
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:647, in MailParser.headers(self)
645 d = {}
646 for i in self.message.keys():
--> 647 d[i] = getattr(self, i)
648 return d
[... skipping similar frames: MailParser.headers at line 647 (2971 times)]
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:647, in MailParser.headers(self)
645 d = {}
646 for i in self.message.keys():
--> 647 d[i] = getattr(self, i)
648 return d
File /usr/local/lib/python3.13/site-packages/mailparser/core.py:592, in MailParser.__getattr__(self, name)
590 elif name_header in ADDRESSES_HEADERS:
591 h = decode_header_part(self.message.get(name_header, six.text_type()))
--> 592 return email.utils.getaddresses([h])
594 # others headers
595 else:
596 return get_header(self.message, name_header)
File /usr/local/lib/python3.13/email/utils.py:175, in getaddresses(fieldvalues, strict)
173 fieldvalues = _pre_parse_validation(fieldvalues)
174 addr = COMMASPACE.join(fieldvalues)
--> 175 a = _AddressList(addr)
176 result = _post_parse_validation(a.addresslist)
178 # Treat output as invalid if the number of addresses is not equal to the
179 # expected number of addresses.
File /usr/local/lib/python3.13/email/_parseaddr.py:520, in AddressList.__init__(self, field)
518 AddrlistClass.__init__(self, field)
519 if field:
--> 520 self.addresslist = self.getaddrlist()
521 else:
522 self.addresslist = []
File /usr/local/lib/python3.13/email/_parseaddr.py:264, in AddrlistClass.getaddrlist(self)
262 result = []
263 while self.pos < len(self.field):
--> 264 ad = self.getaddress()
265 if ad:
266 result += ad
File /usr/local/lib/python3.13/email/_parseaddr.py:311, in AddrlistClass.getaddress(self)
307 returnlist = returnlist + self.getaddress()
309 elif self.field[self.pos] == '<':
310 # Address is a phrase then a route addr
--> 311 routeaddr = self.getrouteaddr()
313 if self.commentlist:
314 returnlist = [(SPACE.join(plist) + ' (' +
315 ' '.join(self.commentlist) + ')', routeaddr)]
File /usr/local/lib/python3.13/email/_parseaddr.py:355, in AddrlistClass.getrouteaddr(self)
353 self.pos += 1
354 else:
--> 355 adlist = self.getaddrspec()
356 self.pos += 1
357 break
File /usr/local/lib/python3.13/email/_parseaddr.py:366, in AddrlistClass.getaddrspec(self)
363 """Parse an RFC 2822 addr-spec."""
364 aslist = []
--> 366 self.gotonext()
367 while self.pos < len(self.field):
368 preserve_ws = True
RecursionError: maximum recursion depth exceeded
Describe the bug
If an email contains a
headers:header, likeheaders: hello-world, processing breaks resulting in aRecursionError: maximum recursion depth exceeded. It only happens if the case is exactlyheaders...Headersis not affected. The issue happens because of https://github.com/SpamScope/mail-parser/blob/develop/src/mailparser/core.py#L647 wheregetattr()will continue to call theheadersproperty of the class.To Reproduce
Steps to reproduce the behavior:
Expected behavior
Email should gracefully handle a
headers:header, even though it is not a valid email header.Raw mail
Environment:
Additional context