libkmime

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 <qtextcodec.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 QCString &h, const QCString &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(QStrList *l)
00055 {
00056   //qDebug("Content::setContent(QStrList *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   QTextStream hts(h_ead, IO_WriteOnly),
00062               bts(b_ody, IO_WriteOnly);
00063   hts.setEncoding(QTextStream::Latin1);
00064   bts.setEncoding(QTextStream::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(QStrList *l) : finished");
00083 }
00084 
00085 
00086 void Content::setContent(const QCString &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   QCString 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             QByteArray body = yenc.binaryParts()[i];
00233             QCString 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   QCString 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 QCString Content::encodedContent(bool useCrLf)
00292 {
00293   QCString 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(QRegExp("MIME-Version: .*\\n"),"");
00317       h_ead.replace(QRegExp("Content-Type: .*\\n"),"");
00318       h_ead.replace(QRegExp("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         QByteArray 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     QCString 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 QByteArray Content::decodedContent()
00367 {
00368   QByteArray 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(QString &s, bool trimText,
00413               bool removeTrailingNewlines)
00414 {
00415   if(!decodeText()) //this is not a text content !!
00416     return;
00417 
00418   bool ok=true;
00419   QTextCodec *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(QStringList &l, bool trimText,
00437               bool removeTrailingNewlines)
00438 {
00439   if(!decodeText()) //this is not a text content !!
00440     return;
00441 
00442   QString unicode;
00443   bool ok=true;
00444 
00445   QTextCodec *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=QStringList::split('\n', unicode, true); //split the string at linebreaks
00461 }
00462 
00463 
00464 void Content::fromUnicodeString(const QString &s)
00465 {
00466   bool ok=true;
00467   QTextCodec *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     QCString 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(QTextStream &ts, bool scrambleFromLines)
00651 {
00652   QCString ret=encodedContent(false);
00653 
00654   if (scrambleFromLines)
00655     ret.replace(QRegExp("\\n\\nFrom "), "\n\n>From ");
00656 
00657   ts << ret;
00658 }
00659 
00660 
00661 Headers::Generic*  Content::getNextHeader(QCString &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(QRegExp("\\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   QCString 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 QCString 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=QCString(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 QCString &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
KDE Home | KDE Accessibility Home | Description of Access Keys