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 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
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
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
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
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"));
00455 setPreBody (preNested);
00456 int localRetVal;
00457 do
00458 {
00459 mimeHeader *aHeader = new mimeHeader;
00460
00461
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);
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);
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;
00493
00494 if (!boundary.isEmpty ())
00495 {
00496 partBoundary = QString ("--") + boundary;
00497 partEnd = QString ("--") + boundary + "--";
00498 }
00499
00500 while (useIO.inputLine (inputStr))
00501 {
00502
00503 if (!partEnd.isEmpty ()
00504 && !qstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1))
00505 {
00506 retVal = 0;
00507 break;
00508 }
00509 else if (!partBoundary.isEmpty ()
00510 && !qstrnicmp (inputStr, partBoundary.latin1 (),
00511 partBoundary.length () - 1))
00512 {
00513 retVal = 1;
00514 break;
00515 }
00516 else if (mbox && inputStr.find ("From ") == 0)
00517 {
00518 retVal = 0;
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
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
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
00614 if (nestedMessage)
00615 nestedMessage->serialize(stream);
00616
00617
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
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
00687 fileName =
00688 KMimeType::mimeType (contentType.lower ())->icon (QString::null, false);
00689 fileName =
00690 KGlobal::instance ()->iconLoader ()->iconPath (fileName, KIcon::Desktop);
00691
00692
00693 return fileName;
00694 }
00695
00696 void
00697 mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
00698 {
00699
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);
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