tdeioslave/imap4

mimeheader.cpp
1/***************************************************************************
2 mimeheader.cpp - description
3 -------------------
4 begin : Fri Oct 20 2000
5 copyright : (C) 2000 by Sven Carstens
6 email : s.carstens@gmx.de
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "mimeheader.h"
19#include "mimehdrline.h"
20#include "mailheader.h"
21#include "rfcdecoder.h"
22
23#include <tqregexp.h>
24
25// #include <iostream.h>
26#include <tdeglobal.h>
27#include <kinstance.h>
28#include <kiconloader.h>
29#include <kmimetype.h>
30#include <kmimemagic.h>
31#include <kmdcodec.h>
32#include <kdebug.h>
33
34mimeHeader::mimeHeader ():
35typeList (17, false), dispositionList (17, false)
36{
37 // Case insensitive hashes are killing us. Also are they too small?
38 originalHdrLines.setAutoDelete (true);
39 additionalHdrLines.setAutoDelete (false); // is also in original lines
40 nestedParts.setAutoDelete (true);
41 typeList.setAutoDelete (true);
42 dispositionList.setAutoDelete (true);
43 nestedMessage = NULL;
44 contentLength = 0;
45 contentType = "application/octet-stream";
46}
47
48mimeHeader::~mimeHeader ()
49{
50}
51
52/*
53TQPtrList<mimeHeader> mimeHeader::getAllParts()
54{
55 TQPtrList<mimeHeader> retVal;
56
57 // caller is responsible for clearing
58 retVal.setAutoDelete( false );
59 nestedParts.setAutoDelete( false );
60
61 // shallow copy
62 retVal = nestedParts;
63
64 // can't have duplicate pointers
65 nestedParts.clear();
66
67 // restore initial state
68 nestedParts.setAutoDelete( true );
69
70 return retVal;
71} */
72
73void
74mimeHeader::addHdrLine (mimeHdrLine * aHdrLine)
75{
76 mimeHdrLine *addLine = new mimeHdrLine (aHdrLine);
77 if (addLine)
78 {
79 originalHdrLines.append (addLine);
80 if (tqstrnicmp (addLine->getLabel (), "Content-", 8))
81 {
82 additionalHdrLines.append (addLine);
83 }
84 else
85 {
86 int skip;
87 const char *aCStr = addLine->getValue ().data ();
88 TQDict < TQString > *aList = 0;
89
90 skip = mimeHdrLine::parseSeparator (';', aCStr);
91 if (skip > 0)
92 {
93 int cut = 0;
94 if (skip >= 2)
95 {
96 if (aCStr[skip - 1] == '\r')
97 cut++;
98 if (aCStr[skip - 1] == '\n')
99 cut++;
100 if (aCStr[skip - 2] == '\r')
101 cut++;
102 if (aCStr[skip - 1] == ';')
103 cut++;
104 }
105 TQCString mimeValue = TQCString (aCStr, skip - cut + 1); // cutting of one because of 0x00
106
107
108 if (!tqstricmp (addLine->getLabel (), "Content-Disposition"))
109 {
110 aList = &dispositionList;
111 _contentDisposition = mimeValue;
112 }
113 else if (!tqstricmp (addLine->getLabel (), "Content-Type"))
114 {
115 aList = &typeList;
116 contentType = mimeValue;
117 }
118 else
119 if (!tqstricmp (addLine->getLabel (), "Content-Transfer-Encoding"))
120 {
121 contentEncoding = mimeValue;
122 }
123 else if (!tqstricmp (addLine->getLabel (), "Content-ID"))
124 {
125 contentID = mimeValue;
126 }
127 else if (!tqstricmp (addLine->getLabel (), "Content-Description"))
128 {
129 _contentDescription = mimeValue;
130 }
131 else if (!tqstricmp (addLine->getLabel (), "Content-MD5"))
132 {
133 contentMD5 = mimeValue;
134 }
135 else if (!tqstricmp (addLine->getLabel (), "Content-Length"))
136 {
137 contentLength = mimeValue.toULong ();
138 }
139 else
140 {
141 additionalHdrLines.append (addLine);
142 }
143// cout << addLine->getLabel().data() << ": '" << mimeValue.data() << "'" << endl;
144
145 aCStr += skip;
146 while ((skip = mimeHdrLine::parseSeparator (';', aCStr)))
147 {
148 if (skip > 0)
149 {
150 addParameter (TQCString (aCStr, skip).simplifyWhiteSpace(), aList);
151// cout << "-- '" << aParm.data() << "'" << endl;
152 mimeValue = TQCString (addLine->getValue ().data (), skip);
153 aCStr += skip;
154 }
155 else
156 break;
157 }
158 }
159 }
160 }
161}
162
163void
164mimeHeader::addParameter (const TQCString& aParameter, TQDict < TQString > *aList)
165{
166 if ( !aList )
167 return;
168
169 TQString *aValue;
170 TQCString aLabel;
171 int pos = aParameter.find ('=');
172// cout << aParameter.left(pos).data();
173 aValue = new TQString ();
174 aValue->setLatin1 (aParameter.right (aParameter.length () - pos - 1));
175 aLabel = aParameter.left (pos);
176 if ((*aValue)[0] == '"')
177 *aValue = aValue->mid (1, aValue->length () - 2);
178
179 aList->insert (aLabel, aValue);
180// cout << "=" << aValue->data() << endl;
181}
182
183TQString
184mimeHeader::getDispositionParm (const TQCString& aStr)
185{
186 return getParameter (aStr, &dispositionList);
187}
188
189TQString
190mimeHeader::getTypeParm (const TQCString& aStr)
191{
192 return getParameter (aStr, &typeList);
193}
194
195void
196mimeHeader::setDispositionParm (const TQCString& aLabel, const TQString& aValue)
197{
198 setParameter (aLabel, aValue, &dispositionList);
199 return;
200}
201
202void
203mimeHeader::setTypeParm (const TQCString& aLabel, const TQString& aValue)
204{
205 setParameter (aLabel, aValue, &typeList);
206}
207
208TQDictIterator < TQString > mimeHeader::getDispositionIterator ()
209{
210 return TQDictIterator < TQString > (dispositionList);
211}
212
213TQDictIterator < TQString > mimeHeader::getTypeIterator ()
214{
215 return TQDictIterator < TQString > (typeList);
216}
217
218TQPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator ()
219{
220 return TQPtrListIterator < mimeHdrLine > (originalHdrLines);
221}
222
223TQPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator ()
224{
225 return TQPtrListIterator < mimeHdrLine > (additionalHdrLines);
226}
227
228void
229mimeHeader::outputHeader (mimeIO & useIO)
230{
231 if (!getDisposition ().isEmpty ())
232 {
233 useIO.outputMimeLine (TQCString ("Content-Disposition: ")
234 + getDisposition ()
235 + outputParameter (&dispositionList));
236 }
237
238 if (!getType ().isEmpty ())
239 {
240 useIO.outputMimeLine (TQCString ("Content-Type: ")
241 + getType () + outputParameter (&typeList));
242 }
243 if (!getDescription ().isEmpty ())
244 useIO.outputMimeLine (TQCString ("Content-Description: ") +
245 getDescription ());
246 if (!getID ().isEmpty ())
247 useIO.outputMimeLine (TQCString ("Content-ID: ") + getID ());
248 if (!getMD5 ().isEmpty ())
249 useIO.outputMimeLine (TQCString ("Content-MD5: ") + getMD5 ());
250 if (!getEncoding ().isEmpty ())
251 useIO.outputMimeLine (TQCString ("Content-Transfer-Encoding: ") +
252 getEncoding ());
253
254 TQPtrListIterator < mimeHdrLine > ait = getAdditionalIterator ();
255 while (ait.current ())
256 {
257 useIO.outputMimeLine (ait.current ()->getLabel () + ": " +
258 ait.current ()->getValue ());
259 ++ait;
260 }
261 useIO.outputMimeLine (TQCString (""));
262}
263
264TQString
265mimeHeader::getParameter (const TQCString& aStr, TQDict < TQString > *aDict)
266{
267 TQString retVal, *found;
268 if (aDict)
269 {
270 //see if it is a normal parameter
271 found = aDict->find (aStr);
272 if (!found)
273 {
274 //might be a continuated or encoded parameter
275 found = aDict->find (aStr + "*");
276 if (!found)
277 {
278 //continuated parameter
279 TQString decoded, encoded;
280 int part = 0;
281
282 do
283 {
284 TQCString search;
285 search.setNum (part);
286 search = aStr + "*" + search;
287 found = aDict->find (search);
288 if (!found)
289 {
290 found = aDict->find (search + "*");
291 if (found)
292 encoded += rfcDecoder::encodeRFC2231String (*found);
293 }
294 else
295 {
296 encoded += *found;
297 }
298 part++;
299 }
300 while (found);
301 if (encoded.find ('\'') >= 0)
302 {
303 retVal = rfcDecoder::decodeRFC2231String (encoded.local8Bit ());
304 }
305 else
306 {
307 retVal =
308 rfcDecoder::decodeRFC2231String (TQCString ("''") +
309 encoded.local8Bit ());
310 }
311 }
312 else
313 {
314 //simple encoded parameter
315 retVal = rfcDecoder::decodeRFC2231String (found->local8Bit ());
316 }
317 }
318 else
319 {
320 retVal = *found;
321 }
322 }
323 return retVal;
324}
325
326void
327mimeHeader::setParameter (const TQCString& aLabel, const TQString& aValue,
328 TQDict < TQString > *aDict)
329{
330 bool encoded = true;
331 uint vlen, llen;
332 TQString val = aValue;
333
334 if (aDict)
335 {
336
337 //see if it needs to get encoded
338 if (encoded && aLabel.find ('*') == -1)
339 {
340 val = rfcDecoder::encodeRFC2231String (aValue);
341 }
342 //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl;
343 //see if it needs to be truncated
344 vlen = val.length();
345 llen = aLabel.length();
346 if (vlen + llen + 4 > 80 && llen < 80 - 8 - 2 )
347 {
348 const int limit = 80 - 8 - 2 - (int)llen;
349 // the -2 is there to allow extending the length of a part of val
350 // by 1 or 2 in order to prevent an encoded character from being
351 // split in half
352 int i = 0;
353 TQString shortValue;
354 TQCString shortLabel;
355
356 while (!val.isEmpty ())
357 {
358 int partLen; // the length of the next part of the value
359 if ( limit >= int(vlen) ) {
360 // the rest of the value fits completely into one continued header
361 partLen = vlen;
362 }
363 else {
364 partLen = limit;
365 // make sure that we don't split an encoded char in half
366 if ( val[partLen-1] == '%' ) {
367 partLen += 2;
368 }
369 else if ( partLen > 1 && val[partLen-2] == '%' ) {
370 partLen += 1;
371 }
372 // make sure partLen does not exceed vlen (could happen in case of
373 // an incomplete encoded char)
374 if ( partLen > int(vlen) ) {
375 partLen = vlen;
376 }
377 }
378 shortValue = val.left( partLen );
379 shortLabel.setNum (i);
380 shortLabel = aLabel + "*" + shortLabel;
381 val = val.right( vlen - partLen );
382 vlen = vlen - partLen;
383 if (encoded)
384 {
385 if (i == 0)
386 {
387 shortValue = "''" + shortValue;
388 }
389 shortLabel += "*";
390 }
391 //kdDebug(7116) << "mimeHeader::setParameter() - shortLabel = '" << shortLabel << "'" << endl;
392 //kdDebug(7116) << "mimeHeader::setParameter() - shortValue = '" << shortValue << "'" << endl;
393 //kdDebug(7116) << "mimeHeader::setParameter() - val = '" << val << "'" << endl;
394 aDict->insert (shortLabel, new TQString (shortValue));
395 i++;
396 }
397 }
398 else
399 {
400 aDict->insert (aLabel, new TQString (val));
401 }
402 }
403}
404
405TQCString
406mimeHeader::outputParameter (TQDict < TQString > *aDict)
407{
408 TQCString retVal;
409 if (aDict)
410 {
411 TQDictIterator < TQString > it (*aDict);
412 while (it.current ())
413 {
414 retVal += (";\n\t" + it.currentKey () + "=").latin1 ();
415 if (it.current ()->find (' ') > 0 || it.current ()->find (';') > 0)
416 {
417 retVal += '"' + it.current ()->utf8 () + '"';
418 }
419 else
420 {
421 retVal += it.current ()->utf8 ();
422 }
423 // << it.current()->utf8() << "'";
424 ++it;
425 }
426 retVal += "\n";
427 }
428 return retVal;
429}
430
431void
432mimeHeader::outputPart (mimeIO & useIO)
433{
434 TQPtrListIterator < mimeHeader > nestedParts = getNestedIterator ();
435 TQCString boundary;
436 if (!getTypeParm ("boundary").isEmpty ())
437 boundary = getTypeParm ("boundary").latin1 ();
438
439 outputHeader (useIO);
440 if (!getPreBody ().isEmpty ())
441 useIO.outputMimeLine (getPreBody ());
442 if (getNestedMessage ())
443 getNestedMessage ()->outputPart (useIO);
444 while (nestedParts.current ())
445 {
446 if (!boundary.isEmpty ())
447 useIO.outputMimeLine ("--" + boundary);
448 nestedParts.current ()->outputPart (useIO);
449 ++nestedParts;
450 }
451 if (!boundary.isEmpty ())
452 useIO.outputMimeLine ("--" + boundary + "--");
453 if (!getPostBody ().isEmpty ())
454 useIO.outputMimeLine (getPostBody ());
455}
456
457int
458mimeHeader::parsePart (mimeIO & useIO, const TQString& boundary)
459{
460 int retVal = 0;
461 bool mbox = false;
462 TQCString preNested, postNested;
463 mbox = parseHeader (useIO);
464
465 kdDebug(7116) << "mimeHeader::parsePart - parsing part '" << getType () << "'" << endl;
466 if (!tqstrnicmp (getType (), "Multipart", 9))
467 {
468 retVal = parseBody (useIO, preNested, getTypeParm ("boundary")); //this is a message in mime format stuff
469 setPreBody (preNested);
470 int localRetVal;
471 do
472 {
473 mimeHeader *aHeader = new mimeHeader;
474
475 // set default type for multipart/digest
476 if (!tqstrnicmp (getType (), "Multipart/Digest", 16))
477 aHeader->setType ("Message/RFC822");
478
479 localRetVal = aHeader->parsePart (useIO, getTypeParm ("boundary"));
480 addNestedPart (aHeader);
481 }
482 while (localRetVal); //get nested stuff
483 }
484 if (!tqstrnicmp (getType (), "Message/RFC822", 14))
485 {
486 mailHeader *msgHeader = new mailHeader;
487 retVal = msgHeader->parsePart (useIO, boundary);
488 setNestedMessage (msgHeader);
489 }
490 else
491 {
492 retVal = parseBody (useIO, postNested, boundary, mbox); //just a simple part remaining
493 setPostBody (postNested);
494 }
495 return retVal;
496}
497
498int
499mimeHeader::parseBody (mimeIO & useIO, TQCString & messageBody,
500 const TQString& boundary, bool mbox)
501{
502 TQCString inputStr;
503 TQCString buffer;
504 TQString partBoundary;
505 TQString partEnd;
506 int retVal = 0; //default is last part
507
508 if (!boundary.isEmpty ())
509 {
510 partBoundary = TQString ("--") + boundary;
511 partEnd = TQString ("--") + boundary + "--";
512 }
513
514 while (useIO.inputLine (inputStr))
515 {
516 //check for the end of all parts
517 if (!partEnd.isEmpty ()
518 && !tqstrnicmp (inputStr, partEnd.latin1 (), partEnd.length () - 1))
519 {
520 retVal = 0; //end of these parts
521 break;
522 }
523 else if (!partBoundary.isEmpty ()
524 && !tqstrnicmp (inputStr, partBoundary.latin1 (),
525 partBoundary.length () - 1))
526 {
527 retVal = 1; //continue with next part
528 break;
529 }
530 else if (mbox && inputStr.find ("From ") == 0)
531 {
532 retVal = 0; // end of mbox
533 break;
534 }
535 buffer += inputStr;
536 if (buffer.length () > 16384)
537 {
538 messageBody += buffer;
539 buffer = "";
540 }
541 }
542
543 messageBody += buffer;
544 return retVal;
545}
546
547bool
548mimeHeader::parseHeader (mimeIO & useIO)
549{
550 bool mbox = false;
551 bool first = true;
552 mimeHdrLine my_line;
553 TQCString inputStr;
554
555 kdDebug(7116) << "mimeHeader::parseHeader - starting parsing" << endl;
556 while (useIO.inputLine (inputStr))
557 {
558 int appended;
559 if (inputStr.find ("From ") != 0 || !first)
560 {
561 first = false;
562 appended = my_line.appendStr (inputStr);
563 if (!appended)
564 {
565 addHdrLine (&my_line);
566 appended = my_line.setStr (inputStr);
567 }
568 if (appended <= 0)
569 break;
570 }
571 else
572 {
573 mbox = true;
574 first = false;
575 }
576 inputStr = (const char *) NULL;
577 }
578
579 kdDebug(7116) << "mimeHeader::parseHeader - finished parsing" << endl;
580 return mbox;
581}
582
584mimeHeader::bodyPart (const TQString & _str)
585{
586 // see if it is nested a little deeper
587 int pt = _str.find('.');
588 if (pt != -1)
589 {
590 TQString tempStr = _str;
591 mimeHeader *tempPart;
592
593 tempStr = _str.right (_str.length () - pt - 1);
594 if (nestedMessage)
595 {
596 kdDebug(7116) << "mimeHeader::bodyPart - recursing message" << endl;
597 tempPart = nestedMessage->nestedParts.at (_str.left(pt).toULong() - 1);
598 }
599 else
600 {
601 kdDebug(7116) << "mimeHeader::bodyPart - recursing mixed" << endl;
602 tempPart = nestedParts.at (_str.left(pt).toULong() - 1);
603 }
604 if (tempPart)
605 tempPart = tempPart->bodyPart (tempStr);
606 return tempPart;
607 }
608
609 kdDebug(7116) << "mimeHeader::bodyPart - returning part " << _str << endl;
610 // or pick just the plain part
611 if (nestedMessage)
612 {
613 kdDebug(7116) << "mimeHeader::bodyPart - message" << endl;
614 return nestedMessage->nestedParts.at (_str.toULong () - 1);
615 }
616 kdDebug(7116) << "mimeHeader::bodyPart - mixed" << endl;
617 return nestedParts.at (_str.toULong () - 1);
618}
619
620void mimeHeader::serialize(TQDataStream& stream)
621{
622 int nestedcount = nestedParts.count();
623 if (nestedParts.isEmpty() && nestedMessage)
624 nestedcount = 1;
625 stream << nestedcount << contentType << TQString (getTypeParm ("name")) << _contentDescription
626 << _contentDisposition << contentEncoding << contentLength << partSpecifier;
627 // serialize nested message
628 if (nestedMessage)
629 nestedMessage->serialize(stream);
630
631 // serialize nested parts
632 if (!nestedParts.isEmpty())
633 {
634 TQPtrListIterator < mimeHeader > it(nestedParts);
635 mimeHeader* part;
636 while ( (part = it.current()) != 0 )
637 {
638 ++it;
639 part->serialize(stream);
640 }
641 }
642}
643
644#ifdef KMAIL_COMPATIBLE
645// compatibility subroutines
646TQString
647mimeHeader::bodyDecoded ()
648{
649 kdDebug(7116) << "mimeHeader::bodyDecoded" << endl;
650 TQByteArray temp;
651
652 temp = bodyDecodedBinary ();
653 return TQString::fromLatin1 (temp.data (), temp.count ());
654}
655
656TQByteArray
657mimeHeader::bodyDecodedBinary ()
658{
659 TQByteArray retVal;
660
661 if (contentEncoding.find ("quoted-printable", 0, false) == 0)
662 retVal = KCodecs::quotedPrintableDecode(postMultipartBody);
663 else if (contentEncoding.find ("base64", 0, false) == 0)
664 KCodecs::base64Decode(postMultipartBody, retVal);
665 else retVal = postMultipartBody;
666
667 kdDebug(7116) << "mimeHeader::bodyDecodedBinary - size is " << retVal.size () << endl;
668 return retVal;
669}
670
671void
672mimeHeader::setBodyEncodedBinary (const TQByteArray & _arr)
673{
674 setBodyEncoded (_arr);
675}
676
677void
678mimeHeader::setBodyEncoded (const TQByteArray & _arr)
679{
680 TQByteArray setVal;
681
682 kdDebug(7116) << "mimeHeader::setBodyEncoded - in size " << _arr.size () << endl;
683 if (contentEncoding.find ("quoted-printable", 0, false) == 0)
684 setVal = KCodecs::quotedPrintableEncode(_arr);
685 else if (contentEncoding.find ("base64", 0, false) == 0)
686 KCodecs::base64Encode(_arr, setVal);
687 else
688 setVal.duplicate (_arr);
689 kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << setVal.size () << endl;
690
691 postMultipartBody.duplicate (setVal);
692 kdDebug(7116) << "mimeHeader::setBodyEncoded - out size " << postMultipartBody.size () << endl;
693}
694
695TQString
696mimeHeader::iconName ()
697{
698 TQString fileName;
699
700 // FIXME: bug? Why throw away this data?
701 fileName =
702 KMimeType::mimeType (contentType.lower ())->icon (TQString(), false);
703 fileName =
704 TDEGlobal::instance ()->iconLoader ()->iconPath (fileName, TDEIcon::Desktop);
705// if (fileName.isEmpty())
706// fileName = TDEGlobal::instance()->iconLoader()->iconPath( "unknown", TDEIcon::Desktop );
707 return fileName;
708}
709
710void
711mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
712{
713// if(nestedMessage && destroy) delete nestedMessage;
714 nestedMessage = inPart;
715}
716
717TQString
718mimeHeader::headerAsString ()
719{
720 mimeIOTQString myIO;
721
722 outputHeader (myIO);
723 return myIO.getString ();
724}
725
726TQString
727mimeHeader::magicSetType (bool aAutoDecode)
728{
729 TQString mimetype;
730 TQByteArray body;
731 KMimeMagicResult *result;
732
733 KMimeMagic::self ()->setFollowLinks (TRUE); // is it necessary ?
734
735 if (aAutoDecode)
736 body = bodyDecodedBinary ();
737 else
738 body = postMultipartBody;
739
740 result = KMimeMagic::self ()->findBufferType (body);
741 mimetype = result->mimeType ();
742 contentType = mimetype;
743 return mimetype;
744}
745#endif
static int parseSeparator(char, const char *)
parses continuated lines
const TQCString & getLabel()
return the label
int setStr(const char *)
parse a Line into the class and report characters slurped
Definition: mimehdrline.cpp:94
const TQCString & getValue()
return the value
Definition: mimeio.h:29
static const TQString encodeRFC2231String(const TQString &_str)
encode a RFC2231 String
Definition: rfcdecoder.cpp:575
static const TQString decodeRFC2231String(const TQString &_str)
decode a RFC2231 String
Definition: rfcdecoder.cpp:629