kmime_content.cpp
00001 /* 00002 kmime_content.cpp 00003 00004 KMime, the KDE internet mail/usenet news message library. 00005 Copyright (c) 2001 the KMime authors. 00006 See file AUTHORS for details 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 You should have received a copy of the GNU General Public License 00013 along with this program; if not, write to the Free Software Foundation, 00014 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US 00015 */ 00016 #include "kmime_content.h" 00017 #include "kmime_parsers.h" 00018 00019 #include <kcharsets.h> 00020 #include <kmdcodec.h> 00021 #include <kglobal.h> 00022 #include <klocale.h> 00023 #include <kdebug.h> 00024 00025 #include <tqtextcodec.h> 00026 00027 using namespace KMime; 00028 00029 namespace KMime { 00030 00031 Content::Content() 00032 : c_ontents(0), h_eaders(0), f_orceDefaultCS(false) 00033 { 00034 d_efaultCS = cachedCharset("ISO-8859-1"); 00035 } 00036 00037 00038 Content::Content(const TQCString &h, const TQCString &b) 00039 : c_ontents(0), h_eaders(0), f_orceDefaultCS(false) 00040 { 00041 d_efaultCS = cachedCharset("ISO-8859-1"); 00042 h_ead=h.copy(); 00043 b_ody=b.copy(); 00044 } 00045 00046 00047 Content::~Content() 00048 { 00049 delete c_ontents; 00050 delete h_eaders; 00051 } 00052 00053 00054 void Content::setContent(TQStrList *l) 00055 { 00056 //qDebug("Content::setContent(TQStrList *l) : start"); 00057 h_ead.resize(0); 00058 b_ody.resize(0); 00059 00060 //usage of textstreams is much faster than simply appending the strings 00061 TQTextStream hts(h_ead, IO_WriteOnly), 00062 bts(b_ody, IO_WriteOnly); 00063 hts.setEncoding(TQTextStream::Latin1); 00064 bts.setEncoding(TQTextStream::Latin1); 00065 00066 bool isHead=true; 00067 for(char *line=l->first(); line; line=l->next()) { 00068 if(isHead && line[0]=='\0') { 00069 isHead=false; 00070 continue; 00071 } 00072 if(isHead) 00073 hts << line << "\n"; 00074 else 00075 bts << line << "\n"; 00076 } 00077 00078 //terminate strings 00079 hts << '\0'; 00080 bts << '\0'; 00081 00082 //qDebug("Content::setContent(TQStrList *l) : finished"); 00083 } 00084 00085 00086 void Content::setContent(const TQCString &s) 00087 { 00088 int pos=s.find("\n\n", 0); 00089 if(pos>-1) { 00090 h_ead=s.left(++pos); //header *must* end with "\n" !! 00091 b_ody=s.mid(pos+1, s.length()-pos-1); 00092 } 00093 else 00094 h_ead=s; 00095 } 00096 00097 00098 //parse the message, split multiple parts 00099 void Content::parse() 00100 { 00101 //qDebug("void Content::parse() : start"); 00102 delete h_eaders; 00103 h_eaders=0; 00104 00105 // check this part has already been partioned into subparts. 00106 // if this is the case, we will not try to reparse the body 00107 // of this part. 00108 if ((b_ody.size() == 0) && (c_ontents != 0) && !c_ontents->isEmpty()) { 00109 // reparse all sub parts 00110 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00111 c->parse(); 00112 return; 00113 } 00114 00115 delete c_ontents; 00116 c_ontents=0; 00117 00118 Headers::ContentType *ct=contentType(); 00119 TQCString tmp; 00120 Content *c; 00121 Headers::contentCategory cat; 00122 00123 // just "text" as mimetype is suspicious, perhaps this article was 00124 // generated by broken software, better check for uuencoded binaries 00125 if (ct->mimeType()=="text") 00126 ct->setMimeType("invalid/invalid"); 00127 00128 if(ct->isText()) 00129 return; //nothing to do 00130 00131 if(ct->isMultipart()) { //this is a multipart message 00132 tmp=ct->boundary(); //get boundary-parameter 00133 00134 if(!tmp.isEmpty()) { 00135 Parser::MultiPart mpp(b_ody, tmp); 00136 if(mpp.parse()) { //at least one part found 00137 00138 c_ontents=new List(); 00139 c_ontents->setAutoDelete(true); 00140 00141 if(ct->isSubtype("alternative")) //examine category for the sub-parts 00142 cat=Headers::CCalternativePart; 00143 else 00144 cat=Headers::CCmixedPart; //default to "mixed" 00145 00146 QCStringList parts=mpp.parts(); 00147 QCStringList::Iterator it; 00148 for(it=parts.begin(); it!=parts.end(); ++it) { //create a new Content for every part 00149 c=new Content(); 00150 c->setContent(*it); 00151 c->parse(); 00152 c->contentType()->setCategory(cat); //set category of the sub-part 00153 c_ontents->append(c); 00154 //qDebug("part:\n%s\n\n%s", c->h_ead.data(), c->b_ody.left(100).data()); 00155 } 00156 00157 //the whole content is now split into single parts, so it's safe delete the message-body 00158 b_ody.resize(0); 00159 } 00160 else { //sh*t, the parsing failed so we have to treat the message as "text/plain" instead 00161 ct->setMimeType("text/plain"); 00162 ct->setCharset("US-ASCII"); 00163 } 00164 } 00165 } 00166 else if (ct->mimeType()=="invalid/invalid") { //non-mime body => check for uuencoded content 00167 Parser::UUEncoded uup(b_ody, rawHeader("Subject")); 00168 00169 if(uup.parse()) { // yep, it is uuencoded 00170 00171 if(uup.isPartial()) { // this seems to be only a part of the message so we treat it as "message/partial" 00172 ct->setMimeType("message/partial"); 00173 //ct->setId(uniqueString()); not needed yet 00174 ct->setPartialParams(uup.partialCount(), uup.partialNumber()); 00175 contentTransferEncoding()->setCte(Headers::CE7Bit); 00176 } 00177 else { //it's a complete message => treat as "multipart/mixed" 00178 //the whole content is now split into single parts, so it's safe to delete the message-body 00179 b_ody.resize(0); 00180 00181 //binary parts 00182 for (unsigned int i=0;i<uup.binaryParts().count();i++) { 00183 c=new Content(); 00184 //generate content with mime-compliant headers 00185 tmp="Content-Type: "; 00186 tmp += uup.mimeTypes().at(i); 00187 tmp += "; name=\""; 00188 tmp += uup.filenames().at(i); 00189 tmp += "\"\nContent-Transfer-Encoding: x-uuencode\nContent-Disposition: attachment; filename=\""; 00190 tmp += uup.filenames().at(i); 00191 tmp += "\"\n\n"; 00192 tmp += uup.binaryParts().at(i); 00193 c->setContent(tmp); 00194 addContent(c); 00195 } 00196 00197 if(c_ontents && c_ontents->first()) { //readd the plain text before the uuencoded part 00198 c_ontents->first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+uup.textPart()); 00199 c_ontents->first()->contentType()->setMimeType("text/plain"); 00200 } 00201 } 00202 } else { 00203 Parser::YENCEncoded yenc(b_ody); 00204 00205 if ( yenc.parse()) { 00206 /* If it is partial, just assume there is exactly one decoded part, 00207 * and make this that part */ 00208 if (yenc.isPartial()) { 00209 ct->setMimeType("message/partial"); 00210 //ct->setId(uniqueString()); not needed yet 00211 ct->setPartialParams(yenc.partialCount(), yenc.partialNumber()); 00212 contentTransferEncoding()->setCte(Headers::CEbinary); 00213 } 00214 else { //it's a complete message => treat as "multipart/mixed" 00215 //the whole content is now split into single parts, so it's safe to delete the message-body 00216 b_ody.resize(0); 00217 00218 //binary parts 00219 for (unsigned int i=0;i<yenc.binaryParts().count();i++) { 00220 c=new Content(); 00221 //generate content with mime-compliant headers 00222 tmp="Content-Type: "; 00223 tmp += yenc.mimeTypes().at(i); 00224 tmp += "; name=\""; 00225 tmp += yenc.filenames().at(i); 00226 tmp += "\"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment; filename=\""; 00227 tmp += yenc.filenames().at(i); 00228 tmp += "\"\n\n"; 00229 c->setContent(tmp); 00230 00231 // the bodies of yenc message parts are binary data, not null-terminated strings: 00232 TQByteArray body = yenc.binaryParts()[i]; 00233 TQCString body_string(body.size()); 00234 memcpy(body_string.data(), body.data(), body.size()); 00235 c->setBody(body_string); 00236 00237 addContent(c); 00238 } 00239 00240 if(c_ontents && c_ontents->first()) { //readd the plain text before the uuencoded part 00241 c_ontents->first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+yenc.textPart()); 00242 c_ontents->first()->contentType()->setMimeType("text/plain"); 00243 } 00244 } 00245 } 00246 else { //no, this doesn't look like uuencoded stuff => we treat it as "text/plain" 00247 ct->setMimeType("text/plain"); 00248 } 00249 } 00250 } 00251 00252 //qDebug("void Content::parse() : finished"); 00253 } 00254 00255 00256 void Content::assemble() 00257 { 00258 TQCString newHead=""; 00259 00260 //Content-Type 00261 newHead+=contentType()->as7BitString()+"\n"; 00262 00263 //Content-Transfer-Encoding 00264 newHead+=contentTransferEncoding()->as7BitString()+"\n"; 00265 00266 //Content-Description 00267 Headers::Base *h=contentDescription(false); 00268 if(h) 00269 newHead+=h->as7BitString()+"\n"; 00270 00271 //Content-Disposition 00272 h=contentDisposition(false); 00273 if(h) 00274 newHead+=h->as7BitString()+"\n"; 00275 00276 h_ead=newHead; 00277 } 00278 00279 00280 void Content::clear() 00281 { 00282 delete h_eaders; 00283 h_eaders=0; 00284 delete c_ontents; 00285 c_ontents=0; 00286 h_ead.resize(0); 00287 b_ody.resize(0); 00288 } 00289 00290 00291 TQCString Content::encodedContent(bool useCrLf) 00292 { 00293 TQCString e; 00294 00295 // hack to convert articles with uuencoded or yencoded binaries into 00296 // proper mime-compliant articles 00297 if(c_ontents && !c_ontents->isEmpty()) { 00298 bool convertNonMimeBinaries=false; 00299 00300 // reencode non-mime binaries... 00301 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) { 00302 if ((c->contentTransferEncoding(true)->cte()==Headers::CEuuenc) || 00303 (c->contentTransferEncoding(true)->cte()==Headers::CEbinary)) { 00304 convertNonMimeBinaries=true; 00305 c->b_ody = KCodecs::base64Encode(c->decodedContent(), true); 00306 c->b_ody.append("\n"); 00307 c->contentTransferEncoding(true)->setCte(Headers::CEbase64); 00308 c->contentTransferEncoding(true)->setDecoded(false); 00309 c->removeHeader("Content-Description"); 00310 c->assemble(); 00311 } 00312 } 00313 00314 // add proper mime headers... 00315 if (convertNonMimeBinaries) { 00316 h_ead.replace(TQRegExp("MIME-Version: .*\\n"),""); 00317 h_ead.replace(TQRegExp("Content-Type: .*\\n"),""); 00318 h_ead.replace(TQRegExp("Content-Transfer-Encoding: .*\\n"),""); 00319 h_ead+="MIME-Version: 1.0\n"; 00320 h_ead+=contentType(true)->as7BitString()+"\n"; 00321 h_ead+=contentTransferEncoding(true)->as7BitString()+"\n"; 00322 } 00323 } 00324 00325 //head 00326 e=h_ead.copy(); 00327 e+="\n"; 00328 00329 //body 00330 if(!b_ody.isEmpty()) { //this message contains only one part 00331 Headers::CTEncoding *enc=contentTransferEncoding(); 00332 00333 if(enc->needToEncode()) { 00334 if(enc->cte()==Headers::CEquPr) { 00335 TQByteArray temp(b_ody.length()); 00336 memcpy(temp.data(), b_ody.data(), b_ody.length()); 00337 e+=KCodecs::quotedPrintableEncode(temp, false); 00338 } else { 00339 e+=KCodecs::base64Encode(b_ody, true); 00340 e+="\n"; 00341 } 00342 } 00343 else 00344 e+=b_ody; 00345 } 00346 else if(c_ontents && !c_ontents->isEmpty()) { //this is a multipart message 00347 Headers::ContentType *ct=contentType(); 00348 TQCString boundary="\n--"+ct->boundary(); 00349 00350 //add all (encoded) contents separated by boundaries 00351 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) { 00352 e+=boundary+"\n"; 00353 e+=c->encodedContent(false); // don't convert LFs here, we do that later!!!!! 00354 } 00355 //finally append the closing boundary 00356 e+=boundary+"--\n"; 00357 }; 00358 00359 if(useCrLf) 00360 return LFtoCRLF(e); 00361 else 00362 return e; 00363 } 00364 00365 00366 TQByteArray Content::decodedContent() 00367 { 00368 TQByteArray temp, ret; 00369 Headers::CTEncoding *ec=contentTransferEncoding(); 00370 bool removeTrailingNewline=false; 00371 int size=ec->cte()==Headers::CEbinary ? b_ody.size() : b_ody.length(); 00372 00373 if (size==0) 00374 return ret; 00375 00376 temp.resize(size); 00377 memcpy(temp.data(), b_ody.data(), size); 00378 00379 if(ec->decoded()) { 00380 ret = temp; 00381 removeTrailingNewline=true; 00382 } else { 00383 switch(ec->cte()) { 00384 case Headers::CEbase64 : 00385 KCodecs::base64Decode(temp, ret); 00386 break; 00387 case Headers::CEquPr : 00388 ret = KCodecs::quotedPrintableDecode(b_ody); 00389 ret.resize(ret.size()-1); // remove null-char 00390 removeTrailingNewline=true; 00391 break; 00392 case Headers::CEuuenc : 00393 KCodecs::uudecode(temp, ret); 00394 break; 00395 case Headers::CEbinary : 00396 ret = temp; 00397 removeTrailingNewline=false; 00398 break; 00399 default : 00400 ret = temp; 00401 removeTrailingNewline=true; 00402 } 00403 } 00404 00405 if (removeTrailingNewline && (ret.size()>0) && (ret[ret.size()-1] == '\n')) 00406 ret.resize(ret.size()-1); 00407 00408 return ret; 00409 } 00410 00411 00412 void Content::decodedText(TQString &s, bool trimText, 00413 bool removeTrailingNewlines) 00414 { 00415 if(!decodeText()) //this is not a text content !! 00416 return; 00417 00418 bool ok=true; 00419 TQTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); 00420 00421 s=codec->toUnicode(b_ody.data(), b_ody.length()); 00422 00423 if (trimText && removeTrailingNewlines) { 00424 int i; 00425 for (i=s.length()-1; i>=0; i--) 00426 if (!s[i].isSpace()) 00427 break; 00428 s.truncate(i+1); 00429 } else { 00430 if (s.right(1)=="\n") 00431 s.truncate(s.length()-1); // remove trailing new-line 00432 } 00433 } 00434 00435 00436 void Content::decodedText(TQStringList &l, bool trimText, 00437 bool removeTrailingNewlines) 00438 { 00439 if(!decodeText()) //this is not a text content !! 00440 return; 00441 00442 TQString unicode; 00443 bool ok=true; 00444 00445 TQTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); 00446 00447 unicode=codec->toUnicode(b_ody.data(), b_ody.length()); 00448 00449 if (trimText && removeTrailingNewlines) { 00450 int i; 00451 for (i=unicode.length()-1; i>=0; i--) 00452 if (!unicode[i].isSpace()) 00453 break; 00454 unicode.truncate(i+1); 00455 } else { 00456 if (unicode.right(1)=="\n") 00457 unicode.truncate(unicode.length()-1); // remove trailing new-line 00458 } 00459 00460 l=TQStringList::split('\n', unicode, true); //split the string at linebreaks 00461 } 00462 00463 00464 void Content::fromUnicodeString(const TQString &s) 00465 { 00466 bool ok=true; 00467 TQTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); 00468 00469 if(!ok) { // no suitable codec found => try local settings and hope the best ;-) 00470 codec=KGlobal::locale()->codecForEncoding(); 00471 TQCString chset=KGlobal::locale()->encoding(); 00472 contentType()->setCharset(chset); 00473 } 00474 00475 b_ody=codec->fromUnicode(s); 00476 contentTransferEncoding()->setDecoded(true); //text is always decoded 00477 } 00478 00479 00480 Content* Content::textContent() 00481 { 00482 Content *ret=0; 00483 00484 //return the first content with mimetype=text/* 00485 if(contentType()->isText()) 00486 ret=this; 00487 else if(c_ontents) 00488 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00489 if( (ret=c->textContent())!=0 ) 00490 break; 00491 00492 return ret; 00493 } 00494 00495 00496 void Content::attachments(Content::List *dst, bool incAlternatives) 00497 { 00498 dst->setAutoDelete(false); //don't delete the contents 00499 00500 if(!c_ontents) 00501 dst->append(this); 00502 else { 00503 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) { 00504 if( !incAlternatives && c->contentType()->category()==Headers::CCalternativePart) 00505 continue; 00506 else 00507 c->attachments(dst, incAlternatives); 00508 } 00509 } 00510 00511 if(type()!=ATmimeContent) { // this is the toplevel article 00512 Content *text=textContent(); 00513 if(text) 00514 dst->removeRef(text); 00515 } 00516 } 00517 00518 00519 void Content::addContent(Content *c, bool prepend) 00520 { 00521 if(!c_ontents) { // this message is not multipart yet 00522 c_ontents=new List(); 00523 c_ontents->setAutoDelete(true); 00524 00525 // first we convert the body to a content 00526 Content *main=new Content(); 00527 00528 //the Mime-Headers are needed, so we move them to the new content 00529 if(h_eaders) { 00530 00531 main->h_eaders=new Headers::Base::List(); 00532 main->h_eaders->setAutoDelete(true); 00533 00534 Headers::Base::List srcHdrs=(*h_eaders); 00535 srcHdrs.setAutoDelete(false); 00536 int idx=0; 00537 for(Headers::Base *h=srcHdrs.first(); h; h=srcHdrs.next()) { 00538 if(h->isMimeHeader()) { 00539 //remove from this content 00540 idx=h_eaders->findRef(h); 00541 h_eaders->take(idx); 00542 //append to new content 00543 main->h_eaders->append(h); 00544 } 00545 } 00546 } 00547 00548 //"main" is now part of a multipart/mixed message 00549 main->contentType()->setCategory(Headers::CCmixedPart); 00550 00551 //the head of "main" is empty, so we assemble it 00552 main->assemble(); 00553 00554 //now we can copy the body and append the new content; 00555 main->b_ody=b_ody.copy(); 00556 c_ontents->append(main); 00557 b_ody.resize(0); //not longer needed 00558 00559 00560 //finally we have to convert this article to "multipart/mixed" 00561 Headers::ContentType *ct=contentType(); 00562 ct->setMimeType("multipart/mixed"); 00563 ct->setBoundary(multiPartBoundary()); 00564 ct->setCategory(Headers::CCcontainer); 00565 contentTransferEncoding()->clear(); // 7Bit, decoded 00566 00567 } 00568 //here we actually add the content 00569 if(prepend) 00570 c_ontents->insert(0, c); 00571 else 00572 c_ontents->append(c); 00573 } 00574 00575 00576 void Content::removeContent(Content *c, bool del) 00577 { 00578 if(!c_ontents) // what the .. 00579 return; 00580 00581 int idx=0; 00582 if(del) 00583 c_ontents->removeRef(c); 00584 else { 00585 idx=c_ontents->findRef(c); 00586 c_ontents->take(idx); 00587 } 00588 00589 //only one content left => turn this message in a single-part 00590 if(c_ontents->count()==1) { 00591 Content *main=c_ontents->first(); 00592 00593 //first we have to move the mime-headers 00594 if(main->h_eaders) { 00595 if(!h_eaders) { 00596 h_eaders=new Headers::Base::List(); 00597 h_eaders->setAutoDelete(true); 00598 } 00599 00600 Headers::Base::List mainHdrs=(*(main->h_eaders)); 00601 mainHdrs.setAutoDelete(false); 00602 00603 for(Headers::Base *h=mainHdrs.first(); h; h=mainHdrs.next()) { 00604 if(h->isMimeHeader()) { 00605 removeHeader(h->type()); //remove the old header first 00606 h_eaders->append(h); //now append the new one 00607 idx=main->h_eaders->findRef(h); 00608 main->h_eaders->take(idx); //remove from the old content 00609 kdDebug(5003) << "Content::removeContent(Content *c, bool del) : mime-header moved: " 00610 << h->as7BitString() << endl; 00611 } 00612 } 00613 } 00614 00615 //now we can copy the body 00616 b_ody=main->b_ody.copy(); 00617 00618 //finally we can delete the content list 00619 delete c_ontents; 00620 c_ontents=0; 00621 } 00622 } 00623 00624 00625 void Content::changeEncoding(Headers::contentEncoding e) 00626 { 00627 Headers::CTEncoding *enc=contentTransferEncoding(); 00628 if(enc->cte()==e) //nothing to do 00629 return; 00630 00631 if(decodeText()) 00632 enc->setCte(e); // text is not encoded until it's sent or saved so we just set the new encoding 00633 else { // this content contains non textual data, that has to be re-encoded 00634 00635 if(e!=Headers::CEbase64) { 00636 //kdWarning(5003) << "Content::changeEncoding() : non textual data and encoding != base64 - this should not happen\n => forcing base64" << endl; 00637 e=Headers::CEbase64; 00638 } 00639 00640 if(enc->cte()!=e) { // ok, we reencode the content using base64 00641 b_ody = KCodecs::base64Encode(decodedContent(), true); 00642 b_ody.append("\n"); 00643 enc->setCte(e); //set encoding 00644 enc->setDecoded(false); 00645 } 00646 } 00647 } 00648 00649 00650 void Content::toStream(TQTextStream &ts, bool scrambleFromLines) 00651 { 00652 TQCString ret=encodedContent(false); 00653 00654 if (scrambleFromLines) 00655 ret.replace(TQRegExp("\\n\\nFrom "), "\n\n>From "); 00656 00657 ts << ret; 00658 } 00659 00660 00661 Headers::Generic* Content::getNextHeader(TQCString &head) 00662 { 00663 int pos1=-1, pos2=0, len=head.length()-1; 00664 bool folded(false); 00665 Headers::Generic *header=0; 00666 00667 pos1 = head.find(": "); 00668 00669 if (pos1>-1) { //there is another header 00670 pos2=pos1+=2; //skip the name 00671 00672 if (head[pos2]!='\n') { // check if the header is not empty 00673 while(1) { 00674 pos2=head.find("\n", pos2+1); 00675 if(pos2==-1 || pos2==len || ( head[pos2+1]!=' ' && head[pos2+1]!='\t') ) //break if we reach the end of the string, honor folded lines 00676 break; 00677 else 00678 folded = true; 00679 } 00680 } 00681 00682 if(pos2<0) pos2=len+1; //take the rest of the string 00683 00684 if (!folded) 00685 header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1)); 00686 else 00687 header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1).replace(TQRegExp("\\s*\\n\\s*")," ")); 00688 00689 head.remove(0,pos2+1); 00690 } 00691 else { 00692 head = ""; 00693 } 00694 00695 return header; 00696 } 00697 00698 00699 Headers::Base* Content::getHeaderByType(const char *type) 00700 { 00701 if(!type) 00702 return 0; 00703 00704 Headers::Base *h=0; 00705 //first we check if the requested header is already cached 00706 if(h_eaders) 00707 for(h=h_eaders->first(); h; h=h_eaders->next()) 00708 if(h->is(type)) return h; //found 00709 00710 //now we look for it in the article head 00711 TQCString raw=rawHeader(type); 00712 if(!raw.isEmpty()) { //ok, we found it 00713 //choose a suitable header class 00714 if(strcasecmp("Message-Id", type)==0) 00715 h=new Headers::MessageID(this, raw); 00716 else if(strcasecmp("Subject", type)==0) 00717 h=new Headers::Subject(this, raw); 00718 else if(strcasecmp("Date", type)==0) 00719 h=new Headers::Date(this, raw); 00720 else if(strcasecmp("From", type)==0) 00721 h=new Headers::From(this, raw); 00722 else if(strcasecmp("Organization", type)==0) 00723 h=new Headers::Organization(this, raw); 00724 else if(strcasecmp("Reply-To", type)==0) 00725 h=new Headers::ReplyTo(this, raw); 00726 else if(strcasecmp("Mail-Copies-To", type)==0) 00727 h=new Headers::MailCopiesTo(this, raw); 00728 else if(strcasecmp("To", type)==0) 00729 h=new Headers::To(this, raw); 00730 else if(strcasecmp("CC", type)==0) 00731 h=new Headers::CC(this, raw); 00732 else if(strcasecmp("BCC", type)==0) 00733 h=new Headers::BCC(this, raw); 00734 else if(strcasecmp("Newsgroups", type)==0) 00735 h=new Headers::Newsgroups(this, raw); 00736 else if(strcasecmp("Followup-To", type)==0) 00737 h=new Headers::FollowUpTo(this, raw); 00738 else if(strcasecmp("References", type)==0) 00739 h=new Headers::References(this, raw); 00740 else if(strcasecmp("Lines", type)==0) 00741 h=new Headers::Lines(this, raw); 00742 else if(strcasecmp("Content-Type", type)==0) 00743 h=new Headers::ContentType(this, raw); 00744 else if(strcasecmp("Content-Transfer-Encoding", type)==0) 00745 h=new Headers::CTEncoding(this, raw); 00746 else if(strcasecmp("Content-Disposition", type)==0) 00747 h=new Headers::CDisposition(this, raw); 00748 else if(strcasecmp("Content-Description", type)==0) 00749 h=new Headers::CDescription(this, raw); 00750 else 00751 h=new Headers::Generic(type, this, raw); 00752 00753 if(!h_eaders) { 00754 h_eaders=new Headers::Base::List(); 00755 h_eaders->setAutoDelete(true); 00756 } 00757 00758 h_eaders->append(h); //add to cache 00759 return h; 00760 } 00761 else 00762 return 0; //header not found 00763 } 00764 00765 00766 void Content::setHeader(Headers::Base *h) 00767 { 00768 if(!h) return; 00769 removeHeader(h->type()); 00770 if(!h_eaders) { 00771 h_eaders=new Headers::Base::List(); 00772 h_eaders->setAutoDelete(true); 00773 } 00774 h_eaders->append(h); 00775 } 00776 00777 00778 bool Content::removeHeader(const char *type) 00779 { 00780 if(h_eaders) 00781 for(Headers::Base *h=h_eaders->first(); h; h=h_eaders->next()) 00782 if(h->is(type)) 00783 return h_eaders->remove(); 00784 00785 return false; 00786 } 00787 00788 00789 int Content::size() 00790 { 00791 int ret=b_ody.length(); 00792 00793 if(contentTransferEncoding()->cte()==Headers::CEbase64) 00794 return (ret*3/4); //base64 => 6 bit per byte 00795 00796 return ret; 00797 } 00798 00799 00800 int Content::storageSize() 00801 { 00802 int s=h_ead.size(); 00803 00804 if(!c_ontents) 00805 s+=b_ody.size(); 00806 else { 00807 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00808 s+=c->storageSize(); 00809 } 00810 00811 return s; 00812 } 00813 00814 00815 int Content::lineCount() 00816 { 00817 int ret=0; 00818 if(type()==ATmimeContent) 00819 ret+=h_ead.contains('\n'); 00820 ret+=b_ody.contains('\n'); 00821 00822 if(c_ontents && !c_ontents->isEmpty()) 00823 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00824 ret+=c->lineCount(); 00825 00826 return ret; 00827 } 00828 00829 00830 TQCString Content::rawHeader(const char *name) 00831 { 00832 return extractHeader(h_ead, name); 00833 } 00834 00835 00836 bool Content::decodeText() 00837 { 00838 Headers::CTEncoding *enc=contentTransferEncoding(); 00839 00840 if(!contentType()->isText()) 00841 return false; //non textual data cannot be decoded here => use decodedContent() instead 00842 if(enc->decoded()) 00843 return true; //nothing to do 00844 00845 switch(enc->cte()) { 00846 case Headers::CEbase64 : 00847 b_ody=KCodecs::base64Decode(b_ody); 00848 b_ody.append("\n"); 00849 break; 00850 case Headers::CEquPr : 00851 b_ody=KCodecs::quotedPrintableDecode(b_ody); 00852 break; 00853 case Headers::CEuuenc : 00854 b_ody=KCodecs::uudecode(b_ody); 00855 b_ody.append("\n"); 00856 break; 00857 case Headers::CEbinary : 00858 b_ody=TQCString(b_ody.data(), b_ody.size()+1); 00859 b_ody.append("\n"); 00860 default : 00861 break; 00862 } 00863 00864 enc->setDecoded(true); 00865 return true; 00866 } 00867 00868 00869 void Content::setDefaultCharset(const TQCString &cs) 00870 { 00871 d_efaultCS = KMime::cachedCharset(cs); 00872 00873 if(c_ontents && !c_ontents->isEmpty()) 00874 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00875 c->setDefaultCharset(cs); 00876 00877 // reparse the part and its sub-parts in order 00878 // to clear cached header values 00879 parse(); 00880 } 00881 00882 00883 void Content::setForceDefaultCS(bool b) 00884 { 00885 f_orceDefaultCS=b; 00886 00887 if(c_ontents && !c_ontents->isEmpty()) 00888 for(Content *c=c_ontents->first(); c; c=c_ontents->next()) 00889 c->setForceDefaultCS(b); 00890 00891 // reparse the part and its sub-parts in order 00892 // to clear cached header values 00893 parse(); 00894 } 00895 00896 00897 } // namespace KMime