libkmime

kmime_content.cpp
1 /*
2  kmime_content.cpp
3 
4  KMime, the KDE internet mail/usenet news message library.
5  Copyright (c) 2001 the KMime authors.
6  See file AUTHORS for details
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
15 */
16 #include "kmime_content.h"
17 #include "kmime_parsers.h"
18 
19 #include <kcharsets.h>
20 #include <kmdcodec.h>
21 #include <tdeglobal.h>
22 #include <tdelocale.h>
23 #include <kdebug.h>
24 
25 #include <tqtextcodec.h>
26 
27 using namespace KMime;
28 
29 namespace KMime {
30 
31 Content::Content()
32  : c_ontents(0), h_eaders(0), f_orceDefaultCS(false)
33 {
34  d_efaultCS = cachedCharset("ISO-8859-1");
35 }
36 
37 
38 Content::Content(const TQCString &h, const TQCString &b)
39  : c_ontents(0), h_eaders(0), f_orceDefaultCS(false)
40 {
41  d_efaultCS = cachedCharset("ISO-8859-1");
42  h_ead=h.copy();
43  b_ody=b.copy();
44 }
45 
46 
47 Content::~Content()
48 {
49  delete c_ontents;
50  delete h_eaders;
51 }
52 
53 
54 void Content::setContent(TQStrList *l)
55 {
56  //tqDebug("Content::setContent(TQStrList *l) : start");
57  h_ead.resize(0);
58  b_ody.resize(0);
59 
60  //usage of textstreams is much faster than simply appending the strings
61  TQTextStream hts(h_ead, IO_WriteOnly),
62  bts(b_ody, IO_WriteOnly);
63  hts.setEncoding(TQTextStream::Latin1);
64  bts.setEncoding(TQTextStream::Latin1);
65 
66  bool isHead=true;
67  for(char *line=l->first(); line; line=l->next()) {
68  if(isHead && line[0]=='\0') {
69  isHead=false;
70  continue;
71  }
72  if(isHead)
73  hts << line << "\n";
74  else
75  bts << line << "\n";
76  }
77 
78  //terminate strings
79  hts << '\0';
80  bts << '\0';
81 
82  //tqDebug("Content::setContent(TQStrList *l) : finished");
83 }
84 
85 
86 void Content::setContent(const TQCString &s)
87 {
88  int pos=s.find("\n\n", 0);
89  if(pos>-1) {
90  h_ead=s.left(++pos); //header *must* end with "\n" !!
91  b_ody=s.mid(pos+1, s.length()-pos-1);
92  }
93  else
94  h_ead=s;
95 }
96 
97 
98 //parse the message, split multiple parts
99 void Content::parse()
100 {
101  //tqDebug("void Content::parse() : start");
102  delete h_eaders;
103  h_eaders=0;
104 
105  // check this part has already been partioned into subparts.
106  // if this is the case, we will not try to reparse the body
107  // of this part.
108  if ((b_ody.size() == 0) && (c_ontents != 0) && !c_ontents->isEmpty()) {
109  // reparse all sub parts
110  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
111  c->parse();
112  return;
113  }
114 
115  delete c_ontents;
116  c_ontents=0;
117 
118  Headers::ContentType *ct=contentType();
119  TQCString tmp;
120  Content *c;
121  Headers::contentCategory cat;
122 
123  // just "text" as mimetype is suspicious, perhaps this article was
124  // generated by broken software, better check for uuencoded binaries
125  if (ct->mimeType()=="text")
126  ct->setMimeType("invalid/invalid");
127 
128  if(ct->isText())
129  return; //nothing to do
130 
131  if(ct->isMultipart()) { //this is a multipart message
132  tmp=ct->boundary(); //get boundary-parameter
133 
134  if(!tmp.isEmpty()) {
135  Parser::MultiPart mpp(b_ody, tmp);
136  if(mpp.parse()) { //at least one part found
137 
138  c_ontents=new List();
139  c_ontents->setAutoDelete(true);
140 
141  if(ct->isSubtype("alternative")) //examine category for the sub-parts
142  cat=Headers::CCalternativePart;
143  else
144  cat=Headers::CCmixedPart; //default to "mixed"
145 
146  QCStringList parts=mpp.parts();
147  QCStringList::Iterator it;
148  for(it=parts.begin(); it!=parts.end(); ++it) { //create a new Content for every part
149  c=new Content();
150  c->setContent(*it);
151  c->parse();
152  c->contentType()->setCategory(cat); //set category of the sub-part
153  c_ontents->append(c);
154  //tqDebug("part:\n%s\n\n%s", c->h_ead.data(), c->b_ody.left(100).data());
155  }
156 
157  //the whole content is now split into single parts, so it's safe delete the message-body
158  b_ody.resize(0);
159  }
160  else { //sh*t, the parsing failed so we have to treat the message as "text/plain" instead
161  ct->setMimeType("text/plain");
162  ct->setCharset("US-ASCII");
163  }
164  }
165  }
166  else if (ct->mimeType()=="invalid/invalid") { //non-mime body => check for uuencoded content
167  Parser::UUEncoded uup(b_ody, rawHeader("Subject"));
168 
169  if(uup.parse()) { // yep, it is uuencoded
170 
171  if(uup.isPartial()) { // this seems to be only a part of the message so we treat it as "message/partial"
172  ct->setMimeType("message/partial");
173  //ct->setId(uniqueString()); not needed yet
174  ct->setPartialParams(uup.partialCount(), uup.partialNumber());
175  contentTransferEncoding()->setCte(Headers::CE7Bit);
176  }
177  else { //it's a complete message => treat as "multipart/mixed"
178  //the whole content is now split into single parts, so it's safe to delete the message-body
179  b_ody.resize(0);
180 
181  //binary parts
182  for (unsigned int i=0;i<uup.binaryParts().count();i++) {
183  c=new Content();
184  //generate content with mime-compliant headers
185  tmp="Content-Type: ";
186  tmp += uup.mimeTypes().at(i);
187  tmp += "; name=\"";
188  tmp += uup.filenames().at(i);
189  tmp += "\"\nContent-Transfer-Encoding: x-uuencode\nContent-Disposition: attachment; filename=\"";
190  tmp += uup.filenames().at(i);
191  tmp += "\"\n\n";
192  tmp += uup.binaryParts().at(i);
193  c->setContent(tmp);
194  addContent(c);
195  }
196 
197  if(c_ontents && c_ontents->first()) { //readd the plain text before the uuencoded part
198  c_ontents->first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+uup.textPart());
199  c_ontents->first()->contentType()->setMimeType("text/plain");
200  }
201  }
202  } else {
203  Parser::YENCEncoded yenc(b_ody);
204 
205  if ( yenc.parse()) {
206  /* If it is partial, just assume there is exactly one decoded part,
207  * and make this that part */
208  if (yenc.isPartial()) {
209  ct->setMimeType("message/partial");
210  //ct->setId(uniqueString()); not needed yet
211  ct->setPartialParams(yenc.partialCount(), yenc.partialNumber());
212  contentTransferEncoding()->setCte(Headers::CEbinary);
213  }
214  else { //it's a complete message => treat as "multipart/mixed"
215  //the whole content is now split into single parts, so it's safe to delete the message-body
216  b_ody.resize(0);
217 
218  //binary parts
219  for (unsigned int i=0;i<yenc.binaryParts().count();i++) {
220  c=new Content();
221  //generate content with mime-compliant headers
222  tmp="Content-Type: ";
223  tmp += yenc.mimeTypes().at(i);
224  tmp += "; name=\"";
225  tmp += yenc.filenames().at(i);
226  tmp += "\"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment; filename=\"";
227  tmp += yenc.filenames().at(i);
228  tmp += "\"\n\n";
229  c->setContent(tmp);
230 
231  // the bodies of yenc message parts are binary data, not null-terminated strings:
232  TQByteArray body = yenc.binaryParts()[i];
233  TQCString body_string(body.size());
234  memcpy(body_string.data(), body.data(), body.size());
235  c->setBody(body_string);
236 
237  addContent(c);
238  }
239 
240  if(c_ontents && c_ontents->first()) { //readd the plain text before the uuencoded part
241  c_ontents->first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+yenc.textPart());
242  c_ontents->first()->contentType()->setMimeType("text/plain");
243  }
244  }
245  }
246  else { //no, this doesn't look like uuencoded stuff => we treat it as "text/plain"
247  ct->setMimeType("text/plain");
248  }
249  }
250  }
251 
252  //tqDebug("void Content::parse() : finished");
253 }
254 
255 
256 void Content::assemble()
257 {
258  TQCString newHead="";
259 
260  //Content-Type
261  newHead+=contentType()->as7BitString()+"\n";
262 
263  //Content-Transfer-Encoding
264  newHead+=contentTransferEncoding()->as7BitString()+"\n";
265 
266  //Content-Description
267  Headers::Base *h=contentDescription(false);
268  if(h)
269  newHead+=h->as7BitString()+"\n";
270 
271  //Content-Disposition
272  h=contentDisposition(false);
273  if(h)
274  newHead+=h->as7BitString()+"\n";
275 
276  h_ead=newHead;
277 }
278 
279 
280 void Content::clear()
281 {
282  delete h_eaders;
283  h_eaders=0;
284  delete c_ontents;
285  c_ontents=0;
286  h_ead.resize(0);
287  b_ody.resize(0);
288 }
289 
290 
291 TQCString Content::encodedContent(bool useCrLf)
292 {
293  TQCString e;
294 
295  // hack to convert articles with uuencoded or yencoded binaries into
296  // proper mime-compliant articles
297  if(c_ontents && !c_ontents->isEmpty()) {
298  bool convertNonMimeBinaries=false;
299 
300  // reencode non-mime binaries...
301  for(Content *c=c_ontents->first(); c; c=c_ontents->next()) {
302  if ((c->contentTransferEncoding(true)->cte()==Headers::CEuuenc) ||
303  (c->contentTransferEncoding(true)->cte()==Headers::CEbinary)) {
304  convertNonMimeBinaries=true;
305  c->b_ody = KCodecs::base64Encode(c->decodedContent(), true);
306  c->b_ody.append("\n");
307  c->contentTransferEncoding(true)->setCte(Headers::CEbase64);
308  c->contentTransferEncoding(true)->setDecoded(false);
309  c->removeHeader("Content-Description");
310  c->assemble();
311  }
312  }
313 
314  // add proper mime headers...
315  if (convertNonMimeBinaries) {
316  h_ead.replace(TQRegExp("MIME-Version: .*\\n"),"");
317  h_ead.replace(TQRegExp("Content-Type: .*\\n"),"");
318  h_ead.replace(TQRegExp("Content-Transfer-Encoding: .*\\n"),"");
319  h_ead+="MIME-Version: 1.0\n";
320  h_ead+=contentType(true)->as7BitString()+"\n";
321  h_ead+=contentTransferEncoding(true)->as7BitString()+"\n";
322  }
323  }
324 
325  //head
326  e=h_ead.copy();
327  e+="\n";
328 
329  //body
330  if(!b_ody.isEmpty()) { //this message contains only one part
331  Headers::CTEncoding *enc=contentTransferEncoding();
332 
333  if(enc->needToEncode()) {
334  if(enc->cte()==Headers::CEquPr) {
335  TQByteArray temp(b_ody.length());
336  memcpy(temp.data(), b_ody.data(), b_ody.length());
337  e+=KCodecs::quotedPrintableEncode(temp, false);
338  } else {
339  e+=KCodecs::base64Encode(b_ody, true);
340  e+="\n";
341  }
342  }
343  else
344  e+=b_ody;
345  }
346  else if(c_ontents && !c_ontents->isEmpty()) { //this is a multipart message
347  Headers::ContentType *ct=contentType();
348  TQCString boundary="\n--"+ct->boundary();
349 
350  //add all (encoded) contents separated by boundaries
351  for(Content *c=c_ontents->first(); c; c=c_ontents->next()) {
352  e+=boundary+"\n";
353  e+=c->encodedContent(false); // don't convert LFs here, we do that later!!!!!
354  }
355  //finally append the closing boundary
356  e+=boundary+"--\n";
357  };
358 
359  if(useCrLf)
360  return LFtoCRLF(e);
361  else
362  return e;
363 }
364 
365 
366 TQByteArray Content::decodedContent()
367 {
368  TQByteArray temp, ret;
369  Headers::CTEncoding *ec=contentTransferEncoding();
370  bool removeTrailingNewline=false;
371  int size=ec->cte()==Headers::CEbinary ? b_ody.size() : b_ody.length();
372 
373  if (size==0)
374  return ret;
375 
376  temp.resize(size);
377  memcpy(temp.data(), b_ody.data(), size);
378 
379  if(ec->decoded()) {
380  ret = temp;
381  removeTrailingNewline=true;
382  } else {
383  switch(ec->cte()) {
384  case Headers::CEbase64 :
385  KCodecs::base64Decode(temp, ret);
386  break;
387  case Headers::CEquPr :
388  ret = KCodecs::quotedPrintableDecode(b_ody);
389  ret.resize(ret.size()-1); // remove null-char
390  removeTrailingNewline=true;
391  break;
392  case Headers::CEuuenc :
393  KCodecs::uudecode(temp, ret);
394  break;
395  case Headers::CEbinary :
396  ret = temp;
397  removeTrailingNewline=false;
398  break;
399  default :
400  ret = temp;
401  removeTrailingNewline=true;
402  }
403  }
404 
405  if (removeTrailingNewline && (ret.size()>0) && (ret[ret.size()-1] == '\n'))
406  ret.resize(ret.size()-1);
407 
408  return ret;
409 }
410 
411 
412 void Content::decodedText(TQString &s, bool trimText,
413  bool removeTrailingNewlines)
414 {
415  if(!decodeText()) //this is not a text content !!
416  return;
417 
418  bool ok=true;
419  TQTextCodec *codec=TDEGlobal::charsets()->codecForName(contentType()->charset(),ok);
420 
421  s=codec->toUnicode(b_ody.data(), b_ody.length());
422 
423  if (trimText && removeTrailingNewlines) {
424  int i;
425  for (i=s.length()-1; i>=0; i--)
426  if (!s[i].isSpace())
427  break;
428  s.truncate(i+1);
429  } else {
430  if (s.right(1)=="\n")
431  s.truncate(s.length()-1); // remove trailing new-line
432  }
433 }
434 
435 
436 void Content::decodedText(TQStringList &l, bool trimText,
437  bool removeTrailingNewlines)
438 {
439  if(!decodeText()) //this is not a text content !!
440  return;
441 
442  TQString unicode;
443  bool ok=true;
444 
445  TQTextCodec *codec=TDEGlobal::charsets()->codecForName(contentType()->charset(),ok);
446 
447  unicode=codec->toUnicode(b_ody.data(), b_ody.length());
448 
449  if (trimText && removeTrailingNewlines) {
450  int i;
451  for (i=unicode.length()-1; i>=0; i--)
452  if (!unicode[i].isSpace())
453  break;
454  unicode.truncate(i+1);
455  } else {
456  if (unicode.right(1)=="\n")
457  unicode.truncate(unicode.length()-1); // remove trailing new-line
458  }
459 
460  l=TQStringList::split('\n', unicode, true); //split the string at linebreaks
461 }
462 
463 
464 void Content::fromUnicodeString(const TQString &s)
465 {
466  bool ok=true;
467  TQTextCodec *codec=TDEGlobal::charsets()->codecForName(contentType()->charset(),ok);
468 
469  if(!ok) { // no suitable codec found => try local settings and hope the best ;-)
470  codec=TDEGlobal::locale()->codecForEncoding();
471  TQCString chset=TDEGlobal::locale()->encoding();
472  contentType()->setCharset(chset);
473  }
474 
475  b_ody=codec->fromUnicode(s);
476  contentTransferEncoding()->setDecoded(true); //text is always decoded
477 }
478 
479 
480 Content* Content::textContent()
481 {
482  Content *ret=0;
483 
484  //return the first content with mimetype=text/*
485  if(contentType()->isText())
486  ret=this;
487  else if(c_ontents)
488  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
489  if( (ret=c->textContent())!=0 )
490  break;
491 
492  return ret;
493 }
494 
495 
496 void Content::attachments(Content::List *dst, bool incAlternatives)
497 {
498  dst->setAutoDelete(false); //don't delete the contents
499 
500  if(!c_ontents)
501  dst->append(this);
502  else {
503  for(Content *c=c_ontents->first(); c; c=c_ontents->next()) {
504  if( !incAlternatives && c->contentType()->category()==Headers::CCalternativePart)
505  continue;
506  else
507  c->attachments(dst, incAlternatives);
508  }
509  }
510 
511  if(type()!=ATmimeContent) { // this is the toplevel article
512  Content *text=textContent();
513  if(text)
514  dst->removeRef(text);
515  }
516 }
517 
518 
519 void Content::addContent(Content *c, bool prepend)
520 {
521  if(!c_ontents) { // this message is not multipart yet
522  c_ontents=new List();
523  c_ontents->setAutoDelete(true);
524 
525  // first we convert the body to a content
526  Content *main=new Content();
527 
528  //the Mime-Headers are needed, so we move them to the new content
529  if(h_eaders) {
530 
531  main->h_eaders=new Headers::Base::List();
532  main->h_eaders->setAutoDelete(true);
533 
534  Headers::Base::List srcHdrs=(*h_eaders);
535  srcHdrs.setAutoDelete(false);
536  int idx=0;
537  for(Headers::Base *h=srcHdrs.first(); h; h=srcHdrs.next()) {
538  if(h->isMimeHeader()) {
539  //remove from this content
540  idx=h_eaders->findRef(h);
541  h_eaders->take(idx);
542  //append to new content
543  main->h_eaders->append(h);
544  }
545  }
546  }
547 
548  //"main" is now part of a multipart/mixed message
549  main->contentType()->setCategory(Headers::CCmixedPart);
550 
551  //the head of "main" is empty, so we assemble it
552  main->assemble();
553 
554  //now we can copy the body and append the new content;
555  main->b_ody=b_ody.copy();
556  c_ontents->append(main);
557  b_ody.resize(0); //not longer needed
558 
559 
560  //finally we have to convert this article to "multipart/mixed"
561  Headers::ContentType *ct=contentType();
562  ct->setMimeType("multipart/mixed");
563  ct->setBoundary(multiPartBoundary());
564  ct->setCategory(Headers::CCcontainer);
565  contentTransferEncoding()->clear(); // 7Bit, decoded
566 
567  }
568  //here we actually add the content
569  if(prepend)
570  c_ontents->insert(0, c);
571  else
572  c_ontents->append(c);
573 }
574 
575 
576 void Content::removeContent(Content *c, bool del)
577 {
578  if(!c_ontents) // what the ..
579  return;
580 
581  int idx=0;
582  if(del)
583  c_ontents->removeRef(c);
584  else {
585  idx=c_ontents->findRef(c);
586  c_ontents->take(idx);
587  }
588 
589  //only one content left => turn this message in a single-part
590  if(c_ontents->count()==1) {
591  Content *main=c_ontents->first();
592 
593  //first we have to move the mime-headers
594  if(main->h_eaders) {
595  if(!h_eaders) {
596  h_eaders=new Headers::Base::List();
597  h_eaders->setAutoDelete(true);
598  }
599 
600  Headers::Base::List mainHdrs=(*(main->h_eaders));
601  mainHdrs.setAutoDelete(false);
602 
603  for(Headers::Base *h=mainHdrs.first(); h; h=mainHdrs.next()) {
604  if(h->isMimeHeader()) {
605  removeHeader(h->type()); //remove the old header first
606  h_eaders->append(h); //now append the new one
607  idx=main->h_eaders->findRef(h);
608  main->h_eaders->take(idx); //remove from the old content
609  kdDebug(5003) << "Content::removeContent(Content *c, bool del) : mime-header moved: "
610  << h->as7BitString() << endl;
611  }
612  }
613  }
614 
615  //now we can copy the body
616  b_ody=main->b_ody.copy();
617 
618  //finally we can delete the content list
619  delete c_ontents;
620  c_ontents=0;
621  }
622 }
623 
624 
625 void Content::changeEncoding(Headers::contentEncoding e)
626 {
627  Headers::CTEncoding *enc=contentTransferEncoding();
628  if(enc->cte()==e) //nothing to do
629  return;
630 
631  if(decodeText())
632  enc->setCte(e); // text is not encoded until it's sent or saved so we just set the new encoding
633  else { // this content contains non textual data, that has to be re-encoded
634 
635  if(e!=Headers::CEbase64) {
636  //kdWarning(5003) << "Content::changeEncoding() : non textual data and encoding != base64 - this should not happen\n => forcing base64" << endl;
637  e=Headers::CEbase64;
638  }
639 
640  if(enc->cte()!=e) { // ok, we reencode the content using base64
641  b_ody = KCodecs::base64Encode(decodedContent(), true);
642  b_ody.append("\n");
643  enc->setCte(e); //set encoding
644  enc->setDecoded(false);
645  }
646  }
647 }
648 
649 
650 void Content::toStream(TQTextStream &ts, bool scrambleFromLines)
651 {
652  TQCString ret=encodedContent(false);
653 
654  if (scrambleFromLines)
655  ret.replace(TQRegExp("\\n\\nFrom "), "\n\n>From ");
656 
657  ts << ret;
658 }
659 
660 
661 Headers::Generic* Content::getNextHeader(TQCString &head)
662 {
663  int pos1=-1, pos2=0, len=head.length()-1;
664  bool folded(false);
665  Headers::Generic *header=0;
666 
667  pos1 = head.find(": ");
668 
669  if (pos1>-1) { //there is another header
670  pos2=pos1+=2; //skip the name
671 
672  if (head[pos2]!='\n') { // check if the header is not empty
673  while(1) {
674  pos2=head.find("\n", pos2+1);
675  if(pos2==-1 || pos2==len || ( head[pos2+1]!=' ' && head[pos2+1]!='\t') ) //break if we reach the end of the string, honor folded lines
676  break;
677  else
678  folded = true;
679  }
680  }
681 
682  if(pos2<0) pos2=len+1; //take the rest of the string
683 
684  if (!folded)
685  header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1));
686  else
687  header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1).replace(TQRegExp("\\s*\\n\\s*")," "));
688 
689  head.remove(0,pos2+1);
690  }
691  else {
692  head = "";
693  }
694 
695  return header;
696 }
697 
698 
699 Headers::Base* Content::getHeaderByType(const char *type)
700 {
701  if(!type)
702  return 0;
703 
704  Headers::Base *h=0;
705  //first we check if the requested header is already cached
706  if(h_eaders)
707  for(h=h_eaders->first(); h; h=h_eaders->next())
708  if(h->is(type)) return h; //found
709 
710  //now we look for it in the article head
711  TQCString raw=rawHeader(type);
712  if(!raw.isEmpty()) { //ok, we found it
713  //choose a suitable header class
714  if(strcasecmp("Message-Id", type)==0)
715  h=new Headers::MessageID(this, raw);
716  else if(strcasecmp("Subject", type)==0)
717  h=new Headers::Subject(this, raw);
718  else if(strcasecmp("Date", type)==0)
719  h=new Headers::Date(this, raw);
720  else if(strcasecmp("From", type)==0)
721  h=new Headers::From(this, raw);
722  else if(strcasecmp("Organization", type)==0)
723  h=new Headers::Organization(this, raw);
724  else if(strcasecmp("Reply-To", type)==0)
725  h=new Headers::ReplyTo(this, raw);
726  else if(strcasecmp("Mail-Copies-To", type)==0)
727  h=new Headers::MailCopiesTo(this, raw);
728  else if(strcasecmp("To", type)==0)
729  h=new Headers::To(this, raw);
730  else if(strcasecmp("CC", type)==0)
731  h=new Headers::CC(this, raw);
732  else if(strcasecmp("BCC", type)==0)
733  h=new Headers::BCC(this, raw);
734  else if(strcasecmp("Newsgroups", type)==0)
735  h=new Headers::Newsgroups(this, raw);
736  else if(strcasecmp("Followup-To", type)==0)
737  h=new Headers::FollowUpTo(this, raw);
738  else if(strcasecmp("References", type)==0)
739  h=new Headers::References(this, raw);
740  else if(strcasecmp("Lines", type)==0)
741  h=new Headers::Lines(this, raw);
742  else if(strcasecmp("Content-Type", type)==0)
743  h=new Headers::ContentType(this, raw);
744  else if(strcasecmp("Content-Transfer-Encoding", type)==0)
745  h=new Headers::CTEncoding(this, raw);
746  else if(strcasecmp("Content-Disposition", type)==0)
747  h=new Headers::CDisposition(this, raw);
748  else if(strcasecmp("Content-Description", type)==0)
749  h=new Headers::CDescription(this, raw);
750  else
751  h=new Headers::Generic(type, this, raw);
752 
753  if(!h_eaders) {
754  h_eaders=new Headers::Base::List();
755  h_eaders->setAutoDelete(true);
756  }
757 
758  h_eaders->append(h); //add to cache
759  return h;
760  }
761  else
762  return 0; //header not found
763 }
764 
765 
766 void Content::setHeader(Headers::Base *h)
767 {
768  if(!h) return;
769  removeHeader(h->type());
770  if(!h_eaders) {
771  h_eaders=new Headers::Base::List();
772  h_eaders->setAutoDelete(true);
773  }
774  h_eaders->append(h);
775 }
776 
777 
778 bool Content::removeHeader(const char *type)
779 {
780  if(h_eaders)
781  for(Headers::Base *h=h_eaders->first(); h; h=h_eaders->next())
782  if(h->is(type))
783  return h_eaders->remove();
784 
785  return false;
786 }
787 
788 
789 int Content::size()
790 {
791  int ret=b_ody.length();
792 
793  if(contentTransferEncoding()->cte()==Headers::CEbase64)
794  return (ret*3/4); //base64 => 6 bit per byte
795 
796  return ret;
797 }
798 
799 
800 int Content::storageSize()
801 {
802  int s=h_ead.size();
803 
804  if(!c_ontents)
805  s+=b_ody.size();
806  else {
807  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
808  s+=c->storageSize();
809  }
810 
811  return s;
812 }
813 
814 
815 int Content::lineCount()
816 {
817  int ret=0;
818  if(type()==ATmimeContent)
819  ret+=h_ead.contains('\n');
820  ret+=b_ody.contains('\n');
821 
822  if(c_ontents && !c_ontents->isEmpty())
823  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
824  ret+=c->lineCount();
825 
826  return ret;
827 }
828 
829 
830 TQCString Content::rawHeader(const char *name)
831 {
832  return extractHeader(h_ead, name);
833 }
834 
835 
836 bool Content::decodeText()
837 {
838  Headers::CTEncoding *enc=contentTransferEncoding();
839 
840  if(!contentType()->isText())
841  return false; //non textual data cannot be decoded here => use decodedContent() instead
842  if(enc->decoded())
843  return true; //nothing to do
844 
845  switch(enc->cte()) {
846  case Headers::CEbase64 :
847  b_ody=KCodecs::base64Decode(b_ody);
848  b_ody.append("\n");
849  break;
850  case Headers::CEquPr :
851  b_ody=KCodecs::quotedPrintableDecode(b_ody);
852  break;
853  case Headers::CEuuenc :
854  b_ody=KCodecs::uudecode(b_ody);
855  b_ody.append("\n");
856  break;
857  case Headers::CEbinary :
858  b_ody=TQCString(b_ody.data(), b_ody.size()+1);
859  b_ody.append("\n");
860  default :
861  break;
862  }
863 
864  enc->setDecoded(true);
865  return true;
866 }
867 
868 
869 void Content::setDefaultCharset(const TQCString &cs)
870 {
871  d_efaultCS = KMime::cachedCharset(cs);
872 
873  if(c_ontents && !c_ontents->isEmpty())
874  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
875  c->setDefaultCharset(cs);
876 
877  // reparse the part and its sub-parts in order
878  // to clear cached header values
879  parse();
880 }
881 
882 
883 void Content::setForceDefaultCS(bool b)
884 {
885  f_orceDefaultCS=b;
886 
887  if(c_ontents && !c_ontents->isEmpty())
888  for(Content *c=c_ontents->first(); c; c=c_ontents->next())
889  c->setForceDefaultCS(b);
890 
891  // reparse the part and its sub-parts in order
892  // to clear cached header values
893  parse();
894 }
895 
896 
897 } // namespace KMime
This class encapsulates a mime-encoded content.
Definition: kmime_content.h:59
Baseclass of all header-classes.
virtual const char * type()
Return the type of this header (e.g.
virtual TQCString as7BitString(bool=true)
Return the encoded header.
bool isMimeHeader()
Check if this header is a MIME header.
bool is(const char *t)
Check if this header is of type t.
Represents a "Date" header.
Represents a "Followup-To" header.
Represents an arbitrary header, that can contain any header-field.
Represents a "Lines" header.
Represents a "Newsgroups" header.
Represents a "Organization" header.
Represents a "Subject" header.
Helper-class: splits a multipart-message into single parts as described in RFC 2046.
Definition: kmime_parsers.h:31
Helper-class: tries to extract the data from a possibly uuencoded message.
Definition: kmime_parsers.h:80
Helper-class: tries to extract the data from a possibly yenc encoded message.
Definition: kmime_parsers.h:97