|
|
|
@ -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}" |
|
|
|
|
|
|
|
|
|
if 'Date' in mms.headers and mms.headers['Date']: |
|
|
|
|
message['Date'] = mms.headers['Date'] |
|
|
|
|
recipients += f"{r}@{self.domain}," |
|
|
|
|
if recipients: |
|
|
|
|
recipients = recipients[:-1] |
|
|
|
|
if to: |
|
|
|
|
message['CC'] = recipients |
|
|
|
|
else: |
|
|
|
|
message['To'] = recipients |
|
|
|
|
|
|
|
|
|
# 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() |
|
|
|
|