envelopes icon indicating copy to clipboard operation
envelopes copied to clipboard

Fails when attaching HTML file

Open anentropic opened this issue 11 years ago • 2 comments

I am trying to attach an HTML file to an email.

I get the following error:

/usr/local/lib/python2.7/email/message.pyc in get(self, name, failobj)
    358         is missing.
    359         """
--> 360         name = name.lower()
    361         for k, v in self._headers:
    362             if k.lower() == name:

AttributeError: 'slice' object has no attribute 'lower'

Stepping into the code with ipython debugger it seems that it detects the MIME type of the file, correctly, as text/html but this causes it to follow a different codepath which is where it fails: https://github.com/tomekwojcik/envelopes/blob/master/envelopes/envelope.py#L292

If I force it to use a generic mime type instead: envelope.add_attachment("myfile.html", mimetype="application/octet-stream")

...then I can send the message but something weird happens - Gmail shows the message as having the file attached twice, even though if I go back and check the envelope object it seems I have only one attachment:

In [18]: envelope._parts
Out[18]:
[('text/plain', 'my body text', 'utf-8'),
 ('application/octet-stream',
  <email.mime.base.MIMEBase instance at 0x270cd40>)]

anentropic avatar Dec 11 '13 11:12 anentropic

the 'double' attachments showing up in Gmail seems to be a bug in Gmail ... the message source appears to be correct :)

anentropic avatar Dec 11 '13 11:12 anentropic

The problem is that the code makes a wrong assumption in line 291 of envelope.py (https://github.com/tomekwojcik/envelopes/blob/master/envelopes/envelope.py#L291).

    for part in self._parts:
        type_maj, type_min = part[0].split('/')
        # Small correction. See thakkarparth007's comment on:
        # https://github.com/tomekwojcik/envelopes/issues/18
        if type_maj == 'text' and type_min in ('html', 'plain'):
            msg.attach(MIMEText(part[1], type_min, self._charset))
        else:
            msg.attach(part[1])

self._parts contains the (mimetype, part, charset) tuples for the "main" content - the text/html part and the text/plain part. Here, part is a (unicode?) string. But, it also contains the attachments - the (mimetype, part) tuples which are added in the add_attachment() function. Here, the part is a MIMEBase object. Now, consider what happens when you attach a text/html or text/plain file - you'll be appending a ('text/html',MIMEBase) tuple to the self._parts list. Now, in the above loop, the condition is satisfied by this text-ish file. So, you try to create a MIMEText object with the first argument being a MIMEBase instead of a (unicode?) string. This causes all the trouble.

A simple (but maybe hackish) solution is to add another condition and make that if statement look something like this:

       if type_maj == 'text' and type_min in ('html', 'plain') and\
            (isinstance(part[1], str) or isinstance(part[1], unicode)):
            msg.attach(MIMEText(part[1], type_min, self._charset))
        else:
            msg.attach(part[1])

This will work (at least for now).

thakkarparth007 avatar Nov 23 '15 22:11 thakkarparth007