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 
34 mimeHeader::mimeHeader ():
35 typeList (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 
48 mimeHeader::~mimeHeader ()
49 {
50 }
51 
52 /*
53 TQPtrList<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 
73 void
74 mimeHeader::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 
163 void
164 mimeHeader::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 
183 TQString
184 mimeHeader::getDispositionParm (const TQCString& aStr)
185 {
186  return getParameter (aStr, &dispositionList);
187 }
188 
189 TQString
190 mimeHeader::getTypeParm (const TQCString& aStr)
191 {
192  return getParameter (aStr, &typeList);
193 }
194 
195 void
196 mimeHeader::setDispositionParm (const TQCString& aLabel, const TQString& aValue)
197 {
198  setParameter (aLabel, aValue, &dispositionList);
199  return;
200 }
201 
202 void
203 mimeHeader::setTypeParm (const TQCString& aLabel, const TQString& aValue)
204 {
205  setParameter (aLabel, aValue, &typeList);
206 }
207 
208 TQDictIterator < TQString > mimeHeader::getDispositionIterator ()
209 {
210  return TQDictIterator < TQString > (dispositionList);
211 }
212 
213 TQDictIterator < TQString > mimeHeader::getTypeIterator ()
214 {
215  return TQDictIterator < TQString > (typeList);
216 }
217 
218 TQPtrListIterator < mimeHdrLine > mimeHeader::getOriginalIterator ()
219 {
220  return TQPtrListIterator < mimeHdrLine > (originalHdrLines);
221 }
222 
223 TQPtrListIterator < mimeHdrLine > mimeHeader::getAdditionalIterator ()
224 {
225  return TQPtrListIterator < mimeHdrLine > (additionalHdrLines);
226 }
227 
228 void
229 mimeHeader::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 
264 TQString
265 mimeHeader::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 
326 void
327 mimeHeader::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 
405 TQCString
406 mimeHeader::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 
431 void
432 mimeHeader::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 
457 int
458 mimeHeader::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 
498 int
499 mimeHeader::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 
547 bool
548 mimeHeader::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 
583 mimeHeader *
584 mimeHeader::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 
620 void 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
646 TQString
647 mimeHeader::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 
656 TQByteArray
657 mimeHeader::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 
671 void
672 mimeHeader::setBodyEncodedBinary (const TQByteArray & _arr)
673 {
674  setBodyEncoded (_arr);
675 }
676 
677 void
678 mimeHeader::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 
695 TQString
696 mimeHeader::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 
710 void
711 mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
712 {
713 // if(nestedMessage && destroy) delete nestedMessage;
714  nestedMessage = inPart;
715 }
716 
717 TQString
718 mimeHeader::headerAsString ()
719 {
720  mimeIOTQString myIO;
721 
722  outputHeader (myIO);
723  return myIO.getString ();
724 }
725 
726 TQString
727 mimeHeader::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