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
00022 #include <QRegExp>
00023
00024
00025 #include <kglobal.h>
00026 #include <kcomponentdata.h>
00027 #include <kiconloader.h>
00028 #include <kmimetype.h>
00029 #include <kcodecs.h>
00030 #include <kdebug.h>
00031
00032 #include <kimap/rfccodecs.h>
00033 using namespace KIMAP;
00034
00035 mimeHeader::mimeHeader ()
00036 : typeList (), dispositionList (),
00037 _contentType("application/octet-stream"),
00038 _contentDisposition(), _contentDescription()
00039 {
00040
00041 nestedMessage = NULL;
00042 contentLength = 0;
00043 }
00044
00045 mimeHeader::~mimeHeader ()
00046 {
00047 }
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 void
00071 mimeHeader::addHdrLine (mimeHdrLine * aHdrLine)
00072 {
00073 mimeHdrLine *addLine = new mimeHdrLine (aHdrLine);
00074 if (addLine)
00075 {
00076 originalHdrLines.append (addLine);
00077 if (qstrnicmp (addLine->getLabel (), "Content-", 8))
00078 {
00079 additionalHdrLines.append (addLine);
00080 }
00081 else
00082 {
00083 int skip;
00084 const char *aCStr = addLine->getValue ().data ();
00085 QHash < QString, QString > aList;
00086
00087 skip = mimeHdrLine::parseSeparator (';', aCStr);
00088 if (skip > 0)
00089 {
00090 int cut = 0;
00091 if (skip >= 2)
00092 {
00093 if (aCStr[skip - 1] == '\r')
00094 cut++;
00095 if (aCStr[skip - 1] == '\n')
00096 cut++;
00097 if (aCStr[skip - 2] == '\r')
00098 cut++;
00099 if (aCStr[skip - 1] == ';')
00100 cut++;
00101 }
00102 QByteArray mimeValue(aCStr, skip - cut);
00103
00104
00105 if (!qstricmp (addLine->getLabel (), "Content-Disposition"))
00106 {
00107 aList = dispositionList;
00108 setDisposition( mimeValue );
00109 }
00110 else if (!qstricmp (addLine->getLabel (), "Content-Type"))
00111 {
00112 aList = typeList;
00113 setType( mimeValue );
00114 }
00115 else
00116 if (!qstricmp (addLine->getLabel (), "Content-Transfer-Encoding"))
00117 {
00118 setEncoding( mimeValue );
00119 }
00120 else if (!qstricmp (addLine->getLabel (), "Content-ID"))
00121 {
00122 setID( mimeValue );
00123 }
00124 else if (!qstricmp (addLine->getLabel (), "Content-Description"))
00125 {
00126 setDescription( mimeValue );
00127 }
00128 else if (!qstricmp (addLine->getLabel (), "Content-MD5"))
00129 {
00130 setMD5( mimeValue );
00131 }
00132 else if (!qstricmp (addLine->getLabel (), "Content-Length"))
00133 {
00134 contentLength = mimeValue.toUInt ();
00135 }
00136 else
00137 {
00138 additionalHdrLines.append (addLine);
00139 }
00140
00141
00142 aCStr += skip;
00143 while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
00144 {
00145 if (skip > 0)
00146 {
00147 addParameter (QByteArray (aCStr, skip).simplified(), aList);
00148
00149 mimeValue = QByteArray (addLine->getValue ().data (), skip);
00150 aCStr += skip;
00151 }
00152 else
00153 break;
00154 }
00155 }
00156 }
00157 }
00158 }
00159
00160 void
00161 mimeHeader::addParameter (const QByteArray& aParameter, QHash < QString, QString > &aList)
00162 {
00163 QString aValue;
00164 QByteArray aLabel;
00165 int pos = aParameter.indexOf ('=');
00166
00167 aValue = QString::fromLatin1 (aParameter.right (aParameter.length () - pos - 1));
00168 aLabel = aParameter.left (pos);
00169 if (aValue[0] == '"')
00170 aValue = aValue.mid (1, aValue.length () - 2);
00171
00172 aList.insert (aLabel.toLower(), aValue);
00173
00174 }
00175
00176 QString
00177 mimeHeader::getDispositionParm (const QByteArray& aStr)
00178 {
00179 return getParameter (aStr, dispositionList);
00180 }
00181
00182 QString
00183 mimeHeader::getTypeParm (const QByteArray& aStr)
00184 {
00185 return getParameter (aStr, typeList);
00186 }
00187
00188 void
00189 mimeHeader::setDispositionParm (const QByteArray& aLabel, const QString& aValue)
00190 {
00191 setParameter (aLabel, aValue, dispositionList);
00192 return;
00193 }
00194
00195 void
00196 mimeHeader::setTypeParm (const QByteArray& aLabel, const QString& aValue)
00197 {
00198 setParameter (aLabel, aValue, typeList);
00199 }
00200
00201 QHashIterator < QString, QString > mimeHeader::getDispositionIterator ()
00202 {
00203 return QHashIterator < QString, QString > (dispositionList);
00204 }
00205
00206 QHashIterator < QString, QString > mimeHeader::getTypeIterator ()
00207 {
00208 return QHashIterator < QString, QString > (typeList);
00209 }
00210
00211 QListIterator < mimeHdrLine *> mimeHeader::getOriginalIterator ()
00212 {
00213 return QListIterator < mimeHdrLine *> (originalHdrLines);
00214 }
00215
00216 QListIterator < mimeHdrLine *> mimeHeader::getAdditionalIterator ()
00217 {
00218 return QListIterator < mimeHdrLine *> (additionalHdrLines);
00219 }
00220
00221 void
00222 mimeHeader::outputHeader (mimeIO & useIO)
00223 {
00224 if (!getDisposition ().isEmpty ())
00225 {
00226 useIO.outputMimeLine (QByteArray ("Content-Disposition: ")
00227 + getDisposition ()
00228 + outputParameter (dispositionList));
00229 }
00230
00231 if (!getType ().isEmpty ())
00232 {
00233 useIO.outputMimeLine (QByteArray ("Content-Type: ")
00234 + getType () + outputParameter (typeList));
00235 }
00236 if (!getDescription ().isEmpty ())
00237 useIO.outputMimeLine (QByteArray ("Content-Description: ") +
00238 getDescription ());
00239 if (!getID ().isEmpty ())
00240 useIO.outputMimeLine (QByteArray ("Content-ID: ") + getID ());
00241 if (!getMD5 ().isEmpty ())
00242 useIO.outputMimeLine (QByteArray ("Content-MD5: ") + getMD5 ());
00243 if (!getEncoding ().isEmpty ())
00244 useIO.outputMimeLine (QByteArray ("Content-Transfer-Encoding: ") +
00245 getEncoding ());
00246
00247 QListIterator < mimeHdrLine *> ait = getAdditionalIterator ();
00248 mimeHdrLine *hdrline;
00249 while (ait.hasNext ())
00250 {
00251 hdrline = ait.next();
00252 useIO.outputMimeLine (hdrline->getLabel () + ": " +
00253 hdrline->getValue ());
00254 }
00255 useIO.outputMimeLine (QByteArray (""));
00256 }
00257
00258 QString
00259 mimeHeader::getParameter (const QByteArray& aStr, QHash < QString, QString > &aDict)
00260 {
00261 QString retVal, found;
00262
00263 found = aDict.value ( aStr );
00264 if ( found.isEmpty() )
00265 {
00266
00267 found = aDict.value ( aStr + '*' );
00268 if ( found.isEmpty() )
00269 {
00270
00271 QString decoded, encoded;
00272 int part = 0;
00273
00274 do
00275 {
00276 QByteArray search;
00277 search.setNum (part);
00278 search = aStr + '*' + search;
00279 found = aDict.value (search);
00280 if ( found.isEmpty() )
00281 {
00282 found = aDict.value (search + '*');
00283 if ( !found.isEmpty() )
00284 encoded += KIMAP::encodeRFC2231String (found);
00285 }
00286 else
00287 {
00288 encoded += found;
00289 }
00290 part++;
00291 }
00292 while ( !found.isEmpty() );
00293 if (encoded.contains ('\''))
00294 {
00295 retVal = KIMAP::decodeRFC2231String (encoded.toLocal8Bit ());
00296 }
00297 else
00298 {
00299 retVal =
00300 KIMAP::decodeRFC2231String (QByteArray ("''") + encoded.toLocal8Bit ());
00301 }
00302 }
00303 else
00304 {
00305
00306 retVal = KIMAP::decodeRFC2231String (found.toLocal8Bit ());
00307 }
00308 }
00309 else
00310 {
00311 retVal = found;
00312 }
00313
00314 return retVal;
00315 }
00316
00317 void
00318 mimeHeader::setParameter (const QByteArray& aLabel, const QString& aValue,
00319 QHash < QString, QString > &aDict)
00320 {
00321 bool encoded = true;
00322 uint vlen, llen;
00323 QString val = aValue;
00324
00325
00326 if (encoded && !aLabel.contains('*'))
00327 {
00328 val = KIMAP::encodeRFC2231String (aValue);
00329 }
00330
00331
00332 vlen = val.length();
00333 llen = aLabel.length();
00334 if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 )
00335 {
00336 const int limit = 80 - 8 - 2 - (int)llen;
00337
00338
00339
00340 int i = 0;
00341 QString shortValue;
00342 QByteArray shortLabel;
00343
00344 while (!val.isEmpty ())
00345 {
00346 int partLen;
00347 if ( limit >= int(vlen) ) {
00348
00349 partLen = vlen;
00350 }
00351 else {
00352 partLen = limit;
00353
00354 if ( val[partLen-1] == '%' ) {
00355 partLen += 2;
00356 }
00357 else if ( partLen > 1 && val[partLen-2] == '%' ) {
00358 partLen += 1;
00359 }
00360
00361
00362 if ( partLen > int(vlen) ) {
00363 partLen = vlen;
00364 }
00365 }
00366 shortValue = val.left( partLen );
00367 shortLabel.setNum (i);
00368 shortLabel = aLabel + '*' + shortLabel;
00369 val = val.right( vlen - partLen );
00370 vlen = vlen - partLen;
00371 if (encoded)
00372 {
00373 if (i == 0)
00374 {
00375 shortValue = "''" + shortValue;
00376 }
00377 shortLabel += '*';
00378 }
00379
00380
00381
00382 aDict.insert (shortLabel.toLower(), shortValue);
00383 i++;
00384 }
00385 }
00386 else
00387 {
00388 aDict.insert (aLabel.toLower(), val);
00389 }
00390 }
00391
00392 QByteArray mimeHeader::outputParameter (QHash < QString, QString > &aDict)
00393 {
00394 QByteArray retVal;
00395 QHashIterator < QString, QString > it (aDict);
00396 while (it.hasNext ())
00397 {
00398 it.next();
00399 retVal += (";\n\t" + it.key() + '=').toLatin1 ();
00400 if (it.value().indexOf (' ') > 0 || it.value().indexOf (';') > 0)
00401 {
00402 retVal += '"' + it.value().toUtf8 () + '"';
00403 }
00404 else
00405 {
00406 retVal += it.value().toUtf8 ();
00407 }
00408 }
00409 retVal += '\n';
00410
00411 return retVal;
00412 }
00413
00414 void
00415 mimeHeader::outputPart (mimeIO & useIO)
00416 {
00417 QListIterator < mimeHeader *> nestedParts = getNestedIterator ();
00418 QByteArray boundary;
00419 if (!getTypeParm ("boundary").isEmpty ())
00420 boundary = getTypeParm ("boundary").toLatin1 ();
00421
00422 outputHeader (useIO);
00423 if (!getPreBody ().isEmpty ())
00424 useIO.outputMimeLine (getPreBody ());
00425 if (getNestedMessage ())
00426 getNestedMessage ()->outputPart (useIO);
00427
00428 mimeHeader *mimeline;
00429 while (nestedParts.hasNext())
00430 {
00431 mimeline = nestedParts.next();
00432 if (!boundary.isEmpty ())
00433 useIO.outputMimeLine ("--" + boundary);
00434 mimeline->outputPart (useIO);
00435 }
00436 if (!boundary.isEmpty ())
00437 useIO.outputMimeLine ("--" + boundary + "--");
00438 if (!getPostBody ().isEmpty ())
00439 useIO.outputMimeLine (getPostBody ());
00440 }
00441
00442 #if 0
00443 int
00444 mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
00445 {
00446 int retVal = 0;
00447 bool mbox = false;
00448 QByteArray preNested, postNested;
00449 mbox = parseHeader (useIO);
00450
00451 kDebug(7116) <<"mimeHeader::parsePart - parsing part '" << getType () <<"'";
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, QByteArray & messageBody,
00486 const QString& boundary, bool mbox)
00487 {
00488 QByteArray inputStr;
00489 QByteArray 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.toLatin1 (), partEnd.length () - 1))
00505 {
00506 retVal = 0;
00507 break;
00508 }
00509 else if (!partBoundary.isEmpty ()
00510 && !qstrnicmp (inputStr, partBoundary.toLatin1 (),
00511 partBoundary.length () - 1))
00512 {
00513 retVal = 1;
00514 break;
00515 }
00516 else if (mbox && inputStr.startsWith ("From ") )
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 #endif
00533
00534 bool mimeHeader::parseHeader (mimeIO & useIO)
00535 {
00536 bool mbox = false;
00537 bool first = true;
00538 mimeHdrLine my_line;
00539 QByteArray inputStr;
00540
00541 kDebug(7116) <<"mimeHeader::parseHeader - starting parsing";
00542 while (useIO.inputLine (inputStr))
00543 {
00544 int appended;
00545 if (!inputStr.startsWith ("From ") || !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 = QByteArray();
00563 }
00564
00565 kDebug(7116) <<"mimeHeader::parseHeader - finished parsing";
00566 return mbox;
00567 }
00568
00569 mimeHeader *
00570 mimeHeader::bodyPart (const QString & _str)
00571 {
00572
00573 int pt = _str.indexOf('.');
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 kDebug(7116) <<"mimeHeader::bodyPart - recursing message";
00583 tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
00584 }
00585 else
00586 {
00587 kDebug(7116) <<"mimeHeader::bodyPart - recursing mixed";
00588 tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
00589 }
00590 if (tempPart)
00591 tempPart = tempPart->bodyPart (tempStr);
00592 return tempPart;
00593 }
00594
00595 kDebug(7116) <<"mimeHeader::bodyPart - returning part" << _str;
00596
00597 if (nestedMessage)
00598 {
00599 kDebug(7116) <<"mimeHeader::bodyPart - message";
00600 return nestedMessage->nestedParts.at (_str.toULong () - 1);
00601 }
00602 kDebug(7116) <<"mimeHeader::bodyPart - mixed";
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;
00612 stream << _contentType;
00613 stream << QString (getTypeParm ("name"));
00614 stream << _contentDescription;
00615 stream << _contentDisposition;
00616 stream << _contentEncoding;
00617 stream << contentLength;
00618 stream << partSpecifier;
00619
00620 if (nestedMessage)
00621 nestedMessage->serialize(stream);
00622
00623
00624 if (!nestedParts.isEmpty())
00625 {
00626 QListIterator < mimeHeader *> it(nestedParts);
00627 mimeHeader* part;
00628 while ( it.hasNext() ) {
00629 part = it.next();
00630 part->serialize( stream );
00631 }
00632 }
00633 }
00634
00635 #ifdef KMAIL_COMPATIBLE
00636
00637 QString
00638 mimeHeader::bodyDecoded ()
00639 {
00640 kDebug(7116) <<"mimeHeader::bodyDecoded";
00641 QByteArray temp = bodyDecodedBinary ();
00642 return QString::fromLatin1 (temp.data (), temp.count ());
00643 }
00644
00645 QByteArray
00646 mimeHeader::bodyDecodedBinary ()
00647 {
00648 QByteArray retVal;
00649
00650 if (contentEncoding.startsWith ("quoted-printable", Qt::CaseInsensitive) )
00651 retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
00652 else if (contentEncoding.startsWith ("base64", Qt::CaseInsensitive) )
00653 KCodecs::base64Decode(postMultipartBody, retVal);
00654 else retVal = postMultipartBody;
00655
00656 kDebug(7116) <<"mimeHeader::bodyDecodedBinary - size is" << retVal.size ();
00657 return retVal;
00658 }
00659
00660 void
00661 mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
00662 {
00663 setBodyEncoded (_arr);
00664 }
00665
00666 void
00667 mimeHeader::setBodyEncoded (const QByteArray & _arr)
00668 {
00669 QByteArray setVal;
00670
00671 kDebug(7116) <<"mimeHeader::setBodyEncoded - in size" << _arr.size ();
00672 if (contentEncoding.startsWith ("quoted-printable", Qt::CaseInsensitive) )
00673 setVal = KCodecs::quotedPrintableEncode(_arr);
00674 else if (contentEncoding.startsWith ("base64", Qt::CaseInsensitive) )
00675 KCodecs::base64Encode(_arr, setVal);
00676 else
00677 setVal.duplicate (_arr);
00678 kDebug(7116) <<"mimeHeader::setBodyEncoded - out size" << setVal.size ();
00679
00680 postMultipartBody.duplicate (setVal);
00681 kDebug(7116) <<"mimeHeader::setBodyEncoded - out size" << postMultipartBody.size ();
00682 }
00683
00684 QString
00685 mimeHeader::iconName ()
00686 {
00687 QString fileName =
00688 KMimeType::mimeType (contentType.toLower ())->icon (QString(), false);
00689 QString iconFileName =
00690 KGlobal::mainComponent().iconLoader ()->iconPath (fileName, KIconLoader::Desktop);
00691
00692
00693 return iconFileName;
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 QByteArray body;
00716
00717 if (aAutoDecode)
00718 body = bodyDecodedBinary ();
00719 else
00720 body = postMultipartBody;
00721
00722 KMimeType::Ptr mime = KMimeType::findByContent (body);
00723 QString mimetype = mime->name();
00724 contentType = mimetype;
00725 return mimetype;
00726 }
00727 #endif