[qt] envoi auto de mail hebdomadaire

Description

Ce programme permet d'envoyer un mail de façon hebdomadaire.
Il est basé sur le framework Qt (exécutable compilé sous Gnome, Qt 4.8).

Il utilise le protocole ASMTP, aussi connu sous le nom de SMTP AUTH (Simple Mail Transfert Protocol with Authentification) pour envoyer le mail (facilement modifiable pour utiliser simplement le protocole SMTP sans authentification). Le protocole est implémenté à l'aide de QTcpSocket. Ce protocole permet l'ajout d'une pièce jointe. Les données (sujet, corps et pièce jointe du mail) sont encodés en base64. Le mail est donc envoyé en "multipart" (voir les MIME-Type pour plus d'infos). Le programme prend en charge du texte riche pour le corps du message.

L'horloge est réalisée avec un QTimer, avec un timeout de 1 seconde. Un widget permet de connaitre l'état de l'envoi du mail,les erreurs, ou simplement le temps restant avant l'envoi du mail.

Il est possible de tester les identifiants du compte mail, et d'envoyer un mail de test. L'adresse mail de départ nécessite un mot de passe.

PS: Il s'agit de la première source que je publie, donc soyez critique si ça ne va pas ;)

Sources:
Qt
Icones: http://www.softicons.com/

Source / Exemple :


#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    srand(QDateTime::currentDateTime().toTime_t());

    set=new QSettings("options.ini", QSettings::IniFormat, this);

    tray=new QSystemTrayIcon(QIcon(":/Mail.png"), this);
    tray->show();
    connect(tray, SIGNAL(messageClicked()), this, SLOT(show()));
    connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
    tray->setContextMenu(new QMenu("Menu", this));
    connect(tray->contextMenu()->addAction("Afficher"), SIGNAL(triggered()), this, SLOT(show()));
    connect(tray->contextMenu()->addAction("Quitter"), SIGNAL(triggered()), qApp, SLOT(quit()));

    widStatus=new WidStatus();

    setupUI();
    show();

    QTimer* timer=new QTimer(this);
    timer->setInterval(1000);
    connect(timer, SIGNAL(timeout()), this, SLOT(checkTime()));
    timer->start();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete set;
    delete tray;
    delete widStatus;
}

void MainWindow::readyRead()
{
    QTcpSocket* socket=(QTcpSocket*)sender();
    buffer+=socket->readAll();
    if(!buffer.endsWith("\n"))
        return;
    if(status==Send::HELO)
    {
        if(buffer.startsWith("220"))
        {
            status=Send::AUTH;
            buffer.clear();
            socket->write("HELO there\n");
        }
        else
            showError("Réponse inconnue du serveur (1)", socket);
        return;
    }
    else if(status==Send::AUTH)
    {
        if(buffer.startsWith("250"))
        {
            status=Send::MAIL_FROM;
            buffer.clear();
            widStatus->showStatus("Connecté. Authentification...");
            socket->write("auth plain "+QByteArray(QByteArray("\x00", 1)+user().toStdString().c_str()+QByteArray("\x00", 1)+pass().toStdString().c_str()).toBase64()+"\n");
        }
        else
            showError("Réponse inconnue du serveur (2)", socket);
        return;
    }
    else if(status==Send::MAIL_FROM)
    {
        if(buffer.startsWith("235"))
        {
            buffer.clear();
            if(sendType!=Send::TEST_PARAMS)
            {
                status=Send::RCPT_TO;
                if(sendType!=Send::SEND_ME_RCPT)
                    widStatus->showStatus("Authentifié. Envoi du mail...");
                else if(sendType==Send::SEND_ME_RCPT)
                    widStatus->showStatus("Authentifié. Envoi des mails...");
                socket->write(QString("MAIL FROM:<"+user()+">\n").toStdString().c_str());
            }
            else if(sendType==Send::TEST_PARAMS)
            {
                status=Send::DISCO;
                widStatus->showStatus("Compte mail OK");
                socket->write("quit\n");
            }
        }
        else
            showError("Mauvais mail ou mot de passe", socket);
        return;
    }
    else if(status==Send::RCPT_TO)
    {
        if(buffer.startsWith("250"))
        {
            status=Send::DATA;
            buffer.clear();
            if(sendType==Send::SEND_RCPT)
                socket->write(QString("RCPT TO:<"+rcpt()+">\n").toStdString().c_str());
            else if(sendType==Send::SEND_ME)
                socket->write(QString("RCPT TO:<"+user()+">\n").toStdString().c_str());
            else if(sendType==Send::SEND_ME_RCPT)
                socket->write(QString("RCPT TO:<"+rcpt()+">\nRCPT TO:<"+user()+">\n").toStdString().c_str());
        }
        else
            showError("Expediteur non valide", socket);
        return;
    }
    else if(status==Send::DATA)
    {
        if(buffer.startsWith("250"))
        {
            status=Send::SENDING_DATA;
            buffer.clear();
            socket->write("DATA\n");
        }
        else
            showError("Destinataire on valide", socket);
        return;
    }
    else if(status==Send::SENDING_DATA)
    {
        if(buffer.startsWith("354"))
        {
            buffer.clear();
            QString bound=boundary(15);
            QString sjt_enc="=?UTF-8?B?"+sjt().toUtf8().toBase64()+"?=";
            QString body_enc=body().toUtf8().toBase64();

            socket->write(QString("Subject: "+sjt_enc+"\r\n").toStdString().c_str());
            socket->write(QString("From: <"+user()+">\r\n").toStdString().c_str());
            if(sendType==Send::SEND_RCPT)
                socket->write(QString("To: <"+rcpt()+">\r\n").toStdString().c_str());
            else if(sendType==Send::SEND_ME)
                socket->write(QString("To: <"+user()+">\r\n").toStdString().c_str());
            else if(sendType==Send::SEND_ME_RCPT)
                socket->write(QString("To: <"+rcpt()+"> <"+user()+">\r\n").toStdString().c_str());
            socket->write("MIME-Version: 1.0\r\n");
            socket->write(QString("Content-Type: multipart/mixed; boundary=\""+bound+"\"\r\n").toStdString().c_str());
            socket->write(QString("Date: "+QDateTime::currentDateTime().toString("ddd, d MMM yyyy hh:mm:ss +0200")+"\r\n").toStdString().c_str());
            socket->write("\r\n");

            socket->write(QString("--"+bound+"\r\n").toStdString().c_str());
            socket->write("Content-Type: text/html; charset=\"UTF-8\"\r\n");
            socket->write("Content-Transfer-Encoding: base64\r\n");
            socket->write("\r\n");
            socket->write(QString(body_enc+"\r\n").toStdString().c_str());
            socket->write("\r\n");

            QFile file(filePath().replace("\\", "/"));

            if(file.exists()&&file.open(QIODevice::ReadOnly))
            {
                widStatus->showStatus("Envoi de la pièce jointe: 0%...");
                socket->write(QString("--"+bound+"\r\n").toStdString().c_str());
                QString filePath_enc="=?UTF-8?B?"+filePath().split("/").last().toUtf8().toBase64()+"?=";
                QString mime="application/octet-stream";
                if(filePath().contains(QRegExp("\\.xls[x]{0,1}$")))
                    mime="application/vnd.ms-excel";
                if(filePath().contains(QRegExp("\\.doc[x]{0,1}$")))
                    mime="application/vnd.msword";
                if(filePath().contains(QRegExp("\\.pdf$")))
                    mime="application/pdf";

                socket->write(QString("Content-Type: "+mime+"\r\n").toStdString().c_str());
                socket->write("Content-Transfer-Encoding: base64\r\n");
                socket->write(QString("Content-Disposition: attachment; filename=\""+filePath_enc+"\"\r\n").toStdString().c_str());
                socket->write("\r\n");
                int p=0;
                QByteArray data=file.readAll();
                data=data.toBase64();
                for(int i=0;i<data.size();i++)
                {
                    if(i*100/data.size()!=p)
                    {
                        p=i*100/data.size();
                        widStatus->showStatus("Envoi de la pièce jointe: "+QString::number(p)+"%...");
                    }
                    socket->write(QByteArray().append(data.at(i)));
                    while(socket->waitForBytesWritten(50))
                        qApp->processEvents();

                }
                socket->write("\r\n\r\n");

            }
            else
            {
                showError("Fichier imossible à ouvrir", socket);
                return;
            }
            socket->write(QString("--"+bound+"--\r\n").toStdString().c_str());

            status=Send::QUIT;
            socket->write(".\r\n");
        }
        else if(sendType==Send::SEND_ME_RCPT)
        {
            if(!buffer.startsWith("250"))
                showError("Expediteur non valide", socket);
            buffer.clear();
        }
        else
            showError("Impossible d'envoyer le corps du mail", socket);
        return;
    }
    else if(status==Send::QUIT)
    {
        if(buffer.startsWith("250"))
        {
            status=Send::DISCO;
            buffer.clear();
            if(sendType!=Send::SEND_ME_RCPT)
                widStatus->showStatus("Mail envoyé. Déconnection...");
            else if(sendType==Send::SEND_ME_RCPT)
                widStatus->showStatus("Mails envoyés. Déconnection...");
            socket->write("quit\n");
        }
        else
            showError("Données invalides envoyées", socket);
        return;
    }
    else if(status==Send::DISCO)
    {
        if(buffer.startsWith("221"))
        {
            setEnabled(true);
            if(sendType==Send::TEST_PARAMS)
                widStatus->showSuccess("Compte mail valide");
            else if(sendType==Send::SEND_ME_RCPT)
                widStatus->showSuccess("Mails tests envoyés");
            else
                widStatus->showSuccess("Mail test envoyé");
            buffer.clear();
            socket->disconnect();
            socket->deleteLater();
        }
        else
            showError("Commande de déconnection refusée", socket);
        return;
    }
}

void MainWindow::error(QAbstractSocket::SocketError)
{
    widStatus->show();
    widStatus->showError(((QTcpSocket*)sender())->errorString());
}

QString MainWindow::boundary(int lenght)
{
    QString chars="AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789";
    QString b="";
    for(int l=0;l<lenght;l++)
        b+=chars.at(qMax(qMin(((double)chars.size()-1), ((double)rand())*chars.size()/RAND_MAX), (double)0));

    return "_"+b+"_";
}

void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason r)
{
    if(r!=QSystemTrayIcon::Context)
        show();
}

void MainWindow::on_grpFilePath_toggled(bool arg1)
{
    if(!arg1)
        ui->lineFilePath->clear();

}

void MainWindow::on_btnBrowseFilePath_clicked()
{
    QString file=QFileDialog::getOpenFileName(this, "Fichier à joindre...");
    if(!file.isEmpty())
        ui->lineFilePath->setText(file);
}

void MainWindow::on_buttonBox_accepted()
{
    set->setValue("User", ui->lineUser->text());
    set->setValue("Pass", ui->linePass->text());
    set->setValue("Rcpt", ui->lineRcpt->text());
    set->setValue("FilePath", ui->lineFilePath->text());
    set->setValue("Sjt", ui->lineSjt->text());
    set->setValue("Body", ui->lineBody->toHtml());
    set->setValue("Day", ui->comboDay->currentIndex());
    set->setValue("Hour", ui->lineHour->time());
}

void MainWindow::send()
{
    if(!isEnabled())
        return;
    setEnabled(false);
    widStatus->show();
    widStatus->showStatus("Connection...");
    status=Send::HELO;
    QTcpSocket* sock=new QTcpSocket(this);
    connect(sock, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
    sock->connectToHost("smtpauth.orange.fr", 25);
}

void MainWindow::setupUI()
{
    ui->lineUser->setText(user());
    ui->linePass->setText(pass());
    ui->lineRcpt->setText(rcpt());
    ui->lineFilePath->setText(filePath());
    ui->lineSjt->setText(sjt());
    ui->lineBody->setHtml(body());
    ui->grpFilePath->setChecked(!filePath().isEmpty());
    ui->comboDay->setCurrentIndex(day());
    ui->lineHour->setTime(hour());
}

void MainWindow::on_buttonBox_rejected()
{
    setupUI();
}

void MainWindow::closeEvent(QCloseEvent *e)
{
    if(!qApp->arguments().contains("-indev"))
    {
        e->ignore();
        hide();
    }
    else
        e->accept();
}

void MainWindow::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Return||e->key()==Qt::Key_Enter)
        on_buttonBox_accepted();
}

void MainWindow::checkTime()
{
    QDateTime next=QDateTime::currentDateTime();
    while(next.date().dayOfWeek()!=day()+1)
        next=next.addDays(1);
    if(QDateTime::currentDateTime().time()>hour())
        next=next.addDays(7);
    next.setTime(hour());

    int remaining=QDateTime::currentDateTime().secsTo(next);
    QTime re;
    re=re.addSecs(remaining);
    if(remaining==3599)
        widStatus->show();
    if(remaining<=3599&&remaining>3585)
        widStatus->showRemainingTime(re);
    else if(remaining==3585)
        widStatus->hide();

    else if(remaining==1800)
        widStatus->show();
    if(remaining<=1800&&remaining>1785)
        widStatus->showRemainingTime(re);
    else if(remaining==1785)
        widStatus->hide();

    else if(remaining==600)
        widStatus->show();
    if(remaining<=600)
    {
        widStatus->showRemainingTime(re);
        if(remaining==0)
        {
            sendType=Send::SEND_RCPT;
            send();
        }
    }

    //qDebug()<<"Current:"<<QDateTime::currentDateTime()<<"Next:"<<remaining;
}

void MainWindow::showError(QString erreur, QTcpSocket *socket)
{
    qDebug()<<"ERREUR:"<<buffer;
    setEnabled(true);
    widStatus->show();
    widStatus->showError(erreur);
    socket->disconnect();
    socket->deleteLater();
    buffer.clear();
}

void MainWindow::on_btnTestUser_clicked()
{
    on_buttonBox_accepted();
    sendType=Send::TEST_PARAMS;
    send();
}

void MainWindow::on_btnTestMail_clicked()
{
    on_buttonBox_accepted();
    DlgMailTest* test=new DlgMailTest(this);
    if(test->exec()==QDialog::Accepted)
    {
        if(test->getUi()->radioMe->isChecked())
            sendType=Send::SEND_ME;
        else if(test->getUi()->radioMeRcpt->isChecked())
            sendType=Send::SEND_ME_RCPT;
        else if(test->getUi()->radioRcpt->isChecked())
            sendType=Send::SEND_RCPT;
        send();
    }
}

void MainWindow::on_lblBody_linkActivated(const QString &link)
{
    if(link=="richText")
    {
        DlgRichText* t=new DlgRichText(this);
        t->setText(ui->lineBody->toHtml());
        if(t->exec()==QDialog::Accepted)
            ui->lineBody->setHtml(t->getText());
        t->deleteLater();
    }
}

Conclusion :


Cette source peut donc servir de base pour implémenter le protocole (A)SMTP, ou simplement une action hebdomadaire.
En espérant que ça aide quelqu'un ;)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.