Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

emailhandler.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2001 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of Qt Palmtop Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 #include <qfileinfo.h>
00021 #include <stdlib.h>
00022 #include <qapplication.h>
00023 #include <qmessagebox.h>
00024 #include <qcstring.h>
00025 #include "emailhandler.h"
00026 #include <qpe/applnk.h>
00027 #include <qpe/filemanager.h>
00028 
00029 QCollection::Item EnclosureList::newItem(QCollection::Item d)
00030 {
00031   return dupl( (Enclosure *) d);
00032 }
00033 
00034 Enclosure* EnclosureList::dupl(Enclosure *in)
00035 {
00036   ac = new Enclosure(*in);
00037   return ac;
00038 }
00039 
00040 EmailHandler::EmailHandler()
00041 {
00042   qDebug("EMailHandler::EmailHandler");
00043 
00044   smtpClient = new SmtpClient();
00045   popClient = new PopClient();
00046   
00047   connect(smtpClient, SIGNAL(errorOccurred(int,const QString&)), this,
00048       SIGNAL(smtpError(int,const QString&)) );
00049   connect(smtpClient, SIGNAL(mailSent()), this, SIGNAL(mailSent()) );
00050   connect(smtpClient, SIGNAL(updateStatus(const QString&)), this,
00051       SIGNAL(updateSmtpStatus(const QString&)) );
00052   
00053   connect(popClient, SIGNAL(errorOccurred(int,const QString&)), this,
00054       SIGNAL(popError(int,const QString&)) );
00055   connect(popClient, SIGNAL(newMessage(const QString&,int,uint,bool)),
00056       this, SLOT(messageArrived(const QString&,int,uint,bool)) );
00057   connect(popClient, SIGNAL(updateStatus(const QString&)), this,
00058       SIGNAL(updatePopStatus(const QString&)) );
00059   connect(popClient, SIGNAL(mailTransfered(int)), this,
00060       SIGNAL(mailTransfered(int)) );
00061 
00062 
00063   //relaying size information
00064   connect(popClient, SIGNAL(currentMailSize(int)),
00065     this, SIGNAL(currentMailSize(int)) );
00066   connect(popClient, SIGNAL(downloadedSize(int)),
00067     this, SIGNAL(downloadedSize(int)) );
00068 }
00069 
00070 void EmailHandler::sendMail(QList<Email> *mailList)
00071 {
00072   Email *currentMail;
00073   QString temp;
00074   QString userName = QString::null;
00075   // not supported by ALL SMTP servers in the MAIL From field
00076   // userName = "\""+mailAccount.name+"\"";
00077   userName += "<" + mailAccount.emailAddress + ">";
00078   
00079   for (currentMail = mailList->first(); currentMail != 0;
00080       currentMail = mailList->next()) {
00081     
00082     if (encodeMime(currentMail) == 0) {
00083       smtpClient->addMail(userName, currentMail->subject,
00084         currentMail->recipients, currentMail->rawMail);
00085     } else {                    //error
00086       temp = tr("Could not locate all files in \nmail with subject: ") +
00087         currentMail->subject;
00088       temp += tr("\nMail has NOT been sent");
00089       QMessageBox::warning(qApp->activeWindow(), tr("Attachment error"), temp, tr("OK\n"));
00090       
00091     }
00092   }
00093   smtpClient->newConnection(mailAccount.smtpServer, 25);
00094 }
00095 
00096 void EmailHandler::setAccount(MailAccount account)
00097 {
00098   mailAccount = account;
00099 }
00100 
00101 void EmailHandler::getMail()
00102 {
00103   popClient->setAccount(mailAccount.popUserName, mailAccount.popPasswd);
00104   if (mailAccount.synchronize) {
00105     popClient->setSynchronize(mailAccount.lastServerMailCount);
00106   } else {
00107     popClient->removeSynchronize();
00108   }
00109   
00110   headers = FALSE;
00111   //popClient->headersOnly(headers, 0);
00112   popClient->newConnection(mailAccount.popServer, 110);
00113 }
00114 
00115 void EmailHandler::getMailHeaders()
00116 {
00117   popClient->setAccount(mailAccount.popUserName, mailAccount.popPasswd);
00118   mailAccount.synchronize ? popClient->setSynchronize(mailAccount.lastServerMailCount): popClient->removeSynchronize();
00119   
00120   headers = TRUE;
00121   popClient->headersOnly(headers, mailAccount.syncLimit);  //less than requested syncLimit, download all
00122   qDebug("Initiating connection");
00123   popClient->newConnection(mailAccount.popServer, 110);
00124 }
00125 
00126 void EmailHandler::getMailByList(MailList *mailList)
00127 {
00128   if (mailList->count() == 0) { //should not occur though
00129     emit mailTransfered(0);
00130     return;
00131   }
00132   
00133   headers = FALSE;
00134   popClient->headersOnly(FALSE, 0);
00135   
00136   popClient->setAccount(mailAccount.popUserName,mailAccount.popPasswd);
00137   popClient->setSelectedMails(mailList);
00138   popClient->newConnection(mailAccount.popServer, 110);
00139  }
00140 
00141 void EmailHandler::messageArrived(const QString &message, int id, uint size, bool complete)
00142 {
00143   Email mail;
00144   
00145   mail.rawMail = message;
00146   mail.serverId = id;
00147   mail.size = size;
00148   mail.downloaded = complete;
00149   
00150   emit mailArrived(mail, FALSE);
00151 }
00152 
00153 bool EmailHandler::parse(const QString &in, const QString &lineShift, Email *mail)
00154 {
00155   QString temp, boundary;
00156   int pos;
00157   QString delimiter, header, body, mimeHeader, mimeBody;
00158   QString content, contentType, contentAttribute, id, encoding;
00159   QString fileName, storedName;
00160   int enclosureId = 0;
00161   
00162   mail->rawMail = in;
00163   mail->received = TRUE;
00164   mail->files.setAutoDelete(TRUE);
00165   
00166   temp = lineShift + "." + lineShift;
00167 
00168   if (in.right(temp.length()) != temp) {
00169     mail->rawMail += temp;
00170   }
00171 
00172   
00173   delimiter = lineShift + lineShift;  // "\n\n" or "\r\n\r\n"
00174   pos = in.find(delimiter, 0, FALSE);
00175   header = in.left(pos);
00176   body = in.right(in.length() - pos - delimiter.length());
00177   if ((body.at(body.length()-2) == '.') && (body.at(body.length()-3) == '\n'))
00178       body.truncate(body.length()-2);
00179   
00180   //  TextParser p(header, lineShift);
00181   TextParser * lp = new TextParser(header, lineShift);
00182 #define p (*lp)
00183 
00184   if ((pos = p.find("FROM",':', 0, TRUE)) != -1) {
00185     pos++;
00186     if (p.separatorAt(pos) == ' ') {
00187       mail->from = p.getString(&pos, '<', false);
00188       mail->from = mail->from.stripWhiteSpace();
00189       if ( (mail->from.length() > 2) && (mail->from[0] == '"') ) {
00190         mail->from = mail->from.left(mail->from.length() - 1);
00191         mail->from = mail->from.right(mail->from.length() - 1);
00192       }
00193       pos++;                 
00194       mail->fromMail = p.getString(&pos, '>', false);
00195     } else {
00196       if (p.separatorAt(pos) == '<')       //No name.. nasty
00197           pos++;
00198       //pos++;
00199       mail->fromMail = p.getString(&pos, 'z', TRUE);
00200       if (mail->fromMail.at(mail->fromMail.length()-1) == '>')
00201       mail->fromMail.truncate(mail->fromMail.length() - 1);
00202       mail->from=mail->fromMail;
00203     }
00204   }
00205   
00206   pos=0;
00207   
00208   //Search for To: after the FROM: attribute to prevent hitting the Delivered-To:
00209   while((pos = p.find("TO",':', pos+1, TRUE))!=-1)
00210   {
00211     QString rec;
00212   
00213     if (p.separatorAt(pos-1)!='-')  //The - separator means that this is a Delivered-To: or Reply-To:
00214   {
00215     pos++;
00216     mail->recipients.append(p.getString(&pos, '\r', TRUE));
00217         } 
00218   }
00219   //
00220   //if (pos==-1) mail->recipients.append (tr("undisclosed recipients") ); 
00221   
00222   if ((pos = p.find("CC",':', 0, TRUE)) != -1)
00223   {
00224       pos++;
00225       mail->carbonCopies.append (p.getString(&pos, 'z', TRUE) );
00226   }
00227  
00228   if ((pos = p.find("SUBJECT",':', 0, TRUE)) != -1) {
00229     pos++;
00230     mail->subject = p.getString(&pos, 'z', TRUE);
00231   }
00232   
00233   if ((pos = p.find("DATE",':', 0, TRUE)) != -1) {
00234     pos++;
00235     mail->date = p.getString(&pos, 'z', TRUE);
00236   }
00237   
00238   
00239   
00240   if ((pos = p.find("MESSAGE",'-', 0, TRUE)) != -1) {
00241     pos++;
00242     if ( (p.wordAt(pos).upper() == "ID") &&
00243       (p.separatorAt(pos) == ':') ) {
00244       
00245       id = p.getString(&pos, 'z', TRUE);
00246       mail->id = id;
00247     }
00248   }
00249   
00250   pos = 0;
00251   while ( ((pos = p.find("MIME",'-', pos, TRUE)) != -1) ) {
00252     pos++;
00253     if ( (p.wordAt(pos).upper() == "VERSION") &&
00254       (p.separatorAt(pos) == ':') ) {
00255         pos++;
00256         if (p.getString(&pos, 'z', true) == "1.0") {
00257           mail->mimeType = 1;
00258         }
00259     }
00260   }
00261   
00262   if (mail->mimeType == 1) {
00263     boundary = "";
00264     if ((pos = p.find("BOUNDARY", '=', 0, TRUE)) != -1) {
00265       pos++;
00266       boundary = p.getString(&pos, 'z', true);
00267       if (boundary[0] == '"') {
00268         boundary = boundary.left(boundary.length() - 1); //strip "
00269         boundary = boundary.right(boundary.length() - 1); //strip "
00270       }
00271       boundary = "--" + boundary;   //create boundary field
00272     }
00273     
00274     if (boundary == "") {     //fooled by Mime-Version
00275       mail->body = body;
00276       mail->bodyPlain = body;
00277      delete lp;
00278       return mail;
00279     }
00280     
00281     while (body.length() > 0) {
00282       pos = body.find(boundary, 0, FALSE);
00283       pos = body.find(delimiter, pos, FALSE);
00284       mimeHeader = body.left(pos);
00285       mimeBody = body.right(body.length() - pos - delimiter.length());
00286       TextParser bp(mimeHeader, lineShift);
00287     
00288       contentType = "";
00289       contentAttribute = "";
00290       fileName = "";
00291       if ((pos = bp.find("CONTENT",'-', 0, TRUE)) != -1) {
00292         pos++;
00293         if ( (bp.wordAt(pos).upper() == "TYPE") &&
00294           (bp.separatorAt(pos) == ':') ) {
00295           contentType = bp.nextWord().upper();
00296           if (bp.nextSeparator() == '/')
00297             contentAttribute = bp.nextWord().upper();
00298           content = contentType + "/" + contentAttribute;
00299         }
00300         if ((pos = bp.find("ENCODING",':', 0, TRUE)) != -1) {
00301           pos++;
00302           encoding = bp.getString(&pos, 'z', TRUE);
00303         }
00304         
00305         if ( (pos = bp.find("FILENAME",'=', 0, TRUE)) != -1) {
00306           pos++;
00307           fileName = bp.getString(&pos, 'z', TRUE);
00308           fileName = fileName.right(fileName.length() - 1);
00309           fileName = fileName.left(fileName.length() - 1);
00310         }
00311 
00312       }
00313       pos = mimeBody.find(boundary, 0, FALSE);
00314       if (pos == -1)            //should not occur, malformed mail
00315         pos = mimeBody.length();
00316       body = mimeBody.right(mimeBody.length() - pos);
00317       mimeBody = mimeBody.left(pos);
00318         
00319       if (fileName != "") { //attatchments of some type, audio, image etc.
00320         
00321         Enclosure e;
00322         e.id = enclosureId;
00323         e.originalName = fileName;
00324         e.contentType = contentType;
00325         e.contentAttribute = contentAttribute;
00326         e.encoding = encoding;
00327         e.body = mimeBody;
00328         e.saved = FALSE;
00329         mail->addEnclosure(&e);
00330         enclosureId++;
00331         
00332       } else if (contentType == "TEXT") {
00333         if (contentAttribute == "PLAIN") {
00334           mail->body = mimeBody;
00335           mail->bodyPlain = mimeBody;
00336         }
00337         if (contentAttribute == "HTML") {
00338           mail->body = mimeBody;
00339         }
00340       }
00341     }
00342   } else {
00343     mail->bodyPlain = body;
00344     mail->body = body;
00345   }
00346   delete lp;
00347   return TRUE;
00348 }
00349 
00350 bool EmailHandler::getEnclosure(Enclosure *ePtr)
00351 {
00352   QFile f(ePtr->path + ePtr->name);
00353   char src[4];
00354   char *destPtr;
00355   QByteArray buffer;
00356   uint bufCount, pos, decodedCount, size, x;
00357   
00358   if (! f.open(IO_WriteOnly) ) {
00359     qWarning("could not save: " + ePtr->path + ePtr->name);
00360     return FALSE;
00361   }
00362   
00363   if (ePtr->encoding.upper() == "BASE64") {
00364     size = (ePtr->body.length() * 3 / 4); //approximate size (always above)
00365     buffer.resize(size);
00366     bufCount = 0;
00367     pos = 0;
00368     destPtr = buffer.data();
00369     
00370     while (pos < ePtr->body.length()) {
00371       decodedCount = 4;
00372       x = 0;
00373       while ( (x < 4) && (pos < ePtr->body.length()) ) {
00374         src[x] = ePtr->body[pos].latin1();
00375         pos++;
00376         if (src[x] == '\r' || src[x] == '\n' || src[x] == ' ')
00377           x--;
00378         x++;
00379       }
00380       if (x > 1) {
00381         decodedCount = parse64base(src, destPtr);
00382         destPtr += decodedCount;
00383         bufCount += decodedCount;
00384       }
00385     }
00386     
00387     buffer.resize(bufCount);  //set correct length of file
00388     f.writeBlock(buffer);
00389   } else {
00390     QTextStream t(&f);
00391     t << ePtr->body;
00392   }
00393   return TRUE;
00394 }
00395 
00396 int EmailHandler::parse64base(char *src, char *bufOut) {
00397   
00398   char c, z;
00399   char li[4];
00400   int processed;
00401 
00402   //conversion table withouth table...
00403   for (int x = 0; x < 4; x++) {
00404     c = src[x];
00405   
00406     if ( (int) c >= 'A' && (int) c <= 'Z')
00407       li[x] = (int) c - (int) 'A';
00408     if ( (int) c >= 'a' && (int) c <= 'z')
00409       li[x] = (int) c - (int) 'a' + 26;
00410     if ( (int) c >= '0' && (int) c <= '9')
00411       li[x] = (int) c - (int) '0' + 52;
00412     if (c == '+')
00413       li[x] = 62;
00414     if (c == '/')
00415       li[x] = 63;
00416   }
00417   
00418   processed = 1;
00419   bufOut[0] = (char) li[0] & (32+16+8+4+2+1); //mask out top 2 bits
00420   bufOut[0] <<= 2;
00421   z = li[1] >> 4;
00422   bufOut[0] = bufOut[0] | z;    //first byte retrived
00423     
00424   if (src[2] != '=') {
00425     bufOut[1] = (char) li[1] & (8+4+2+1); //mask out top 4 bits
00426     bufOut[1] <<= 4;
00427     z = li[2] >> 2;
00428     bufOut[1] = bufOut[1] | z;    //second byte retrived
00429     processed++;
00430 
00431     if (src[3] != '=') {
00432       bufOut[2] = (char) li[2] & (2+1); //mask out top 6 bits
00433       bufOut[2] <<= 6;
00434       z = li[3];
00435       bufOut[2] = bufOut[2] | z;  //third byte retrieved
00436       processed++;
00437     }
00438   }
00439   return processed;
00440 }
00441 
00442 int EmailHandler::encodeMime(Email *mail) 
00443 {
00444   
00445   QString fileName, fileType, contentType, newBody, boundary;
00446   Enclosure *ePtr;
00447   QString userName;
00448   
00449   if ( ! mailAccount.name.isEmpty() ) {
00450     userName = "\"" + mailAccount.name + "\" <" + mailAccount.emailAddress + ">";
00451   } else {
00452     userName = "<" + mailAccount.emailAddress + ">";
00453   }
00454   
00455   //add standard headers
00456   newBody = "From: " + userName + "\r\nTo: ";
00457         for (QStringList::Iterator it = mail->recipients.begin(); it != mail->recipients.end(); ++it ) {
00458     newBody += *it + " ";
00459   }
00460   
00461    newBody += "\r\nCC: ";
00462    
00463    for (QStringList::Iterator it = mail->carbonCopies.begin(); it != mail->carbonCopies.end(); ++it ) {
00464     newBody += *it + " ";
00465   }
00466   
00467   newBody += "\r\nSubject: " + mail->subject + "\r\n";
00468   
00469   if (mail->files.count() == 0) {         //just a simple mail
00470     newBody += "\r\n" + mail->body;
00471     mail->rawMail = newBody;
00472     return 0;
00473   }
00474 
00475   //Build mime encoded mail
00476   boundary = "-----4345=next_bound=0495----";
00477   
00478   newBody += "Mime-Version: 1.0\r\n";
00479   newBody += "Content-Type: multipart/mixed; boundary=\"" +
00480         boundary + "\"\r\n\r\n";
00481   
00482   newBody += "This is a multipart message in Mime 1.0 format\r\n\r\n";
00483   newBody += "--" + boundary + "\r\nContent-Type: text/plain\r\n\r\n";
00484   newBody += mail->body;
00485   
00486   for ( ePtr=mail->files.first(); ePtr != 0; ePtr=mail->files.next() ) {
00487     fileName = ePtr->originalName;
00488     fileType = ePtr->contentType;
00489     QFileInfo fi(fileName);
00490     
00491     // This specification of contentType is temporary
00492     contentType = "";
00493     if (fileType == "Picture") {
00494       contentType = "image/x-image";
00495     } else if (fileType == "Document") {
00496       contentType = "text/plain";
00497     } else if (fileType == "Sound") {
00498       contentType = "audio/x-wav";
00499     } else if (fileType == "Movie") {
00500       contentType = "video/mpeg";
00501     } else {
00502       contentType = "application/octet-stream";
00503     }
00504     
00505     newBody += "\r\n\r\n--" + boundary + "\r\n";
00506     newBody += "Content-Type: " + contentType + "; name=\"" +
00507           fi.fileName() + "\"\r\n";
00508     newBody += "Content-Transfer-Encoding: base64\r\n";
00509     newBody += "Content-Disposition: inline; filename=\"" +
00510           fi.fileName() + "\"\r\n\r\n";
00511     
00512     if (encodeFile(fileName, &newBody) == -1)   //file not found?
00513       return -1;
00514   }
00515   
00516   newBody += "\r\n\r\n--" + boundary + "--";
00517   mail->rawMail = newBody;
00518   
00519   return 0;
00520 }
00521 
00522 int EmailHandler::encodeFile(const QString &fileName, QString *toBody)
00523 {
00524   char *fileData;
00525   char *dataPtr;
00526   QString temp;
00527   uint dataSize, count;
00528   QFile f(fileName);
00529   
00530   if (! f.open(IO_ReadOnly) ) {
00531     qWarning("could not open file: " + fileName);
00532     return -1;
00533   }
00534   QTextStream s(&f);
00535   dataSize = f.size();
00536   fileData = (char *) malloc(dataSize + 3);
00537   s.readRawBytes(fileData, dataSize);
00538   
00539   temp = "";
00540   dataPtr = fileData;
00541   count  = 0;
00542   while (dataSize > 0) {
00543     if (dataSize < 3) {
00544       encode64base(dataPtr, &temp, dataSize);
00545       dataSize = 0;
00546     } else {
00547       encode64base(dataPtr, &temp, 3);
00548       dataSize -= 3;
00549       dataPtr += 3;
00550       count += 4;
00551     }
00552     if (count > 72) {
00553       count = 0;
00554       temp += "\r\n";
00555     }
00556   }
00557   toBody->append(temp);
00558   
00559   delete(fileData);
00560   f.close();
00561   return 0;
00562 }
00563 
00564 void EmailHandler::encode64base(char *src, QString *dest, int len)
00565 {
00566   QString temp;
00567   uchar c;
00568   uchar bufOut[4];
00569   
00570   bufOut[0] = src[0];
00571   bufOut[0] >>= 2;                  //Done byte 0
00572   
00573   bufOut[1] = src[0];
00574   bufOut[1] = bufOut[1] & (1 + 2);            //mask out top 6 bits
00575   bufOut[1] <<= 4;                  //copy up 4 places
00576   if (len > 1) {
00577     c = src[1];
00578   } else {
00579     c = 0;
00580   }
00581   
00582   c = c & (16 + 32 + 64 + 128);
00583   c >>= 4;
00584   bufOut[1] = bufOut[1] | c;              //Done byte 1
00585   
00586   bufOut[2] = src[1];
00587   bufOut[2] = bufOut[2] & (1 + 2 + 4 + 8);
00588   bufOut[2] <<= 2;
00589   if (len > 2) {
00590     c = src[2];
00591   } else {
00592     c = 0;
00593   }
00594   c >>= 6;
00595   bufOut[2] = bufOut[2] | c;
00596   
00597   bufOut[3] = src[2];
00598   bufOut[3] = bufOut[3] & (1 + 2 + 4 + 8 + 16 + 32);
00599   
00600   if (len == 1) {
00601     bufOut[2] = 64;
00602     bufOut[3] = 64;
00603   }
00604   if (len == 2) {
00605     bufOut[3] = 64;
00606   }
00607   for (int x = 0; x < 4; x++) {
00608     if (bufOut[x] <= 25)
00609       bufOut[x] += (uint) 'A';
00610     else if (bufOut[x] >= 26 && bufOut[x] <= 51)
00611       bufOut[x] += (uint) 'a' - 26;
00612     else if (bufOut[x] >= 52 && bufOut[x] <= 61)
00613       bufOut[x] += (uint) '0' - 52;
00614     else if (bufOut[x] == 62)
00615       bufOut[x] = '+';
00616     else if (bufOut[x] == 63)
00617       bufOut[x] = '/';
00618     else if (bufOut[x] == 64)
00619       bufOut[x] = '=';
00620 
00621     dest->append(bufOut[x]);
00622   }
00623 }
00624 
00625 void EmailHandler::cancel()
00626 {
00627   popClient->errorHandling(ErrCancel);
00628   smtpClient->errorHandling(ErrCancel);
00629 }
00630 

Generated on Sat Nov 5 16:18:09 2005 for OPIE by  doxygen 1.4.2