MailboxList MailboxRepository::select(const string& where)
{
	soci::rowset<row> rs = 	dataBase.prepare << "SELECT  mailbox.username as Mailbox_username, mailbox.password as Mailbox_password, mailbox.name as Mailbox_name, mailbox.home as Mailbox_home, mailbox.maildir as Mailbox_maildir, mailbox.quota as Mailbox_quota, mailbox.domain as Mailbox_domain, mailbox.create_date as Mailbox_create_date, mailbox.change_date as Mailbox_change_date, mailbox.active as Mailbox_active, mailbox.passwd_expire as Mailbox_passwd_expire, mailbox.uid as Mailbox_uid, mailbox.gid as Mailbox_gid, mailbox.id_mentor as Mailbox_id_mentor, mailbox.tipo_conta as Mailbox_tipo_conta, mailbox.assina as Mailbox_assina, mailbox.matricula as Mailbox_matricula "
	" FROM mailbox" 
	<< (where.size()?" WHERE "+where:"");
	MailboxList mailboxList;
	for(row& r: rs)
	{
		MailboxPtr mailbox(new Mailbox);
		type_conversion<Mailbox>::from_base(r, i_ok, *mailbox);
		mailboxList.push_back(mailbox);
	}
	return mailboxList;
}
Exemple #2
0
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // Un message de courriel est arrivé à l'adresse prefixe+id_lot_et_cle_du_lot_destinataire@domaine.
    // exemple : [email protected]

    // sur le serveur MX du domaine, dans /etc/alias, l'administrateur aura placé cette ligne :
    // prefixe: |/usr/bin/relai_de_courriel

    // Les rêglages sont à faire dans un fichier de configuration sous /etc ou ~/.config

    QSettings settings("Les Développements Durables", "Laguntzaile");

    // Vérifications préalables

    QString programme = settings.value("sendmail", "/usr/sbin/sendmail").toString();

    if (!QFile::exists(programme)) {
        qCritical()
                << "Introuvable programme d'envoi du courrier " << programme;
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    vector<const char*> env_requis;
    env_requis.push_back("EXTENSION");
    env_requis.push_back("SENDER");
    env_requis.push_back("USER");
    env_requis.push_back("DOMAIN");

    for (vector<const char*>::const_iterator i = env_requis.begin(); i != env_requis.end(); i++) {
        if (getenv(*i) == NULL) {
            qCritical()
                << "Erreur de lecture de la variable d'environnement" << *i
                << "- normalement le MTA renseigne cette variable.";
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_USAGE;
        }
    }

    // De EXTENSION, tirer l'id du lot de sa clé
    QString extension(getenv("EXTENSION"));

    bool aller = extension.contains(QRegExp("^\\d+_\\d+$"));
    bool retour = extension.contains(QRegExp("^\\d+_\\d+_\\d+$"));
    if (!aller && !retour) {
        qCritical()
                << "Cette adresse est invalide.";
        cout << "5.1.3 Bad destination mailbox address syntax" << endl;
        return EX_NOUSER;
    }

    // Le lot des destinataires est défini dans la base de données
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");

    // Connexion à la base de données
    // FIXME : permettre un accès sans mot de passe
    db.setHostName      (settings.value("database/hostName",        "localhost"     ).toString());
    db.setPort          (settings.value("database/port",            5432            ).toInt()   );
    db.setDatabaseName  (settings.value("database/databaseName",    "laguntzaile"   ).toString());
    db.setUserName      (settings.value("database/userName",        qgetenv("USER") ).toString());
    db.setPassword      (settings.value("database/password",        qgetenv("USER") ).toString());

    if(!db.open()) {
        qCritical()
                << "Erreur d'ouverture de la connexion à la base de données :"
                << db.lastError()
                << "Veuillez vérifier le fichier de configuration"
                << settings.fileName();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    // Un retour en erreur ; débarrassons nous de ce cas spécial en premier
    if (retour) {
        // lire id_lot, id_personne et cle
        QStringList identifiant = extension.split('_');
        int id_lot = identifiant.at(0).toInt();
        int id_personne = identifiant.at(1).toInt();
        int cle = identifiant.at(2).toInt();
        // vérification standard : lot_personne avec la bonne cle, traité et reussi et sans erreur
        QSqlQuery query_lot_personne;
        if(!query_lot_personne.prepare(
                    "select *"
                    " from lot_personne"
                    " where"
                    "  id_lot = :id_lot"
                    "  and id_personne = :id_personne"
                    "  and cle = :cle"
                    "  and traite"
                    "  and reussi"
                    "  and erreur is null")) {
            qCritical()
                    << "Erreur de préparation de la requête d'identification de l'envoi :"
                    << query_lot_personne.lastError();
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_CONFIG;
        }
        query_lot_personne.bindValue(":id_lot", id_lot);
        query_lot_personne.bindValue(":id_personne", id_personne);
        query_lot_personne.bindValue(":cle", cle);
        if(!query_lot_personne.exec()) {
            qCritical()
                    << "Erreur d'execution de la requête d'identification de l'envoi :"
                    << query_lot_personne.lastError();
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_CONFIG;
        }
        if (query_lot_personne.size() != 1) {
            qCritical()
                    << "Cette adresse retour est invalide."
                    << "query_lot_personne.size() = " << query_lot_personne.size()
                    << "id_lot" << id_lot
                    << "id_personne" << id_personne
                    << "cle" << cle
                    << query_lot_personne.executedQuery()
                    << query_lot_personne.boundValues()
                    << ".first()" << query_lot_personne.first();
            cout << "5.1.1 Bad destination mailbox address" << endl;
            return EX_NOUSER;
        }
        // marquer cet envoi comme pas réussi et renseigner l'erreur
        QSqlQuery setLotPersonneEnErreur;
        if (!setLotPersonneEnErreur.prepare(
                    "update lot_personne"
                    " set reussi = false,"
                    " erreur = :erreur"
                    " where id_lot = :id_lot"
                    " and id_personne = :id_personne")) {
            qCritical()
                    << "Erreur de préparation de la requête d'enregistrement de l'erreur d'envoi :"
                    << setLotPersonneEnErreur.lastError();
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_CONFIG;
        }
        QFile in;
        in.open(stdin, QIODevice::ReadOnly);
        setLotPersonneEnErreur.bindValue(":id_lot", id_lot);
        setLotPersonneEnErreur.bindValue(":id_personne", id_personne);
        setLotPersonneEnErreur.bindValue(":erreur", QString(in.readAll()));
        if(!setLotPersonneEnErreur.exec()) {
            qCritical()
                    << "Erreur d'execution de la requête d''identification de l'enregistrement de l'erreur d'envoi :"
                    << setLotPersonneEnErreur.lastError();
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_CONFIG;
        }

        cout << "2.1.5 Destination address valid" << endl;
        return EX_OK;
    }

    // Pas un retour mais un envoi vers un lot de destinataires

    QStringList identifiant = extension.split('_');
    int id_lot = identifiant.at(0).toInt();
    int cle = identifiant.at(1).toInt();

    // Lecture du lot
    QSqlQuery query_lot;
    if (!query_lot.prepare("select * from lot where id=? and cle=?")) {
        qCritical()
                << "Erreur de préparation de la requête de lecture du lot de destinataires :"
                << query_lot.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }
    query_lot.addBindValue(id_lot);
    query_lot.addBindValue(cle);
    if (!query_lot.exec()) {
        qCritical()
                << "Erreur d'execution de la requête de lecture du lot de destinataires :"
                << query_lot.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }
    if (!query_lot.first()) {
        qCritical()
                << "Cette adresse ne correspond pas à un lot de destinataires";
        cout << "5.1.1 Bad destination mailbox address" << endl;
        return EX_NOUSER;
    }
    if (query_lot.value("traite").toBool()) {
       qCritical()
               << "Ce lot a déjà été traité une fois. Il n'est pas possible de réutiliser un même lot.";
       cout << "4.2.1 Mailbox disabled, not accepting messages" << endl;
       return EX_UNAVAILABLE;
    }
#ifndef SKIP_DATE_TESTS
    if (query_lot.value("date_de_creation").toDateTime().secsTo(QDateTime::currentDateTime()) > 24*60*60) {
        qCritical()
                << "Ce lot est périmé. Un lot ne reste valide que pendant 24 heures."
                << "Date de création de ce lot : " << query_lot.value("date_de_creation");
        cout << "4.2.1 Mailbox disabled, not accepting messages" << endl;
        return EX_UNAVAILABLE;
    }
#endif

    // Lecture de l'évènement
    int id_evenement = query_lot.value("id_evenement").toInt();
    QSqlQuery query_evenement;
    if (!query_evenement.prepare("select fin from evenement where id=?")) {
        qCritical()
                << "Erreur de préparation de la requête de lecture de l'évenement :"
                << query_evenement.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }
    query_evenement.addBindValue(id_evenement);
    if (!query_evenement.exec()) {
        qCritical()
                << "Erreur d'execution de la requête de lecture de l'évènement :"
                << query_evenement.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }
    if (!query_evenement.first()) {
        qCritical()
                << "Ce lot ne correspond à aucun évènement (base de donnée incohérente)";
        cout << "5.1.1 Bad destination mailbox address" << endl;
        return EX_NOUSER;
    }
#ifndef SKIP_DATE_TESTS
    if (query_evenement.value("fin").toDateTime() < QDateTime::currentDateTime()) {
       qCritical()
               << "Cet évènement est déjà terminé.";
       cout << "4.2.1 Mailbox disabled, not accepting messages" << endl;
       return EX_UNAVAILABLE;
    }
#endif

    // Le message lui-même est à lire sur l'entrée standard

    istreambuf_iterator<char> bit(cin), eit;
    MimeEntity modele(bit, eit);

    // Adaptons le format du message et vérifions si on y trouve bien _URL_

    bool marqueurTrouve = preparer(&modele);

    if (!marqueurTrouve) {
        qCritical()
                << "Marqueur _URL_ introuvable dans le corps du message.";
        cout << "4.2.4 Mailing list expansion problem" << endl;
        return EX_DATAERR;
    }

    // Virer les headers Return-Path, X-Original-To, Delivered-To, X-*, Received ...
    for (Header::iterator i = modele.header().begin(); i != modele.header().end(); i++) {
        if (i->name() == "Received" ||
                i->name().substr(0,2) == "X-" ||
                i->name() == "Return-Path" ||
                i->name() == "Delivered-To" ||
                i->name() == "Message-Id"
                ) {
            modele.header().erase(i);
        }
    }

    // Récupération de la liste des destinataires
    QSqlQuery query_destinataires;
    if (!query_destinataires.prepare(
                "select distinct concat_ws(' ', prenom, nom) as libelle, email, id_personne, disponibilite.id as id_disponibilite, lot_personne.cle"
                " from lot_personne join personne on id_personne = personne.id"
                " join lot on id_lot = lot.id"
                " left join disponibilite using(id_personne, id_evenement)"
                " where id_lot=?"
                " and email like '%@%'"
                )) {
        qCritical()
                << "Erreur de préparation de la requête de lecture des destinataires du lot :"
                << query_destinataires.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }
    query_destinataires.addBindValue(id_lot);
    if (!query_destinataires.exec()) {
        qCritical()
                << "Erreur d'execution de la requête de lecture des destinataires du lot :"
                << query_destinataires.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    // Préparation des autres requètes dont on va avoir besoin
    QSqlQuery query_affectations;
    if (!query_affectations.prepare(
                "select affectation.id, affectation.statut, affectation.commentaire, tour.debut, tour.fin, poste.nom, poste.description"
                " from affectation"
                " join tour on id_tour = tour.id"
                " join poste on id_poste = poste.id"
                " where id_disponibilite = ?"
                )) {
        qCritical()
                << "Erreur de préparation de la requête de lecture des affectations du destinataire :"
                << query_affectations.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    QSqlQuery setLotPersonneTraite;
    if (!setLotPersonneTraite.prepare(
                "update lot_personne"
                " set"
                " traite = true,"
                " reussi = :reussi,"
                " erreur = :erreur"
                " where id_lot=:id_lot and id_personne=:id_personne"
                )) {
        qCritical()
                << "Erreur de préparation de la requête de marquage des envois traités :"
                << setLotPersonneTraite.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    QSqlQuery setLotTraite;
    if (!setLotTraite.prepare(
                "update lot"
                " set"
                " traite = true,"
                " modele = :modele,"
                " expediteur = :expediteur"
                " where id=:id_lot"
                )) {
        qCritical()
                << "Erreur de préparation de la requête de marquage du lot traité :"
                << setLotTraite.lastError();
        cout << "4.3.5 System incorrectly configured" << endl;
        return EX_CONFIG;
    }

    // Génération et envoi des messages personnalisés
    while (query_destinataires.next()) {
        QString libelle = query_destinataires.value("libelle").toString().trimmed();
        QString email = query_destinataires.value("email").toString().trimmed();
        int id_personne = query_destinataires.value("id_personne").toInt();
        int cle = query_destinataires.value("cle").toInt();

        stringstream b;
        b << modele;
        string s = b.str();
        MimeEntity instance(s.begin(), s.end());

        // Personnalisation du from, pour pouvoir identifier les personnes injoignables
        QString fromMailbox = QString("%1+%2_%3_%4").arg(getenv("USER")).arg(id_lot).arg(id_personne).arg(cle);
        QString fromDomaine = getenv("DOMAIN");

        MailboxList& from = instance.header().from();
        for (MailboxList::iterator i = from.begin(); i != from.end(); i++) {
            i->mailbox(fromMailbox.toStdString());
            i->domain(fromDomaine.toStdString());
        }

        // Génération du to
        Mailbox mailbox;
        mailbox.label(libelle.toStdString());
        mailbox.mailbox(email.split('@').at(0).toStdString());
        mailbox.domain(email.split('@').at(1).toStdString());
        MailboxList to;
        to.push_back(mailbox);
        instance.header().to(to.str());

        // substitution des marqueurs
        string url = settings.value("modele_url", "http://localhost/%1/%2").toString().arg(id_evenement).arg(id_personne).toStdString();
        string affectations = "";
        string affectations_html = "";
        if (!query_destinataires.value("id_disponibilite").isNull()) { // le destinataire est inscrit à l'évènement et a peut-être des affectations
            int id_disponibilite = query_destinataires.value("id_disponibilite").toInt();
            query_affectations.addBindValue(id_disponibilite);
            if (!query_affectations.exec()) {
                qCritical()
                        << "Erreur d'execution de la requête de lecture des affectations du destinataire :"
                        << query_affectations.lastError();
                cout << "4.3.5 System incorrectly configured" << endl;
                return EX_CONFIG;
            }
            if (query_affectations.size() > 0) { // il a des affectations
                affectations_html = settings.value("modele_affectations_html_prefixe", "<table><tr><th>De</th><th>à</th><th>Poste</th></tr>").toString().toStdString();
                while (query_affectations.next()) {
                    QString debut = query_affectations.value("debut").toDateTime().toString(); // TODO : formater les dates et les heures
                    QString fin = query_affectations.value("fin").toDateTime().toString();
                    QString nom = query_affectations.value("nom").toString();
                    affectations += settings.value("modele_affectations_texte", "%1 → %2 : %3\n").toString().arg(debut, fin, nom).toStdString();
                    affectations_html += settings.value("modele_affectations_html", "<tr><td>%1</td><td>%2</td><td>%3</td></tr>").toString().arg(debut, fin, nom).toStdString(); // TODO : htmlentities()
                }
                affectations_html += settings.value("modele_affectations_html_suffixe", "</table>").toString().toStdString();
            }
        }
        substituer(&instance, url, affectations, affectations_html);

        // envoi du message et marquage des destinataires traités
        QProcess sendmail;
        QStringList arguments;
        arguments << "-f" << QString("%1@%2").arg(fromMailbox).arg(fromDomaine);
        arguments << email;
        stringstream ss; ss << instance;
        QString entree = QString::fromStdString(ss.str());
        entree.replace(QString("\n.\n"), QString("\n..\n"));
        sendmail.start(programme, arguments);
        if (sendmail.waitForStarted()) {
            sendmail.write(entree.toUtf8());
            sendmail.closeWriteChannel();
            if (sendmail.waitForFinished() && sendmail.exitStatus() == QProcess::NormalExit && sendmail.exitCode() == EX_OK) {
                setLotPersonneTraite.bindValue(":reussi", true);
                setLotPersonneTraite.bindValue("erreur", QVariant());
            } else {
                setLotPersonneTraite.bindValue(":reussi", false);
                setLotPersonneTraite.bindValue("erreur", strerror(sendmail.exitCode()));
            }
        } else {
            setLotPersonneTraite.bindValue(":reussi", false);
            setLotPersonneTraite.bindValue(":erreur", sendmail.readAllStandardError());
        }
        setLotPersonneTraite.bindValue(":id_lot", id_lot);
        setLotPersonneTraite.bindValue(":id_personne", id_personne);
        if(!setLotPersonneTraite.exec()) {
            qCritical()
                << "Erreur d'execution de la requête de marquage des envois traités :"
                << setLotPersonneTraite.lastError();
            cout << "4.3.5 System incorrectly configured" << endl;
            return EX_CONFIG;
        }
    }
    // Marquage du lot traité
    QString sender(getenv("SENDER"));
    setLotTraite.bindValue(":id_lot", id_lot);

    stringstream b;
    b << modele;
    string modele_lot = b.str();
    setLotTraite.bindValue(":modele", modele_lot.c_str());
    setLotTraite.bindValue(":expediteur", sender);
    if(!setLotTraite.exec()) {
        qCritical()
            << "Erreur d'execution de la requête de marquage des envois traités :"
            << setLotTraite.lastError();
        cout << "2.1.5 Destination address valid mais le lot n'a pas été marqué 'traité'" << endl;
        return EX_OK;
    }

    // TODO : poster à SENDER la liste des adresses, nom, prenom, ville et identifiant des destinataires en erreur, le nombre d'envois faits (réussis et ratés), un rappel des sujet et date du message original

    cout << "2.1.5 Destination address valid" << endl;
    return EX_OK;
}