diff options
Diffstat (limited to 'kioslaves/imap4/mimeheader.cc')
-rw-r--r-- | kioslaves/imap4/mimeheader.cc | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/kioslaves/imap4/mimeheader.cc b/kioslaves/imap4/mimeheader.cc new file mode 100644 index 000000000..17d3a3fd8 --- /dev/null +++ b/kioslaves/imap4/mimeheader.cc @@ -0,0 +1,745 @@ +/*************************************************************************** + mimeheader.cc - description + ------------------- + begin : Fri Oct 20 2000 + copyright : (C) 2000 by Sven Carstens + email : s.carstens@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "mimeheader.h" +#include "mimehdrline.h" +#include "mailheader.h" +#include "rfcdecoder.h" + +#include <qregexp.h> + +// #include <iostream.h> +#include <kglobal.h> +#include <kinstance.h> +#include <kiconloader.h> +#include <kmimetype.h> +#include <kmimemagic.h> +#include <kmdcodec.h> +#include <kdebug.h> + +mimeHeader::mimeHeader (): +typeList (17, false), dispositionList (17, false) +{ + // Case insensitive hashes are killing us. Also are they too small? + originalHdrLines.setAutoDelete (true); + additionalHdrLines.setAutoDelete (false); // is also in original lines + nestedParts.setAutoDelete (true); + typeList.setAutoDelete (true); + dispositionList.setAutoDelete (true); + nestedMessage = NULL; + contentLength = 0; + contentType = "application/octet-stream"; +} + +mimeHeader::~mimeHeader () +{ +} + +/* +QPtrList<mimeHeader> mimeHeader::getAllParts() +{ + QPtrList<mimeHeader> retVal; + + // caller is responsible for clearing + retVal.setAutoDelete( false ); + nestedParts.setAutoDelete( false ); + + // shallow copy + retVal = nestedParts; + + // can't have duplicate pointers + nestedParts.clear(); + + // restore initial state + nestedParts.setAutoDelete( true ); + + return retVal; +} */ + +void +mimeHeader::addHdrLine (mimeHdrLine * aHdrLine) +{ + mimeHdrLine *addLine = new mimeHdrLine (aHdrLine); + if (addLine) + { + originalHdrLines.append (addLine); + if (qstrnicmp (addLine->getLabel (), "Content-", 8)) + { + additionalHdrLines.append (addLine); + } + else + { + int skip; + char *aCStr = addLine->getValue ().data (); + QDict < QString > *aList = 0; + + skip = mimeHdrLine::parseSeparator (';', aCStr); + if (skip > 0) + { + int cut = 0; + if (skip >= 2) + { + if (aCStr[skip - 1] == '\r') + cut++; + if (aCStr[skip - 1] == '\n') + cut++; + if (aCStr[skip - 2] == '\r') + cut++; + if (aCStr[skip - 1] == ';') + cut++; + } + QCString mimeValue = QCString (aCStr, skip - cut + 1); // cutting of one because of 0x00 + + + if (!qstricmp (addLine->getLabel (), "Content-Disposition")) + { + aList = &dispositionList; + _contentDisposition = mimeValue; + } + else if (!qstricmp (addLine->getLabel (), "Content-Type")) + { + aList = &typeList; + contentType = mimeValue; + } + else + if (!qstricmp (addLine->getLabel (), "Content-Transfer-Encoding")) + { + contentEncoding = mimeValue; + } + else if (!qstricmp (addLine->getLabel (), "Content-ID")) + { + contentID = mimeValue; + } + else if (!qstricmp (addLine->getLabel (), "Content-Description")) + { + _contentDescription = mimeValue; + } + else if (!qstricmp (addLine->getLabel (), "Content-MD5")) + { + contentMD5 = mimeValue; + } + else if (!qstricmp (addLine->getLabel (), "Content-Length")) + { + contentLength = mimeValue.toULong (); + } + else + { + additionalHdrLines.append (addLine); + } +// cout << addLine->getLabel().data() << ": '" << mimeValue.data() << "'" << endl; + + aCStr += skip; + while ((skip = mimeHdrLine::parseSeparator (';', aCStr))) + { + if (skip > 0) + { + addParameter (QCString (aCStr, skip).simplifyWhiteSpace(), aList); +// cout << "-- '" << aParm.data() << "'" << endl; + mimeValue = QCString (addLine->getValue ().data (), skip); + aCStr += skip; + } + else + break; + } + } + } + } +} + +void +mimeHeader::addParameter (const QCString& aParameter, QDict < QString > *aList) +{ + if ( !aList ) + return; + + QString *aValue; + QCString aLabel; + int pos = aParameter.find ('='); +// cout << aParameter.left(pos).data(); + aValue = new QString (); + aValue->setLatin1 (aParameter.right (aParameter.length () - pos - 1)); + aLabel = aParameter.left (pos); + if ((*aValue)[0] == '"') + *aValue = aValue->mid (1, aValue->length () - 2); + + aList->insert (aLabel, aValue); +// cout << "=" << aValue->data() << endl; +} + +QString +mimeHeader::getDispositionParm (const QCString& aStr) +{ + return getParameter (aStr, &dispositionList); +} + +QString +mimeHeader::getTypeParm (const QCString& aStr) +{ + return getParameter (aStr, &typeList); +} + +void +mimeHeader::setDispositionParm (const QCString& aLabel, const QString& aValue) +{ + setParameter (aLabel, aValue, &dispositionList); + return; +} + +void +mimeHeader::setTypeParm (const QCString& aLabel, const QString& aValue) +{ + setParameter (aLabel, aValue, &typeList); +} + +QDictIterator < QString > mimeHeader::getDispositionIterator () +{ + return QDictIterator < QString > (dispositionList); +} + +QDictIterator < QString > mimeHeader::getTypeIterator () +{ + return QDictIterator < QString > (typeList); +} + +QPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator () +{ + return QPtrListIterator < mimeHdrLine > (originalHdrLines); +} + +QPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator () +{ + return QPtrListIterator < mimeHdrLine > (additionalHdrLines); +} + +void +mimeHeader::outputHeader (mimeIO & useIO) +{ + if (!getDisposition ().isEmpty ()) + { + useIO.outputMimeLine (QCString ("Content-Disposition: ") + + getDisposition () + + outputParameter (&dispositionList)); + } + + if (!getType ().isEmpty ()) + { + useIO.outputMimeLine (QCString ("Content-Type: ") + + getType () + outputParameter (&typeList)); + } + if (!getDescription ().isEmpty ()) + useIO.outputMimeLine (QCString ("Content-Description: ") + + getDescription ()); + if (!getID ().isEmpty ()) + useIO.outputMimeLine (QCString ("Content-ID: ") + getID ()); + if (!getMD5 ().isEmpty ()) + useIO.outputMimeLine (QCString ("Content-MD5: ") + getMD5 ()); + if (!getEncoding ().isEmpty ()) + useIO.outputMimeLine (QCString ("Content-Transfer-Encoding: ") + + getEncoding ()); + + QPtrListIterator < mimeHdrLine > ait = getAdditionalIterator (); + while (ait.current ()) + { + useIO.outputMimeLine (ait.current ()->getLabel () + ": " + + ait.current ()->getValue ()); + ++ait; + } + useIO.outputMimeLine (QCString ("")); +} + +QString +mimeHeader::getParameter (const QCString& aStr, QDict < QString > *aDict) +{ + QString retVal, *found; + if (aDict) + { + //see if it is a normal parameter + found = aDict->find (aStr); + if (!found) + { + //might be a continuated or encoded parameter + found = aDict->find (aStr + "*"); + if (!found) + { + //continuated parameter + QString decoded, encoded; + int part = 0; + + do + { + QCString search; + search.setNum (part); + search = aStr + "*" + search; + found = aDict->find (search); + if (!found) + { + found = aDict->find (search + "*"); + if (found) + encoded += rfcDecoder::encodeRFC2231String (*found); + } + else + { + encoded += *found; + } + part++; + } + while (found); + if (encoded.find ('\'') >= 0) + { + retVal = rfcDecoder::decodeRFC2231String (encoded.local8Bit ()); + } + else + { + retVal = + rfcDecoder::decodeRFC2231String (QCString ("''") + + encoded.local8Bit ()); + } + } + else + { + //simple encoded parameter + retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ()); + } + } + else + { + retVal = *found; + } + } + return retVal; +} + +void +mimeHeader::setParameter (const QCString& aLabel, const QString& aValue, + QDict < QString > *aDict) +{ + bool encoded = true; + uint vlen, llen; + QString val = aValue; + + if (aDict) + { + + //see if it needs to get encoded + if (encoded && aLabel.find ('*') == -1) + { + val = rfcDecoder::encodeRFC2231String (aValue); + } + //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl; + //see if it needs to be truncated + vlen = val.length(); + llen = aLabel.length(); + if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 ) + { + const int limit = 80 - 8 - 2 - (int)llen; + // the -2 is there to allow extending the length of a part of val + // by 1 or 2 in order to prevent an encoded character from being + // split in half + int i = 0; + QString shortValue; + QCString shortLabel; + + while (!val.isEmpty ()) + { + int partLen; // the length of the next part of the value + if ( limit >= int(vlen) ) { + // the rest of the value fits completely into one continued header + partLen = vlen; + } + else { + partLen = limit; + // make sure that we don't split an encoded char in half + if ( val[partLen-1] == '%' ) { + partLen += 2; + } + else if ( partLen > 1 && val[partLen-2] == '%' ) { + partLen += 1; + } + // make sure partLen does not exceed vlen (could happen in case of + // an incomplete encoded char) + if ( partLen > int(vlen) ) { + partLen = vlen; + } + } + shortValue = val.left( partLen ); + shortLabel.setNum (i); + shortLabel = aLabel + "*" + shortLabel; + val = val.right( vlen - partLen ); + vlen = vlen - partLen; + if (encoded) + { + if (i == 0) + { + shortValue = "''" + shortValue; + } + shortLabel += "*"; + } + //kdDebug(7116) << "mimeHeader::setParameter() - shortLabel = '" << shortLabel << "'" << endl; + //kdDebug(7116) << "mimeHeader::setParameter() - shortValue = '" << shortValue << "'" << endl; + //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl; + aDict->insert (shortLabel, new QString (shortValue)); + i++; + } + } + else + { + aDict->insert (aLabel, new QString (val)); + } + } +} + +QCString +mimeHeader::outputParameter (QDict < QString > *aDict) +{ + QCString retVal; + if (aDict) + { + QDictIterator < QString > it (*aDict); + while (it.current ()) + { + retVal += (";\n\t" + it.currentKey () + "=").latin1 (); + if (it.current ()->find (' ') > 0 || it.current ()->find (';') > 0) + { + retVal += '"' + it.current ()->utf8 () + '"'; + } + else + { + retVal += it.current ()->utf8 (); + } + // << it.current()->utf8() << "'"; + ++it; + } + retVal += "\n"; + } + return retVal; +} + +void +mimeHeader::outputPart (mimeIO & useIO) +{ + QPtrListIterator < mimeHeader > nestedParts = getNestedIterator (); + QCString boundary; + if (!getTypeParm ("boundary").isEmpty ()) + boundary = getTypeParm ("boundary").latin1 (); + + outputHeader (useIO); + if (!getPreBody ().isEmpty ()) + useIO.outputMimeLine (getPreBody ()); + if (getNestedMessage ()) + getNestedMessage ()->outputPart (useIO); + while (nestedParts.current ()) + { + if (!boundary.isEmpty ()) + useIO.outputMimeLine ("--" + boundary); + nestedParts.current ()->outputPart (useIO); + ++nestedParts; + } + if (!boundary.isEmpty ()) + useIO.outputMimeLine ("--" + boundary + "--"); + if (!getPostBody ().isEmpty ()) + useIO.outputMimeLine (getPostBody ()); +} + +int +mimeHeader::parsePart (mimeIO & useIO, const QString& boundary) +{ + int retVal = 0; + bool mbox = false; + QCString preNested, postNested; + mbox = parseHeader (useIO); + + kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl; + if (!qstrnicmp (getType (), "Multipart", 9)) + { + retVal = parseBody (useIO, preNested, getTypeParm ("boundary")); //this is a message in mime format stuff + setPreBody (preNested); + int localRetVal; + do + { + mimeHeader *aHeader = new mimeHeader; + + // set default type for multipart/digest + if (!qstrnicmp (getType (), "Multipart/Digest", 16)) + aHeader->setType ("Message/RFC822"); + + localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary")); + addNestedPart (aHeader); + } + while (localRetVal); //get nested stuff + } + if (!qstrnicmp (getType (), "Message/RFC822", 14)) + { + mailHeader *msgHeader = new mailHeader; + retVal = msgHeader->parsePart (useIO, boundary); + setNestedMessage (msgHeader); + } + else + { + retVal = parseBody (useIO, postNested, boundary, mbox); //just a simple part remaining + setPostBody (postNested); + } + return retVal; +} + +int +mimeHeader::parseBody (mimeIO & useIO, QCString & messageBody, + const QString& boundary, bool mbox) +{ + QCString inputStr; + QCString buffer; + QString partBoundary; + QString partEnd; + int retVal = 0; //default is last part + + if (!boundary.isEmpty ()) + { + partBoundary = QString ("--") + boundary; + partEnd = QString ("--") + boundary + "--"; + } + + while (useIO.inputLine (inputStr)) + { + //check for the end of all parts + if (!partEnd.isEmpty () + && !qstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1)) + { + retVal = 0; //end of these parts + break; + } + else if (!partBoundary.isEmpty () + && !qstrnicmp (inputStr, partBoundary.latin1 (), + partBoundary.length () - 1)) + { + retVal = 1; //continue with next part + break; + } + else if (mbox && inputStr.find ("From ") == 0) + { + retVal = 0; // end of mbox + break; + } + buffer += inputStr; + if (buffer.length () > 16384) + { + messageBody += buffer; + buffer = ""; + } + } + + messageBody += buffer; + return retVal; +} + +bool +mimeHeader::parseHeader (mimeIO & useIO) +{ + bool mbox = false; + bool first = true; + mimeHdrLine my_line; + QCString inputStr; + + kdDebug(7116) << "mimeHeader::parseHeader - starting parsing" << endl; + while (useIO.inputLine (inputStr)) + { + int appended; + if (inputStr.find ("From ") != 0 || !first) + { + first = false; + appended = my_line.appendStr (inputStr); + if (!appended) + { + addHdrLine (&my_line); + appended = my_line.setStr (inputStr); + } + if (appended <= 0) + break; + } + else + { + mbox = true; + first = false; + } + inputStr = (const char *) NULL; + } + + kdDebug(7116) << "mimeHeader::parseHeader - finished parsing" << endl; + return mbox; +} + +mimeHeader * +mimeHeader::bodyPart (const QString & _str) +{ + // see if it is nested a little deeper + int pt = _str.find('.'); + if (pt != -1) + { + QString tempStr = _str; + mimeHeader *tempPart; + + tempStr = _str.right (_str.length () - pt - 1); + if (nestedMessage) + { + kdDebug(7116) << "mimeHeader::bodyPart - recursing message" << endl; + tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1); + } + else + { + kdDebug(7116) << "mimeHeader::bodyPart - recursing mixed" << endl; + tempPart = nestedParts.at (_str.left(pt).toULong() - 1); + } + if (tempPart) + tempPart = tempPart->bodyPart (tempStr); + return tempPart; + } + + kdDebug(7116) << "mimeHeader::bodyPart - returning part " << _str << endl; + // or pick just the plain part + if (nestedMessage) + { + kdDebug(7116) << "mimeHeader::bodyPart - message" << endl; + return nestedMessage->nestedParts.at (_str.toULong () - 1); + } + kdDebug(7116) << "mimeHeader::bodyPart - mixed" << endl; + return nestedParts.at (_str.toULong () - 1); +} + +void mimeHeader::serialize(QDataStream& stream) +{ + int nestedcount = nestedParts.count(); + if (nestedParts.isEmpty() && nestedMessage) + nestedcount = 1; + stream << nestedcount << contentType << QString (getTypeParm ("name")) << _contentDescription + << _contentDisposition << contentEncoding << contentLength << partSpecifier; + // serialize nested message + if (nestedMessage) + nestedMessage->serialize(stream); + + // serialize nested parts + if (!nestedParts.isEmpty()) + { + QPtrListIterator < mimeHeader > it(nestedParts); + mimeHeader* part; + while ( (part = it.current()) != 0 ) + { + ++it; + part->serialize(stream); + } + } +} + +#ifdef KMAIL_COMPATIBLE +// compatibility subroutines +QString +mimeHeader::bodyDecoded () +{ + kdDebug(7116) << "mimeHeader::bodyDecoded" << endl; + QByteArray temp; + + temp = bodyDecodedBinary (); + return QString::fromLatin1 (temp.data (), temp.count ()); +} + +QByteArray +mimeHeader::bodyDecodedBinary () +{ + QByteArray retVal; + + if (contentEncoding.find ("quoted-printable", 0, false) == 0) + retVal = KCodecs::quotedPrintableDecode(postMultipartBody); + else if (contentEncoding.find ("base64", 0, false) == 0) + KCodecs::base64Decode(postMultipartBody, retVal); + else retVal = postMultipartBody; + + kdDebug(7116) << "mimeHeader::bodyDecodedBinary - size is " << retVal.size () << endl; + return retVal; +} + +void +mimeHeader::setBodyEncodedBinary (const QByteArray & _arr) +{ + setBodyEncoded (_arr); +} + +void +mimeHeader::setBodyEncoded (const QByteArray & _arr) +{ + QByteArray setVal; + + kdDebug(7116) << "mimeHeader::setBodyEncoded - in size " << _arr.size () << endl; + if (contentEncoding.find ("quoted-printable", 0, false) == 0) + setVal = KCodecs::quotedPrintableEncode(_arr); + else if (contentEncoding.find ("base64", 0, false) == 0) + KCodecs::base64Encode(_arr, setVal); + else + setVal.duplicate (_arr); + kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << setVal.size () << endl; + + postMultipartBody.duplicate (setVal); + kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << postMultipartBody.size () << endl; +} + +QString +mimeHeader::iconName () +{ + QString fileName; + + // FIXME: bug? Why throw away this data? + fileName = + KMimeType::mimeType (contentType.lower ())->icon (QString::null, false); + fileName = + KGlobal::instance ()->iconLoader ()->iconPath (fileName, KIcon::Desktop); +// if (fileName.isEmpty()) +// fileName = KGlobal::instance()->iconLoader()->iconPath( "unknown", KIcon::Desktop ); + return fileName; +} + +void +mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy) +{ +// if(nestedMessage && destroy) delete nestedMessage; + nestedMessage = inPart; +} + +QString +mimeHeader::headerAsString () +{ + mimeIOQString myIO; + + outputHeader (myIO); + return myIO.getString (); +} + +QString +mimeHeader::magicSetType (bool aAutoDecode) +{ + QString mimetype; + QByteArray body; + KMimeMagicResult *result; + + KMimeMagic::self ()->setFollowLinks (TRUE); // is it necessary ? + + if (aAutoDecode) + body = bodyDecodedBinary (); + else + body = postMultipartBody; + + result = KMimeMagic::self ()->findBufferType (body); + mimetype = result->mimeType (); + contentType = mimetype; + return mimetype; +} +#endif |