LINUX.ORG.RU

SMTP клиент - разрывы в тексте письма.

 ,


1

1

Собственно пытаюсь сделать smtp клиент для отправки html письма. Письмо отправляется и кодировки нормально проходят кроме одного НО. Переодически выскакивает лаг в виде лишних пробелов посредине слова или знаков ??. Причем этот лаг смещается если менять текст

Выглядит что то типа:

Просим Вас, после оплаты уведомить нас о продлен ии

А в оригинале должно быть:

Просим Вас, после оплаты уведомить нас о продлении

Собственно навели на мысль, что данный дефект возникает при отправке сообщения серверу - байты сообщения сжираются функцией, которая принимает их за размер блока.

Вот собственно код клиента:

#include "smtp.h"
 
Smtp::Smtp(QString host, QString username, QString pass){
    qDebug() << "### Smtp start";
    smtphost = host;
    smtpusername = username;
    smtppass = pass;
}
void Smtp::SetHost(QString host, QString username, QString pass){
    smtphost = host;
    smtpusername = username;
    smtppass = pass;
}
 
void Smtp::Send(const QString &from, const QString &to, const QString &subject, const QString &body){
    int waittime = 500000;
    this->from = from;
    rcpt = to;
    ErrorMSG.clear();
    Timeout = waittime;
    linesend = 0;
    isconnected = false;
    message = TimeStampMail()+"\n";
    message.append("User-Agent: Qt SMTP client\n");
    message.append("X-Accept-Language: en-us, en\n");
    message.append("MIME-Version: 1.0\n");
    message.append("To: " + to + "\n");
    message.append("From: " + from + "\n");
    message.append("Subject: " + subject + "\n");
    message.append("Content-Type: text/html; charset=UTF-8;\n");   /* или txt */
    message.append("Content-transfer-encoding: 8BIT\n\n\n\n");
    message.append(body);
    message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) );
    message.replace( QString::fromLatin1( "\r\n.\r\n" ),QString::fromLatin1( "\r\n..\r\n" ) );
 
    qDebug() << "### Launch mail compose....  "  << from << to << subject << body;
    qDebug() << "### Config server smtp connect to......  "  << smtphost;
    smtpsocket = new QTcpSocket(this);
    connect( this, SIGNAL(ConnectorSuccess()), this, SLOT(ReadLiner()));
    connect( this, SIGNAL(SendLine()), this, SLOT(PutSendLine()));
    if (smtphost.size() > 0)
    {
        smtpsocket->connectToHost(smtphost, 587);
    }
    else
    {
        smtpsocket->connectToHost("localhost", 25);
    }
    if (smtpsocket->waitForConnected(Timeout))
    {
        qDebug() <<"### connected on  " << smtphost;
        if (smtpsocket->waitForReadyRead(Timeout))
        {
            qDebug() <<"### emit from waitForReadyRead connect go can read";
            isconnected = true;
            emit ConnectorSuccess();
        }
    }
    else
    {
        emit ErrorCloseAll();
	AddErrorLogs("Время ожидания подключения к STMP-серверу <<"+smtphost.toStdString()+">> истекло!");
    }
}
 
void Smtp::ReadLiner(){
	m_nNextBlockSize = 0;
    if (isconnected) {
        qDebug() << "### socketType = " << smtpsocket->socketType();
        qDebug() << "### ReadLiner is start by textstream ";

	int loops = 0;
while (smtpsocket->canReadLine()) {
        loops++;
	response = smtpsocket->readLine();
        qDebug() << loops << " in line  " << response;
        }

        if (response.size() > 0) {
        RemoteServerName = response;
        mailstatus = response.left(3);
        qDebug() << "###Status=" << mailstatus;
            if (mailstatus == "220") {
                response="";
                linesend = 1;
                emit SendLine();
            }
        }else{
            emit ErrorCloseAll();
        }
    }
	
}
 
Smtp::~Smtp(){
    qDebug() << "### Smtp stop and delete ";
}
 
void Smtp::PutSendLine(){
	
    int current = linesend;
    qDebug() <<"### Go and Send line " << linesend;
    switch(current) {
      case 1:
          response = SendLineAndGrab("ehlo localhost");
          if (response.size() > 0) {
              ErrorMSG.append(response);
              qDebug() << "1---- " << response;
              linesend = 2;
              emit SendLine();
          }else{
              qDebug() << "Connection lost";
	      AddErrorLogs("Подключение потеряно!");
          }
          response ="";
      break;
      case 2:
          response = SendLineAndGrab("AUTH LOGIN");
           if (response.size() > 0) {
           ErrorMSG.append(response);
                qDebug() << "2---- " << response;
                linesend = 3;
                emit SendLine();
           } else {
                qDebug() << "Connection lost";
		AddErrorLogs("Подключение потеряно на <<ehlo localhost>>!");
           }
          response ="";
      break;
      case 3:
          response = SendLineAndGrab(encodeBase64(smtpusername));
           if (response.size() > 0) {
           ErrorMSG.append(response);
                qDebug() << "3---- " << response;
                linesend = 4;
                emit SendLine();
           } else {
                qDebug() << "Connection lost";
		AddErrorLogs("Подключение потеряно на запросе пользователя!");
           }
          response ="";
      break;
      case 4:
          response = SendLineAndGrab(encodeBase64(smtppass));
           qDebug() << "4---- " << response;
           if (response.size() > 0) {
           ErrorMSG.append(response);
                if (response.contains("ok", Qt::CaseInsensitive) ||
                    response.contains("succeeded", Qt::CaseInsensitive)||response.contains("successful", Qt::CaseInsensitive) ) {
                   linesend = 5;
                   emit SendLine();
                } else {
                   qDebug() << "Connection lost!";
		   AddErrorLogs("Неверный логин("+smtpusername.toStdString()+") или пароль("+smtppass.toStdString()+")!");
                }
           } else {
                qDebug() << "Connection lost!!";
		AddErrorLogs("Подключение потеряно на запросе пароля!");
           }
          response ="";
      break;
      case 5:
          response = SendLineAndGrab("MAIL FROM: "+from);
           qDebug() << "5---- " << response;
           if (response.size() > 0) {
                linesend = 6;
                emit SendLine();
           }
 
      break;
      case 6:
          response = SendLineAndGrab("RCPT TO: "+rcpt);
           qDebug() << "6---- " << response;
           if (response.size() > 0) {
                ErrorMSG.append(response);
                response ="";
                response = SendLineAndGrab("DATA");
                         if (!response.contains("not", Qt::CaseInsensitive)) {
                         ErrorMSG.append(response);
                         response ="";
                         linesend = 7;
                         emit SendLine();
                         }
           }
          response ="";
      break;
      case 7:
          response = SendLineAndGrab(message+"\r\n.");
           qDebug() << "7---- " << response;
           if (response.size() && response.contains("ok", Qt::CaseInsensitive) ) {
                ErrorMSG.append(response);
                linesend = 8;
                emit SendLine();
           }
           response ="";
      break;
      case 8:
          SendLineAndGrab("QUIT");
      break;
      default:
        qDebug() << "Last line ...";
        emit ErrorCloseAll();
        return;
        break;
    }
}
 
QString Smtp::SendLineAndGrab(QString senddata){
   QString incommingData = "";
    if (isconnected) {
   /////////QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        int current = linesend;
        int loops = 0;
 	
      qDebug() << "####Send" << current << "Now => " << senddata;
	/*QByteArray  arrBlock;
	QDataStream out(&arrBlock, QIODevice::WriteOnly);
	out << quint16(0) << senddata<<"\r\n";
	out.device()->seek(0);
	out << quint16(arrBlock.size() - sizeof(quint16));
	qDebug()<<arrBlock.size();
	smtpsocket->write(arrBlock);
	qDebug()<<sizeof(senddata.toUtf8())*senddata.size();*/
	senddata=senddata+"\r\n";
	int size=sizeof(senddata.toUtf8())*senddata.size();
	int real=smtpsocket->write(senddata.toUtf8());
	qDebug()<<size<<"А было отправленно: "<<real;
        if (senddata != "QUIT") {
            if (smtpsocket->waitForReadyRead(Timeout)) {

		while (smtpsocket->canReadLine()) {
        	loops++;
		QString opera = smtpsocket->readLine()+"\n";
		incommingData = opera + incommingData;
        	qDebug() << loops << "|#" << opera << "#|";
        	}	
            }
        } else {
            ////////QApplication::restoreOverrideCursor();
            delete smtpsocket;
            isconnected = false;
            emit SuccessQuit();
         return incommingData;
        }
        ///////QApplication::restoreOverrideCursor();
    } else {
        emit ErrorCloseAll();
    }
    return incommingData;
}
 
void Smtp::disconnected(){
    qDebug() <<"disconneted";
    qDebug() << "error "  << smtpsocket->errorString();
}
 
void Smtp::connected(){
    output.append("connected");
    qDebug() << "Connected ";
}
 
QString Smtp::encodeBase64( QString xml ) {
    QByteArray text;
    text.append(xml);
    return text.toBase64();
}
 
QString Smtp::decodeBase64( QString xml ) {
    QByteArray xcode("");;
    xcode.append(xml);
    QByteArray precode(QByteArray::fromBase64(xcode));
    QString notetxt = precode.data();
    return notetxt;
}
 
QStringList Smtp::GetErrorlist(){
    return ErrorMSG;
}
 
int Smtp::dateswap(QString form, uint unixtime ) {
     QDateTime fromunix;
     fromunix.setTime_t(unixtime);
     QString numeric = fromunix.toString((const QString)form);
     bool ok;
     return (int)numeric.toFloat(&ok);
}
 
QString Smtp::TimeStampMail(){
     /* mail rtf Date format! [url]http://www.faqs.org/rfcs/rfc788.html[/url] */
       QDateTime timer1( QDateTime::currentDateTime() );
                 /////////timer1.setTimeSpec(Qt::UTC);
 
     uint unixtime = timer1.toTime_t();
     QDateTime fromunix;
     fromunix.setTime_t(unixtime);
     QStringList RTFdays = QStringList() << "giorno_NULL" << "Mon" << "Tue"
                                         << "Wed" << "Thu" << "Fri" << "Sat"
                                         << "Sun";
     QStringList RTFmonth = QStringList() << "mese_NULL" << "Jan" << "Feb"
                                          << "Mar" << "Apr" << "May" << "Jun"
                                          << "Jul" << "Aug" << "Sep" << "Oct"
                                          << "Nov" << "Dec";
     QDate timeroad(dateswap("yyyy",unixtime),
                    dateswap("M",unixtime), dateswap("d",unixtime));
     qDebug() << "### RTFdays " << RTFdays.at(timeroad.dayOfWeek());
     qDebug() << "### RTFmonth " << RTFmonth.at(dateswap("M",unixtime));
     qDebug() << "### yyyy " << dateswap("yyyy",unixtime);
     qDebug() << "### M " << dateswap("M",unixtime);
     qDebug() << "### d " << dateswap("d",unixtime);
     QStringList rtfd_line;
     rtfd_line.clear();
     rtfd_line.append("Date: ");
     rtfd_line.append(RTFdays.at(timeroad.dayOfWeek()));
     rtfd_line.append(", ");
     rtfd_line.append(QString::number(dateswap("d",unixtime)));
     rtfd_line.append(" ");
     rtfd_line.append(RTFmonth.at(dateswap("M",unixtime)));
     rtfd_line.append(" ");
     rtfd_line.append(QString::number(dateswap("yyyy",unixtime)));
     rtfd_line.append(" ");
     rtfd_line.append(fromunix.toString("hh:mm:ss"));
     rtfd_line.append(" +0100");
     qDebug() << "### mail rtf Date format " << rtfd_line.join("");
    return QString(rtfd_line.join(""));
}

Функция отправки - QString Smtp::SendLineAndGrab(QString senddata)

В документации функция записи в сокет имеет вид:

qint64 QIODevice::write ( const char * data, qint64 maxSize )

Как правильно посчитать размер сообщения в байтах. Так?

int size=sizeof(senddata.toUtf8())*senddata.size()
Если так тогда почему функция возвращает число отличное от size.

int real=smtpsocket->write(senddata.toUtf8());
	qDebug()<<size<<"А было отправленно: "<<real;

4792 А было отправленно: 1810

PS прошу опощи разобраться в данной проблеме. =)


код не читал. Рассуждаю.

int size=sizeof(senddata.toUtf8())*senddata.size()

size = sizeof(1 символ УТФ) * senddata.size() * senddata.size();

т.е. имхается, что sizeof(senddata.toUtf8()) = sizeof(1 символ УТФ) * senddata.size(). Но надо проверять.

Попробуйте что-то типа:

int size=sizeof(QString("").toUtf8())*senddata.size();

или

int size=2*senddata.size();

или вообще

int size=senddata.size();
bvn13 ★★★★★ ()
Последнее исправление: bvn13 (всего исправлений: 1)

Правильно — это

int size=senddata.toUtf8().size();

sizeof(senddate.toUtf8()) — это sizeof(QByteArray) и оно не имеет ничего общего с этим делом.

vzzo ★★★ ()
Ответ на: комментарий от vzzo

Спасибо, тут разобрался - сходится тютелька в тютельку =) Но проблема не ушла. Разрывы по прежнему есть. В чем может быть дело?

PS здесь на форуме есть тег спойлера? Пробывал в

посметить код - не сработало.

Aeooe ()
Ответ на: комментарий от Aeooe

Но проблема не ушла. Разрывы по прежнему есть.

а в исходнике письма проблему видно? сгенереный оригинал от полученного отличается?

и попробуй еще весь этот юникод в base64 заворачивать

Skolotovich ★★★ ()
Ответ на: комментарий от Skolotovich

еще возможна проблема с «Content-Transfer-Encoding: 8bit» и UTF-8

нарыл вот такое http://stackoverflow.com/questions/7009648/sending-mail-in-utf-8

You could also send the raw UTF-8 as-is and set Content-Transfer-Encoding: 8bit but I would not recommend it. You risk breaking the SMTP standard just by sending very long lines. Also, you have no idea of what kind of legacy programs this will go through.

Skolotovich ★★★ ()
Ответ на: комментарий от Skolotovich

Генерированная строка отличается. Дефекты в строке до отправки не наблюдаются.

Если перекодировать в base64, то сервер не понимает тело письма.

Aeooe ()
Ответ на: комментарий от Skolotovich

Проблема решилась когда перекодил тело письма в base64 и отправил заголовки:

Content-Type: text/html; charset= UTF-8; Content-transfer-encoding: base64

Благодарю за столь явную инициативу и безвозмездную помощь. =)

Aeooe ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.