Add MMS sending capacilities #1
3 changed files with 85 additions and 95 deletions
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.enabled": true
|
||||
}
|
15
README.md
15
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```:
|
||||
```
|
||||
|
|
160
mms2mail
160
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()
|
||||
|
|
Loading…
Reference in a new issue