-1

I want to open a .msg file, take the attachments, and send them to a specific mail address. I use extract_msg, I don't seem to be able to attach only the attachments in the .msg to my mail.

What I have so far:

mail = f"{path}/{attachment}"
msg = extract_msg.Message(mail)
msg_sender = str(msg.sender)
msg_subj = str(msg.subject)
msg_message = str(msg.body)

email_sender = 'automailsender123@gmail.com'
password = 'XXX'
send_to_email = send_to
print("----------------------------------------------------------------------------------------------------")
print(f"Send to: {send_to}\nAttachement: {attachment}\n")

msgMime = MIMEMultipart()
msgMime['From'] = msg_sender
msgMime['To'] = send_to_email
msgMime['Subject'] = msg_subj
body = MIMEText(msg_message)
msgMime.attach(body)

resources_dir = "resources"
attachments_dir = os.path.join(resources_dir, "attachments")
part = MIMEBase('application', 'octet-stream')

if msg.attachments:
    with tempfile.TemporaryDirectory() as tmp_dir_name:
        for att in msg.attachments:
            att_save_path = os.path.join(tmp_dir_name, att.longFilename)
            att.save(customPath=tmp_dir_name)

            attachment_stream = open(att_save_path, 'rb')
            part.set_payload(attachment_stream.read())
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', "attachment; filename= %s" % att_save_path)
            msgMime.attach(part)

else: print("No attachments")

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
try:
    server.login(email_sender, password)
except:
    print(f"Login failed for user: {email_sender}\nWith password: {password}")

text = msgMime.as_string()
server.sendmail(email_sender, send_to_email, text)
server.quit()

I get error:

     File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 596, in _rmtree_unsafe
    with os.scandir(path) as scandir_it:
NotADirectoryError: [WinError 267] The directory name is invalid: 'C:\\Users\\XXX.XXX\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'

The only reason I write to temp is that it is seemingly impossible to just attach the attachments of the original .msg to the Mime.

Any help is welcome, with the error or a better way to handle the attachments.

EDIT: Stacktrace:

Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 616, in _rmtree_unsafe
    os.unlink(fullname)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 801, in onerror
    _os.unlink(path)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 804, in onerror
    cls._rmtree(path)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\tempfile.py", line 812, in _rmtree
    _shutil.rmtree(name, onerror=onerror)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 740, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 599, in _rmtree_unsafe
    onerror(os.scandir, path, sys.exc_info())
  File "C:\Users\Kenneth.VanGysegem\AppData\Local\Programs\Python\Python39\lib\shutil.py", line 596, in _rmtree_unsafe
    with os.scandir(path) as scandir_it:
NotADirectoryError: [WinError 267] The directory name is invalid: 'C:\\Users\\KENNET~1.VAN\\AppData\\Local\\Temp\\tmpguckqnkr\\image001.jpg'
KVG
  • 59
  • 8
  • 1
    Seems that att.save(customPath=tmp_dir_name) tries to save to the folder name, instead of filename. – Yaroslav Kornachevskyi Aug 09 '21 at 15:52
  • 1
    As an aside. it looks like your `email` code was written for an older Python version. The `email` module in the standard library was overhauled in Python 3.6 to be more logical, versatile, and succinct; new code should target the (no longer very) new `EmailMessage` API. Probably throw away this code and start over with modern code from [the Python `email` examples documentation.](https://docs.python.org/3/library/email.examples.html) – tripleee Aug 09 '21 at 16:27
  • 1
    By `msg` do you mean Outlook? That part of your code is unclear; please provide a [mre]. – tripleee Aug 09 '21 at 16:38
  • 1
    Is this part of a course? This is like the fourth similar question in two days. – tripleee Aug 09 '21 at 16:39
  • 1
    Please provide a full traceback. There is no `os.scandir` in your own code, and we can't guess which precise line triggers this error. – tripleee Aug 09 '21 at 16:42
  • 1
    Thanks for the edit, but this still does not look complete. There is no `os.unlink(fullname)` in your code either. Please review the guidance for providing a [mre]. – tripleee Aug 10 '21 at 06:28
  • @triplee I noticed that too, but I don't anywhere do that. I'll make a minimal reproducable example. Thx for the input! – KVG Aug 10 '21 at 06:44

2 Answers2

1

It's not clear where the Message() part comes from or what exactly it does. I'm going to assume that it works as you hope, and speculate that you are probably an Outlook victim who had the fortune to find a library which manipulates its proprietary messages acceptably.

Your code has two problematic flows: It sends an email even if there were no attachments extracted, and it tries to send even when connecting to an SMTP server failed. You want to refactor these to something like

else:
    print("No attachments")
    sys.exit(0)

to quit when there was nothing extracted, and

try:
    server.login(email_sender, password)
except:
    print(f"Login failed for user: {email_sender}\nWith password: {password}")
    sys.exit(1)

to exit with an error when you can't proceed (or just take out the try / except and let the script crash with a traceback; if it's only for your personal use, this is probably acceptable, and easier to debug when you see the debugging information).

(Not a proper answer, but I felt I needed to point out these problems, too.)

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Thanks, that's axactly right! Message() is from the extract_msg library. In my use case it's not a problem when an email is sent when it has no attachements, I just have to make sure when it does have attachements those are sent too. But your second remark is very on point, thanks for pointing it out. – KVG Aug 11 '21 at 08:39
0

Ok guys this is how I made it work:

def send_mails_from_msg(attachment, send_to, path):
  mail = f"{path}/{attachment}"
  msg = extract_msg.Message(mail)

  msg_sender = str(msg.sender)
  msg_subj = str(msg.subject)
  msg_message = str(msg.body)
  email_sender = 'mailsender123@gmail.com'
  password = 'XXXXXX'

  print("----------------------------------------------------------------------------------------------------")
  print(f"Send to: {send_to}\nAttachement: {attachment}\n")

  msgMime = MIMEMultipart()
  msgMime['From'] = msg_sender
  msgMime['To'] = send_to
  msgMime['Subject'] = msg_subj
  body = MIMEText(msg_message)
  msgMime.attach(body)

  resources_dir = "resources"
  attachments_dir = os.path.join(resources_dir, "attachments")
  part = MIMEBase('application', 'octet-stream')

  if msg.attachments:
      for x in msg.attachments:
          x.save(customPath=attachments_dir)
      for files in os.listdir(attachments_dir):
          print(f"Files: {files}")
          attachment = open(f"{attachments_dir}/{files}", 'rb')
          part.set_payload(attachment.read())
          encoders.encode_base64(part)
          part.add_header('Content-Disposition', "attachment; 

filename= %s" % attachment) msgMime.attach(part)

  else: print("No attachments")

  server = smtplib.SMTP('smtp.gmail.com', 587)
  server.starttls()
  try:
      server.login(email_sender, password)
  except:
      print(f"Login failed for user: {email_sender}\nWith password: {password}")

  text = msgMime.as_string()
  server.sendmail(email_sender, send_to, text)
  server.quit()

Now this sends the jpg with the mail, mission accomplished! BUT.... The attachement looks like this: __io.BufferedReader name='resources_attachments_image001.jpg'_, it registers as attachement but can't be openend... I'm now trying to solve this issue

KVG
  • 59
  • 8
  • 1
    The attachment itself is fine, but you are passing `"attachment; filename= %s" % attachment` where probably you mean `'attachment; filename="%s"' % files` with `files` for the file name (probably rename to singular) and proper formatting of the MIME keyword (no space after the equals sign; double quotes around the filename). – tripleee Aug 10 '21 at 09:53