diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d9d7eb9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.linting.flake8Enabled": true, + "python.linting.pylintEnabled": false, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/README.md b/README.md index a240f40..e10b9af 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ By default: - python3 - python3-aiosmtpd - python3-pydbus - - python-messaging (pip install python-messaging) ### setup Install the dependency and mms2mail (on debian based distribution): @@ -26,7 +25,6 @@ Install the dependency and mms2mail (on debian based distribution): sudo apt-get install python3 sudo apt-get install python3-pydbus sudo apt-get install python3-aiosmtpd -pip install --user python-messaging mkdir -p ~/.local/bin cp mms2mail ~/.local/bin @@ -66,23 +64,24 @@ port = 2525 ### reference ``` - mms2mail [-h] [-d | -f FILES [FILES ...]] [--disable-smtp] [--disable-mms-delivery] [--delete] [--force-read] [--force-unlock] [-l {critical,error,warning,info,debug}] +mms2mail [-h] [--disable-smtp] [--disable-mms-delivery] [--force-read] [--force-unlock] [-l {critical,error,warning,info,debug}] optional arguments: -h, --help show this help message and exit - -d, --daemon start in daemon mode - -f FILES [FILES ...], --file FILES [FILES ...] - Start in batch mode, parse specified mms files --disable-smtp --disable-mms-delivery - --delete Ask mmsd to delete the converted MMS --force-read Force conversion even if MMS is marked as read --force-unlock BEWARE COULD LEAD TO WHOLE MBOX CORRUPTION Force unlocking the mbox after a few minutes /!\ -l {critical,error,warning,info,debug}, --logging {critical,error,warning,info,debug} Define the logger output level ``` -### Using with Mutt : +### Sending MMS + +To send MMS, mail address not in the following format would be ignored : +```+123456789@domain``` with phone number in international format. + +#### with Mutt : To be able to send mms with mutt you need it to be built with SMTP support. And and the following line in your ```$HOME/.muttrc```: ``` diff --git a/mms2mail b/mms2mail index 6c03f6a..cc0793c 100755 --- a/mms2mail +++ b/mms2mail @@ -9,12 +9,12 @@ import time import logging from pathlib import Path -from messaging.mms.message import MMSMessage import mailbox import email from gi.repository import GLib -import pydbus +from pydbus import SessionBus +from datetime import datetime import os import re @@ -69,7 +69,7 @@ class MMS2Mail: self.attach_mms = cfg.getboolean('mail', 'attach_mms', fallback=False) self.delete = cfg.getboolean('mail', 'delete_from_mmsd', - fallback=False) + fallback=False) self.domain = cfg.get('mail', 'domain', fallback=socket.getfqdn()) self.user = cfg.get('mail', 'user', fallback=getpass.getuser()) @@ -87,13 +87,16 @@ class MMS2Mail: """ self.dbus = dbusmmsd - def check_mms(self, path): + def check_mms(self, path, properties): """ Check wether the provided file would be converted. :param path: the mms filesystem path :type path: str + :param properties: the mms properties + :type properties: Array + :return the mms status or None :rtype str """ @@ -121,7 +124,7 @@ class MMS2Mail: else: log.debug(f"New outgoing MMS found ({name.split('/')[-1]})") - def convert(self, path, dbus_path=None, properties=None): + def convert(self, path, dbus_path, properties): """ Convert a provided mms file to a mail stored in a mbox. @@ -130,80 +133,77 @@ class MMS2Mail: :param dbus_path: the mms dbus path :type dbus_path: str + + :param properties: the mms properties + :type properties: Array """ # Check if the provided file present - status = self.check_mms(path) + status = self.check_mms(path, properties) if not status: log.error("MMS file not convertible.") return - # Generate its dbus path, for future operation (mark as read, delete) - if not dbus_path: - dbus_path = f"/org/ofono/mms/modemmanager/{path.split('/')[-1]}" - mms = MMSMessage.from_file(path) message = email.message.EmailMessage() # Generate Mail Headers - mms_h_from = mms.headers.get('From', 'unknown/undef') - log.debug(f"MMS[From]: {mms_h_from}") - if 'not inserted' in mms_h_from: - mms_h_from = 'unknown/undef' - mms_from, mms_from_type = mms_h_from.split('/') - message['From'] = f"{mms_from}@{self.domain}" - - mms_h_to = mms.headers.get('To', 'unknown/undef') - log.debug(f"MMS[To]: {mms_h_to}") - if 'not inserted' in mms_h_to: - mms_h_to = 'unknown/undef' - mms_to, mms_to_type = mms_h_to.split('/') - message['To'] = f"{mms_to}@{self.domain}" - - # Get other recipients from dbus signal - # https://github.com/pmarti/python-messaging/issues/49 - if properties: - cc = "" - for r in properties['Recipients']: - if mms_to in r: - continue - log.debug(f'MMS/MAIL CC : {r}') - cc += f"{r}@{self.domain}," - if cc: - cc = cc[:-1] - message['CC'] = cc - - if 'Subject' in mms.headers and mms.headers['Subject']: - message['Subject'] = mms.headers['Subject'] + mms_from = properties.get('Sender', "unknown") + log.debug(f"MMS[From]: {mms_from}") + if '@' in mms_from: + message['From'] = mms_from else: - if status == 'sent' or status == 'draft': - message['Subject'] = f"MMS to {mms_to}" + message['From'] = f"{mms_from}@{self.domain}" + + to = properties.get('Modem Number', None) + if to: + message['To'] = f"{mms_from}@{self.domain}" + recipients = "" + for r in properties['Recipients']: + if to and to in r: + continue + log.debug(f'MMS[CC] : {r}') + if '@' in r: + recipients += f"{r}," else: - message['Subject'] = f"MMS from {mms_from}" + recipients += f"{r}@{self.domain}," + if recipients: + recipients = recipients[:-1] + if to: + message['CC'] = recipients + else: + message['To'] = recipients - if 'Date' in mms.headers and mms.headers['Date']: - message['Date'] = mms.headers['Date'] - - # Recopy MMS HEADERS - for header in mms.headers: - message.add_header(f"X-MMS-{header}", f"{mms.headers[header]}") + message['Subject'] = properties.get('Subject', + f"MMS from {mms_from}") + mms_date = properties.get('Date') + if mms_date: + mms_datetime = datetime.strptime(mms_date, '%Y-%m-%dT%H:%M:%S%z') + mail_date = email.utils.format_datetime(mms_datetime) + message['Date'] = mail_date or email.utils.formatdate() message.preamble = "This mail is converted from a MMS." body = "" - data_id = 1 attachments = [] - for data_part in mms.data_parts: - datacontent = data_part.headers['Content-Type'] - if datacontent is not None: - maintype, subtype = datacontent[0].split('/', 1) - if 'text/plain' in datacontent[0]: - encoding = datacontent[1].get('Charset', 'utf-8') - body += data_part.data.decode(encoding, - errors='replace') + '\n' + for attachment in properties['Attachments']: + cid = attachment[0] + mimetype = attachment[1] + contentfile = attachment[2] + offset = attachment[3] + size = attachment[4] + with open(contentfile, 'rb') as f: + f.seek(offset, 0) + content = f.read(size) + if mimetype is not None: + if 'text/plain' in mimetype: + mimetype, charset = mimetype.split(';', 1) + encoding = charset.split('=')[1] + body += content.decode(encoding, + errors='replace') + '\n' continue - extension = str(mimetypes.guess_extension(datacontent[0])) - filename = datacontent[1].get('Name', str(data_id)) - attachments.append([data_part.data, maintype, + maintype, subtype = mimetype.split('/', 1) + extension = str(mimetypes.guess_extension(mimetype)) + filename = cid + attachments.append([content, maintype, subtype, filename + extension]) - data_id = data_id + 1 if body: message.set_content(body) for a in attachments: @@ -212,7 +212,8 @@ class MMS2Mail: subtype=a[2], filename=a[3]) - # Add MMS binary file, for debugging purpose or reparsing in the future + # Add MMS binary file, for debugging purpose + # or reparsing in the future if self.attach_mms: with open(path, 'rb') as fp: message.add_attachment(fp.read(), @@ -385,7 +386,7 @@ class DbusMMSd(): :type mms2mail: mms2mail() """ self.mms2mail = mms2mail - self.bus = pydbus.SessionBus() + self.bus = SessionBus() def set_mms2mail(self, mms2mail): """ @@ -534,13 +535,6 @@ class DbusMMSd(): def main(): """Run the different functions handling mms and mail.""" parser = argparse.ArgumentParser() - mode = parser.add_mutually_exclusive_group() - mode.add_argument("-d", "--daemon", - help="start in daemon mode ", - action='store_true', dest='daemon') - mode.add_argument("-f", "--file", nargs='+', - help="Start in batch mode, parse specified mms files", - dest='files') parser.add_argument('--disable-smtp', action='store_true', dest='disable_smtp') parser.add_argument('--disable-mms-delivery', action='store_true', @@ -585,23 +579,15 @@ def main(): force_unlock=args.force_unlock) m.set_dbus(d) - if args.files: - for mms_file in args.files: - m.convert(path=mms_file) - return - elif args.daemon: - log.info("Starting mms2mail in daemon mode") - if not args.disable_smtp: - log.info("Activating smtp to mmsd server") - controller.start() - if not args.disable_mms_delivery: - log.info("Activating mms to mbox server") - d.set_mms2mail(m) - d.add_signal_receiver() - m.convert_stored_mms() - else: - parser.print_help() - return + log.info("Starting mms2mail") + if not args.disable_smtp: + log.info("Activating smtp to mmsd server") + controller.start() + if not args.disable_mms_delivery: + log.info("Activating mms to mbox server") + d.set_mms2mail(m) + d.add_signal_receiver() + m.convert_stored_mms() d.run() controller.stop()