kioslaves/imap4

mimeheader.cc

00001 /***************************************************************************
00002                           mimeheader.cc  -  description
00003                              -------------------
00004     begin                : Fri Oct 20 2000
00005     copyright            : (C) 2000 by Sven Carstens
00006     email                : s.carstens@gmx.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "mimeheader.h"
00019 #include "mimehdrline.h"
00020 #include "mailheader.h"
00021 #include "rfcdecoder.h"
00022 
00023 #include <qregexp.h>
00024 
00025 // #include <iostream.h>
00026 #include <kglobal.h>
00027 #include <kinstance.h>
00028 #include <kiconloader.h>
00029 #include <kmimetype.h>
00030 #include <kmimemagic.h>
00031 #include <kmdcodec.h>
00032 #include <kdebug.h>
00033 
00034 mimeHeader::mimeHeader ():
00035 typeList (17, false), dispositionList (17, false)
00036 {
00037   // Case insensitive hashes are killing us.  Also are they too small?
00038   originalHdrLines.setAutoDelete (true);
00039   additionalHdrLines.setAutoDelete (false); // is also in original lines
00040   nestedParts.setAutoDelete (true);
00041   typeList.setAutoDelete (true);
00042   dispositionList.setAutoDelete (true);
00043   nestedMessage = NULL;
00044   contentLength = 0;
00045   contentType = "application/octet-stream";
00046 }
00047 
00048 mimeHeader::~mimeHeader ()
00049 {
00050 }
00051 
00052 /*
00053 QPtrList<mimeHeader> mimeHeader::getAllParts()
00054 {
00055     QPtrList<mimeHeader> retVal;
00056 
00057     // caller is responsible for clearing
00058     retVal.setAutoDelete( false );
00059     nestedParts.setAutoDelete( false );
00060 
00061     // shallow copy
00062     retVal = nestedParts;
00063 
00064     // can't have duplicate pointers
00065     nestedParts.clear();
00066 
00067     // restore initial state
00068     nestedParts.setAutoDelete( true );
00069 
00070     return retVal;
00071 } */
00072 
00073 void
00074 mimeHeader::addHdrLine (mimeHdrLine * aHdrLine)
00075 {
00076   mimeHdrLine *addLine = new mimeHdrLine (aHdrLine);
00077   if (addLine)
00078   {
00079     originalHdrLines.append (addLine);
00080     if (qstrnicmp (addLine->getLabel (), "Content-", 8))
00081     {
00082       additionalHdrLines.append (addLine);
00083     }
00084     else
00085     {
00086       int skip;
00087       char *aCStr = addLine->getValue ().data ();
00088       QDict < QString > *aList = 0;
00089 
00090       skip = mimeHdrLine::parseSeparator (';', aCStr);
00091       if (skip > 0)
00092       {
00093         int cut = 0;
00094         if (skip >= 2)
00095         {
00096           if (aCStr[skip - 1] == '\r')
00097             cut++;
00098           if (aCStr[skip - 1] == '\n')
00099             cut++;
00100           if (aCStr[skip - 2] == '\r')
00101             cut++;
00102           if (aCStr[skip - 1] == ';')
00103             cut++;
00104         }
00105         QCString mimeValue = QCString (aCStr, skip - cut + 1);  // cutting of one because of 0x00
00106 
00107 
00108         if (!qstricmp (addLine->getLabel (), "Content-Disposition"))
00109         {
00110           aList = &dispositionList;
00111           _contentDisposition = mimeValue;
00112         }
00113         else if (!qstricmp (addLine->getLabel (), "Content-Type"))
00114         {
00115           aList = &typeList;
00116           contentType = mimeValue;
00117         }
00118         else
00119           if (!qstricmp (addLine->getLabel (), "Content-Transfer-Encoding"))
00120         {
00121           contentEncoding = mimeValue;
00122         }
00123         else if (!qstricmp (addLine->getLabel (), "Content-ID"))
00124         {
00125           contentID = mimeValue;
00126         }
00127         else if (!qstricmp (addLine->getLabel (), "Content-Description"))
00128         {
00129           _contentDescription = mimeValue;
00130         }
00131         else if (!qstricmp (addLine->getLabel (), "Content-MD5"))
00132         {
00133           contentMD5 = mimeValue;
00134         }
00135         else if (!qstricmp (addLine->getLabel (), "Content-Length"))
00136         {
00137           contentLength = mimeValue.toULong ();
00138         }
00139         else
00140         {
00141           additionalHdrLines.append (addLine);
00142         }
00143 //        cout << addLine->getLabel().data() << ": '" << mimeValue.data() << "'" << endl;
00144 
00145         aCStr += skip;
00146         while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
00147         {
00148           if (skip > 0)
00149           {
00150             addParameter (QCString (aCStr, skip).simplifyWhiteSpace(), aList);
00151 //            cout << "-- '" << aParm.data() << "'" << endl;
00152             mimeValue = QCString (addLine->getValue ().data (), skip);
00153             aCStr += skip;
00154           }
00155           else
00156             break;
00157         }
00158       }
00159     }
00160   }
00161 }
00162 
00163 void
00164 mimeHeader::addParameter (const QCString& aParameter, QDict < QString > *aList)
00165 {
00166   if ( !aList )
00167     return;
00168 
00169   QString *aValue;
00170   QCString aLabel;
00171   int pos = aParameter.find ('=');
00172 //  cout << aParameter.left(pos).data();
00173   aValue = new QString ();
00174   aValue->setLatin1 (aParameter.right (aParameter.length () - pos - 1));
00175   aLabel = aParameter.left (pos);
00176   if ((*aValue)[0] == '"')
00177     *aValue = aValue->mid (1, aValue->length () - 2);
00178 
00179   aList->insert (aLabel, aValue);
00180 //  cout << "=" << aValue->data() << endl;
00181 }
00182 
00183 QString
00184 mimeHeader::getDispositionParm (const QCString& aStr)
00185 {
00186   return getParameter (aStr, &dispositionList);
00187 }
00188 
00189 QString
00190 mimeHeader::getTypeParm (const QCString& aStr)
00191 {
00192   return getParameter (aStr, &typeList);
00193 }
00194 
00195 void
00196 mimeHeader::setDispositionParm (const QCString& aLabel, const QString& aValue)
00197 {
00198   setParameter (aLabel, aValue, &dispositionList);
00199   return;
00200 }
00201 
00202 void
00203 mimeHeader::setTypeParm (const QCString& aLabel, const QString& aValue)
00204 {
00205   setParameter (aLabel, aValue, &typeList);
00206 }
00207 
00208 QDictIterator < QString > mimeHeader::getDispositionIterator ()
00209 {
00210   return QDictIterator < QString > (dispositionList);
00211 }
00212 
00213 QDictIterator < QString > mimeHeader::getTypeIterator ()
00214 {
00215   return QDictIterator < QString > (typeList);
00216 }
00217 
00218 QPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator ()
00219 {
00220   return QPtrListIterator < mimeHdrLine > (originalHdrLines);
00221 }
00222 
00223 QPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator ()
00224 {
00225   return QPtrListIterator < mimeHdrLine > (additionalHdrLines);
00226 }
00227 
00228 void
00229 mimeHeader::outputHeader (mimeIO & useIO)
00230 {
00231   if (!getDisposition ().isEmpty ())
00232   {
00233     useIO.outputMimeLine (QCString ("Content-Disposition: ")
00234                           + getDisposition ()
00235                           + outputParameter (&dispositionList));
00236   }
00237 
00238   if (!getType ().isEmpty ())
00239   {
00240     useIO.outputMimeLine (QCString ("Content-Type: ")
00241                           + getType () + outputParameter (&typeList));
00242   }
00243   if (!getDescription ().isEmpty ())
00244     useIO.outputMimeLine (QCString ("Content-Description: ") +
00245                           getDescription ());
00246   if (!getID ().isEmpty ())
00247     useIO.outputMimeLine (QCString ("Content-ID: ") + getID ());
00248   if (!getMD5 ().isEmpty ())
00249     useIO.outputMimeLine (QCString ("Content-MD5: ") + getMD5 ());
00250   if (!getEncoding ().isEmpty ())
00251     useIO.outputMimeLine (QCString ("Content-Transfer-Encoding: ") +
00252                           getEncoding ());
00253 
00254   QPtrListIterator < mimeHdrLine > ait = getAdditionalIterator ();
00255   while (ait.current ())
00256   {
00257     useIO.outputMimeLine (ait.current ()->getLabel () + ": " +
00258                           ait.current ()->getValue ());
00259     ++ait;
00260   }
00261   useIO.outputMimeLine (QCString (""));
00262 }
00263 
00264 QString
00265 mimeHeader::getParameter (const QCString& aStr, QDict < QString > *aDict)
00266 {
00267   QString retVal, *found;
00268   if (aDict)
00269   {
00270     //see if it is a normal parameter
00271     found = aDict->find (aStr);
00272     if (!found)
00273     {
00274       //might be a continuated or encoded parameter
00275       found = aDict->find (aStr + "*");
00276       if (!found)
00277       {
00278         //continuated parameter
00279         QString decoded, encoded;
00280         int part = 0;
00281 
00282         do
00283         {
00284           QCString search;
00285           search.setNum (part);
00286           search = aStr + "*" + search;
00287           found = aDict->find (search);
00288           if (!found)
00289           {
00290             found = aDict->find (search + "*");
00291             if (found)
00292               encoded += rfcDecoder::encodeRFC2231String (*found);
00293           }
00294           else
00295           {
00296             encoded += *found;
00297           }
00298           part++;
00299         }
00300         while (found);
00301         if (encoded.find ('\'') >= 0)
00302         {
00303           retVal = rfcDecoder::decodeRFC2231String (encoded.local8Bit ());
00304         }
00305         else
00306         {
00307           retVal =
00308             rfcDecoder::decodeRFC2231String (QCString ("''") +
00309                                              encoded.local8Bit ());
00310         }
00311       }
00312       else
00313       {
00314         //simple encoded parameter
00315         retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ());
00316       }
00317     }
00318     else
00319     {
00320       retVal = *found;
00321     }
00322   }
00323   return retVal;
00324 }
00325 
00326 void
00327 mimeHeader::setParameter (const QCString& aLabel, const QString& aValue,
00328                           QDict < QString > *aDict)
00329 {
00330   bool encoded = true;
00331   uint vlen, llen;
00332   QString val = aValue;
00333 
00334   if (aDict)
00335   {
00336 
00337     //see if it needs to get encoded
00338     if (encoded && aLabel.find ('*') == -1)
00339     {
00340       val = rfcDecoder::encodeRFC2231String (aValue);
00341     }
00342     //see if it needs to be truncated
00343     vlen = val.length();
00344     llen = aLabel.length();
00345     if (vlen + llen + 4 > 80 && llen < 80 - 8 )
00346     {
00347       int limit = 80 - 8 - (int)llen;
00348       int i = 0;
00349       QString shortValue;
00350       QCString shortLabel;
00351 
00352       while (!val.isEmpty ())
00353       {
00354         //don't truncate the encoded chars
00355         int offset = 0;
00356         if (limit > int(vlen))
00357           limit = vlen;
00358         offset = val.findRev ('%', limit);
00359         if (offset == limit - 1 || offset == limit - 2)
00360         {
00361 //          cout << "offset " << offset << "-" << limit << "=" << limit-offset << endl;
00362           offset = limit - offset;
00363         }
00364         else
00365           offset = 0;
00366         shortValue = val.left (limit - offset);
00367         shortLabel.setNum (i);
00368         shortLabel = aLabel + "*" + shortLabel;
00369         val = val.right (vlen - limit + offset);
00370         vlen = vlen - limit + offset;
00371         if (encoded)
00372         {
00373           if (i == 0)
00374           {
00375             shortValue = "''" + shortValue;
00376           }
00377           shortLabel += "*";
00378         }
00379 //        cout << shortLabel << "-" << shortValue << endl;
00380         aDict->insert (shortLabel, new QString (shortValue));
00381         i++;
00382       }
00383     }
00384     else
00385     {
00386       aDict->insert (aLabel, new QString (val));
00387     }
00388   }
00389 }
00390 
00391 QCString
00392 mimeHeader::outputParameter (QDict < QString > *aDict)
00393 {
00394   QCString retVal;
00395   if (aDict)
00396   {
00397     QDictIterator < QString > it (*aDict);
00398     while (it.current ())
00399     {
00400       retVal += (";\n\t" + it.currentKey () + "=").latin1 ();
00401       if (it.current ()->find (' ') > 0 || it.current ()->find (';') > 0)
00402       {
00403         retVal += '"' + it.current ()->utf8 () + '"';
00404       }
00405       else
00406       {
00407         retVal += it.current ()->utf8 ();
00408       }
00409       // << it.current()->utf8() << "'";
00410       ++it;
00411     }
00412     retVal += "\n";
00413   }
00414   return retVal;
00415 }
00416 
00417 void
00418 mimeHeader::outputPart (mimeIO & useIO)
00419 {
00420   QPtrListIterator < mimeHeader > nestedParts = getNestedIterator ();
00421   QCString boundary;
00422   if (!getTypeParm ("boundary").isEmpty ())
00423     boundary = getTypeParm ("boundary").latin1 ();
00424 
00425   outputHeader (useIO);
00426   if (!getPreBody ().isEmpty ())
00427     useIO.outputMimeLine (getPreBody ());
00428   if (getNestedMessage ())
00429     getNestedMessage ()->outputPart (useIO);
00430   while (nestedParts.current ())
00431   {
00432     if (!boundary.isEmpty ())
00433       useIO.outputMimeLine ("--" + boundary);
00434     nestedParts.current ()->outputPart (useIO);
00435     ++nestedParts;
00436   }
00437   if (!boundary.isEmpty ())
00438     useIO.outputMimeLine ("--" + boundary + "--");
00439   if (!getPostBody ().isEmpty ())
00440     useIO.outputMimeLine (getPostBody ());
00441 }
00442 
00443 int
00444 mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
00445 {
00446   int retVal = 0;
00447   bool mbox = false;
00448   QCString preNested, postNested;
00449   mbox = parseHeader (useIO);
00450 
00451   kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl;
00452   if (!qstrnicmp (getType (), "Multipart", 9))
00453   {
00454     retVal = parseBody (useIO, preNested, getTypeParm ("boundary"));  //this is a message in mime format stuff
00455     setPreBody (preNested);
00456     int localRetVal;
00457     do
00458     {
00459       mimeHeader *aHeader = new mimeHeader;
00460 
00461       // set default type for multipart/digest
00462       if (!qstrnicmp (getType (), "Multipart/Digest", 16))
00463         aHeader->setType ("Message/RFC822");
00464 
00465       localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary"));
00466       addNestedPart (aHeader);
00467     }
00468     while (localRetVal);        //get nested stuff
00469   }
00470   if (!qstrnicmp (getType (), "Message/RFC822", 14))
00471   {
00472     mailHeader *msgHeader = new mailHeader;
00473     retVal = msgHeader->parsePart (useIO, boundary);
00474     setNestedMessage (msgHeader);
00475   }
00476   else
00477   {
00478     retVal = parseBody (useIO, postNested, boundary, mbox); //just a simple part remaining
00479     setPostBody (postNested);
00480   }
00481   return retVal;
00482 }
00483 
00484 int
00485 mimeHeader::parseBody (mimeIO & useIO, QCString & messageBody,
00486                        const QString& boundary, bool mbox)
00487 {
00488   QCString inputStr;
00489   QCString buffer;
00490   QString partBoundary;
00491   QString partEnd;
00492   int retVal = 0;               //default is last part
00493 
00494   if (!boundary.isEmpty ())
00495   {
00496     partBoundary = QString ("--") + boundary;
00497     partEnd = QString ("--") + boundary + "--";
00498   }
00499 
00500   while (useIO.inputLine (inputStr))
00501   {
00502     //check for the end of all parts
00503     if (!partEnd.isEmpty ()
00504         && !qstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1))
00505     {
00506       retVal = 0;               //end of these parts
00507       break;
00508     }
00509     else if (!partBoundary.isEmpty ()
00510              && !qstrnicmp (inputStr, partBoundary.latin1 (),
00511                             partBoundary.length () - 1))
00512     {
00513       retVal = 1;               //continue with next part
00514       break;
00515     }
00516     else if (mbox && inputStr.find ("From ") == 0)
00517     {
00518       retVal = 0;               // end of mbox
00519       break;
00520     }
00521     buffer += inputStr;
00522     if (buffer.length () > 16384)
00523     {
00524       messageBody += buffer;
00525       buffer = "";
00526     }
00527   }
00528 
00529   messageBody += buffer;
00530   return retVal;
00531 }
00532 
00533 bool
00534 mimeHeader::parseHeader (mimeIO & useIO)
00535 {
00536   bool mbox = false;
00537   bool first = true;
00538   mimeHdrLine my_line;
00539   QCString inputStr;
00540 
00541   kdDebug(7116) << "mimeHeader::parseHeader - starting parsing" << endl;
00542   while (useIO.inputLine (inputStr))
00543   {
00544     int appended;
00545     if (inputStr.find ("From ") != 0 || !first)
00546     {
00547       first = false;
00548       appended = my_line.appendStr (inputStr);
00549       if (!appended)
00550       {
00551         addHdrLine (&my_line);
00552         appended = my_line.setStr (inputStr);
00553       }
00554       if (appended <= 0)
00555         break;
00556     }
00557     else
00558     {
00559       mbox = true;
00560       first = false;
00561     }
00562     inputStr = (const char *) NULL;
00563   }
00564 
00565   kdDebug(7116) << "mimeHeader::parseHeader - finished parsing" << endl;
00566   return mbox;
00567 }
00568 
00569 mimeHeader *
00570 mimeHeader::bodyPart (const QString & _str)
00571 {
00572   // see if it is nested a little deeper
00573   int pt = _str.find('.');
00574   if (pt != -1)
00575   {
00576     QString tempStr = _str;
00577     mimeHeader *tempPart;
00578 
00579     tempStr = _str.right (_str.length () - pt - 1);
00580     if (nestedMessage)
00581     {
00582       kdDebug(7116) << "mimeHeader::bodyPart - recursing message" << endl;
00583       tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
00584     }
00585     else
00586     {
00587       kdDebug(7116) << "mimeHeader::bodyPart - recursing mixed" << endl;
00588       tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
00589     }
00590     if (tempPart)
00591       tempPart = tempPart->bodyPart (tempStr);
00592     return tempPart;
00593   }
00594 
00595   kdDebug(7116) << "mimeHeader::bodyPart - returning part " << _str << endl;
00596   // or pick just the plain part
00597   if (nestedMessage)
00598   {
00599     kdDebug(7116) << "mimeHeader::bodyPart - message" << endl;
00600     return nestedMessage->nestedParts.at (_str.toULong () - 1);
00601   }
00602   kdDebug(7116) << "mimeHeader::bodyPart - mixed" << endl;
00603   return nestedParts.at (_str.toULong () - 1);
00604 }
00605 
00606 void mimeHeader::serialize(QDataStream& stream)
00607 {
00608   int nestedcount = nestedParts.count();
00609   if (nestedParts.isEmpty() && nestedMessage)
00610     nestedcount = 1;
00611   stream << nestedcount << contentType << QString (getTypeParm ("name")) << _contentDescription
00612     << _contentDisposition << contentEncoding << contentLength << partSpecifier;
00613   // serialize nested message
00614   if (nestedMessage)
00615     nestedMessage->serialize(stream);
00616 
00617   // serialize nested parts
00618   if (!nestedParts.isEmpty())
00619   {
00620     QPtrListIterator < mimeHeader > it(nestedParts);
00621     mimeHeader* part;
00622     while ( (part = it.current()) != 0 )
00623     {
00624       ++it;
00625       part->serialize(stream);
00626     }
00627   }
00628 }
00629 
00630 #ifdef KMAIL_COMPATIBLE
00631 // compatibility subroutines
00632 QString
00633 mimeHeader::bodyDecoded ()
00634 {
00635   kdDebug(7116) << "mimeHeader::bodyDecoded" << endl;
00636   QByteArray temp;
00637 
00638   temp = bodyDecodedBinary ();
00639   return QString::fromLatin1 (temp.data (), temp.count ());
00640 }
00641 
00642 QByteArray
00643 mimeHeader::bodyDecodedBinary ()
00644 {
00645   QByteArray retVal;
00646 
00647   if (contentEncoding.find ("quoted-printable", 0, false) == 0)
00648     retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
00649   else if (contentEncoding.find ("base64", 0, false) == 0)
00650     KCodecs::base64Decode(postMultipartBody, retVal);
00651   else retVal = postMultipartBody;
00652 
00653   kdDebug(7116) << "mimeHeader::bodyDecodedBinary - size is " << retVal.size () << endl;
00654   return retVal;
00655 }
00656 
00657 void
00658 mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
00659 {
00660   setBodyEncoded (_arr);
00661 }
00662 
00663 void
00664 mimeHeader::setBodyEncoded (const QByteArray & _arr)
00665 {
00666   QByteArray setVal;
00667 
00668   kdDebug(7116) << "mimeHeader::setBodyEncoded - in size " << _arr.size () << endl;
00669   if (contentEncoding.find ("quoted-printable", 0, false) == 0)
00670     setVal = KCodecs::quotedPrintableEncode(_arr);
00671   else if (contentEncoding.find ("base64", 0, false) == 0)
00672     KCodecs::base64Encode(_arr, setVal);
00673   else
00674     setVal.duplicate (_arr);
00675   kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << setVal.size () << endl;
00676 
00677   postMultipartBody.duplicate (setVal);
00678   kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << postMultipartBody.size () << endl;
00679 }
00680 
00681 QString
00682 mimeHeader::iconName ()
00683 {
00684   QString fileName;
00685 
00686   // FIXME: bug?  Why throw away this data?
00687   fileName =
00688     KMimeType::mimeType (contentType.lower ())->icon (QString::null, false);
00689   fileName =
00690     KGlobal::instance ()->iconLoader ()->iconPath (fileName, KIcon::Desktop);
00691 //  if (fileName.isEmpty())
00692 //    fileName = KGlobal::instance()->iconLoader()->iconPath( "unknown", KIcon::Desktop );
00693   return fileName;
00694 }
00695 
00696 void
00697 mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
00698 {
00699 //  if(nestedMessage && destroy) delete nestedMessage;
00700   nestedMessage = inPart;
00701 }
00702 
00703 QString
00704 mimeHeader::headerAsString ()
00705 {
00706   mimeIOQString myIO;
00707 
00708   outputHeader (myIO);
00709   return myIO.getString ();
00710 }
00711 
00712 QString
00713 mimeHeader::magicSetType (bool aAutoDecode)
00714 {
00715   QString mimetype;
00716   QByteArray body;
00717   KMimeMagicResult *result;
00718 
00719   KMimeMagic::self ()->setFollowLinks (TRUE); // is it necessary ?
00720 
00721   if (aAutoDecode)
00722     body = bodyDecodedBinary ();
00723   else
00724     body = postMultipartBody;
00725 
00726   result = KMimeMagic::self ()->findBufferType (body);
00727   mimetype = result->mimeType ();
00728   contentType = mimetype;
00729   return mimetype;
00730 }
00731 #endif
KDE Home | KDE Accessibility Home | Description of Access Keys