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
27using namespace KMime;
28
29namespace KMime {
30
31Content::Content()
32 : c_ontents(0), h_eaders(0), f_orceDefaultCS(false)
33{
34 d_efaultCS = cachedCharset("ISO-8859-1");
35}
36
37
38Content::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
47Content::~Content()
48{
49 delete c_ontents;
50 delete h_eaders;
51}
52
53
54void 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
86void 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
99void 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
256void 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
280void 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
291TQCString 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
366TQByteArray 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
412void 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
436void 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
464void 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
480Content* 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
496void 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
519void 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
576void 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
625void 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
650void 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
661Headers::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
699Headers::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
766void 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
778bool 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
789int 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
800int 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
815int 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
830TQCString Content::rawHeader(const char *name)
831{
832 return extractHeader(h_ead, name);
833}
834
835
836bool 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
869void 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
883void 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