00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
00038 originalHdrLines.setAutoDelete (true);
00039 additionalHdrLines.setAutoDelete (false);
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
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
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);
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
00144
00145 aCStr += skip;
00146 while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
00147 {
00148 if (skip > 0)
00149 {
00150 addParameter (QCString (aCStr, skip).simplifyWhiteSpace(), aList);
00151
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
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
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
00271 found = aDict->find (aStr);
00272 if (!found)
00273 {
00274
00275 found = aDict->find (aStr + "*");
00276 if (!found)
00277 {
00278
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
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
00338 if (encoded && aLabel.find ('*') == -1)
00339 {
00340 val = rfcDecoder::encodeRFC2231String (aValue);
00341 }
00342
00343
00344 vlen = val.length();
00345 llen = aLabel.length();
00346 if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 )
00347 {
00348 const int limit = 80 - 8 - 2 - (int)llen;
00349
00350
00351
00352 int i = 0;
00353 QString shortValue;
00354 QCString shortLabel;
00355
00356 while (!val.isEmpty ())
00357 {
00358 int partLen;
00359 if ( limit >= int(vlen) ) {
00360
00361 partLen = vlen;
00362 }
00363 else {
00364 partLen = limit;
00365
00366 if ( val[partLen-1] == '%' ) {
00367 partLen += 2;
00368 }
00369 else if ( partLen > 1 && val[partLen-2] == '%' ) {
00370 partLen += 1;
00371 }
00372
00373
00374 if ( partLen > int(vlen) ) {
00375 partLen = vlen;
00376 }
00377 }
00378 shortValue = val.left( partLen );
00379 shortLabel.setNum (i);
00380 shortLabel = aLabel + "*" + shortLabel;
00381 val = val.right( vlen - partLen );
00382 vlen = vlen - partLen;
00383 if (encoded)
00384 {
00385 if (i == 0)
00386 {
00387 shortValue = "''" + shortValue;
00388 }
00389 shortLabel += "*";
00390 }
00391
00392
00393
00394 aDict->insert (shortLabel, new QString (shortValue));
00395 i++;
00396 }
00397 }
00398 else
00399 {
00400 aDict->insert (aLabel, new QString (val));
00401 }
00402 }
00403 }
00404
00405 QCString
00406 mimeHeader::outputParameter (QDict < QString > *aDict)
00407 {
00408 QCString retVal;
00409 if (aDict)
00410 {
00411 QDictIterator < QString > it (*aDict);
00412 while (it.current ())
00413 {
00414 retVal += (";\n\t" + it.currentKey () + "=").latin1 ();
00415 if (it.current ()->find (' ') > 0 || it.current ()->find (';') > 0)
00416 {
00417 retVal += '"' + it.current ()->utf8 () + '"';
00418 }
00419 else
00420 {
00421 retVal += it.current ()->utf8 ();
00422 }
00423
00424 ++it;
00425 }
00426 retVal += "\n";
00427 }
00428 return retVal;
00429 }
00430
00431 void
00432 mimeHeader::outputPart (mimeIO & useIO)
00433 {
00434 QPtrListIterator < mimeHeader > nestedParts = getNestedIterator ();
00435 QCString boundary;
00436 if (!getTypeParm ("boundary").isEmpty ())
00437 boundary = getTypeParm ("boundary").latin1 ();
00438
00439 outputHeader (useIO);
00440 if (!getPreBody ().isEmpty ())
00441 useIO.outputMimeLine (getPreBody ());
00442 if (getNestedMessage ())
00443 getNestedMessage ()->outputPart (useIO);
00444 while (nestedParts.current ())
00445 {
00446 if (!boundary.isEmpty ())
00447 useIO.outputMimeLine ("--" + boundary);
00448 nestedParts.current ()->outputPart (useIO);
00449 ++nestedParts;
00450 }
00451 if (!boundary.isEmpty ())
00452 useIO.outputMimeLine ("--" + boundary + "--");
00453 if (!getPostBody ().isEmpty ())
00454 useIO.outputMimeLine (getPostBody ());
00455 }
00456
00457 int
00458 mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
00459 {
00460 int retVal = 0;
00461 bool mbox = false;
00462 QCString preNested, postNested;
00463 mbox = parseHeader (useIO);
00464
00465 kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl;
00466 if (!qstrnicmp (getType (), "Multipart", 9))
00467 {
00468 retVal = parseBody (useIO, preNested, getTypeParm ("boundary"));
00469 setPreBody (preNested);
00470 int localRetVal;
00471 do
00472 {
00473 mimeHeader *aHeader = new mimeHeader;
00474
00475
00476 if (!qstrnicmp (getType (), "Multipart/Digest", 16))
00477 aHeader->setType ("Message/RFC822");
00478
00479 localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary"));
00480 addNestedPart (aHeader);
00481 }
00482 while (localRetVal);
00483 }
00484 if (!qstrnicmp (getType (), "Message/RFC822", 14))
00485 {
00486 mailHeader *msgHeader = new mailHeader;
00487 retVal = msgHeader->parsePart (useIO, boundary);
00488 setNestedMessage (msgHeader);
00489 }
00490 else
00491 {
00492 retVal = parseBody (useIO, postNested, boundary, mbox);
00493 setPostBody (postNested);
00494 }
00495 return retVal;
00496 }
00497
00498 int
00499 mimeHeader::parseBody (mimeIO & useIO, QCString & messageBody,
00500 const QString& boundary, bool mbox)
00501 {
00502 QCString inputStr;
00503 QCString buffer;
00504 QString partBoundary;
00505 QString partEnd;
00506 int retVal = 0;
00507
00508 if (!boundary.isEmpty ())
00509 {
00510 partBoundary = QString ("--") + boundary;
00511 partEnd = QString ("--") + boundary + "--";
00512 }
00513
00514 while (useIO.inputLine (inputStr))
00515 {
00516
00517 if (!partEnd.isEmpty ()
00518 && !qstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1))
00519 {
00520 retVal = 0;
00521 break;
00522 }
00523 else if (!partBoundary.isEmpty ()
00524 && !qstrnicmp (inputStr, partBoundary.latin1 (),
00525 partBoundary.length () - 1))
00526 {
00527 retVal = 1;
00528 break;
00529 }
00530 else if (mbox && inputStr.find ("From ") == 0)
00531 {
00532 retVal = 0;
00533 break;
00534 }
00535 buffer += inputStr;
00536 if (buffer.length () > 16384)
00537 {
00538 messageBody += buffer;
00539 buffer = "";
00540 }
00541 }
00542
00543 messageBody += buffer;
00544 return retVal;
00545 }
00546
00547 bool
00548 mimeHeader::parseHeader (mimeIO & useIO)
00549 {
00550 bool mbox = false;
00551 bool first = true;
00552 mimeHdrLine my_line;
00553 QCString inputStr;
00554
00555 kdDebug(7116) << "mimeHeader::parseHeader - starting parsing" << endl;
00556 while (useIO.inputLine (inputStr))
00557 {
00558 int appended;
00559 if (inputStr.find ("From ") != 0 || !first)
00560 {
00561 first = false;
00562 appended = my_line.appendStr (inputStr);
00563 if (!appended)
00564 {
00565 addHdrLine (&my_line);
00566 appended = my_line.setStr (inputStr);
00567 }
00568 if (appended <= 0)
00569 break;
00570 }
00571 else
00572 {
00573 mbox = true;
00574 first = false;
00575 }
00576 inputStr = (const char *) NULL;
00577 }
00578
00579 kdDebug(7116) << "mimeHeader::parseHeader - finished parsing" << endl;
00580 return mbox;
00581 }
00582
00583 mimeHeader *
00584 mimeHeader::bodyPart (const QString & _str)
00585 {
00586
00587 int pt = _str.find('.');
00588 if (pt != -1)
00589 {
00590 QString tempStr = _str;
00591 mimeHeader *tempPart;
00592
00593 tempStr = _str.right (_str.length () - pt - 1);
00594 if (nestedMessage)
00595 {
00596 kdDebug(7116) << "mimeHeader::bodyPart - recursing message" << endl;
00597 tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
00598 }
00599 else
00600 {
00601 kdDebug(7116) << "mimeHeader::bodyPart - recursing mixed" << endl;
00602 tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
00603 }
00604 if (tempPart)
00605 tempPart = tempPart->bodyPart (tempStr);
00606 return tempPart;
00607 }
00608
00609 kdDebug(7116) << "mimeHeader::bodyPart - returning part " << _str << endl;
00610
00611 if (nestedMessage)
00612 {
00613 kdDebug(7116) << "mimeHeader::bodyPart - message" << endl;
00614 return nestedMessage->nestedParts.at (_str.toULong () - 1);
00615 }
00616 kdDebug(7116) << "mimeHeader::bodyPart - mixed" << endl;
00617 return nestedParts.at (_str.toULong () - 1);
00618 }
00619
00620 void mimeHeader::serialize(QDataStream& stream)
00621 {
00622 int nestedcount = nestedParts.count();
00623 if (nestedParts.isEmpty() && nestedMessage)
00624 nestedcount = 1;
00625 stream << nestedcount << contentType << QString (getTypeParm ("name")) << _contentDescription
00626 << _contentDisposition << contentEncoding << contentLength << partSpecifier;
00627
00628 if (nestedMessage)
00629 nestedMessage->serialize(stream);
00630
00631
00632 if (!nestedParts.isEmpty())
00633 {
00634 QPtrListIterator < mimeHeader > it(nestedParts);
00635 mimeHeader* part;
00636 while ( (part = it.current()) != 0 )
00637 {
00638 ++it;
00639 part->serialize(stream);
00640 }
00641 }
00642 }
00643
00644 #ifdef KMAIL_COMPATIBLE
00645
00646 QString
00647 mimeHeader::bodyDecoded ()
00648 {
00649 kdDebug(7116) << "mimeHeader::bodyDecoded" << endl;
00650 QByteArray temp;
00651
00652 temp = bodyDecodedBinary ();
00653 return QString::fromLatin1 (temp.data (), temp.count ());
00654 }
00655
00656 QByteArray
00657 mimeHeader::bodyDecodedBinary ()
00658 {
00659 QByteArray retVal;
00660
00661 if (contentEncoding.find ("quoted-printable", 0, false) == 0)
00662 retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
00663 else if (contentEncoding.find ("base64", 0, false) == 0)
00664 KCodecs::base64Decode(postMultipartBody, retVal);
00665 else retVal = postMultipartBody;
00666
00667 kdDebug(7116) << "mimeHeader::bodyDecodedBinary - size is " << retVal.size () << endl;
00668 return retVal;
00669 }
00670
00671 void
00672 mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
00673 {
00674 setBodyEncoded (_arr);
00675 }
00676
00677 void
00678 mimeHeader::setBodyEncoded (const QByteArray & _arr)
00679 {
00680 QByteArray setVal;
00681
00682 kdDebug(7116) << "mimeHeader::setBodyEncoded - in size " << _arr.size () << endl;
00683 if (contentEncoding.find ("quoted-printable", 0, false) == 0)
00684 setVal = KCodecs::quotedPrintableEncode(_arr);
00685 else if (contentEncoding.find ("base64", 0, false) == 0)
00686 KCodecs::base64Encode(_arr, setVal);
00687 else
00688 setVal.duplicate (_arr);
00689 kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << setVal.size () << endl;
00690
00691 postMultipartBody.duplicate (setVal);
00692 kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << postMultipartBody.size () << endl;
00693 }
00694
00695 QString
00696 mimeHeader::iconName ()
00697 {
00698 QString fileName;
00699
00700
00701 fileName =
00702 KMimeType::mimeType (contentType.lower ())->icon (QString::null, false);
00703 fileName =
00704 KGlobal::instance ()->iconLoader ()->iconPath (fileName, KIcon::Desktop);
00705
00706
00707 return fileName;
00708 }
00709
00710 void
00711 mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
00712 {
00713
00714 nestedMessage = inPart;
00715 }
00716
00717 QString
00718 mimeHeader::headerAsString ()
00719 {
00720 mimeIOQString myIO;
00721
00722 outputHeader (myIO);
00723 return myIO.getString ();
00724 }
00725
00726 QString
00727 mimeHeader::magicSetType (bool aAutoDecode)
00728 {
00729 QString mimetype;
00730 QByteArray body;
00731 KMimeMagicResult *result;
00732
00733 KMimeMagic::self ()->setFollowLinks (TRUE);
00734
00735 if (aAutoDecode)
00736 body = bodyDecodedBinary ();
00737 else
00738 body = postMultipartBody;
00739
00740 result = KMimeMagic::self ()->findBufferType (body);
00741 mimetype = result->mimeType ();
00742 contentType = mimetype;
00743 return mimetype;
00744 }
00745 #endif