#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

u"""%(scriptname)s: Afficher un mail correctement encodé pour son envoi

USAGE
    echo "BODY" | %(scriptname)s [options] <SUBJECT> RECIPIENTS... | /usr/sbin/sendmail -t -i

OPTIONS
    -F, --from FROM
        Spécifier l'adresse de l'expéditeur. Par défaut, utiliser l'adresse
        %(DEFAULT_FROM)s
    -c, --cc CCRECIPIENTS
        Destinataires en copie du message, séparés par une virgule.
        Cette option peut être spécifiée autant de fois que nécessaire.
    -b, --bcc BCCRECIPIENTS
        Destinataires en copie cachée du message, séparés par une virgule.
        Cette option peut être spécifiée autant de fois que nécessaire.
    -a, --attach FILE
        Attacher un fichier au mail. Cette option peut être spécifiée autant de
        fois que nécessaire pour attacher plusieurs fichiers.
    SUBJECT
        Sujet du mail. Cette valeur est requise.
    RECIPIENTS
        Destinataires du mail. Il faut au moins un destinataire.
    BODY
        Corps du mail. Il est spécifié sur l'entrée standard.
    -f, --body FILE
        Spécifier un fichier qui contient le corps du message, au lieu de lire
        sur l'entrée standard.
    --gencmd
        Afficher la commande complète avec sendmail et l'option -f. En effet, la
        commande donnée en exemple dans la section USAGE utilise une valeur par
        défaut pour l'en-tête From: de l'enveloppe. Pour certains serveurs SMTP
        qui requièrent que le From: de l'enveloppe corresponde à un domaine
        valide, il faut forcer la valeur avec l'option -f de sendmail:
            %(scriptname)s [-F FROM] ... | sendmail -t -i -f FROM
        L'option --gencmd affiche une commande complète qu'il suffit d'évaluer,
        e.g:
            sendmail -t -i -f FROM <<EOF
            ...
            EOF"""

DEFAULT_FROM = 'no-reply@univ-reunion.fr'

import i_need_py25 # nécessite module email version 4.0

import os, sys, mimetypes, re
from os import path

from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.multipart import MIMEMultipart
from email.generator import Generator

from ulib.all import *

RE_UNSAFE_SH = re.compile(r'[^a-zA-Z0-9_^@%+=:,./-]')
def quotesh(s):
    if not s: return "''"
    if RE_UNSAFE_SH.search(s) is None: return s
    return "'%s'" % s.replace("'", "'\\''")

def display_help():
    uprint(__doc__ % globals())

def run_umail():
    options, longoptions = build_options([
        ('h', 'help', "Afficher l'aide"),
        ('F:', 'from=', "Spécifier l'expéditeur"),
        ('c:', 'cc=', "Destinataires en copie"),
        ('b:', 'bcc=', "Destinataires en copie cachée"),
        ('a:', 'attach=', "Attacher un fichier"),
        ('t:', 'content-type=', "Spécifier le type de contenu du fichier"),
        ('f:', 'body=', "Spécifier un fichier contenant le corps du message"),
        (None, 'html', "Indiquer que le corps du message est du type text/html. Par défaut, il s'agit de text/plain"),
        (None, 'gencmd', "Générer une commande à évaluer pour envoyer le mail"),
        ])
    options, args = get_args(None, options, longoptions)
    mfrom = DEFAULT_FROM
    subject = None
    mtos = []
    mccs = []
    mbccs = []
    body = None
    bodyfile = None
    bodymtype = 'plain'
    afiles = []
    amimetypes = []
    gencmd = False
    for option, value in options:
        if option in ('-h', '--help'):
            display_help()
            sys.exit(0)
        elif option in ('-F', '--from'):
            mfrom = value
        elif option in ('-c', '--cc'):
            mccs.extend(value.split(','))
        elif option in ('-b', '--bcc'):
            mbccs.extend(value.split(','))
        elif option in ('-a', '--attach'):
            afiles.append(value)
        elif option in ('-t', '--content-type'):
            amimetypes.append(value)
        elif option in ('-f', '--body'):
            bodyfile = value
            if bodyfile == "-":
                bodyfile = None
            elif not path.exists(bodyfile):
                die("%s: fichier introuvable" % bodyfile)
        elif option in ('--html',):
            bodymtype = 'html'
        elif option in ('--gencmd',):
            gencmd = True

    if not args[0:1]: die("Vous devez spécifier le sujet")
    subject = args[0]
    if not args[1:]: die("Vous devez spécifier les destinataires")
    mtos = args[1:]

    lines = BLines()
    close = False
    if bodyfile is None:
        inf = sys.stdin
        close = False
    else:
        inf = open(bodyfile, 'rb')
        close = True
    try:
        lines.readlines(inf)
    finally:
        if close: inf.close()

    if not afiles:
        # Sans attachement, faire un message simple
        msg = MIMEText('\n'.join(lines), bodymtype)
        msg.set_charset('utf-8')
    else:
        # Il y a des attachement, faire un multipart
        msg = MIMEMultipart()
        #msg.set_charset('utf-8')
        if lines:
            body = MIMEText('\n'.join(lines), bodymtype)
            body.set_charset('utf-8')
            msg.attach(body)
        i = 0
        for afile in afiles:
            if not path.isfile(afile): die("%s: Fichier introuvable" % afile)
            mimetype = amimetypes[i:i + 1] and amimetypes[i] or None
            encoding = None
            i += 1
            if mimetype is None:
                mimetype, encoding = mimetypes.guess_type(afile)
            if mimetype is None or encoding is not None:
                mimetype = 'application/octet-stream'
            maintype, subtype = mimetype.split('/', 1)
            if maintype == 'text':
                inf = open(afile, 'rb')
                try:
                    #XXX on considère que tous les fichiers texte sont encodés en
                    # utf-8. Ce n'est pas forcément vrai, mais cette approximation
                    # devrait être suffisante pour le moment
                    part = MIMEText(inf.read(), _subtype=subtype, _charset='utf-8')
                finally:
                    inf.close()
            elif maintype == 'image':
                inf = open(afile, 'rb')
                try:
                    part = MIMEImage(inf.read(), _subtype=subtype)
                finally:
                    inf.close()
            elif maintype == 'audio':
                inf = open(afile, 'rb')
                try:
                    part = MIMEAudio(inf.read(), _subtype=subtype)
                finally:
                    inf.close()
            else:
                part = MIMEBase(maintype, subtype)
                inf = open(afile, 'rb')
                try:
                    part.set_payload(inf.read())
                finally:
                    inf.close()
                encoders.encode_base64(part)
            part.add_header('Content-Disposition', 'attachment', filename=path.basename(afile))
            msg.attach(part)
    msg['From'] = mfrom
    msg['To'] = ', '.join(mtos)
    if mccs: msg['Cc'] = ', '.join(mccs)
    if mbccs: msg['Bcc'] = ', '.join(mbccs)
    msg['Subject'] = Header(subject, 'utf-8')

    if gencmd: print "sendmail -i -t -f %s <<EOF" % quotesh(mfrom)
    Generator(sys.stdout).flatten(msg)
    if gencmd: print "EOF"

if __name__ == '__main__':
    run_umail()
