00001 #include "mailsender.h"
00002 #include <QString>
00003 #include <QSslSocket>
00004 #include <QTextStream>
00005 #include <QByteArray>
00006 #include <QDateTime>
00007 #include <QTextCodec>
00008 #include <QFile>
00009 #include <QFileInfo>
00010 #include <QHostInfo>
00011 #include <time.h>
00012 #include <QCryptographicHash>
00013
00014 static int dateswap(QString form, uint unixtime)
00015 {
00016 QDateTime fromunix;
00017 fromunix.setTime_t(unixtime);
00018 QString numeric = fromunix.toString((const QString)form);
00019 bool ok;
00020 return (int)numeric.toFloat(&ok);
00021 }
00022
00023 static QString encodeBase64(QString xml)
00024 {
00025 QByteArray text;
00026 text.append(xml);
00027 return text.toBase64();
00028 }
00029
00030 static QString TimeStampMail()
00031 {
00032
00033 uint unixtime = (uint)time(NULL);
00034 QDateTime fromunix;
00035 fromunix.setTime_t(unixtime);
00036 QStringList RTFdays = QStringList() << "giorno_NULL" << "Mon" << "Tue" << "Wed" << "Thu" << "Fri" << "Sat" << "Sun";
00037 QStringList RTFmonth = QStringList() << "mese_NULL" << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun" << "Jul" << "Aug" << "Sep" << "Oct" << "Nov" << "Dec";
00038 QDate timeroad(dateswap("yyyy", unixtime), dateswap("M", unixtime), dateswap("d", unixtime));
00039 QStringList rtfd_line;
00040 rtfd_line.clear();
00041 rtfd_line.append("Date: ");
00042 rtfd_line.append(RTFdays.at(timeroad.dayOfWeek()));
00043 rtfd_line.append(", ");
00044 rtfd_line.append(QString::number(dateswap("d",unixtime)));
00045 rtfd_line.append(" ");
00046 rtfd_line.append(RTFmonth.at(dateswap("M",unixtime)));
00047 rtfd_line.append(" ");
00048 rtfd_line.append(QString::number(dateswap("yyyy",unixtime)));
00049 rtfd_line.append(" ");
00050 rtfd_line.append(fromunix.toString("hh:mm:ss"));
00051 rtfd_line.append("");
00052
00053 return QString(rtfd_line.join(""));
00054 }
00055
00056 static QString createBoundary()
00057 {
00058 QByteArray hash = QCryptographicHash::hash(QString(QString::number(qrand())).toUtf8(), QCryptographicHash::Md5);
00059 QString boundary = hash.toHex();
00060 boundary.truncate(26);
00061 boundary.prepend("----=_NextPart_");
00062 return boundary;
00063 }
00064
00065 MailSender::MailSender(const QString &smtpServer, const QString &from, const QStringList &to, const QString &subject, const QString &body)
00066 {
00067 setSmtpServer(smtpServer);
00068 setPort(25);
00069 setTimeout(30000);
00070 setFrom(from);
00071 setTo(to);
00072 setSubject(subject);
00073 setBody(body);
00074 setPriority (normal);
00075 setContentType(text);
00076 setEncoding(_7bit);
00077 setISO(utf8);
00078 }
00079
00080 MailSender::~MailSender()
00081 {
00082 if(_socket)
00083 {
00084 delete _socket;
00085 }
00086 }
00087
00088 void MailSender::setFrom(const QString &from)
00089 {
00090 _from = from;
00091 _fromName = from;
00092 _replyTo = from;
00093 }
00094
00095 void MailSender::setISO(ISO iso)
00096 {
00097 switch(iso)
00098 {
00099 case iso88591:
00100 {
00101 _iso = "iso-8859-1"; _bodyCodec = "ISO 8859-1"; break;
00102 }
00103 case utf8:
00104 {
00105 _iso = "utf-8"; _bodyCodec = "UTF-8"; break;
00106 }
00107 }
00108 }
00109
00110 void MailSender::setEncoding(Encoding encoding)
00111 {
00112 switch(encoding)
00113 {
00114 case _7bit:
00115 {
00116 _encoding = "7bit";
00117 break;
00118 }
00119 case _8bit:
00120 {
00121 _encoding = "8bit";
00122 break;
00123 }
00124 case base64:
00125 {
00126 _encoding = "base64";
00127 break;
00128 }
00129 }
00130 }
00131
00132 QString MailSender::contentType()
00133 {
00134 switch(_contentType)
00135 {
00136 case html:
00137 {
00138 return "text/html";
00139 }
00140 case multipartmixed:
00141 {
00142 return "multipart/mixed";
00143 }
00144 case text:
00145 default:
00146 {
00147 return "text/plain";
00148 }
00149 }
00150 }
00151
00152 QString MailSender::mailData()
00153 {
00154 QString data;
00155
00156 QString boundary1 = createBoundary();
00157 QString boundary2 = createBoundary();
00158
00159 QByteArray hash = QCryptographicHash::hash(QString(QString::number(qrand())).toUtf8(),QCryptographicHash::Md5);
00160 QString id = hash.toHex();
00161 data.append("Message-ID: " + id + "@" + QHostInfo::localHostName() + "\n");
00162
00163 data.append("From: \"" + _fromName + "\"");
00164 data.append(" <" + _from + ">\n");
00165
00166 if(_to.count() > 0)
00167 {
00168 data.append("To: ");
00169 for (int i = 0; i < _to.count(); i++)
00170 {
00171 data.append("<" + _to.at(i) + ">" + ",");
00172 }
00173 data.append("\n");
00174 }
00175
00176 if(_cc.count() > 0)
00177 {
00178 data.append("Cc: ");
00179 for (int i = 0; i < _cc.count(); i++)
00180 {
00181 data.append(_cc.at(i) + ",");
00182 if(i < _cc.count() - 1)
00183 {
00184 data.append(",");
00185 }
00186 }
00187 data.append("\n");
00188 }
00189
00190 data.append("Subject: " + _subject + "\n");
00191 data.append(TimeStampMail() + "\n");
00192
00193 data.append("MIME-Version: 1.0\n");
00194 data.append("Content-Type: Multipart/Mixed; boundary=\"" + boundary1 + "\"\n");
00195
00196 switch(_priority)
00197 {
00198 case low:
00199 data.append("X-Priority: 5\n");
00200 data.append("Priority: Non-Urgent\n");
00201 data.append("X-MSMail-Priority: Low\n");
00202 data.append("Importance: low\n");
00203 break;
00204 case high:
00205 data.append("X-Priority: 1\n");
00206 data.append("Priority: Urgent\n");
00207 data.append("X-MSMail-Priority: High\n");
00208 data.append("Importance: high\n");
00209 break;
00210 default:
00211 data.append("X-Priority: 3\n");
00212 data.append(" X-MSMail-Priority: Normal\n");
00213 }
00214
00215 data.append("X-Mailer: QT4\r\n");
00216
00217 if(! _confirmTo.isEmpty())
00218 {
00219 data.append("Disposition-Notification-To: " + _confirmTo + "\n");
00220 }
00221
00222 if(! _replyTo.isEmpty() && _confirmTo != _from)
00223 {
00224 data.append("Reply-to: " + _replyTo + "\n");
00225 data.append("Return-Path: <" + _replyTo + ">\n");
00226 }
00227
00228 data.append("\n");
00229
00230 data.append("This is a multi-part message in MIME format.\r\n\n");
00231 data.append("--" + boundary1 + "\n");
00232 data.append("Content-Type: multipart/alternative; boundary=\"" + boundary2 + "\"\n\n\n");
00233 data.append("--" + boundary2 + "\n");
00234
00235 data.append("Content-Type: " + contentType() + ";\n");
00236 data.append(" charset=" + _iso + "\r\n");
00237 data.append("Content-Transfer-Encoding: " + _encoding + "\r\n");
00238 data.append("\r\n\n");
00239
00240 if(_contentType == html)
00241 {
00242 data.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n");
00243 data.append("<HTML><HEAD>\r\n");
00244 data.append("<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=" + _iso + "\">\r\n");
00245 data.append("<META content=\"MSHTML 6.00.2900.2802\" name=GENERATOR>\r\n");
00246 data.append("<STYLE></STYLE>\r\n");
00247 data.append("</head>\r\n");
00248 data.append("<body bgColor=#ffffff>\r\n");
00249 }
00250
00251 QByteArray encodedBody(_body.toUtf8());
00252 QTextCodec *codec = QTextCodec::codecForName(_bodyCodec.toUtf8());
00253 data.append(codec->toUnicode(encodedBody) + "\r\n");
00254
00255 if(_contentType == html)
00256 {
00257 data.append("<DIV> </DIV></body></html>\r\n\n");
00258 data.append("--" + boundary2 + "--\r\n\n");
00259 }
00260
00261 if(_attachments.count() > 0)
00262 {
00263 for(int i = 0; i < _attachments.count(); i++)
00264 {
00265 data.append("--" + boundary1 + "\n");
00266 QFileInfo fileinfo(_attachments.value(i));
00267 QString type = getMimeType(fileinfo.suffix());
00268 data.append("Content-Type: " + type + ";\n");
00269 data.append(" name=" + fileinfo.fileName() + "\n");
00270
00271 QString tt = fileinfo.fileName();
00272
00273 data.append("Content-Transfer-Encoding: base64\n");
00274 data.append("Content-Disposition: attachment\n");
00275 data.append(" filename=" + fileinfo.fileName() + "\n\n");
00276 QString encodedFile;
00277 QString f =_attachments.value(i);
00278 QFile file(_attachments.value(i));
00279 file.open(QIODevice::ReadOnly);
00280 QDataStream in(&file);
00281 quint8 a;
00282 char c;
00283 QString b;
00284 while(!in.atEnd())
00285 {
00286 in >> a;
00287 c = a;
00288 b.append(c);
00289 }
00290 encodedFile = encodeBase64(b);
00291 data.append(encodedFile);
00292 data.append("\r\n\n");
00293 }
00294 data.append("--" + boundary1 + "--\r\n\n");
00295 }
00296
00297 _lastMailData = data;
00298 return data;
00299 }
00300
00301 QString MailSender::getMimeType(QString ext)
00302 {
00303
00304 if(ext == "txt") return "text/plain";
00305 if(ext == "htm" || ext == "html") return "text/html";
00306 if(ext == "css") return "text/css";
00307
00308
00309 if(ext == "png") return "image/png";
00310 if(ext == "gif") return "image/gif";
00311 if(ext == "jpg" || ext == "jpeg") return "image/jpeg";
00312 if(ext == "bmp") return "image/bmp";
00313 if(ext == "tif") return "image/tiff";
00314
00315
00316 if(ext == "bz2") return "application/x-bzip";
00317 if(ext == "gz") return "application/x-gzip";
00318 if(ext == "tar") return "application/x-tar";
00319 if(ext == "zip") return "application/zip";
00320
00321
00322 if(ext == "aif" || ext == "aiff") return "audio/aiff";
00323 if(ext == "mid" || ext == "midi") return "audio/mid";
00324 if(ext == "mp3") return "audio/mpeg";
00325 if(ext == "ogg") return "audio/ogg";
00326 if(ext == "wav") return "audio/wav";
00327 if(ext == "wma") return "audio/x-ms-wma";
00328
00329
00330 if(ext == "asf" || ext == "asx") return "video/x-ms-asf";
00331 if(ext == "avi") return "video/avi";
00332 if(ext == "mpg" || ext == "mpeg") return "video/mpeg";
00333 if(ext == "wmv") return "video/x-ms-wmv";
00334 if(ext == "wmx") return "video/x-ms-wmx";
00335
00336
00337 if(ext == "xml") return "text/xml";
00338 if(ext == "xsl") return "text/xsl";
00339
00340
00341 if(ext == "doc" || ext == "rtf") return "application/msword";
00342 if(ext == "xls") return "application/excel";
00343 if(ext == "ppt" || ext == "pps") return "application/vnd.ms-powerpoint";
00344
00345
00346 if(ext == "pdf") return "application/pdf";
00347 if(ext == "ai" || ext == "eps") return "application/postscript";
00348 if(ext == "psd") return "image/psd";
00349
00350
00351 if(ext == "swf") return "application/x-shockwave-flash";
00352
00353
00354 if(ext == "ra") return "audio/vnd.rn-realaudio";
00355 if(ext == "ram") return "audio/x-pn-realaudio";
00356 if(ext == "rm") return "application/vnd.rn-realmedia";
00357 if(ext == "rv") return "video/vnd.rn-realvideo";
00358
00359
00360 if(ext == "exe") return "application/x-msdownload";
00361 if(ext == "pls") return "audio/scpls";
00362 if(ext == "m3u") return "audio/x-mpegurl";
00363
00364 return "text/plain";
00365 }
00366
00367 void MailSender::errorReceived(QAbstractSocket::SocketError socketError)
00368 {
00369 QString msg;
00370
00371 switch(socketError)
00372 {
00373 case QAbstractSocket::ConnectionRefusedError: msg = "ConnectionRefusedError"; break;
00374 case QAbstractSocket::RemoteHostClosedError: msg = "RemoteHostClosedError"; break;
00375 case QAbstractSocket::HostNotFoundError: msg = "HostNotFoundError"; break;
00376 case QAbstractSocket::SocketAccessError: msg = "SocketAccessError"; break;
00377 case QAbstractSocket::SocketResourceError: msg = "SocketResourceError"; break;
00378 case QAbstractSocket::SocketTimeoutError: msg = "SocketTimeoutError"; break;
00379 case QAbstractSocket::DatagramTooLargeError: msg = "DatagramTooLargeError"; break;
00380 case QAbstractSocket::NetworkError: msg = "NetworkError"; break;
00381 case QAbstractSocket::AddressInUseError: msg = "AddressInUseError"; break;
00382 case QAbstractSocket::SocketAddressNotAvailableError: msg = "SocketAddressNotAvailableError"; break;
00383 case QAbstractSocket::UnsupportedSocketOperationError: msg = "UnsupportedSocketOperationError"; break;
00384 case QAbstractSocket::ProxyAuthenticationRequiredError: msg = "ProxyAuthenticationRequiredError"; break;
00385 default: msg = "Unknown Error";
00386 }
00387
00388 error("Socket error [" + msg + "]");
00389 }
00390
00391
00392 bool MailSender::send()
00393 {
00394 _lastError = "";
00395
00396 if(_socket)
00397 {
00398 delete _socket;
00399 }
00400
00401 _socket = _ssl ? new QSslSocket(this) : new QTcpSocket(this);
00402 connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorReceived(QAbstractSocket::SocketError)));
00403 connect(_socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy & , QAuthenticator *)), this, SLOT(proxyAuthentication(const QNetworkProxy &, QAuthenticator *)));
00404
00405 bool auth = ! _login.isEmpty();
00406
00407 _socket->connectToHost(_smtpServer, _port);
00408
00409 if(!_socket->waitForConnected(_timeout))
00410 {
00411 error("Time out connecting host");
00412 return false;
00413 }
00414
00415 if(!read("220"))
00416 {
00417 return false;
00418 }
00419
00420 if(!sendCommand("EHLO there", "250"))
00421 {
00422 if(!sendCommand("HELO there", "250"))
00423 {
00424 return false;
00425 }
00426 }
00427
00428 if(_ssl)
00429 {
00430 if(!sendCommand("STARTTLS", "220"))
00431 {
00432 return false;
00433 }
00434 QSslSocket *pssl = qobject_cast<QSslSocket *>(_socket);
00435 if(pssl == 0)
00436 {
00437 error("internal error casting to QSslSocket");
00438 return false;
00439 }
00440 pssl->startClientEncryption ();
00441 }
00442
00443 if(auth)
00444 {
00445 if(!sendCommand("AUTH LOGIN", "334"))
00446 {
00447 return false;
00448 }
00449 if(!sendCommand(encodeBase64(_login), "334"))
00450 {
00451 return false;
00452 }
00453 if(!sendCommand(encodeBase64(_password), "235"))
00454 {
00455 return false;
00456 }
00457 }
00458
00459 if(!sendCommand(QString::fromLatin1("MAIL FROM:<") +_from + QString::fromLatin1(">"), "250"))
00460 {
00461 return false;
00462 }
00463
00464 QStringList recipients = _to + _cc + _bcc;
00465 for (int i=0; i< recipients.count(); i++)
00466 {
00467 if(!sendCommand(QString::fromLatin1("RCPT TO:<") + recipients.at(i) + QString::fromLatin1(">"), "250"))
00468 {
00469 return false;
00470 }
00471 }
00472
00473 if(!sendCommand(QString::fromLatin1("DATA"), "354"))
00474 {
00475 return false;
00476 }
00477 if(!sendCommand(mailData() + QString::fromLatin1("\r\n."), "250"))
00478 {
00479 return false;
00480 }
00481 if(!sendCommand(QString::fromLatin1("QUIT"), "221"))
00482 {
00483 return false;
00484 }
00485
00486 _socket->disconnectFromHost();
00487 return true;
00488 }
00489
00490 bool MailSender::read(const QString &waitfor)
00491 {
00492 if(!_socket->waitForReadyRead(_timeout))
00493 {
00494 error("Read timeout");
00495 return false;
00496 }
00497
00498 if(!_socket->canReadLine())
00499 {
00500 error("Can't read");
00501 return false;
00502 }
00503
00504 QString responseLine;
00505
00506 do
00507 {
00508 responseLine = _socket->readLine();
00509 }
00510 while(_socket->canReadLine() && responseLine[3] != ' ');
00511
00512 _lastResponse = responseLine;
00513
00514 QString prefix = responseLine.left(3);
00515 bool isOk = (prefix == waitfor);
00516 if(!isOk)
00517 {
00518 error("waiting for " + waitfor + ", received " + prefix);
00519 }
00520
00521 return isOk;
00522 }
00523
00524 bool MailSender::sendCommand(const QString &cmd, const QString &waitfor)
00525 {
00526 QTextStream t(_socket);
00527 t << cmd + "\r\n";
00528 t.flush();
00529
00530 _lastCmd = cmd;
00531
00532 return read(waitfor);
00533 }
00534
00535 void MailSender::error(const QString &msg)
00536 {
00537 _lastError = msg;
00538 }
00539
00540 void MailSender::proxyAuthentication(const QNetworkProxy &, QAuthenticator * authenticator)
00541 {
00542 *authenticator = _authenticator;
00543 }
00544
00545 void MailSender::setProxyAuthenticator(const QAuthenticator &authenticator)
00546 {
00547 _authenticator = authenticator;
00548 }