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 = 0;
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 if (!qstricmp (addLine->getLabel (), "Content-Transfer-Encoding"))
00116 {
00117 setEncoding( mimeValue );
00118 }
00119 else if (!qstricmp (addLine->getLabel (), "Content-ID"))
00120 {
00121 setID( mimeValue );
00122 }
00123 else if (!qstricmp (addLine->getLabel (), "Content-Description"))
00124 {
00125 setDescription( mimeValue );
00126 }
00127 else if (!qstricmp (addLine->getLabel (), "Content-MD5"))
00128 {
00129 setMD5( mimeValue );
00130 }
00131 else if (!qstricmp (addLine->getLabel (), "Content-Length"))
00132 {
00133 contentLength = mimeValue.toUInt ();
00134 }
00135 else
00136 {
00137 additionalHdrLines.append (addLine);
00138 }
00139
00140
00141 aCStr += skip;
00142 while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
00143 {
00144 if (skip > 0)
00145 {
00146 if (aList)
00147 addParameter (QByteArray (aCStr, skip).simplified(), *aList);
00148 mimeValue = QByteArray (addLine->getValue ().data (), skip);
00149 aCStr += skip;
00150 }
00151 else
00152 break;
00153 }
00154 }
00155 }
00156 }
00157 }
00158
00159 void
00160 mimeHeader::addParameter (const QByteArray& aParameter, QHash < QString, QString > &aList)
00161 {
00162 QString aValue;
00163 QByteArray aLabel;
00164 int pos = aParameter.indexOf ('=');
00165
00166 aValue = QString::fromLatin1 (aParameter.right (aParameter.length () - pos - 1));
00167 aLabel = aParameter.left (pos);
00168 if (aValue[0] == '"')
00169 aValue = aValue.mid (1, aValue.length () - 2);
00170
00171 aList.insert (aLabel.toLower(), aValue);
00172
00173 }
00174
00175 QString
00176 mimeHeader::getDispositionParm (const QByteArray& aStr)
00177 {
00178 return getParameter (aStr, dispositionList);
00179 }
00180
00181 QString
00182 mimeHeader::getTypeParm (const QByteArray& aStr)
00183 {
00184 return getParameter (aStr, typeList);
00185 }
00186
00187 void
00188 mimeHeader::setDispositionParm (const QByteArray& aLabel, const QString& aValue)
00189 {
00190 setParameter (aLabel, aValue, dispositionList);
00191 return;
00192 }
00193
00194 void
00195 mimeHeader::setTypeParm (const QByteArray& aLabel, const QString& aValue)
00196 {
00197 setParameter (aLabel, aValue, typeList);
00198 }
00199
00200 QHashIterator < QString, QString > mimeHeader::getDispositionIterator ()
00201 {
00202 return QHashIterator < QString, QString > (dispositionList);
00203 }
00204
00205 QHashIterator < QString, QString > mimeHeader::getTypeIterator ()
00206 {
00207 return QHashIterator < QString, QString > (typeList);
00208 }
00209
00210 QListIterator < mimeHdrLine *> mimeHeader::getOriginalIterator ()
00211 {
00212 return QListIterator < mimeHdrLine *> (originalHdrLines);
00213 }
00214
00215 QListIterator < mimeHdrLine *> mimeHeader::getAdditionalIterator ()
00216 {
00217 return QListIterator < mimeHdrLine *> (additionalHdrLines);
00218 }
00219
00220 void
00221 mimeHeader::outputHeader (mimeIO & useIO)
00222 {
00223 if (!getDisposition ().isEmpty ())
00224 {
00225 useIO.outputMimeLine (QByteArray ("Content-Disposition: ")
00226 + getDisposition ()
00227 + outputParameter (dispositionList));
00228 }
00229
00230 if (!getType ().isEmpty ())
00231 {
00232 useIO.outputMimeLine (QByteArray ("Content-Type: ")
00233 + getType () + outputParameter (typeList));
00234 }
00235 if (!getDescription ().isEmpty ())
00236 useIO.outputMimeLine (QByteArray ("Content-Description: ") +
00237 getDescription ());
00238 if (!getID ().isEmpty ())
00239 useIO.outputMimeLine (QByteArray ("Content-ID: ") + getID ());
00240 if (!getMD5 ().isEmpty ())
00241 useIO.outputMimeLine (QByteArray ("Content-MD5: ") + getMD5 ());
00242 if (!getEncoding ().isEmpty ())
00243 useIO.outputMimeLine (QByteArray ("Content-Transfer-Encoding: ") +
00244 getEncoding ());
00245
00246 QListIterator < mimeHdrLine *> ait = getAdditionalIterator ();
00247 mimeHdrLine *hdrline;
00248 while (ait.hasNext ())
00249 {
00250 hdrline = ait.next();
00251 useIO.outputMimeLine (hdrline->getLabel () + ": " +
00252 hdrline->getValue ());
00253 }
00254 useIO.outputMimeLine (QByteArray (""));
00255 }
00256
00257 QString
00258 mimeHeader::getParameter (const QByteArray& aStr, QHash < QString, QString > &aDict)
00259 {
00260 QString retVal, found;
00261
00262 found = aDict.value ( aStr );
00263 if ( found.isEmpty() )
00264 {
00265
00266 found = aDict.value ( aStr + '*' );
00267 if ( found.isEmpty() )
00268 {
00269
00270 QString decoded, encoded;
00271 int part = 0;
00272
00273 do
00274 {
00275 QByteArray search;
00276 search.setNum (part);
00277 search = aStr + '*' + search;
00278 found = aDict.value (search);
00279 if ( found.isEmpty() )
00280 {
00281 found = aDict.value (search + '*');
00282 if ( !found.isEmpty() )
00283 encoded += KIMAP::encodeRFC2231String (found);
00284 }
00285 else
00286 {
00287 encoded += found;
00288 }
00289 part++;
00290 }
00291 while ( !found.isEmpty() );
00292 if (encoded.contains ('\''))
00293 {
00294 retVal = KIMAP::decodeRFC2231String (encoded.toLocal8Bit ());
00295 }
00296 else
00297 {
00298 retVal =
00299 KIMAP::decodeRFC2231String (QByteArray ("''") + encoded.toLocal8Bit ());
00300 }
00301 }
00302 else
00303 {
00304
00305 retVal = KIMAP::decodeRFC2231String (found.toLocal8Bit ());
00306 }
00307 }
00308 else
00309 {
00310 retVal = found;
00311 }
00312
00313 return retVal;
00314 }
00315
00316 void
00317 mimeHeader::setParameter (const QByteArray& aLabel, const QString& aValue,
00318 QHash < QString, QString > &aDict)
00319 {
00320 bool encoded = true;
00321 uint vlen, llen;
00322 QString val = aValue;
00323
00324
00325 if (encoded && !aLabel.contains('*'))
00326 {
00327 val = KIMAP::encodeRFC2231String (aValue);
00328 }
00329
00330
00331 vlen = val.length();
00332 llen = aLabel.length();
00333 if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 )
00334 {
00335 const int limit = 80 - 8 - 2 - (int)llen;
00336
00337
00338
00339 int i = 0;
00340 QString shortValue;
00341 QByteArray shortLabel;
00342
00343 while (!val.isEmpty ())
00344 {
00345 int partLen;
00346 if ( limit >= int(vlen) ) {
00347
00348 partLen = vlen;
00349 }
00350 else {
00351 partLen = limit;
00352
00353 if ( val[partLen-1] == '%' ) {
00354 partLen += 2;
00355 }
00356 else if ( partLen > 1 && val[partLen-2] == '%' ) {
00357 partLen += 1;
00358 }
00359
00360
00361 if ( partLen > int(vlen) ) {
00362 partLen = vlen;
00363 }
00364 }
00365 shortValue = val.left( partLen );
00366 shortLabel.setNum (i);
00367 shortLabel = aLabel + '*' + shortLabel;
00368 val = val.right( vlen - partLen );
00369 vlen = vlen - partLen;
00370 if (encoded)
00371 {
00372 if (i == 0)
00373 {
00374 shortValue = "''" + shortValue;
00375 }
00376 shortLabel += '*';
00377 }
00378
00379
00380
00381 aDict.insert (shortLabel.toLower(), shortValue);
00382 i++;
00383 }
00384 }
00385 else
00386 {
00387 aDict.insert (aLabel.toLower(), val);
00388 }
00389 }
00390
00391 QByteArray mimeHeader::outputParameter (QHash < QString, QString > &aDict)
00392 {
00393 QByteArray retVal;
00394 QHashIterator < QString, QString > it (aDict);
00395 while (it.hasNext ())
00396 {
00397 it.next();
00398 retVal += (";\n\t" + it.key() + '=').toLatin1 ();
00399 if (it.value().indexOf (' ') > 0 || it.value().indexOf (';') > 0)
00400 {
00401 retVal += '"' + it.value().toUtf8 () + '"';
00402 }
00403 else
00404 {
00405 retVal += it.value().toUtf8 ();
00406 }
00407 }
00408 retVal += '\n';
00409
00410 return retVal;
00411 }
00412
00413 void
00414 mimeHeader::outputPart (mimeIO & useIO)
00415 {
00416 QListIterator < mimeHeader *> nestedParts = getNestedIterator ();
00417 QByteArray boundary;
00418 if (!getTypeParm ("boundary").isEmpty ())
00419 boundary = getTypeParm ("boundary").toLatin1 ();
00420
00421 outputHeader (useIO);
00422 if (!getPreBody ().isEmpty ())
00423 useIO.outputMimeLine (getPreBody ());
00424 if (getNestedMessage ())
00425 getNestedMessage ()->outputPart (useIO);
00426
00427 mimeHeader *mimeline;
00428 while (nestedParts.hasNext())
00429 {
00430 mimeline = nestedParts.next();
00431 if (!boundary.isEmpty ())
00432 useIO.outputMimeLine ("--" + boundary);
00433 mimeline->outputPart (useIO);
00434 }
00435 if (!boundary.isEmpty ())
00436 useIO.outputMimeLine ("--" + boundary + "--");
00437 if (!getPostBody ().isEmpty ())
00438 useIO.outputMimeLine (getPostBody ());
00439 }
00440
00441 #if 0
00442 int
00443 mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
00444 {
00445 int retVal = 0;
00446 bool mbox = false;
00447 QByteArray preNested, postNested;
00448 mbox = parseHeader (useIO);
00449
00450 kDebug(7116) <<"mimeHeader::parsePart - parsing part '" << getType () <<"'";
00451 if (!qstrnicmp (getType (), "Multipart", 9))
00452 {
00453 retVal = parseBody (useIO, preNested, getTypeParm ("boundary"));
00454 setPreBody (preNested);
00455 int localRetVal;
00456 do
00457 {
00458 mimeHeader *aHeader = new mimeHeader;
00459
00460
00461 if (!qstrnicmp (getType (), "Multipart/Digest", 16))
00462 aHeader->setType ("Message/RFC822");
00463
00464 localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary"));
00465 addNestedPart (aHeader);
00466 }
00467 while (localRetVal);
00468 }
00469 if (!qstrnicmp (getType (), "Message/RFC822", 14))
00470 {
00471 mailHeader *msgHeader = new mailHeader;
00472 retVal = msgHeader->parsePart (useIO, boundary);
00473 setNestedMessage (msgHeader);
00474 }
00475 else
00476 {
00477 retVal = parseBody (useIO, postNested, boundary, mbox);
00478 setPostBody (postNested);
00479 }
00480 return retVal;
00481 }
00482
00483 int
00484 mimeHeader::parseBody (mimeIO & useIO, QByteArray & messageBody,
00485 const QString& boundary, bool mbox)
00486 {
00487 QByteArray inputStr;
00488 QByteArray buffer;
00489 QString partBoundary;
00490 QString partEnd;
00491 int retVal = 0;
00492
00493 if (!boundary.isEmpty ())
00494 {
00495 partBoundary = QString ("--") + boundary;
00496 partEnd = QString ("--") + boundary + "--";
00497 }
00498
00499 while (useIO.inputLine (inputStr))
00500 {
00501
00502 if (!partEnd.isEmpty ()
00503 && !qstrnicmp (inputStr, partEnd.toLatin1 (), partEnd.length () - 1))
00504 {
00505 retVal = 0;
00506 break;
00507 }
00508 else if (!partBoundary.isEmpty ()
00509 && !qstrnicmp (inputStr, partBoundary.toLatin1 (),
00510 partBoundary.length () - 1))
00511 {
00512 retVal = 1;
00513 break;
00514 }
00515 else if (mbox && inputStr.startsWith ("From ") )
00516 {
00517 retVal = 0;
00518 break;
00519 }
00520 buffer += inputStr;
00521 if (buffer.length () > 16384)
00522 {
00523 messageBody += buffer;
00524 buffer = "";
00525 }
00526 }
00527
00528 messageBody += buffer;
00529 return retVal;
00530 }
00531 #endif
00532
00533 bool mimeHeader::parseHeader (mimeIO & useIO)
00534 {
00535 bool mbox = false;
00536 bool first = true;
00537 mimeHdrLine my_line;
00538 QByteArray inputStr;
00539
00540 kDebug(7116) <<"mimeHeader::parseHeader - starting parsing";
00541 while (useIO.inputLine (inputStr))
00542 {
00543 int appended;
00544 if (!inputStr.startsWith("From ") || !first)
00545 {
00546 first = false;
00547 appended = my_line.appendStr (inputStr);
00548 if (!appended)
00549 {
00550 addHdrLine (&my_line);
00551 appended = my_line.setStr (inputStr);
00552 }
00553 if (appended <= 0)
00554 break;
00555 }
00556 else
00557 {
00558 mbox = true;
00559 first = false;
00560 }
00561 inputStr = QByteArray();
00562 }
00563
00564 kDebug(7116) <<"mimeHeader::parseHeader - finished parsing";
00565 return mbox;
00566 }
00567
00568 mimeHeader *
00569 mimeHeader::bodyPart (const QString & _str)
00570 {
00571
00572 int pt = _str.indexOf('.');
00573 if (pt != -1)
00574 {
00575 QString tempStr = _str;
00576 mimeHeader *tempPart;
00577
00578 tempStr = _str.right (_str.length () - pt - 1);
00579 if (nestedMessage)
00580 {
00581 kDebug(7116) <<"mimeHeader::bodyPart - recursing message";
00582 tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
00583 }
00584 else
00585 {
00586 kDebug(7116) <<"mimeHeader::bodyPart - recursing mixed";
00587 tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
00588 }
00589 if (tempPart)
00590 tempPart = tempPart->bodyPart (tempStr);
00591 return tempPart;
00592 }
00593
00594 kDebug(7116) <<"mimeHeader::bodyPart - returning part" << _str;
00595
00596 if (nestedMessage)
00597 {
00598 kDebug(7116) <<"mimeHeader::bodyPart - message";
00599 return nestedMessage->nestedParts.at (_str.toULong () - 1);
00600 }
00601 kDebug(7116) <<"mimeHeader::bodyPart - mixed";
00602 return nestedParts.at (_str.toULong () - 1);
00603 }
00604
00605 void mimeHeader::serialize(QDataStream& stream)
00606 {
00607 int nestedcount = nestedParts.count();
00608 if (nestedParts.isEmpty() && nestedMessage)
00609 nestedcount = 1;
00610 stream << nestedcount;
00611 stream << _contentType;
00612 stream << QString (getTypeParm ("name"));
00613 stream << _contentDescription;
00614 stream << _contentDisposition;
00615 stream << _contentEncoding;
00616 stream << contentLength;
00617 stream << partSpecifier;
00618
00619 if (nestedMessage)
00620 nestedMessage->serialize(stream);
00621
00622
00623 if (!nestedParts.isEmpty())
00624 {
00625 QListIterator < mimeHeader *> it(nestedParts);
00626 mimeHeader* part;
00627 while ( it.hasNext() ) {
00628 part = it.next();
00629 part->serialize( stream );
00630 }
00631 }
00632 }
00633
00634 #ifdef KMAIL_COMPATIBLE
00635
00636 QString
00637 mimeHeader::bodyDecoded ()
00638 {
00639 kDebug(7116) <<"mimeHeader::bodyDecoded";
00640 QByteArray temp = bodyDecodedBinary ();
00641 return QString::fromLatin1 (temp.data (), temp.count ());
00642 }
00643
00644 QByteArray
00645 mimeHeader::bodyDecodedBinary ()
00646 {
00647 QByteArray retVal;
00648
00649 if (contentEncoding.startsWith (QLatin1String("quoted-printable"), Qt::CaseInsensitive) )
00650 retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
00651 else if (contentEncoding.startsWith (QLatin1String("base64"), Qt::CaseInsensitive) )
00652 KCodecs::base64Decode(postMultipartBody, retVal);
00653 else retVal = postMultipartBody;
00654
00655 kDebug(7116) << "mimeHeader::bodyDecodedBinary - size is" << retVal.size ();
00656 return retVal;
00657 }
00658
00659 void
00660 mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
00661 {
00662 setBodyEncoded (_arr);
00663 }
00664
00665 void
00666 mimeHeader::setBodyEncoded (const QByteArray & _arr)
00667 {
00668 QByteArray setVal;
00669
00670 kDebug(7116) <<"mimeHeader::setBodyEncoded - in size" << _arr.size ();
00671 if (contentEncoding.startsWith (QLatin1String("quoted-printable"), Qt::CaseInsensitive) )
00672 setVal = KCodecs::quotedPrintableEncode(_arr);
00673 else if (contentEncoding.startsWith (QLatin1String("base64"), Qt::CaseInsensitive) )
00674 KCodecs::base64Encode(_arr, setVal);
00675 else
00676 setVal.duplicate (_arr);
00677 kDebug(7116) <<"mimeHeader::setBodyEncoded - out size" << setVal.size ();
00678
00679 postMultipartBody.duplicate (setVal);
00680 kDebug(7116) <<"mimeHeader::setBodyEncoded - out size" << postMultipartBody.size ();
00681 }
00682
00683 QString
00684 mimeHeader::iconName ()
00685 {
00686 QString fileName =
00687 KMimeType::mimeType (contentType.toLower ())->icon (QString(), false);
00688 QString iconFileName =
00689 KGlobal::mainComponent().iconLoader ()->iconPath (fileName, KIconLoader::Desktop);
00690
00691
00692 return iconFileName;
00693 }
00694
00695 void
00696 mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
00697 {
00698
00699 nestedMessage = inPart;
00700 }
00701
00702 QString
00703 mimeHeader::headerAsString ()
00704 {
00705 mimeIOQString myIO;
00706
00707 outputHeader (myIO);
00708 return myIO.getString ();
00709 }
00710
00711 QString
00712 mimeHeader::magicSetType (bool aAutoDecode)
00713 {
00714 QByteArray body;
00715
00716 if (aAutoDecode)
00717 body = bodyDecodedBinary ();
00718 else
00719 body = postMultipartBody;
00720
00721 KMimeType::Ptr mime = KMimeType::findByContent (body);
00722 QString mimetype = mime->name();
00723 contentType = mimetype;
00724 return mimetype;
00725 }
00726 #endif