Can't send file via SMTP protocol
-
Hi all! The problem is that I can't send an Excel file via SMTP protocol in any way. Please help me, I don't have any strength left. I will be very grateful
Code works everywhere except file upload
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(ui->btn_transFile, &QPushButton::clicked, this, &MainWindow::connectAndSendMsg); } MainWindow::~MainWindow() { delete ui; } void MainWindow::connectAndSendMsg() { if(ui->lEdit_addrFrom->text().isEmpty()) { QMessageBox::warning(this, tr("info"), tr("Error"), QMessageBox::Ok); return; } QByteArray fileData; QString fileName = QFileDialog::getOpenFileName(this, "Excel", QDir::homePath(), QString("Excel *xlsx")); QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("info"), tr("Error: ") + file.errorString(), QMessageBox::Ok); return; } else { fileData = file.readAll(); file.close(); } QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(fileName); if(!mime.isValid()) { QMessageBox::warning(this, tr("info"), tr("Error: ") + fileName, QMessageBox::Ok); return; } if(mime.name() != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { QMessageBox::warning(this, tr("info"), tr("Error"), QMessageBox::Ok); return; } // SMTP Server Settings const QString smtpServer = "mail.adm.tools"; const int smtpPort = 465; const QString username = "YOUR_MAIL"; const QString password = "YOUR_PASSWORD"; const QString from = "YOUR_MAIL"; const QString to = ui->lEdit_addrFrom->text(); //mail to whom to send the file // Message QString subject = dateTime; const QString body = "Test message"; // Creating SSL-socket QSslSocket socket; socket.setProtocol(QSsl::TlsV1_2); socket.setPeerVerifyMode(QSslSocket::VerifyNone); socket.connectToHostEncrypted(smtpServer, smtpPort); if (!socket.waitForConnected()) { qDebug() << "Error connect to SMTP server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } QString response = QString::fromUtf8(socket.readAll()); qDebug() << response; // EHLO socket.write("EHLO localhost\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error EHLO"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error EHLO"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // Auth on SMTP-server socket.write("AUTH LOGIN\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error AUTH LOGIN on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; QByteArray usernameBase64 = username.toUtf8().toBase64(); socket.write(usernameBase64 + "\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error USERNAME on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; QByteArray passwordBase64 = password.toUtf8().toBase64(); socket.write(passwordBase64 + "\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error PASSWORD on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; //MAIL FROM socket.write(QString("MAIL FROM:<%1>\r\n").arg(from).toUtf8()); if (!socket.waitForBytesWritten()) { qDebug() << "Error MAIL FROM on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // RCPT TO socket.write(QString("RCPT TO:<%1>\r\n").arg(to).toUtf8()); if (!socket.waitForBytesWritten()) { qDebug() << "Error RCPT TO on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // DATA socket.write("DATA\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error DATA on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // Send the messagwe //QTextStream message(&socket); //message << "From: " << from << "\r\n"; //message << "To: " << to << "\r\n"; //message << "Subject: " << subject << "\r\n"; //message << "\r\n"; //message << body << "\r\n.\r\n"; //message.flush(); //==================================================================================== QByteArray boundary = "boundary_12345"; QByteArray messageData; messageData.append("From: <YOUR_MAIL>\r\n"); messageData.append(QString("To: <%1>\r\n").arg(ui->lEdit_addrFrom->text()).toUtf8()); messageData.append(QString("Subject: %1\r\n").arg(subject).toUtf8()); messageData.append("\r\n"); messageData.append(QString(body).toUtf8() + "\r\n.\r\n"); messageData.append(QString("Content-Type: multipart/mixed; boundary=%1\r\n").arg(boundary).toUtf8()); messageData.append("\r\n"); messageData.append(QString("--%1\r\n").arg(boundary).toUtf8()); messageData.append("Content-Type: text/plain\r\n"); messageData.append("\r\n"); messageData.append(body.toUtf8()); messageData.append("\r\n"); messageData.append(QString("--%1\r\n").arg(boundary).toUtf8()); messageData.append(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg(QFileInfo(fileName).fileName()).toUtf8()); messageData.append(QString("Content-Type: %1\r\n").arg(mime.name()).toUtf8()); messageData.append("Content-Transfer-Encoding: base64\r\n"); messageData.append("\r\n"); messageData.append(fileData.toBase64()); messageData.append("\r\n"); messageData.append(QString("--%1--\r\n").arg(boundary).toUtf8()); socket.write(messageData); socket.flush(); //==================================================================================== if (!socket.waitForBytesWritten()) { qDebug() << "Error MESSAGE on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // Отправка команды QUIT socket.write("QUIT\r\n"); if (!socket.waitForBytesWritten()) { qDebug() << "Error QUIT on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; } response = QString::fromUtf8(socket.readAll()); qDebug() << response; // Closing the socket socket.close(); }
-
@razorqhex This could be because your call to QAbstractSocket::flush() has already written all the bytes to the underlying operating system. You then wait for more bytes to be written but there are none so you time out.
-
@razorqhex You should add error handling. For example connect a slot to
https://doc.qt.io/qt-6/qabstractsocket.html#errorOccurred and print out the error. -
@jsulm said in Can't send file via SMTP protocol:
@razorqhex You should add error handling. For example connect a slot to
https://doc.qt.io/qt-6/qabstractsocket.html#errorOccurred and print out the error.At the end of the code, I wrote
response->errorString()
and the result was "Unknown Error"
-
@razorqhex said in Can't send file via SMTP protocol:
This part of your message:
messageData.append(QString("Content-Type: multipart/mixed; boundary=%1\r\n").arg(boundary).toUtf8());
Looks to be in the wrong place. That header should be with the mail headers as in the simple example in RFC 1341 7.2.1
There are a couple of poor assumptions made in the code that will break sometime in the future, even if they are not breaking now. The main one is that when the first readyRead() signal is received that the entire response has been received.
-
@JonB, I wrote an error handler and this is what it outputs.
It seems to me that the problem is in this block of code:
QByteArray boundary = "boundary_12345"; QByteArray messageData; messageData.append("From: <YOUR_MAIL>\r\n"); messageData.append(QString("To: <%1>\r\n").arg(ui->lEdit_addrFrom->text()).toUtf8()); messageData.append(QString("Subject: %1\r\n").arg(subject).toUtf8()); messageData.append("\r\n"); messageData.append(QString(body).toUtf8() + "\r\n.\r\n"); messageData.append(QString("Content-Type: multipart/mixed; boundary=%1\r\n").arg(boundary).toUtf8()); messageData.append("\r\n"); messageData.append(QString("--%1\r\n").arg(boundary).toUtf8()); messageData.append("Content-Type: text/plain\r\n"); messageData.append("\r\n"); messageData.append(body.toUtf8()); messageData.append("\r\n"); messageData.append(QString("--%1\r\n").arg(boundary).toUtf8()); messageData.append(QString("Content-Disposition: attachment; filename=\"%1\"\r\n").arg(QFileInfo(fileName).fileName()).toUtf8()); messageData.append(QString("Content-Type: %1\r\n").arg(mime.name()).toUtf8()); messageData.append("Content-Transfer-Encoding: base64\r\n"); messageData.append("\r\n"); socket.flush(); messageData.append(fileData.toBase64()); messageData.append("\r\n"); messageData.append(QString("--%1--\r\n").arg(boundary).toUtf8()); socket.write(messageData); socket.flush();
But I don't know exactly what the problem is.
-
@razorqhex Your mail data is malformed (see my previous)
This is what a well-formed SMTP conversation should look like:
EHLO host.example.com MAIL FROM: sender@example.com RCPT TO: recipient@example.com DATA From: sender@example.com To: recipient@example.com Subject: Test email Content-Type: multipart/mixed; boundary="test-boundary" This is the preamble. It is to be ignored, though it is a handy place for mail composers to include an explanatory note to non-MIME compliant readers. --test-boundary Content-Type: text/plain; charset=utf-8 Body text --test-boundary Content-Disposition: attachment; filename="test.png" Content-Type: image/png Content-Transfer-Encoding: base64 iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9 kT1Iw1AUhU/TiiIVBzuoOGSoThZERQQXrUIRKoRaoVUHk5f+QZOGJMXFUXAtOPizWHVwcdbVwVUQ BH9AXF2cFF2kxPuSQosYLzzex3n3HN67DxDqZaZZoTFA020zlYiLmeyq2PmKIAYQQAgzMrOMOUlK wre+7qmT6i7Gs/z7/qweNWcxICASzzLDtIk3iKc2bYPzPnGEFWWV+Jx41KQLEj9yXfH4jXPBZYFn Rsx0ap44QiwW2lhpY1Y0NeJJ4qiq6ZQvZDxWOW9x1spV1rwnf2E4p68sc53WEBJYxBIkiFBQRQll 2IjRrpNiIUXncR//oOuXyKWQqwRGjgVUoEF2/eB/8Hu2Vn5i3EsKx4GOF8f5GAY6d4FGzXG+jx2n cQIEn4ErveWv1IHpT9JrLS16BPRuAxfXLU3ZAy53gP4nQzZlVwrSEvJ54P2MvikL9N0C3Wve3Jrn OH0A0jSr5A1wcAiMFCh73efdXe1z+7enOb8fV1pynAvpCcAAAAAJcEhZcwAALiMAAC4jAXilP3YA AAAHdElNRQfnAxkBBS96QEB0AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAA AChJREFUSMftzUENAAAIBKDT/p01hQ83KEBNbnUEAoFAIBAIBALBk2ABo8QBP1gd7ycAAAAASUVO RK5CYII= --test-boundary This is the epilogue. It is also to be ignored. . QUIT
-
@ChrisW67, I rewrote the code like this:
QByteArray boundary = "my_boundary"; QByteArray message; message.append(QString("From: ").toUtf8() + QString(from).toUtf8() + QString("\r\n").toUtf8()); message.append(QString("To: %1").arg(ui->lEdit_addrFrom->text()).toUtf8() + QString(to).toUtf8() + QString("\r\n").toUtf8()); message.append("Subject: " + QString(subject).toUtf8() + "\r\n"); message.append("MIME-Version: 1.0\r\n"); message.append("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n\r\n"); // Body message.append("--" + boundary + "\r\n"); message.append("Content-Type: text/plain\r\n\r\n"); message.append(QString(body).toUtf8() + "\r\n\r\n"); // Attachment message.append("--" + boundary + "\r\n"); message.append("Content-Disposition: attachment; filename=\"" + fileInfo.fileName().toUtf8() + "\"\r\n"); message.append("Content-Type: " + mime.name().toUtf8() + "; name=\"" + fileInfo.fileName().toUtf8() + "\"\r\n"); message.append("Content-Transfer-Encoding: base64\r\n\r\n"); message.append(fileData.toBase64() + "\r\n\r\n"); // Completing a message message.append("--" + boundary + "--\r\n"); socket.write(message); socket.write(".\r\n"); socket.flush();
And then I get a message with the correct attachment. But it still gets an error in this function:
//Message if (!socket.waitForBytesWritten()) { qDebug() << "Error MESSAGE on SMTP-server"; return; } if (!socket.waitForReadyRead()) { qDebug() << "Error"; return; }
And with all this, the error handler is not called.
Is it still a mistake in the structure of the message or what? I don't understand anything in this world
-
@razorqhex said in Can't send file via SMTP protocol:
And then I get a message with the correct attachment. But it still gets an error in this function:
waitForBytesWritten()
has a default value of 30 seconds as a timeout. If your operation takes longer than 30 seconds this function will return false and you'll see "Error MESSAGE on SMTP-server". -
@SimonSchroeder, I know, but the fact is that without any pauses, the waitForBytesWritten () function immediately executes
-
@razorqhex Then you should try
qDebug() << socket.errorString();
to figure out what's happening. -
@SimonSchroeder, this command show me "Unknown error"
-
@razorqhex This could be because your call to QAbstractSocket::flush() has already written all the bytes to the underlying operating system. You then wait for more bytes to be written but there are none so you time out.
-
@ChrisW67, Yes! You're right! Thank you very much. The error was only that the logic of the code was incorrect.
I have corrected the structure of creating a message:
QByteArray boundary = "my_boundary"; QByteArray message; message.append(QString("From: ").toUtf8() + QString(from).toUtf8() + QString("\r\n").toUtf8()); // message.append(QString("To: %1").arg(ui->lEdit_addrFrom->text()).toUtf8() + QString(to).toUtf8() + QString("\r\n").toUtf8()); message.append(QString("To: ").toUtf8() + QString(to).toUtf8() + QString("\r\n").toUtf8()); message.append("Subject: " + QString(subject).toUtf8() + "\r\n"); message.append("MIME-Version: 1.0\r\n"); message.append("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n\r\n"); // Body message.append("--" + boundary + "\r\n"); message.append("Content-Type: text/plain\r\n\r\n"); message.append(QString(body).toUtf8() + "\r\n\r\n"); // Attachment message.append("--" + boundary + "\r\n"); message.append("Content-Disposition: attachment; filename=\"" + fileInfo.fileName().toUtf8() + "\"\r\n"); message.append("Content-Type: " + mime.name().toUtf8() + "; name=\"" + fileInfo.fileName().toUtf8() + "\"\r\n"); message.append("Content-Transfer-Encoding: base64\r\n\r\n"); message.append(fileData.toBase64() + "\r\n\r\n"); // Completing a message message.append("--" + boundary + "--\r\n"); //==================================================================================== socket.write(message); socket.write(".\r\n"); // socket.flush();
And also removed
socket.flush();
when sending the MESSAGE command and when sending the QUIT commandAnd now everything works as it should. Once again thank you very much.
Now there is only one problem left to fix. Some SMTP servers do not understand Ukrainian and Russian encodings. Incomprehensible letters are shown instead of Cyrillic
UPD: I also corrected the encoding.
Where is the comment BODY
Instead of a linemessage.append("Content-Type: text/plain\r\n\r\n");
Need to add
message.append("Content-Type: text/plain; charset=UTF-8\r\n\r\n");
And above, where the addressee is formed, you need instead of this line
message.append("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n\r\n");
Write this line:
message.append("Content-Type: multipart/mixed; charset=UTF-8; boundary=" + boundary + "\r\n\r\n");
Thank you all for your help :)
-