diff --git a/kmime/Makefile.am b/kmime/Makefile.am index bac203aab..6b97cfbbd 100644 --- a/kmime/Makefile.am +++ b/kmime/Makefile.am @@ -1,36 +1,36 @@ INCLUDES= -I$(top_srcdir)/libemailfunctions \ $(all_includes) +KDE_CXXFLAGS = -UQT3_SUPPORT lib_LTLIBRARIES = libkmime.la libkmime_la_SOURCES = \ kmime_charfreq.cpp \ kmime_util.cpp \ kmime_mdn.cpp \ kmime_codecs.cpp \ kmime_codec_base64.cpp \ kmime_codec_uuencode.cpp \ kmime_codec_qp.cpp \ kmime_codec_identity.cpp \ kmime_parsers.cpp \ kmime_header_parsing.cpp \ kmime_content.cpp \ kmime_headers.cpp \ kmime_message.cpp \ kmime_newsarticle.cpp \ boolflags.cpp \ - kqcstringsplitter.cpp \ kautodeletehash.cpp libkmime_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 4:0:2 libkmime_la_LIBADD = $(top_builddir)/libemailfunctions/libemailfunctions.la \ $(LIB_KDECORE) $(LIB_POLL) METASOURCES = AUTO messages: $(XGETTEXT) *.cpp *.h -o $(podir)/libkmime.pot DOXYGEN_EXCLUDE = kmime_headers_obs.h include $(top_srcdir)/admin/Doxyfile.am diff --git a/kmime/kmime_charfreq.h b/kmime/kmime_charfreq.h index 29a9f3c06..a67594691 100644 --- a/kmime/kmime_charfreq.h +++ b/kmime/kmime_charfreq.h @@ -1,68 +1,68 @@ /* -*- c++ -*- kmime_charfreq.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 Marc Mutz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifndef __KMIME_CHARFREQ_H__ #define __KMIME_CHARFREQ_H__ -#include +#include #include #undef None namespace KMime { class KDE_EXPORT CharFreq { public: CharFreq( const QByteArray & buf ); CharFreq( const char * buf, size_t len ); enum Type { None = 0, EightBitData, Binary = EightBitData, SevenBitData, EightBitText, SevenBitText }; Type type() const; bool isEightBitData() const; bool isEightBitText() const; bool isSevenBitData() const; bool isSevenBitText() const; /** Returns true if buf has trailing whitespace, i.e. if any line ends with space (' ') or tab ('\t'). */ bool hasTrailingWhitespace() const; /** Returns true if buf contains a line that starts with "From ". */ bool hasLeadingFrom() const; /** Returns the percentage of printable characters: printable/total. If total == 0, the result is undefined. */ float printableRatio() const; /** Returns the percentage of control code (CTLs): CTL/total. If total == 0, the result is undefined. */ float controlCodesRatio() const; protected: uint NUL; // count of NUL chars uint CTL; // count of CTLs (incl. DEL, excl. CR, LF, HT) uint CR, LF; // count of CRs and LFs uint CRLF; // count of LFs, preceded by CRs uint printable; // count of printable US-ASCII chars (SPC..~) uint eightBit; // count of other latin1 chars (those with 8th bit set) uint total; uint lineMin; uint lineMax; bool mTrailingWS; // does the buffer contain trailing whitespace? bool mLeadingFrom; // does the buffer contain lines starting with "From "? private: void count( const char * buf, size_t len ); }; } // namespace KMime #endif /* __KMIME_CHARFREQ_H__ */ diff --git a/kmime/kmime_codecs.cpp b/kmime/kmime_codecs.cpp index bd7cf6d77..62e3e2d88 100644 --- a/kmime/kmime_codecs.cpp +++ b/kmime/kmime_codecs.cpp @@ -1,221 +1,224 @@ /* -*- c++ -*- This file is part of KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 Marc Mutz KMime is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMime is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this library with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "kmime_codecs.h" #include "kmime_util.h" #include "kmime_codec_base64.h" #include "kmime_codec_qp.h" #include "kmime_codec_uuencode.h" #include "kmime_codec_identity.h" #include "kautodeletehash.h" #include #include #include #include #include +#include using namespace KMime; using namespace KPIM; namespace KMime { // global list of KMime::Codec's KAutoDeleteHash * Codec::all = 0; static KStaticDeleter< KAutoDeleteHash > sdAll; #if defined(QT_THREAD_SUPPORT) QMutex* Codec::dictLock = 0; static KStaticDeleter sdDictLock; #endif void Codec::fillDictionary() { //all->insert( "7bit", new SevenBitCodec() ); //all->insert( "8bit", new EightBitCodec() ); all->insert( "base64", new Base64Codec() ); all->insert( "quoted-printable", new QuotedPrintableCodec() ); all->insert( "b", new Rfc2047BEncodingCodec() ); all->insert( "q", new Rfc2047QEncodingCodec() ); all->insert( "x-kmime-rfc2231", new Rfc2231EncodingCodec() ); all->insert( "x-uuencode", new UUCodec() ); //all->insert( "binary", new BinaryCodec() ); } Codec * Codec::codecForName( const char * name ) { const QByteArray ba( name ); return codecForName( ba ); } Codec * Codec::codecForName( const QByteArray & name ) { #if defined(QT_THREAD_SUPPORT) if ( !dictLock ) sdDictLock.setObject( dictLock, new QMutex ); dictLock->lock(); // protect "all" #endif if ( !all ) { sdAll.setObject( all, new KAutoDeleteHash() ); fillDictionary(); } QByteArray lowerName = name; KPIM::kAsciiToLower( lowerName.data() ); Codec * codec = (*all)[ lowerName ]; #if defined(QT_THREAD_SUPPORT) dictLock->unlock(); #endif if ( !codec ) kdDebug() << "Unknown codec \"" << name << "\" requested!" << endl; return codec; } bool Codec::encode( const char* & scursor, const char * const send, char* & dcursor, const char * const dend, bool withCRLF ) const { // get an encoder: Encoder * enc = makeEncoder( withCRLF ); assert( enc ); // encode and check for output buffer overflow: while ( !enc->encode( scursor, send, dcursor, dend ) ) if ( dcursor == dend ) { delete enc; return false; // not enough space in output buffer } // finish and check for output buffer overflow: while ( !enc->finish( dcursor, dend ) ) if ( dcursor == dend ) { delete enc; return false; // not enough space in output buffer } // cleanup and return: delete enc; return true; // successfully encoded. } QByteArray Codec::encode( const QByteArray & src, bool withCRLF ) const { // allocate buffer for the worst case: - QByteArray result( maxEncodedSizeFor( src.size(), withCRLF ) ); + QByteArray result; + result.resize( maxEncodedSizeFor( src.size(), withCRLF ) ); // set up iterators: QByteArray::ConstIterator iit = src.begin(); QByteArray::ConstIterator iend = src.end(); QByteArray::Iterator oit = result.begin(); QByteArray::ConstIterator oend = result.end(); // encode if ( !encode( iit, iend, oit, oend, withCRLF ) ) kdFatal() << name() << " codec lies about it's mEncodedSizeFor()" << endl; // shrink result to actual size: result.truncate( oit - result.begin() ); return result; } QByteArray Codec::decode( const QByteArray & src, bool withCRLF ) const { // allocate buffer for the worst case: - QByteArray result( maxDecodedSizeFor( src.size(), withCRLF ) ); + QByteArray result; + result.resize( maxDecodedSizeFor( src.size(), withCRLF ) ); // set up iterators: QByteArray::ConstIterator iit = src.begin(); QByteArray::ConstIterator iend = src.end(); QByteArray::Iterator oit = result.begin(); QByteArray::ConstIterator oend = result.end(); // decode if ( !decode( iit, iend, oit, oend, withCRLF ) ) kdFatal() << name() << " codec lies about it's maxDecodedSizeFor()" << endl; // shrink result to actual size: result.truncate( oit - result.begin() ); return result; } bool Codec::decode( const char* & scursor, const char * const send, char* & dcursor, const char * const dend, bool withCRLF ) const { // get a decoder: Decoder * dec = makeDecoder( withCRLF ); assert( dec ); // decode and check for output buffer overflow: while ( !dec->decode( scursor, send, dcursor, dend ) ) if ( dcursor == dend ) { delete dec; return false; // not enough space in output buffer } // finish and check for output buffer overflow: while ( !dec->finish( dcursor, dend ) ) if ( dcursor == dend ) { delete dec; return false; // not enough space in output buffer } // cleanup and return: delete dec; return true; // successfully encoded. } // write as much as possible off the output buffer. Return true if // flushing was complete, false if some chars could not be flushed. bool Encoder::flushOutputBuffer( char* & dcursor, const char * const dend ) { int i; // copy output buffer to output stream: for ( i = 0 ; dcursor != dend && i < mOutputBufferCursor ; ++i ) *dcursor++ = mOutputBuffer[i]; // calculate the number of missing chars: int numCharsLeft = mOutputBufferCursor - i; // push the remaining chars to the begin of the buffer: if ( numCharsLeft ) - qmemmove( mOutputBuffer, mOutputBuffer + i, numCharsLeft ); + ::memmove( mOutputBuffer, mOutputBuffer + i, numCharsLeft ); // adjust cursor: mOutputBufferCursor = numCharsLeft; return !numCharsLeft; } } // namespace KMime diff --git a/kmime/kmime_content.cpp b/kmime/kmime_content.cpp index fd05b4d3d..a26e3764a 100644 --- a/kmime/kmime_content.cpp +++ b/kmime/kmime_content.cpp @@ -1,893 +1,890 @@ /* kmime_content.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include "kmime_content.h" #include "kmime_parsers.h" #include #include #include #include #include #include -//Added by qt3to4: #include -#include +#include using namespace KMime; namespace KMime { Content::Content() : f_orceDefaultCS( false ) { d_efaultCS = cachedCharset("ISO-8859-1"); } -Content::Content(const Q3CString &h, const Q3CString &b) +Content::Content( const QByteArray &h, const QByteArray &b ) : f_orceDefaultCS( false ) { d_efaultCS = cachedCharset("ISO-8859-1"); - h_ead=h.copy(); - b_ody=b.copy(); + h_ead = h; + b_ody = b; } Content::~Content() { qDeleteAll( c_ontents ); c_ontents.clear(); qDeleteAll( h_eaders ); h_eaders.clear(); } void Content::setContent( const QList & l ) { //qDebug("Content::setContent( const QList & l ) : start"); - h_ead.resize(0); - b_ody.resize(0); + h_ead.clear(); + b_ody.clear(); //usage of textstreams is much faster than simply appending the strings - QTextStream hts(h_ead, QIODevice::WriteOnly), - bts(b_ody, QIODevice::WriteOnly); - hts.setEncoding(QTextStream::Latin1); - bts.setEncoding(QTextStream::Latin1); + QTextStream hts(&h_ead, QIODevice::WriteOnly), + bts(&b_ody, QIODevice::WriteOnly); + hts.setCodec( "ISO 8859-1" ); + bts.setCodec( "ISO 8859-1" ); bool isHead=true; foreach ( QByteArray line, l ) { if ( isHead && line.isEmpty() ) { isHead=false; continue; } if(isHead) hts << line << "\n"; else bts << line << "\n"; } //terminate strings hts << '\0'; bts << '\0'; //qDebug("Content::setContent( const QList & l ) : finished"); } void Content::setContent( const QByteArray &s ) { int pos = s.indexOf( "\n\n", 0 ); if(pos>-1) { h_ead=s.left(++pos); //header *must* end with "\n" !! b_ody=s.mid(pos+1, s.length()-pos-1); } else h_ead=s; } //parse the message, split multiple parts void Content::parse() { //qDebug("void Content::parse() : start"); qDeleteAll( h_eaders ); h_eaders.clear(); // check this part has already been partioned into subparts. // if this is the case, we will not try to reparse the body // of this part. if ( b_ody.size() == 0 && !c_ontents.isEmpty() ) { // reparse all sub parts foreach ( Content *c, c_ontents ) c->parse(); return; } qDeleteAll( c_ontents ); c_ontents.clear(); Headers::ContentType *ct=contentType(); - Q3CString tmp; + QByteArray tmp; Content *c; Headers::contentCategory cat; // just "text" as mimetype is suspicious, perhaps this article was // generated by broken software, better check for uuencoded binaries if (ct->mimeType()=="text") ct->setMimeType("invalid/invalid"); if(ct->isText()) return; //nothing to do if(ct->isMultipart()) { //this is a multipart message tmp=ct->boundary(); //get boundary-parameter if(!tmp.isEmpty()) { Parser::MultiPart mpp(b_ody, tmp); if(mpp.parse()) { //at least one part found if(ct->isSubtype("alternative")) //examine category for the sub-parts cat=Headers::CCalternativePart; else cat=Headers::CCmixedPart; //default to "mixed" QList parts=mpp.parts(); QList::Iterator it; for(it=parts.begin(); it!=parts.end(); ++it) { //create a new Content for every part c=new Content(); c->setContent(*it); c->parse(); c->contentType()->setCategory(cat); //set category of the sub-part c_ontents.append( c ); //qDebug("part:\n%s\n\n%s", c->h_ead.data(), c->b_ody.left(100).data()); } //the whole content is now split into single parts, so it's safe delete the message-body - b_ody.resize(0); + b_ody.clear(); } else { //sh*t, the parsing failed so we have to treat the message as "text/plain" instead ct->setMimeType("text/plain"); ct->setCharset("US-ASCII"); } } } else if (ct->mimeType()=="invalid/invalid") { //non-mime body => check for uuencoded content Parser::UUEncoded uup(b_ody, rawHeader("Subject")); if(uup.parse()) { // yep, it is uuencoded if(uup.isPartial()) { // this seems to be only a part of the message so we treat it as "message/partial" ct->setMimeType("message/partial"); //ct->setId(uniqueString()); not needed yet ct->setPartialParams(uup.partialCount(), uup.partialNumber()); contentTransferEncoding()->setCte(Headers::CE7Bit); } else { //it's a complete message => treat as "multipart/mixed" //the whole content is now split into single parts, so it's safe to delete the message-body - b_ody.resize(0); + b_ody.clear(); //binary parts for ( int i = 0; i < uup.binaryParts().count(); ++i ) { c=new Content(); //generate content with mime-compliant headers tmp="Content-Type: "; tmp += uup.mimeTypes().at(i); tmp += "; name=\""; tmp += uup.filenames().at(i); tmp += "\"\nContent-Transfer-Encoding: x-uuencode\nContent-Disposition: attachment; filename=\""; tmp += uup.filenames().at(i); tmp += "\"\n\n"; tmp += uup.binaryParts().at(i); c->setContent(tmp); addContent(c); } if ( !c_ontents.isEmpty() && c_ontents.first() ) { //readd the plain text before the uuencoded part c_ontents.first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+uup.textPart()); c_ontents.first()->contentType()->setMimeType("text/plain"); } } } else { Parser::YENCEncoded yenc(b_ody); if ( yenc.parse()) { /* If it is partial, just assume there is exactly one decoded part, * and make this that part */ if (yenc.isPartial()) { ct->setMimeType("message/partial"); //ct->setId(uniqueString()); not needed yet ct->setPartialParams(yenc.partialCount(), yenc.partialNumber()); contentTransferEncoding()->setCte(Headers::CEbinary); } else { //it's a complete message => treat as "multipart/mixed" //the whole content is now split into single parts, so it's safe to delete the message-body - b_ody.resize(0); + b_ody.clear(); //binary parts for (int i=0;isetContent(tmp); // the bodies of yenc message parts are binary data, not null-terminated strings: - QByteArray body = yenc.binaryParts()[i]; - Q3CString body_string(body.size()); - memcpy(body_string.data(), body.data(), body.size()); - c->setBody(body_string); + c->setBody(yenc.binaryParts()[i]); addContent(c); } if( !c_ontents.isEmpty() && c_ontents.first() ) { //readd the plain text before the uuencoded part c_ontents.first()->setContent("Content-Type: text/plain\nContent-Transfer-Encoding: 7Bit\n\n"+yenc.textPart()); c_ontents.first()->contentType()->setMimeType("text/plain"); } } } else { //no, this doesn't look like uuencoded stuff => we treat it as "text/plain" ct->setMimeType("text/plain"); } } } //qDebug("void Content::parse() : finished"); } void Content::assemble() { - Q3CString newHead=""; + QByteArray newHead=""; //Content-Type - newHead+=contentType()->as7BitString()+"\n"; + newHead+=contentType()->as7BitString()+'\n'; //Content-Transfer-Encoding - newHead+=contentTransferEncoding()->as7BitString()+"\n"; + newHead+=contentTransferEncoding()->as7BitString()+'\n'; //Content-Description Headers::Base *h=contentDescription(false); if(h) newHead+=h->as7BitString()+"\n"; //Content-Disposition h=contentDisposition(false); if(h) newHead+=h->as7BitString()+"\n"; h_ead=newHead; } void Content::clear() { qDeleteAll( h_eaders ); h_eaders.clear(); qDeleteAll( c_ontents ); c_ontents.clear(); - h_ead.resize(0); - b_ody.resize(0); + h_ead.clear(); + b_ody.clear(); } -Q3CString Content::encodedContent(bool useCrLf) +QByteArray Content::encodedContent(bool useCrLf) { - Q3CString e; + QByteArray e; // hack to convert articles with uuencoded or yencoded binaries into // proper mime-compliant articles if ( !c_ontents.isEmpty() ) { bool convertNonMimeBinaries=false; // reencode non-mime binaries... foreach ( Content *c, c_ontents ) { if ((c->contentTransferEncoding(true)->cte()==Headers::CEuuenc) || (c->contentTransferEncoding(true)->cte()==Headers::CEbinary)) { convertNonMimeBinaries=true; c->b_ody = KCodecs::base64Encode(c->decodedContent(), true); c->b_ody.append("\n"); c->contentTransferEncoding(true)->setCte(Headers::CEbase64); c->contentTransferEncoding(true)->setDecoded(false); c->removeHeader("Content-Description"); c->assemble(); } } // add proper mime headers... if (convertNonMimeBinaries) { int beg = 0, end = 0; beg = h_ead.indexOf( "MIME-Version: " ); if ( beg >= 0 ) end = h_ead.indexOf( '\n', beg ); if ( beg >= 0 && end > beg ) h_ead.remove( beg, end - beg ); beg = h_ead.indexOf( "Content-Type: " ); if ( beg >= 0 ) end = h_ead.indexOf( '\n', beg ); if ( beg >= 0 && end > beg ) h_ead.remove( beg, end - beg ); beg = h_ead.indexOf( "Content-Transfer-Encoding: " ); if ( beg >= 0 ) end = h_ead.indexOf( '\n', beg ); if ( beg >= 0 && end > beg ) h_ead.remove( beg, end - beg ); h_ead+="MIME-Version: 1.0\n"; h_ead+=contentType(true)->as7BitString()+"\n"; h_ead+=contentTransferEncoding(true)->as7BitString()+"\n"; } } //head - e=h_ead.copy(); - e+="\n"; + e=h_ead; + e+='\n'; //body if(!b_ody.isEmpty()) { //this message contains only one part Headers::CTEncoding *enc=contentTransferEncoding(); if(enc->needToEncode()) { if(enc->cte()==Headers::CEquPr) { - QByteArray temp(b_ody.length()); - memcpy(temp.data(), b_ody.data(), b_ody.length()); - e+=KCodecs::quotedPrintableEncode(temp, false); + e+=KCodecs::quotedPrintableEncode(b_ody, false); } else { e+=KCodecs::base64Encode(b_ody, true); e+="\n"; } } else e+=b_ody; } else if( !c_ontents.isEmpty() ) { //this is a multipart message Headers::ContentType *ct=contentType(); - Q3CString boundary="--"+ct->boundary(); + QByteArray boundary="--"+ct->boundary(); //add all (encoded) contents separated by boundaries foreach ( Content *c, c_ontents ) { e+=boundary+"\n"; e+=c->encodedContent(false); // don't convert LFs here, we do that later!!!!! } //finally append the closing boundary e+=boundary+"--\n"; }; if(useCrLf) return LFtoCRLF(e); else return e; } QByteArray Content::decodedContent() { QByteArray temp, ret; Headers::CTEncoding *ec=contentTransferEncoding(); bool removeTrailingNewline=false; - int size=ec->cte()==Headers::CEbinary ? b_ody.size() : b_ody.length(); + int size = b_ody.length(); if (size==0) return ret; temp.resize(size); memcpy(temp.data(), b_ody.data(), size); if(ec->decoded()) { ret = temp; removeTrailingNewline=true; } else { switch(ec->cte()) { case Headers::CEbase64 : KCodecs::base64Decode(temp, ret); break; case Headers::CEquPr : ret = KCodecs::quotedPrintableDecode(b_ody); ret.resize(ret.size()-1); // remove null-char removeTrailingNewline=true; break; case Headers::CEuuenc : KCodecs::uudecode(temp, ret); break; case Headers::CEbinary : ret = temp; removeTrailingNewline=false; default : ret = temp; removeTrailingNewline=true; } } if (removeTrailingNewline && (ret.size()>0) && (ret[ret.size()-1] == '\n')) ret.resize(ret.size()-1); return ret; } void Content::decodedText(QString &s, bool trimText, bool removeTrailingNewlines) { if(!decodeText()) //this is not a text content !! return; bool ok=true; QTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); s=codec->toUnicode(b_ody.data(), b_ody.length()); if (trimText && removeTrailingNewlines) { int i; for (i=s.length()-1; i>=0; i--) if (!s[i].isSpace()) break; s.truncate(i+1); } else { if (s.right(1)=="\n") s.truncate(s.length()-1); // remove trailing new-line } } void Content::decodedText(QStringList &l, bool trimText, bool removeTrailingNewlines) { if(!decodeText()) //this is not a text content !! return; QString unicode; bool ok=true; QTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); unicode=codec->toUnicode(b_ody.data(), b_ody.length()); if (trimText && removeTrailingNewlines) { int i; for (i=unicode.length()-1; i>=0; i--) if (!unicode[i].isSpace()) break; unicode.truncate(i+1); } else { if (unicode.right(1)=="\n") unicode.truncate(unicode.length()-1); // remove trailing new-line } - l=QStringList::split('\n', unicode, true); //split the string at linebreaks + l = unicode.split( '\n' ); //split the string at linebreaks } void Content::fromUnicodeString(const QString &s) { bool ok=true; QTextCodec *codec=KGlobal::charsets()->codecForName(contentType()->charset(),ok); if(!ok) { // no suitable codec found => try local settings and hope the best ;-) codec=KGlobal::locale()->codecForEncoding(); - Q3CString chset=KGlobal::locale()->encoding(); + QByteArray chset = KGlobal::locale()->encoding(); contentType()->setCharset(chset); } b_ody=codec->fromUnicode(s); contentTransferEncoding()->setDecoded(true); //text is always decoded } Content* Content::textContent() { Content *ret=0; //return the first content with mimetype=text/* if(contentType()->isText()) ret=this; else foreach ( Content *c, c_ontents ) if( (ret=c->textContent())!=0 ) break; return ret; } void Content::attachments( Content::List &dst, bool incAlternatives ) { if ( c_ontents.isEmpty() ) dst.append(this); else { foreach ( Content *c, c_ontents ) { if( !incAlternatives && c->contentType()->category()==Headers::CCalternativePart) continue; else c->attachments(dst, incAlternatives); } } if(type()!=ATmimeContent) { // this is the toplevel article Content *text=textContent(); if(text) dst.removeAll( text ); } } void Content::addContent(Content *c, bool prepend) { if ( c_ontents.isEmpty() ) { // this message is not multipart yet // first we convert the body to a content Content *main=new Content(); //the Mime-Headers are needed, so we move them to the new content for ( Headers::Base::List::iterator it = h_eaders.begin(); it != h_eaders.end(); ) { if ( (*it)->isMimeHeader() ) { // append to new content main->h_eaders.append( *it ); // and remove from this content h_eaders.erase( it ); } else ++it; } //"main" is now part of a multipart/mixed message main->contentType()->setCategory(Headers::CCmixedPart); //the head of "main" is empty, so we assemble it main->assemble(); //now we can copy the body and append the new content; - main->b_ody=b_ody.copy(); + main->b_ody = b_ody; c_ontents.append( main ); - b_ody.resize(0); //not longer needed + b_ody.clear(); //not longer needed //finally we have to convert this article to "multipart/mixed" Headers::ContentType *ct=contentType(); ct->setMimeType("multipart/mixed"); ct->setBoundary(multiPartBoundary()); ct->setCategory(Headers::CCcontainer); contentTransferEncoding()->clear(); // 7Bit, decoded } //here we actually add the content if(prepend) c_ontents.insert( 0, c ); else c_ontents.append( c ); } void Content::removeContent(Content *c, bool del) { if( c_ontents.isEmpty() ) // what the .. return; c_ontents.removeAll( c ); if(del) delete c; //only one content left => turn this message in a single-part if ( c_ontents.count() == 1 ) { Content *main = c_ontents.first(); //first we have to move the mime-headers for ( Headers::Base::List::iterator it = main->h_eaders.begin(); it != main->h_eaders.end(); ) { if ( (*it)->isMimeHeader() ) { kdDebug(5003) << "Content::removeContent(Content *c, bool del) : mime-header moved: " << (*it)->as7BitString() << endl; // first remove the old header removeHeader( (*it)->type() ); // then append to new content h_eaders.append( *it ); // and finally remove from this content main->h_eaders.erase( it ); } else ++it; } //now we can copy the body - b_ody=main->b_ody.copy(); + b_ody = main->b_ody; //finally we can delete the content list qDeleteAll( c_ontents ); c_ontents.clear(); } } void Content::changeEncoding(Headers::contentEncoding e) { Headers::CTEncoding *enc=contentTransferEncoding(); if(enc->cte()==e) //nothing to do return; if(decodeText()) enc->setCte(e); // text is not encoded until it's sent or saved so we just set the new encoding else { // this content contains non textual data, that has to be re-encoded if(e!=Headers::CEbase64) { //kdWarning(5003) << "Content::changeEncoding() : non textual data and encoding != base64 - this should not happen\n => forcing base64" << endl; e=Headers::CEbase64; } if(enc->cte()!=e) { // ok, we reencode the content using base64 b_ody = KCodecs::base64Encode(decodedContent(), true); b_ody.append("\n"); enc->setCte(e); //set encoding enc->setDecoded(false); } } } void Content::toStream(QTextStream &ts, bool scrambleFromLines) { - Q3CString ret=encodedContent(false); + QByteArray ret=encodedContent(false); if (scrambleFromLines) + // FIXME Why are only From lines with a preceding empty line considered? + // And, of course, all lines starting with >*From have to be escaped + // because otherwise the transformation is not revertable. ret.replace( "\n\nFrom ", "\n\n>From "); ts << ret; } -Headers::Generic* Content::getNextHeader(Q3CString &head) +Headers::Generic* Content::getNextHeader(QByteArray &head) { int pos1=-1, pos2=0, len=head.length()-1; bool folded(false); Headers::Generic *header=0; - pos1 = head.find(": "); + pos1 = head.indexOf(": "); if (pos1>-1) { //there is another header pos2=pos1+=2; //skip the name if (head[pos2]!='\n') { // check if the header is not empty while(1) { - pos2=head.find("\n", pos2+1); + pos2=head.indexOf('\n', pos2+1); if(pos2==-1 || pos2==len || ( head[pos2+1]!=' ' && head[pos2+1]!='\t') ) //break if we reach the end of the string, honor folded lines break; else folded = true; } } if(pos2<0) pos2=len+1; //take the rest of the string if (!folded) header = new Headers::Generic(head.left(pos1-2), this, head.mid(pos1, pos2-pos1)); else { QByteArray hdrValue = head.mid( pos1, pos2 - pos1 ); // unfold header int beg = 0, mid = 0, end = 0; while ( (mid = hdrValue.indexOf( '\n' )) >= 0 ) { beg = end = mid; while ( beg > 0 ) { if ( !QChar( hdrValue[beg] ).isSpace() ) break; --beg; } while ( end < hdrValue.length() - 1 ) { if ( !QChar( hdrValue[end] ).isSpace() ) break; ++end; } hdrValue.remove( beg, end - beg ); } header = new Headers::Generic( head.left( pos1 - 2 ), this, hdrValue ); } head.remove(0,pos2+1); } else { head = ""; } return header; } Headers::Base* Content::getHeaderByType(const char *type) { if(!type) return 0; //first we check if the requested header is already cached foreach ( Headers::Base *h, h_eaders ) if ( h->is( type ) ) return h; //found //now we look for it in the article head Headers::Base *h = 0; - Q3CString raw=rawHeader(type); + QByteArray raw=rawHeader(type); if(!raw.isEmpty()) { //ok, we found it //choose a suitable header class if(strcasecmp("Message-Id", type)==0) h=new Headers::MessageID(this, raw); else if(strcasecmp("Subject", type)==0) h=new Headers::Subject(this, raw); else if(strcasecmp("Date", type)==0) h=new Headers::Date(this, raw); else if(strcasecmp("From", type)==0) h=new Headers::From(this, raw); else if(strcasecmp("Organization", type)==0) h=new Headers::Organization(this, raw); else if(strcasecmp("Reply-To", type)==0) h=new Headers::ReplyTo(this, raw); else if(strcasecmp("Mail-Copies-To", type)==0) h=new Headers::MailCopiesTo(this, raw); else if(strcasecmp("To", type)==0) h=new Headers::To(this, raw); else if(strcasecmp("CC", type)==0) h=new Headers::CC(this, raw); else if(strcasecmp("BCC", type)==0) h=new Headers::BCC(this, raw); else if(strcasecmp("Newsgroups", type)==0) h=new Headers::Newsgroups(this, raw); else if(strcasecmp("Followup-To", type)==0) h=new Headers::FollowUpTo(this, raw); else if(strcasecmp("References", type)==0) h=new Headers::References(this, raw); else if(strcasecmp("Lines", type)==0) h=new Headers::Lines(this, raw); else if(strcasecmp("Content-Type", type)==0) h=new Headers::ContentType(this, raw); else if(strcasecmp("Content-Transfer-Encoding", type)==0) h=new Headers::CTEncoding(this, raw); else if(strcasecmp("Content-Disposition", type)==0) h=new Headers::CDisposition(this, raw); else if(strcasecmp("Content-Description", type)==0) h=new Headers::CDescription(this, raw); else h=new Headers::Generic(type, this, raw); h_eaders.append( h ); //add to cache return h; } else return 0; //header not found } void Content::setHeader(Headers::Base *h) { if(!h) return; removeHeader(h->type()); h_eaders.append( h ); } bool Content::removeHeader(const char *type) { for ( Headers::Base::List::iterator it = h_eaders.begin(); it != h_eaders.end(); ++it ) if ( (*it)->is(type) ) { delete (*it); h_eaders.erase( it ); return true; } return false; } int Content::size() { int ret=b_ody.length(); if(contentTransferEncoding()->cte()==Headers::CEbase64) return (ret*3/4); //base64 => 6 bit per byte return ret; } int Content::storageSize() { int s=h_ead.size(); if ( c_ontents.isEmpty() ) s+=b_ody.size(); else { foreach ( Content *c, c_ontents ) s+=c->storageSize(); } return s; } int Content::lineCount() { int ret=0; if(type()==ATmimeContent) ret+=h_ead.count('\n'); ret+=b_ody.count('\n'); foreach ( Content *c, c_ontents ) ret+=c->lineCount(); return ret; } -Q3CString Content::rawHeader(const char *name) +QByteArray Content::rawHeader(const char *name) { - return extractHeader(h_ead, name); + return KMime::extractHeader(h_ead, name); } bool Content::decodeText() { Headers::CTEncoding *enc=contentTransferEncoding(); if(!contentType()->isText()) return false; //non textual data cannot be decoded here => use decodedContent() instead if(enc->decoded()) return true; //nothing to do switch(enc->cte()) { case Headers::CEbase64 : b_ody=KCodecs::base64Decode(b_ody); b_ody.append("\n"); break; case Headers::CEquPr : b_ody=KCodecs::quotedPrintableDecode(b_ody); break; case Headers::CEuuenc : b_ody=KCodecs::uudecode(b_ody); b_ody.append("\n"); break; case Headers::CEbinary : - b_ody=Q3CString(b_ody.data(), b_ody.size()+1); + // nothing to decode b_ody.append("\n"); default : break; } enc->setDecoded(true); return true; } void Content::setDefaultCharset( const QByteArray &cs ) { d_efaultCS = KMime::cachedCharset(cs); foreach ( Content *c, c_ontents ) c->setDefaultCharset(cs); // reparse the part and its sub-parts in order // to clear cached header values parse(); } void Content::setForceDefaultCS(bool b) { f_orceDefaultCS=b; foreach ( Content *c, c_ontents ) c->setForceDefaultCS(b); // reparse the part and its sub-parts in order // to clear cached header values parse(); } } // namespace KMime diff --git a/kmime/kmime_content.h b/kmime/kmime_content.h index 0a89cc85c..4e9da7fd7 100644 --- a/kmime/kmime_content.h +++ b/kmime/kmime_content.h @@ -1,169 +1,167 @@ /* kmime_content.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifndef __KMIME_CONTENT_H__ #define __KMIME_CONTENT_H__ //forward declarations #if 0 class KMime::Headers::Base; class KMime::Headers::Generic; class KMime::Headers::ContentType; class KMime::Headers::CTEncoding; class KMime::Headers::CDisposition; class KMime::Headers::List; #endif #include "kmime_util.h" #include "kmime_headers.h" #include -//Added by qt3to4: -#include +#include #include namespace KMime { /** Base class for messages in mime format It contains all the enums, static functions and parser-classes, that are needed for mime handling */ class Base { public: //enums enum articleType { ATmimeContent, ATremote, ATlocal }; }; /** This class encapsulates a mime-encoded content. It parses the given data and creates a tree-like structure, that represents the structure of the message */ class KDE_EXPORT Content : public Base { public: typedef QList List; Content(); - Content(const Q3CString &h, const Q3CString &b); + Content( const QByteArray &h, const QByteArray &b ); virtual ~Content(); //type virtual articleType type() { return ATmimeContent; } //content handling bool hasContent() { return ( !h_ead.isEmpty() && (!b_ody.isEmpty() || !c_ontents.isEmpty()) ); } void setContent( const QList & l ); void setContent( const QByteArray &s ); virtual void parse(); virtual void assemble(); virtual void clear(); //header access - Q3CString head() { return h_ead; } + QByteArray head() { return h_ead; } // extracts and removes the next header from head. The caller has to delete the returned header; - Headers::Generic* getNextHeader(Q3CString &head); + Headers::Generic* getNextHeader(QByteArray &head); virtual Headers::Base* getHeaderByType(const char *type); virtual void setHeader(Headers::Base *h); virtual bool removeHeader(const char *type); bool hasHeader(const char *type) { return (getHeaderByType(type)!=0); } Headers::ContentType* contentType(bool create=true) { Headers::ContentType *p=0; return getHeaderInstance(p, create); } Headers::CTEncoding* contentTransferEncoding(bool create=true) { Headers::CTEncoding *p=0; return getHeaderInstance(p, create); } Headers::CDisposition* contentDisposition(bool create=true) { Headers::CDisposition *p=0; return getHeaderInstance(p, create); } Headers::CDescription* contentDescription(bool create=true) { Headers::CDescription *p=0; return getHeaderInstance(p, create); } //content access int size(); int storageSize(); int lineCount(); - Q3CString body() { return b_ody; } - void setBody( const Q3CString & str ) { b_ody = str; } - Q3CString encodedContent(bool useCrLf=false); + QByteArray body() { return b_ody; } + void setBody( const QByteArray & str ) { b_ody = str; } + QByteArray encodedContent(bool useCrLf=false); QByteArray decodedContent(); void decodedText(QString &s, bool trimText=false, bool removeTrailingNewlines=false); void decodedText(QStringList &s, bool trimText=false, bool removeTrailingNewlines=false); void fromUnicodeString(const QString &s); Content* textContent(); void attachments(List &dst, bool incAlternatives=false); void addContent(Content *c, bool prepend=false); void removeContent(Content *c, bool del=false); void changeEncoding(Headers::contentEncoding e); //saves the encoded content to the given textstream // scrambleFromLines: replace "\nFrom " with "\n>From ", this is // needed to avoid problem with mbox-files void toStream(QTextStream &ts, bool scrambleFromLines=false); // this charset is used for all headers and the body // if the charset is not declared explictly - QByteArray defaultCharset() const { return QByteArray( d_efaultCS ); } + QByteArray defaultCharset() const { return d_efaultCS; } void setDefaultCharset( const QByteArray &cs ); // use the default charset even if a different charset is // declared in the article bool forceDefaultCS() { return f_orceDefaultCS; } // enables/disables the force mode, housekeeping. // works correctly only when the article is completely empty or // completely loaded virtual void setForceDefaultCS(bool b); protected: - Q3CString rawHeader(const char *name); + QByteArray rawHeader(const char *name); bool decodeText(); template T* getHeaderInstance(T *ptr, bool create); - Q3CString h_ead, - b_ody; + QByteArray h_ead, + b_ody; List c_ontents; Headers::Base::List h_eaders; - const char *d_efaultCS; + QByteArray d_efaultCS; bool f_orceDefaultCS; - }; // some compilers (for instance Compaq C++) need template inline functions // here rather than in the *.cpp file template T* Content::getHeaderInstance(T *ptr, bool create) { T dummy; //needed to access virtual member T::type() ptr=static_cast (getHeaderByType(dummy.type())); if(!ptr && create) { //no such header found, but we need one => create it ptr=new T(this); h_eaders.append( ptr ); } return ptr; } } // namespace KMime #endif // __KMIME_CONTENT_H__ diff --git a/kmime/kmime_header_parsing.cpp b/kmime/kmime_header_parsing.cpp index 9653e038d..f2ce358b4 100644 --- a/kmime/kmime_header_parsing.cpp +++ b/kmime/kmime_header_parsing.cpp @@ -1,1738 +1,1739 @@ /* -*- c++ -*- kmime_header_parsing.cpp This file is part of KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 Marc Mutz KMime is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMime is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this library with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "kmime_header_parsing.h" #include "kmime_codecs.h" #include "kmime_util.h" #include "kmime_warning.h" #include #include #include #include -#include #include #include // for isdigit #include using namespace KMime; using namespace KMime::Types; namespace KMime { namespace Types { QString AddrSpec::asString() const { bool needsQuotes = false; QString result; for ( int i = 0 ; i < localPart.length() ; ++i ) { const char ch = localPart[i].toLatin1(); if ( ch == '.' || isAText( ch ) ) result += ch; else { needsQuotes = true; if ( ch == '\\' || ch == '"' ) result += '\\'; result += ch; } } if ( needsQuotes ) return '"' + result + "\"@" + domain; else return result + '@' + domain; } } namespace HeaderParsing { // parse the encoded-word (scursor points to after the initial '=') bool parseEncodedWord( const char* & scursor, const char * const send, QString & result, QByteArray & language ) { // make sure the caller already did a bit of the work. assert( *(scursor-1) == '=' ); // // STEP 1: // scan for the charset/language portion of the encoded-word // char ch = *scursor++; if ( ch != '?' ) { kdDebug() << "first" << endl; KMIME_WARN_PREMATURE_END_OF(EncodedWord); return false; } // remember start of charset (ie. just after the initial "=?") and // language (just after the first '*') fields: const char * charsetStart = scursor; const char * languageStart = 0; // find delimiting '?' (and the '*' separating charset and language // tags, if any): for ( ; scursor != send ; scursor++ ) if ( *scursor == '?') break; - else if ( *scursor == '*' && !languageStart ) + else if ( *scursor == '*' && languageStart == 0 ) languageStart = scursor + 1; // not found? can't be an encoded-word! if ( scursor == send || *scursor != '?' ) { kdDebug() << "second" << endl; KMIME_WARN_PREMATURE_END_OF(EncodedWord); return false; } // extract the language information, if any (if languageStart is 0, // language will be null, too): - Q3CString maybeLanguage( languageStart, scursor - languageStart + 1 /*for NUL*/); + QByteArray maybeLanguage( languageStart, scursor - languageStart ); // extract charset information (keep in mind: the size given to the // ctor is one off due to the \0 terminator): - Q3CString maybeCharset( charsetStart, ( languageStart ? languageStart : scursor + 1 ) - charsetStart ); + QByteArray maybeCharset( charsetStart, ( languageStart > 0 ? languageStart - 1 : scursor ) - charsetStart ); // // STEP 2: // scan for the encoding portion of the encoded-word // // remember start of encoding (just _after_ the second '?'): scursor++; const char * encodingStart = scursor; // find next '?' (ending the encoding tag): for ( ; scursor != send ; scursor++ ) if ( *scursor == '?' ) break; // not found? Can't be an encoded-word! if ( scursor == send || *scursor != '?' ) { kdDebug() << "third" << endl; KMIME_WARN_PREMATURE_END_OF(EncodedWord); return false; } // extract the encoding information: - Q3CString maybeEncoding( encodingStart, scursor - encodingStart + 1 ); + QByteArray maybeEncoding( encodingStart, scursor - encodingStart ); kdDebug() << "parseEncodedWord: found charset == \"" << maybeCharset << "\"; language == \"" << maybeLanguage << "\"; encoding == \"" << maybeEncoding << "\"" << endl; // // STEP 3: // scan for encoded-text portion of encoded-word // // remember start of encoded-text (just after the third '?'): scursor++; const char * encodedTextStart = scursor; // find next '?' (ending the encoded-text): for ( ; scursor != send ; scursor++ ) if ( *scursor == '?' ) break; // not found? Can't be an encoded-word! // ### maybe evaluate it nonetheless if the rest is OK? if ( scursor == send || *scursor != '?' ) { kdDebug() << "fourth" << endl; KMIME_WARN_PREMATURE_END_OF(EncodedWord); return false; } scursor++; // check for trailing '=': if ( scursor == send || *scursor != '=' ) { kdDebug() << "fifth" << endl; KMIME_WARN_PREMATURE_END_OF(EncodedWord); return false; } scursor++; // set end sentinel for encoded-text: const char * const encodedTextEnd = scursor - 2; // // STEP 4: // setup decoders for the transfer encoding and the charset // // try if there's a codec for the encoding found: Codec * codec = Codec::codecForName( maybeEncoding ); if ( !codec ) { KMIME_WARN_UNKNOWN(Encoding,maybeEncoding); return false; } // get an instance of a corresponding decoder: Decoder * dec = codec->makeDecoder(); assert( dec ); // try if there's a (text)codec for the charset found: bool matchOK = false; QTextCodec *textCodec = KGlobal::charsets()->codecForName( maybeCharset, matchOK ); if ( !matchOK || !textCodec ) { KMIME_WARN_UNKNOWN(Charset,maybeCharset); delete dec; return false; }; - kdDebug() << "mimeName(): \"" << textCodec->mimeName() << "\"" << endl; + kdDebug() << "mimeName(): \"" << textCodec->name() << "\"" << endl; // allocate a temporary buffer to store the 8bit text: int encodedTextLength = encodedTextEnd - encodedTextStart; - QByteArray buffer( codec->maxDecodedSizeFor( encodedTextLength ) ); + QByteArray buffer; + buffer.resize( codec->maxDecodedSizeFor( encodedTextLength ) ); QByteArray::Iterator bit = buffer.begin(); QByteArray::ConstIterator bend = buffer.end(); // // STEP 5: // do the actual decoding // if ( !dec->decode( encodedTextStart, encodedTextEnd, bit, bend ) ) KMIME_WARN << codec->name() << " codec lies about it's maxDecodedSizeFor( " << encodedTextLength << " )\nresult may be truncated" << endl; result = textCodec->toUnicode( buffer.begin(), bit - buffer.begin() ); kdDebug() << "result now: \"" << result << "\"" << endl; // cleanup: delete dec; language = maybeLanguage; return true; } static inline void eatWhiteSpace( const char* & scursor, const char * const send ) { while ( scursor != send && ( *scursor == ' ' || *scursor == '\n' || *scursor == '\t' || *scursor == '\r' ) ) scursor++; } bool parseAtom( const char * & scursor, const char * const send, QString & result, bool allow8Bit ) { QPair maybeResult; if ( parseAtom( scursor, send, maybeResult, allow8Bit ) ) { result += QString::fromLatin1( maybeResult.first, maybeResult.second ); return true; } return false; } bool parseAtom( const char * & scursor, const char * const send, QPair & result, bool allow8Bit ) { bool success = false; const char * start = scursor; while ( scursor != send ) { signed char ch = *scursor++; if ( ch > 0 && isAText(ch) ) { // AText: OK success = true; } else if ( allow8Bit && ch < 0 ) { // 8bit char: not OK, but be tolerant. KMIME_WARN_8BIT(ch); success = true; } else { // CTL or special - marking the end of the atom: // re-set sursor to point to the offending // char and return: scursor--; break; } } result.first = start; result.second = scursor - start; return success; } bool parseToken( const char * & scursor, const char * const send, QString & result, bool allow8Bit ) { QPair maybeResult; if ( parseToken( scursor, send, maybeResult, allow8Bit ) ) { result += QString::fromLatin1( maybeResult.first, maybeResult.second ); return true; } return false; } bool parseToken( const char * & scursor, const char * const send, QPair & result, bool allow8Bit ) { bool success = false; const char * start = scursor; while ( scursor != send ) { signed char ch = *scursor++; if ( ch > 0 && isTText(ch) ) { // TText: OK success = true; } else if ( allow8Bit && ch < 0 ) { // 8bit char: not OK, but be tolerant. KMIME_WARN_8BIT(ch); success = true; } else { // CTL or tspecial - marking the end of the atom: // re-set sursor to point to the offending // char and return: scursor--; break; } } result.first = start; result.second = scursor - start; return success; } #define READ_ch_OR_FAIL if ( scursor == send ) { \ KMIME_WARN_PREMATURE_END_OF(GenericQuotedString); \ return false; \ } else { \ ch = *scursor++; \ } // known issues: // // - doesn't handle quoted CRLF bool parseGenericQuotedString( const char* & scursor, const char * const send, QString & result, bool isCRLF, const char openChar, const char closeChar ) { char ch; // We are in a quoted-string or domain-literal or comment and the // cursor points to the first char after the openChar. // We will apply unfolding and quoted-pair removal. // We return when we either encounter the end or unescaped openChar // or closeChar. assert( *(scursor-1) == openChar || *(scursor-1) == closeChar ); while ( scursor != send ) { ch = *scursor++; if ( ch == closeChar || ch == openChar ) { // end of quoted-string or another opening char: // let caller decide what to do. return true; } switch( ch ) { case '\\': // quoted-pair // misses "\" CRLF LWSP-char handling, see rfc822, 3.4.5 READ_ch_OR_FAIL; KMIME_WARN_IF_8BIT(ch); result += QChar(ch); break; case '\r': // ### // The case of lonely '\r' is easy to solve, as they're // not part of Unix Line-ending conventions. // But I see a problem if we are given Unix-native // line-ending-mails, where we cannot determine anymore // whether a given '\n' was part of a CRLF or was occurring // on it's own. READ_ch_OR_FAIL; if ( ch != '\n' ) { // CR on it's own... KMIME_WARN_LONE(CR); result += QChar('\r'); scursor--; // points to after the '\r' again } else { // CRLF encountered. // lookahead: check for folding READ_ch_OR_FAIL; if ( ch == ' ' || ch == '\t' ) { // correct folding; // position cursor behind the CRLF WSP (unfolding) // and add the WSP to the result result += QChar(ch); } else { // this is the "shouldn't happen"-case. There is a CRLF // inside a quoted-string without it being part of FWS. // We take it verbatim. KMIME_WARN_NON_FOLDING(CRLF); result += "\r\n"; // the cursor is decremented again, so's we need not // duplicate the whole switch here. "ch" could've been // everything (incl. openChar or closeChar). scursor--; } } break; case '\n': // Note: CRLF has been handled above already! // ### LF needs special treatment, depending on whether isCRLF // is true (we can be sure a lonely '\n' was meant this way) or // false ('\n' alone could have meant LF or CRLF in the original // message. This parser assumes CRLF iff the LF is followed by // either WSP (folding) or NULL (premature end of quoted-string; // Should be fixed, since NULL is allowed as per rfc822). READ_ch_OR_FAIL; if ( !isCRLF && ( ch == ' ' || ch == '\t' ) ) { // folding // correct folding result += QChar(ch); } else { // non-folding KMIME_WARN_LONE(LF); result += QChar('\n'); // pos is decremented, so's we need not duplicate the whole // switch here. ch could've been everything (incl. <">, "\"). scursor--; } break; default: KMIME_WARN_IF_8BIT(ch); result += QChar(ch); } } return false; } // known issues: // // - doesn't handle encoded-word inside comments. bool parseComment( const char* & scursor, const char * const send, QString & result, bool isCRLF, bool reallySave ) { int commentNestingDepth = 1; const char * afterLastClosingParenPos = 0; QString maybeCmnt; const char * oldscursor = scursor; assert( *(scursor-1) == '(' ); while ( commentNestingDepth ) { QString cmntPart; if ( parseGenericQuotedString( scursor, send, cmntPart, isCRLF, '(', ')' ) ) { assert( *(scursor-1) == ')' || *(scursor-1) == '(' ); // see the kdoc for above function for the possible conditions // we have to check: switch ( *(scursor-1) ) { case ')': if ( reallySave ) { // add the chunk that's now surely inside the comment. result += maybeCmnt; result += cmntPart; if ( commentNestingDepth > 1 ) // don't add the outermost ')'... result += QChar(')'); maybeCmnt.clear(); } afterLastClosingParenPos = scursor; --commentNestingDepth; break; case '(': if ( reallySave ) { // don't add to "result" yet, because we might find that we // are already outside the (broken) comment... maybeCmnt += cmntPart; maybeCmnt += QChar('('); } ++commentNestingDepth; break; default: assert( 0 ); } // switch } else { // !parseGenericQuotedString, ie. premature end if ( afterLastClosingParenPos ) scursor = afterLastClosingParenPos; else scursor = oldscursor; return false; } } // while return true; } // known issues: none. bool parsePhrase( const char* & scursor, const char * const send, QString & result, bool isCRLF ) { enum { None, Phrase, Atom, EncodedWord, QuotedString } found = None; QString tmp; - Q3CString lang; + QByteArray lang; const char * successfullyParsed = 0; // only used by the encoded-word branch const char * oldscursor; // used to suppress whitespace between adjacent encoded-words // (rfc2047, 6.2): bool lastWasEncodedWord = false; while ( scursor != send ) { char ch = *scursor++; switch ( ch ) { case '.': // broken, but allow for intorop's sake if ( found == None ) { --scursor; return false; } else { if ( scursor != send && ( *scursor == ' ' || *scursor == '\t' ) ) result += ". "; else result += '.'; successfullyParsed = scursor; } break; case '"': // quoted-string tmp.clear(); if ( parseGenericQuotedString( scursor, send, tmp, isCRLF, '"', '"' ) ) { successfullyParsed = scursor; assert( *(scursor-1) == '"' ); switch ( found ) { case None: found = QuotedString; break; case Phrase: case Atom: case EncodedWord: case QuotedString: found = Phrase; result += QChar(' '); // rfc822, 3.4.4 break; default: assert( 0 ); } lastWasEncodedWord = false; result += tmp; } else { // premature end of quoted string. // What to do? Return leading '"' as special? Return as quoted-string? // We do the latter if we already found something, else signal failure. if ( found == None ) { return false; } else { result += QChar(' '); // rfc822, 3.4.4 result += tmp; return true; } } break; case '(': // comment // parse it, but ignore content: tmp.clear(); if ( parseComment( scursor, send, tmp, isCRLF, false /*don't bother with the content*/ ) ) { successfullyParsed = scursor; lastWasEncodedWord = false; // strictly interpreting rfc2047, 6.2 } else { if ( found == None ) return false; else { scursor = successfullyParsed; return true; } } break; case '=': // encoded-word tmp.clear(); oldscursor = scursor; - lang = 0; + lang.clear(); if ( parseEncodedWord( scursor, send, tmp, lang ) ) { successfullyParsed = scursor; switch ( found ) { case None: found = EncodedWord; break; case Phrase: case EncodedWord: case Atom: case QuotedString: if ( !lastWasEncodedWord ) result += QChar(' '); // rfc822, 3.4.4 found = Phrase; break; default: assert( 0 ); } lastWasEncodedWord = true; result += tmp; break; } else // parse as atom: scursor = oldscursor; // fall though... default: //atom tmp.clear(); scursor--; if ( parseAtom( scursor, send, tmp, true /* allow 8bit */ ) ) { successfullyParsed = scursor; switch ( found ) { case None: found = Atom; break; case Phrase: case Atom: case EncodedWord: case QuotedString: found = Phrase; result += QChar(' '); // rfc822, 3.4.4 break; default: assert( 0 ); } lastWasEncodedWord = false; result += tmp; } else { if ( found == None ) return false; else { scursor = successfullyParsed; return true; } } } eatWhiteSpace( scursor, send ); } return ( found != None ); } bool parseDotAtom( const char* & scursor, const char * const send, QString & result, bool isCRLF ) { // always points to just after the last atom parsed: const char * successfullyParsed; QString tmp; if ( !parseAtom( scursor, send, tmp, false /* no 8bit */ ) ) return false; result += tmp; successfullyParsed = scursor; while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // end of header or no '.' -> return if ( scursor == send || *scursor != '.' ) return true; scursor++; // eat '.' eatCFWS( scursor, send, isCRLF ); if ( scursor == send || !isAText( *scursor ) ) { // end of header or no AText, but this time following a '.'!: // reset cursor to just after last successfully parsed char and // return: scursor = successfullyParsed; return true; } // try to parse the next atom: QString maybeAtom; if ( !parseAtom( scursor, send, maybeAtom, false /*no 8bit*/ ) ) { scursor = successfullyParsed; return true; } result += QChar('.'); result += maybeAtom; successfullyParsed = scursor; } scursor = successfullyParsed; return true; } void eatCFWS( const char* & scursor, const char * const send, bool isCRLF ) { QString dummy; while ( scursor != send ) { const char * oldscursor = scursor; char ch = *scursor++; switch( ch ) { case ' ': case '\t': // whitespace case '\r': case '\n': // folding continue; case '(': // comment if ( parseComment( scursor, send, dummy, isCRLF, false /*don't save*/ ) ) continue; scursor = oldscursor; return; default: scursor = oldscursor; return; } } } bool parseDomain( const char* & scursor, const char * const send, QString & result, bool isCRLF ) { eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // domain := dot-atom / domain-literal / atom *("." atom) // // equivalent to: // domain = dot-atom / domain-literal, // since parseDotAtom does allow CFWS between atoms and dots if ( *scursor == '[' ) { // domain-literal: QString maybeDomainLiteral; // eat '[': scursor++; while ( parseGenericQuotedString( scursor, send, maybeDomainLiteral, isCRLF, '[', ']' ) ) { if ( scursor == send ) { // end of header: check for closing ']': if ( *(scursor-1) == ']' ) { // OK, last char was ']': result = maybeDomainLiteral; return true; } else { // not OK, domain-literal wasn't closed: return false; } } // we hit openChar in parseGenericQuotedString. // include it in maybeDomainLiteral and keep on parsing: if ( *(scursor-1) == '[' ) { maybeDomainLiteral += QChar('['); continue; } // OK, real end of domain-literal: result = maybeDomainLiteral; return true; } } else { // dot-atom: QString maybeDotAtom; if ( parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) { result = maybeDotAtom; return true; } } return false; } bool parseObsRoute( const char* & scursor, const char* const send, QStringList & result, bool isCRLF, bool save ) { while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // empty entry: if ( *scursor == ',' ) { scursor++; if ( save ) result.append( QString() ); continue; } // empty entry ending the list: if ( *scursor == ':' ) { scursor++; if ( save ) result.append( QString() ); return true; } // each non-empty entry must begin with '@': if ( *scursor != '@' ) return false; else scursor++; QString maybeDomain; if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) return false; if ( save ) result.append( maybeDomain ); // eat the following (optional) comma: eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; if ( *scursor == ':' ) { scursor++; return true; } if ( *scursor == ',' ) scursor++; } return false; } bool parseAddrSpec( const char* & scursor, const char * const send, AddrSpec & result, bool isCRLF ) { // // STEP 1: // local-part := dot-atom / quoted-string / word *("." word) // // this is equivalent to: // local-part := word *("." word) QString maybeLocalPart; QString tmp; while ( scursor != send ) { // first, eat any whitespace eatCFWS( scursor, send, isCRLF ); char ch = *scursor++; switch ( ch ) { case '.': // dot maybeLocalPart += QChar('.'); break; case '@': goto SAW_AT_SIGN; break; case '"': // quoted-string tmp.clear(); if ( parseGenericQuotedString( scursor, send, tmp, isCRLF, '"', '"' ) ) maybeLocalPart += tmp; else return false; break; default: // atom scursor--; // re-set scursor to point to ch again tmp.clear(); if ( parseAtom( scursor, send, tmp, false /* no 8bit */ ) ) maybeLocalPart += tmp; else return false; // parseAtom can only fail if the first char is non-atext. break; } } return false; // // STEP 2: // domain // SAW_AT_SIGN: assert( *(scursor-1) == '@' ); QString maybeDomain; if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) return false; result.localPart = maybeLocalPart; result.domain = maybeDomain; return true; } bool parseAngleAddr( const char* & scursor, const char * const send, AddrSpec & result, bool isCRLF ) { // first, we need an opening angle bracket: eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '<' ) return false; scursor++; // eat '<' eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; if ( *scursor == '@' || *scursor == ',' ) { // obs-route: parse, but ignore: KMIME_WARN << "obsolete source route found! ignoring." << endl; QStringList dummy; if ( !parseObsRoute( scursor, send, dummy, isCRLF, false /* don't save */ ) ) return false; // angle-addr isn't complete until after the '>': if ( scursor == send ) return false; } // parse addr-spec: AddrSpec maybeAddrSpec; if ( !parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) return false; eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '>' ) return false; scursor++; result = maybeAddrSpec; return true; } bool parseMailbox( const char* & scursor, const char * const send, Mailbox & result, bool isCRLF ) { // rfc: // mailbox := addr-spec / ([ display-name ] angle-addr) // us: // mailbox := addr-spec / ([ display-name ] angle-addr) // / (angle-addr "(" display-name ")") eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; AddrSpec maybeAddrSpec; // first, try if it's a vanilla addr-spec: const char * oldscursor = scursor; if ( parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) { result.displayName.clear(); result.addrSpec = maybeAddrSpec; return true; } scursor = oldscursor; // second, see if there's a display-name: QString maybeDisplayName; if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) { // failed: reset cursor, note absent display-name maybeDisplayName.clear(); scursor = oldscursor; } else { // succeeded: eat CFWS eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; } // third, parse the angle-addr: if ( !parseAngleAddr( scursor, send, maybeAddrSpec, isCRLF ) ) return false; if ( maybeDisplayName.isNull() ) { // check for the obsolete form of display-name (as comment): eatWhiteSpace( scursor, send ); if ( scursor != send && *scursor == '(' ) { scursor++; if ( !parseComment( scursor, send, maybeDisplayName, isCRLF, true /*keep*/ ) ) return false; } } result.displayName = maybeDisplayName; result.addrSpec = maybeAddrSpec; return true; } bool parseGroup( const char* & scursor, const char * const send, Address & result, bool isCRLF ) { // group := display-name ":" [ mailbox-list / CFWS ] ";" [CFWS] // // equivalent to: // group := display-name ":" [ obs-mbox-list ] ";" eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // get display-name: QString maybeDisplayName; if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) return false; // get ":": eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != ':' ) return false; result.displayName = maybeDisplayName; // get obs-mbox-list (may contain empty entries): scursor++; while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // empty entry: if ( *scursor == ',' ) { scursor++; continue; } // empty entry ending the list: if ( *scursor == ';' ) { scursor++; return true; } Mailbox maybeMailbox; if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) return false; result.mailboxList.append( maybeMailbox ); eatCFWS( scursor, send, isCRLF ); // premature end: if ( scursor == send ) return false; // regular end of the list: if ( *scursor == ';' ) { scursor++; return true; } // eat regular list entry separator: if ( *scursor == ',' ) scursor++; } return false; } bool parseAddress( const char* & scursor, const char * const send, Address & result, bool isCRLF ) { // address := mailbox / group eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // first try if it's a single mailbox: Mailbox maybeMailbox; const char * oldscursor = scursor; if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) { // yes, it is: result.displayName.clear(); result.mailboxList.append( maybeMailbox ); return true; } scursor = oldscursor; Address maybeAddress; // no, it's not a single mailbox. Try if it's a group: if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) ) return false; result = maybeAddress; return true; } bool parseAddressList( const char* & scursor, const char * const send, AddressList & result, bool isCRLF ) { while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // end of header: this is OK. if ( scursor == send ) return true; // empty entry: ignore: if ( *scursor == ',' ) { scursor++; continue; } // parse one entry Address maybeAddress; if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) ) return false; result.append( maybeAddress ); eatCFWS( scursor, send, isCRLF ); // end of header: this is OK. if ( scursor == send ) return true; // comma separating entries: eat it. if ( *scursor == ',' ) scursor++; } return true; } static QString asterisk = QString::fromLatin1("*0*",1); static QString asteriskZero = QString::fromLatin1("*0*",2); //static QString asteriskZeroAsterisk = QString::fromLatin1("*0*",3); bool parseParameter( const char* & scursor, const char * const send, QPair & result, bool isCRLF ) { // parameter = regular-parameter / extended-parameter // regular-parameter = regular-parameter-name "=" value // extended-parameter = // value = token / quoted-string // // note that rfc2231 handling is out of the scope of this function. // Therefore we return the attribute as QString and the value as // (start,length) tupel if we see that the value is encoded // (trailing asterisk), for parseParameterList to decode... eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // // parse the parameter name: // QString maybeAttribute; if ( !parseToken( scursor, send, maybeAttribute, false /* no 8bit */ ) ) return false; eatCFWS( scursor, send, isCRLF ); // premature end: not OK (haven't seen '=' yet). if ( scursor == send || *scursor != '=' ) return false; scursor++; // eat '=' eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { // don't choke on attribute=, meaning the value was omitted: if ( maybeAttribute.endsWith( asterisk ) ) { KMIME_WARN << "attribute ends with \"*\", but value is empty! " "Chopping away \"*\"." << endl; maybeAttribute.truncate( maybeAttribute.length() - 1 ); } result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() ); return true; } const char * oldscursor = scursor; // // parse the parameter value: // QStringOrQPair maybeValue; if ( *scursor == '"' ) { // value is a quoted-string: scursor++; if ( maybeAttribute.endsWith( asterisk ) ) { // attributes ending with "*" designate extended-parameters, // which cannot have quoted-strings as values. So we remove the // trailing "*" to not confuse upper layers. KMIME_WARN << "attribute ends with \"*\", but value is a quoted-string! " "Chopping away \"*\"." << endl; maybeAttribute.truncate( maybeAttribute.length() - 1 ); } if ( !parseGenericQuotedString( scursor, send, maybeValue.qstring, isCRLF ) ) { scursor = oldscursor; result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() ); return false; // this case needs further processing by upper layers!! } } else { // value is a token: if ( !parseToken( scursor, send, maybeValue.qpair, false /* no 8bit */ ) ) { scursor = oldscursor; result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() ); return false; // this case needs further processing by upper layers!! } } result = qMakePair( maybeAttribute.toLower(), maybeValue ); return true; } bool parseRawParameterList( const char* & scursor, const char * const send, QMap & result, bool isCRLF ) { // we use parseParameter() consecutively to obtain a map of raw // attributes to raw values. "Raw" here means that we don't do // rfc2231 decoding and concatenation. This is left to // parseParameterList(), which will call this function. // // The main reason for making this chunk of code a separate // (private) method is that we can deal with broken parameters // _here_ and leave the rfc2231 handling solely to // parseParameterList(), which will still be enough work. while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // empty entry ending the list: OK. if ( scursor == send ) return true; // empty list entry: ignore. if ( *scursor == ';' ) { scursor++; continue; } QPair maybeParameter; if ( !parseParameter( scursor, send, maybeParameter, isCRLF ) ) { // we need to do a bit of work if the attribute is not // NULL. These are the cases marked with "needs further // processing" in parseParameter(). Specifically, parsing of the // token or the quoted-string, which should represent the value, // failed. We take the easy way out and simply search for the // next ';' to start parsing again. (Another option would be to // take the text between '=' and ';' as value) if ( maybeParameter.first.isNull() ) return false; while ( scursor != send ) { if ( *scursor++ == ';' ) goto IS_SEMICOLON; } // scursor == send case: end of list. return true; IS_SEMICOLON: // *scursor == ';' case: parse next entry. continue; } // successful parsing brings us here: result.insert( maybeParameter.first, maybeParameter.second ); eatCFWS( scursor, send, isCRLF ); // end of header: ends list. if ( scursor == send ) return true; // regular separator: eat it. if ( *scursor == ';' ) scursor++; } return true; } static void decodeRFC2231Value( Codec* & rfc2231Codec, QTextCodec* & textcodec, bool isContinuation, QString & value, QPair & source ) { // // parse the raw value into (charset,language,text): // const char * decBegin = source.first; const char * decCursor = decBegin; const char * decEnd = decCursor + source.second; if ( !isContinuation ) { // find the first single quote while ( decCursor != decEnd ) { if ( *decCursor == '\'' ) break; else decCursor++; } if ( decCursor == decEnd ) { // there wasn't a single single quote at all! // take the whole value to be in latin-1: KMIME_WARN << "No charset in extended-initial-value. " "Assuming \"iso-8859-1\"." << endl; value += QString::fromLatin1( decBegin, source.second ); return; } - Q3CString charset( decBegin, decCursor - decBegin + 1 ); + QByteArray charset( decBegin, decCursor - decBegin ); const char * oldDecCursor = ++decCursor; // find the second single quote (we ignore the language tag): while ( decCursor != decEnd ) { if ( *decCursor == '\'' ) break; else decCursor++; } if ( decCursor == decEnd ) { KMIME_WARN << "No language in extended-initial-value. " "Trying to recover." << endl; decCursor = oldDecCursor; } else decCursor++; // decCursor now points to the start of the // "extended-other-values": // // get the decoders: // bool matchOK = false; textcodec = KGlobal::charsets()->codecForName( charset, matchOK ); if ( !matchOK ) { textcodec = 0; KMIME_WARN_UNKNOWN(Charset,charset); } } if ( !rfc2231Codec ) { rfc2231Codec = Codec::codecForName("x-kmime-rfc2231"); assert( rfc2231Codec ); } if ( !textcodec ) { value += QString::fromLatin1( decCursor, decEnd - decCursor ); return; } Decoder * dec = rfc2231Codec->makeDecoder(); assert( dec ); // // do the decoding: // - QByteArray buffer( rfc2231Codec->maxDecodedSizeFor( decEnd - decCursor ) ); + QByteArray buffer; + buffer.resize( rfc2231Codec->maxDecodedSizeFor( decEnd - decCursor ) ); QByteArray::Iterator bit = buffer.begin(); QByteArray::ConstIterator bend = buffer.end(); if ( !dec->decode( decCursor, decEnd, bit, bend ) ) KMIME_WARN << rfc2231Codec->name() << " codec lies about it's maxDecodedSizeFor()\n" "result may be truncated" << endl; value += textcodec->toUnicode( buffer.begin(), bit - buffer.begin() ); kdDebug() << "value now: \"" << value << "\"" << endl; // cleanup: delete dec; } // known issues: // - permutes rfc2231 continuations when the total number of parts // exceeds 10 (other-sections then becomes *xy, ie. two digits) bool parseParameterList( const char* & scursor, const char * const send, QMap & result, bool isCRLF ) { // parse the list into raw attribute-value pairs: QMap rawParameterList; if (!parseRawParameterList( scursor, send, rawParameterList, isCRLF ) ) return false; if ( rawParameterList.isEmpty() ) return true; // decode rfc 2231 continuations and alternate charset encoding: // NOTE: this code assumes that what QMapIterator delivers is sorted // by the key! Codec * rfc2231Codec = 0; QTextCodec * textcodec = 0; QString attribute; QString value; enum Modes { NoMode = 0x0, Continued = 0x1, Encoded = 0x2 } mode; QMap::Iterator it, end = rawParameterList.end(); for ( it = rawParameterList.begin() ; it != end ; ++it ) { if ( attribute.isNull() || !it.key().startsWith( attribute ) ) { // // new attribute: // // store the last attribute/value pair in the result map now: if ( !attribute.isNull() ) result.insert( attribute, value ); // and extract the information from the new raw attribute: value.clear(); attribute = it.key(); mode = NoMode; // is the value encoded? if ( attribute.endsWith( asterisk ) ) { attribute.truncate( attribute.length() - 1 ); mode = (Modes) ((int) mode | Encoded); } // is the value continued? if ( attribute.endsWith( asteriskZero ) ) { attribute.truncate( attribute.length() - 2 ); mode = (Modes) ((int) mode | Continued); } // // decode if necessary: // if ( mode & Encoded ) { decodeRFC2231Value( rfc2231Codec, textcodec, false, /* isn't continuation */ value, (*it).qpair ); } else { // not encoded. if ( (*it).qpair.first ) value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second ); else value += (*it).qstring; } // // shortcut-processing when the value isn't encoded: // if ( !(mode & Continued) ) { // save result already: result.insert( attribute, value ); // force begin of a new attribute: attribute.clear(); } } else /* it.key().startsWith( attribute ) */ { // // continuation // // ignore the section and trust QMap to have sorted the keys: if ( it.key().endsWith( asterisk ) ) { // encoded decodeRFC2231Value( rfc2231Codec, textcodec, true, /* is continuation */ value, (*it).qpair ); } else { // not encoded if ( (*it).qpair.first ) value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second ); else value += (*it).qstring; } } } // write last attr/value pair: if ( !attribute.isNull() ) result.insert( attribute, value ); return true; } static const char * stdDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const int stdDayNamesLen = sizeof stdDayNames / sizeof *stdDayNames; static bool parseDayName( const char* & scursor, const char * const send ) { // check bounds: if ( send - scursor < 3 ) return false; for ( int i = 0 ; i < stdDayNamesLen ; ++i ) if ( qstrnicmp( scursor, stdDayNames[i], 3 ) == 0 ) { scursor += 3; kdDebug() << "found " << stdDayNames[i] << endl; return true; } return false; } static const char * stdMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dez" }; static const int stdMonthNamesLen = sizeof stdMonthNames / sizeof *stdMonthNames; static bool parseMonthName( const char* & scursor, const char * const send, int & result ) { // check bounds: if ( send - scursor < 3 ) return false; for ( result = 0 ; result < stdMonthNamesLen ; ++result ) if ( qstrnicmp( scursor, stdMonthNames[result], 3 ) == 0 ) { scursor += 3; return true; } // not found: return false; } static const struct { const char * tzName; long int secsEastOfGMT; } timeZones[] = { // rfc 822 timezones: { "GMT", 0 }, { "UT", 0 }, { "EDT", -4*3600 }, { "EST", -5*3600 }, { "MST", -5*3600 }, { "CST", -6*3600 }, { "MDT", -6*3600 }, { "MST", -7*3600 }, { "PDT", -7*3600 }, { "PST", -8*3600 }, // common, non-rfc-822 zones: { "CET", 1*3600 }, { "MET", 1*3600 }, { "UTC", 0 }, { "CEST", 2*3600 }, { "BST", 1*3600 }, // rfc 822 military timezones: { "Z", 0 }, { "A", -1*3600 }, { "B", -2*3600 }, { "C", -3*3600 }, { "D", -4*3600 }, { "E", -5*3600 }, { "F", -6*3600 }, { "G", -7*3600 }, { "H", -8*3600 }, { "I", -9*3600 }, // J is not used! { "K", -10*3600 }, { "L", -11*3600 }, { "M", -12*3600 }, { "N", 1*3600 }, { "O", 2*3600 }, { "P", 3*3600 }, { "Q", 4*3600 }, { "R", 5*3600 }, { "S", 6*3600 }, { "T", 7*3600 }, { "U", 8*3600 }, { "V", 9*3600 }, { "W", 10*3600 }, { "X", 11*3600 }, { "Y", 12*3600 }, }; static const int timeZonesLen = sizeof timeZones / sizeof *timeZones; static bool parseAlphaNumericTimeZone( const char* & scursor, const char * const send, long int & secsEastOfGMT, bool & timeZoneKnown ) { QPair maybeTimeZone(0,0); if ( !parseToken( scursor, send, maybeTimeZone, false /*no 8bit*/ ) ) return false; for ( int i = 0 ; i < timeZonesLen ; ++i ) if ( qstrnicmp( timeZones[i].tzName, maybeTimeZone.first, maybeTimeZone.second ) == 0 ) { scursor += maybeTimeZone.second; secsEastOfGMT = timeZones[i].secsEastOfGMT; timeZoneKnown = true; return true; } // don't choke just because we don't happen to know the time zone - KMIME_WARN_UNKNOWN(time zone,Q3CString( maybeTimeZone.first, maybeTimeZone.second+1 )); + KMIME_WARN_UNKNOWN(time zone,QByteArray( maybeTimeZone.first, maybeTimeZone.second )); secsEastOfGMT = 0; timeZoneKnown = false; return true; } // parse a number and return the number of digits parsed: static int parseDigits( const char* & scursor, const char * const send, int & result ) { result = 0; int digits = 0; for ( ; scursor != send && isdigit( *scursor ) ; scursor++, digits++ ) { result *= 10; result += int( *scursor - '0' ); } return digits; } static bool parseTimeOfDay( const char* & scursor, const char * const send, int & hour, int & min, int & sec, bool isCRLF=false ) { // time-of-day := 2DIGIT [CFWS] ":" [CFWS] 2DIGIT [ [CFWS] ":" 2DIGIT ] // // 2DIGIT representing "hour": // if ( !parseDigits( scursor, send, hour ) ) return false; eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != ':' ) return false; scursor++; // eat ':' eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // // 2DIGIT representing "minute": // if ( !parseDigits( scursor, send, min ) ) return false; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return true; // seconds are optional // // let's see if we have a 2DIGIT representing "second": // if ( *scursor == ':' ) { // yepp, there are seconds: scursor++; // eat ':' eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; if ( !parseDigits( scursor, send, sec ) ) return false; } else { sec = 0; } return true; } bool parseTime( const char* & scursor, const char * send, int & hour, int & min, int & sec, long int & secsEastOfGMT, bool & timeZoneKnown, bool isCRLF ) { // time := time-of-day CFWS ( zone / obs-zone ) // // obs-zone := "UT" / "GMT" / // "EST" / "EDT" / ; -0500 / -0400 // "CST" / "CDT" / ; -0600 / -0500 // "MST" / "MDT" / ; -0700 / -0600 // "PST" / "PDT" / ; -0800 / -0700 // "A"-"I" / "a"-"i" / // "K"-"Z" / "k"-"z" eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; if ( !parseTimeOfDay( scursor, send, hour, min, sec, isCRLF ) ) return false; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { timeZoneKnown = false; secsEastOfGMT = 0; return true; // allow missing timezone } timeZoneKnown = true; if ( *scursor == '+' || *scursor == '-' ) { // remember and eat '-'/'+': const char sign = *scursor++; // numerical timezone: int maybeTimeZone; if ( parseDigits( scursor, send, maybeTimeZone ) != 4 ) return false; secsEastOfGMT = 60 * ( maybeTimeZone / 100 * 60 + maybeTimeZone % 100 ); if ( sign == '-' ) { secsEastOfGMT *= -1; if ( secsEastOfGMT == 0 ) timeZoneKnown = false; // -0000 means indetermined tz } } else { // maybe alphanumeric timezone: if ( !parseAlphaNumericTimeZone( scursor, send, secsEastOfGMT, timeZoneKnown ) ) return false; } return true; } bool parseDateTime( const char* & scursor, const char * const send, Types::DateTime & result, bool isCRLF ) { // Parsing date-time; strict mode: // // date-time := [ [CFWS] day-name [CFWS] "," ] ; wday // (expanded) [CFWS] 1*2DIGIT CFWS month-name CFWS 2*DIGIT [CFWS] ; date // time // // day-name := "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" // month-name := "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dez" struct tm maybeDateTime = { #ifdef HAVE_TM_GMTOFF 0, 0, // initializers for members tm_gmtoff and tm_zone #endif 0, 0, 0, 0, 0, 0, 0, 0, 0 }; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // // let's see if there's a day-of-week: // if ( parseDayName( scursor, send ) ) { eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // day-name should be followed by ',' but we treat it as optional: if ( *scursor == ',' ) { scursor++; // eat ',' eatCFWS( scursor, send, isCRLF ); } } // // 1*2DIGIT representing "day" (of month): // int maybeDay; if ( !parseDigits( scursor, send, maybeDay ) ) return false; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // success: store maybeDay in maybeDateTime: maybeDateTime.tm_mday = maybeDay; // // month-name: // int maybeMonth = 0; if ( !parseMonthName( scursor, send, maybeMonth ) ) return false; if ( scursor == send ) return false; assert( maybeMonth >= 0 ); assert( maybeMonth <= 11 ); eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // success: store maybeMonth in maybeDateTime: maybeDateTime.tm_mon = maybeMonth; // // 2*DIGIT representing "year": // int maybeYear; if ( !parseDigits( scursor, send, maybeYear ) ) return false; // RFC 2822 4.3 processing: if ( maybeYear < 50 ) maybeYear += 2000; else if ( maybeYear < 1000 ) maybeYear += 1900; // else keep as is if ( maybeYear < 1900 ) return false; // rfc2822, 3.3 eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; // success: store maybeYear in maybeDateTime: maybeDateTime.tm_year = maybeYear - 1900; // // time // int maybeHour, maybeMinute, maybeSecond; long int secsEastOfGMT; bool timeZoneKnown = true; if ( !parseTime( scursor, send, maybeHour, maybeMinute, maybeSecond, secsEastOfGMT, timeZoneKnown, isCRLF ) ) return false; // success: store everything in maybeDateTime: maybeDateTime.tm_hour = maybeHour; maybeDateTime.tm_min = maybeMinute; maybeDateTime.tm_sec = maybeSecond; maybeDateTime.tm_isdst = DateFormatter::isDaylight(); // now put everything together and check if mktime(3) likes it: result.time = mktime( &maybeDateTime ); if ( result.time == (time_t)(-1) ) return false; // adjust to UTC/GMT: //result.time -= secsEastOfGMT; result.secsEastOfGMT = secsEastOfGMT; result.timeZoneKnown = timeZoneKnown; return true; } #if 0 bool tryToMakeAnySenseOfDateString( const char* & scursor, const char * const send, time_t & result, bool isCRLF ) { return false; } #endif } // namespace HeaderParsing } // namespace KMime diff --git a/kmime/kmime_headers.cpp b/kmime/kmime_headers.cpp index 30b502586..027774d58 100644 --- a/kmime/kmime_headers.cpp +++ b/kmime/kmime_headers.cpp @@ -1,1593 +1,1597 @@ /* kmime_headers.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include "kmime_headers.h" #include "kmime_util.h" #include "kmime_content.h" #include "kmime_codecs.h" #include "kmime_header_parsing.h" #include "kmime_warning.h" #include #include -#include #include #include #include #include #include using namespace KMime; using namespace KMime::Headers; using namespace KMime::Types; using namespace KMime::HeaderParsing; namespace KMime { namespace Headers { //--------------------------------------- -Q3CString Base::rfc2047Charset() +QByteArray Base::rfc2047Charset() { - if( (e_ncCS==0) || forceCS() ) + if ( e_ncCS.isEmpty() || forceCS() ) return defaultCS(); else - return Q3CString(e_ncCS); + return e_ncCS; } -void Base::setRFC2047Charset(const Q3CString &cs) +void Base::setRFC2047Charset( const QByteArray &cs ) { e_ncCS=cachedCharset(cs); } bool Base::forceCS() { return ( p_arent!=0 ? p_arent->forceDefaultCS() : false ); } QByteArray Base::defaultCS() { return ( p_arent!=0 ? p_arent->defaultCharset() : Latin1 ); } //-------------------------------------- namespace Generics { //------------------------------ -void GUnstructured::from7BitString( const Q3CString & str ) +void GUnstructured::from7BitString( const QByteArray & str ) { - d_ecoded = decodeRFC2047String( str, &e_ncCS, defaultCS(), forceCS() ); + d_ecoded = decodeRFC2047String( str, e_ncCS, defaultCS(), forceCS() ); } -Q3CString GUnstructured::as7BitString( bool withHeaderType ) +QByteArray GUnstructured::as7BitString( bool withHeaderType ) { - Q3CString result; + QByteArray result; if ( withHeaderType ) result = typeIntro(); result += encodeRFC2047String( d_ecoded, e_ncCS ) ; return result; } void GUnstructured::fromUnicodeString( const QString & str, - const Q3CString & suggestedCharset ) + const QByteArray & suggestedCharset ) { d_ecoded = str; e_ncCS = cachedCharset( suggestedCharset ); } QString GUnstructured::asUnicodeString() { return d_ecoded; } //------------------------------ //------------------------------ //------------------------------ //------------------------------ //------------------------------ //------------------------------ bool MailboxList::parse( const char* & scursor, const char * const send, bool isCRLF ) { // examples: // from := "From:" mailbox-list CRLF // sender := "Sender:" mailbox CRLF // parse an address-list: QList
maybeAddressList; if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) return false; mMailboxList.clear(); // extract the mailboxes and complain if there are groups: QList
::Iterator it; for ( it = maybeAddressList.begin(); it != maybeAddressList.end() ; ++it ) { if ( !(*it).displayName.isEmpty() ) { KMIME_WARN << "mailbox groups in header disallowing them! Name: \"" << (*it).displayName << "\"" << endl; } mMailboxList += (*it).mailboxList; } return true; } //------------------------------ //------------------------------ bool SingleMailbox::parse( const char* & scursor, const char * const send, bool isCRLF ) { if ( !MailboxList::parse( scursor, send, isCRLF ) ) return false; if ( mMailboxList.count() > 1 ) { KMIME_WARN << "multiple mailboxes in header allowing only a single one!" << endl; } return true; } //------------------------------ //------------------------------ bool AddressList::parse( const char* & scursor, const char * const send, bool isCRLF ) { QList
maybeAddressList; if ( !parseAddressList( scursor, send, maybeAddressList, isCRLF ) ) return false; mAddressList = maybeAddressList; return true; } //------------------------------ //------------------------------ bool GToken::parse( const char* & scursor, const char * const send, bool isCRLF ) { eatCFWS( scursor, send, isCRLF ); // must not be empty: if ( scursor == send ) return false; QPair maybeToken; if ( !parseToken( scursor, send, maybeToken, false /* no 8bit chars */ ) ) return false; - mToken = Q3CString( maybeToken.first, maybeToken.second ); + mToken = QByteArray( maybeToken.first, maybeToken.second ); // complain if trailing garbage is found: eatCFWS( scursor, send, isCRLF ); if ( scursor != send ) { KMIME_WARN << "trailing garbage after token in header allowing " "only a single token!" << endl; } return true; } //------------------------------ //------------------------------ bool GPhraseList::parse( const char* & scursor, const char * const send, bool isCRLF ) { mPhraseList.clear(); while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // empty entry ending the list: OK. if ( scursor == send ) return true; // empty entry: ignore. if ( *scursor != ',' ) { scursor++; continue; } QString maybePhrase; if ( !parsePhrase( scursor, send, maybePhrase, isCRLF ) ) return false; mPhraseList.append( maybePhrase ); eatCFWS( scursor, send, isCRLF ); // non-empty entry ending the list: OK. if ( scursor == send ) return true; // comma separating the phrases: eat. if ( *scursor != ',' ) scursor++; } return true; } //------------------------------ //------------------------------ bool GDotAtom::parse( const char* & scursor, const char * const send, bool isCRLF ) { QString maybeDotAtom; if ( !parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) return false; mDotAtom = maybeDotAtom; eatCFWS( scursor, send, isCRLF ); if ( scursor != send ) { KMIME_WARN << "trailing garbage after dot-atom in header allowing " "only a single dot-atom!" << endl; } return true; } //------------------------------ //------------------------------ //------------------------------ //------------------------------ bool GContentType::parse( const char* & scursor, const char * const send, bool isCRLF ) { // content-type: type "/" subtype *(";" parameter) mMimeType = 0; mMimeSubType = 0; mParameterHash.clear(); eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) { // empty header return false; } // // type // QPair maybeMimeType; if ( !parseToken( scursor, send, maybeMimeType, false /* no 8Bit */ ) ) return false; - mMimeType = Q3CString( maybeMimeType.first, maybeMimeType.second ).lower(); + mMimeType = QByteArray( maybeMimeType.first, maybeMimeType.second ).toLower(); // // subtype // eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '/' ) return false; scursor++; eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; QPair maybeSubType; if ( !parseToken( scursor, send, maybeSubType, false /* no 8bit */ ) ) return false; - mMimeSubType = Q3CString( maybeSubType.first, maybeSubType.second ).lower(); + mMimeSubType = QByteArray( maybeSubType.first, maybeSubType.second ).toLower(); // // parameter list // eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return true; // no parameters if ( *scursor != ';' ) return false; scursor++; if ( !parseParameterList( scursor, send, mParameterHash, isCRLF ) ) return false; return true; } //------------------------------ //------------------------------ bool GCISTokenWithParameterList::parse( const char* & scursor, const char * const send, bool isCRLF ) { mToken = 0; mParameterHash.clear(); // // token // eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; QPair maybeToken; if ( !parseToken( scursor, send, maybeToken, false /* no 8Bit */ ) ) return false; - mToken = Q3CString( maybeToken.first, maybeToken.second ).lower(); + mToken = QByteArray( maybeToken.first, maybeToken.second ).toLower(); // // parameter list // eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return true; // no parameters if ( *scursor != ';' ) return false; scursor++; if ( !parseParameterList( scursor, send, mParameterHash, isCRLF ) ) return false; return true; } //------------------------------ //------------------------------ bool GIdent::parse( const char* & scursor, const char * const send, bool isCRLF ) { // msg-id := "<" id-left "@" id-right ">" // id-left := dot-atom-text / no-fold-quote / local-part // id-right := dot-atom-text / no-fold-literal / domain // // equivalent to: // msg-id := angle-addr mMsgIdList.clear(); while ( scursor != send ) { eatCFWS( scursor, send, isCRLF ); // empty entry ending the list: OK. if ( scursor == send ) return true; // empty entry: ignore. if ( *scursor == ',' ) { scursor++; continue; } AddrSpec maybeMsgId; if ( !parseAngleAddr( scursor, send, maybeMsgId, isCRLF ) ) return false; mMsgIdList.append( maybeMsgId ); eatCFWS( scursor, send, isCRLF ); // header end ending the list: OK. if ( scursor == send ) return true; // regular item separator: eat it. if ( *scursor == ',' ) scursor++; } return true; } //------------------------------ //------------------------------ bool GSingleIdent::parse( const char* & scursor, const char * const send, bool isCRLF ) { if ( !GIdent::parse( scursor, send, isCRLF ) ) return false; if ( mMsgIdList.count() > 1 ) { KMIME_WARN << "more than one msg-id in header " "allowing only a single one!" << endl; } return true; } //------------------------------ } // namespace Generics //------------------------------ bool ReturnPath::parse( const char* & scursor, const char * const send, bool isCRLF ) { eatCFWS( scursor, send, isCRLF ); if ( scursor == send ) return false; const char * oldscursor = scursor; Mailbox maybeMailbox; if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) { // mailbox parsing failed, but check for empty brackets: scursor = oldscursor; if ( *scursor != '<' ) return false; scursor++; eatCFWS( scursor, send, isCRLF ); if ( scursor == send || *scursor != '>' ) return false; scursor++; // prepare a Null mailbox: AddrSpec emptyAddrSpec; maybeMailbox.displayName.clear(); maybeMailbox.addrSpec = emptyAddrSpec; } else // check that there was no display-name: if ( !maybeMailbox.displayName.isEmpty() ) { KMIME_WARN << "display-name \"" << maybeMailbox.displayName << "\" in Return-Path!" << endl; } // see if that was all: eatCFWS( scursor, send, isCRLF ); // and warn if it wasn't: if ( scursor != send ) { KMIME_WARN << "trailing garbage after angle-addr in Return-Path!" << endl; } return true; } //------------------------------ //------------------------------------ void Generic::setType(const char *type) { if(t_ype) delete[] t_ype; if(type) { t_ype=new char[strlen(type)+1]; strcpy(t_ype, type); } else t_ype=0; } //------------------------------------ #if !defined(KMIME_NEW_STYLE_CLASSTREE) //---------------------------------- -void MessageID::from7BitString(const Q3CString &s) +void MessageID::from7BitString(const QByteArray &s) { m_id=s; } -Q3CString MessageID::as7BitString(bool incType) +QByteArray MessageID::as7BitString(bool incType) { if(incType) return ( typeIntro()+m_id ); else return m_id; } -void MessageID::fromUnicodeString(const QString &s, const Q3CString&) +void MessageID::fromUnicodeString(const QString &s, const QByteArray&) { - m_id=s.latin1(); //Message-Ids can only contain us-ascii chars + m_id=s.toLatin1(); //Message-Ids can only contain us-ascii chars } QString MessageID::asUnicodeString() { return QString::fromLatin1(m_id); } -void MessageID::generate(const Q3CString &fqdn) +void MessageID::generate(const QByteArray &fqdn) { m_id="<"+uniqueString()+"@"+fqdn+">"; } //--------------------------------- #endif //------------------------------------ -void Control::from7BitString(const Q3CString &s) +void Control::from7BitString(const QByteArray &s) { c_trlMsg=s; } -Q3CString Control::as7BitString(bool incType) +QByteArray Control::as7BitString(bool incType) { if(incType) return ( typeIntro()+c_trlMsg ); else return c_trlMsg; } -void Control::fromUnicodeString(const QString &s, const Q3CString&) +void Control::fromUnicodeString(const QString &s, const QByteArray&) { - c_trlMsg=s.latin1(); + c_trlMsg=s.toLatin1(); } QString Control::asUnicodeString() { return QString::fromLatin1(c_trlMsg); } //----------------------------------- #if !defined(KMIME_NEW_STYLE_CLASSTREE) //------------------------------- -void AddressField::from7BitString(const Q3CString &s) +void AddressField::from7BitString(const QByteArray &s) { int pos1=0, pos2=0, type=0; - Q3CString n; + QByteArray n; //so what do we have here ? - if(QString(s).contains( QRegExp("*@*(*)", false, true) )) type=2; // From: foo@bar.com (John Doe) - else if(QString(s).contains( QRegExp("*<*@*>", false, true) )) type=1; // From: John Doe - else if(QString(s).contains( QRegExp("*@*", false, true) )) type=0; // From: foo@bar.com + if(QString(s).contains( QRegExp("*@*(*)", Qt::CaseInsensitive, QRegExp::Wildcard) )) + type=2; // From: foo@bar.com (John Doe) + else if(QString(s).contains( QRegExp("*<*@*>", Qt::CaseInsensitive, QRegExp::Wildcard) )) + type=1; // From: John Doe + else if(QString(s).contains( QRegExp("*@*", Qt::CaseInsensitive, QRegExp::Wildcard) )) + type=0; // From: foo@bar.com else { //broken From header => just decode it - n_ame=decodeRFC2047String(s, &e_ncCS, defaultCS(), forceCS()); + n_ame=decodeRFC2047String(s, e_ncCS, defaultCS(), forceCS()); return; } switch(type) { case 0: - e_mail=s.copy(); + e_mail=s; break; case 1: pos1=0; - pos2=s.find('<'); + pos2=s.indexOf('<'); if(pos2!=-1) { n=s.mid(pos1, pos2-pos1).trimmed(); pos1=pos2+1; - pos2=s.find('>', pos1); + pos2=s.indexOf('>', pos1); if(pos2!=-1) e_mail=s.mid(pos1, pos2-pos1); } else return; break; case 2: pos1=0; - pos2=s.find('('); + pos2=s.indexOf('('); if(pos2!=-1) { e_mail=s.mid(pos1, pos2-pos1).trimmed(); pos1=pos2+1; - pos2=s.find(')', pos1); + pos2=s.indexOf(')', pos1); if(pos2!=-1) n=s.mid(pos1, pos2-pos1).trimmed(); } break; default: break; } if(!n.isEmpty()) { removeQuots(n); - n_ame=decodeRFC2047String(n, &e_ncCS, defaultCS(), forceCS()); + n_ame=decodeRFC2047String(n, e_ncCS, defaultCS(), forceCS()); } } -Q3CString AddressField::as7BitString(bool incType) +QByteArray AddressField::as7BitString(bool incType) { - Q3CString ret; + QByteArray ret; if(incType && type()[0]!='\0') ret=typeIntro(); if(n_ame.isEmpty()) ret+=e_mail; else { if (isUsAscii(n_ame)) { - Q3CString tmp(n_ame.latin1()); + QByteArray tmp = n_ame.toLatin1(); addQuotes(tmp, false); ret+=tmp; } else { ret+=encodeRFC2047String(n_ame, e_ncCS, true); } if (!e_mail.isEmpty()) ret += " <"+e_mail+">"; } return ret; } -void AddressField::fromUnicodeString(const QString &s, const Q3CString &cs) +void AddressField::fromUnicodeString(const QString &s, const QByteArray &cs) { int pos1=0, pos2=0, type=0; - Q3CString n; + QByteArray n; e_ncCS=cachedCharset(cs); //so what do we have here ? - if(s.find( QRegExp("*@*(*)", false, true) )!=-1) type=2; // From: foo@bar.com (John Doe) - else if(s.find( QRegExp("*<*@*>", false, true) )!=-1) type=1; // From: John Doe - else if(s.find( QRegExp("*@*", false, true) )!=-1) type=0; // From: foo@bar.com + if(s.contains( QRegExp("*@*(*)", Qt::CaseInsensitive, QRegExp::Wildcard) ) ) + type=2; // From: foo@bar.com (John Doe) + else if(s.contains( QRegExp("*<*@*>", Qt::CaseInsensitive, QRegExp::Wildcard) ) ) + type=1; // From: John Doe + else if(s.contains( QRegExp("*@*", Qt::CaseInsensitive, QRegExp::Wildcard) ) ) + type=0; // From: foo@bar.com else { //broken From header => just copy it n_ame=s; return; } switch(type) { case 0: - e_mail=s.latin1(); + e_mail=s.toLatin1(); break; case 1: pos1=0; - pos2=s.find('<'); + pos2=s.indexOf('<'); if(pos2!=-1) { n_ame=s.mid(pos1, pos2-pos1).trimmed(); pos1=pos2+1; - pos2=s.find('>', pos1); + pos2=s.indexOf('>', pos1); if(pos2!=-1) - e_mail=s.mid(pos1, pos2-pos1).latin1(); + e_mail=s.mid(pos1, pos2-pos1).toLatin1(); } else return; break; case 2: pos1=0; - pos2=s.find('('); + pos2=s.indexOf('('); if(pos2!=-1) { - e_mail=s.mid(pos1, pos2-pos1).trimmed().latin1(); + e_mail=s.mid(pos1, pos2-pos1).trimmed().toLatin1(); pos1=pos2+1; - pos2=s.find(')', pos1); + pos2=s.indexOf(')', pos1); if(pos2!=-1) n_ame=s.mid(pos1, pos2-pos1).trimmed(); } break; default: break; } if(!n_ame.isEmpty()) removeQuots(n_ame); } QString AddressField::asUnicodeString() { if(n_ame.isEmpty()) return QString(e_mail); else { QString s = n_ame; if (!e_mail.isEmpty()) s += " <"+e_mail+">"; return s; } } -Q3CString AddressField::nameAs7Bit() +QByteArray AddressField::nameAs7Bit() { return encodeRFC2047String(n_ame, e_ncCS); } -void AddressField::setNameFrom7Bit(const Q3CString &s) +void AddressField::setNameFrom7Bit(const QByteArray &s) { - n_ame=decodeRFC2047String(s, &e_ncCS, defaultCS(), forceCS()); + n_ame=decodeRFC2047String(s, e_ncCS, defaultCS(), forceCS()); } //------------------------------ #endif //------------------------------- bool MailCopiesTo::isValid() { if (hasEmail()) return true; if ((n_ame == "nobody") || (n_ame == "never") || (n_ame == "poster") || (n_ame == "always")) return true; else return false; } bool MailCopiesTo::alwaysCopy() { return (hasEmail() || (n_ame == "poster") || (n_ame == "always")); } bool MailCopiesTo::neverCopy() { return ((n_ame == "nobody") || (n_ame == "never")); } //------------------------------ //--------------------------------------- -void Date::from7BitString(const Q3CString &s) +void Date::from7BitString(const QByteArray &s) { t_ime=KRFCDate::parseDate(s); } -Q3CString Date::as7BitString(bool incType) +QByteArray Date::as7BitString(bool incType) { if(incType) return ( typeIntro()+KRFCDate::rfc2822DateString(t_ime) ); else - return Q3CString(KRFCDate::rfc2822DateString(t_ime)); + return KRFCDate::rfc2822DateString(t_ime); } -void Date::fromUnicodeString(const QString &s, const Q3CString&) +void Date::fromUnicodeString(const QString &s, const QByteArray&) { - from7BitString( Q3CString(s.latin1()) ); + from7BitString( s.toLatin1() ); } QString Date::asUnicodeString() { return QString::fromLatin1(as7BitString(false)); } QDateTime Date::qdt() { QDateTime dt; dt.setTime_t(t_ime); return dt; } int Date::ageInDays() { QDate today=QDate::currentDate(); return ( qdt().date().daysTo(today) ); } //-------------------------------------- #if !defined(KMIME_NEW_STYLE_CLASSTREE) //----------------------------------------- -void To::from7BitString(const Q3CString &s) +void To::from7BitString(const QByteArray &s) { qDeleteAll( a_ddrList ); a_ddrList.clear(); QList split = s.split( ',' ); foreach ( QByteArray s, split ) a_ddrList.append( new AddressField( p_arent, s ) ); e_ncCS = cachedCharset( a_ddrList.first()->rfc2047Charset() ); } -Q3CString To::as7BitString(bool incType) +QByteArray To::as7BitString(bool incType) { - Q3CString ret; + QByteArray ret; if(incType) ret+=typeIntro(); if ( !a_ddrList.isEmpty() ) { ObsAddressList::Iterator it = a_ddrList.begin(); if ( *it ) ret += (*it)->as7BitString( false ); for ( ++it; it != a_ddrList.end(); ++it ) ret += "," + (*it)->as7BitString( false ); } return ret; } -void To::fromUnicodeString(const QString &s, const Q3CString &cs) +void To::fromUnicodeString(const QString &s, const QByteArray &cs) { qDeleteAll( a_ddrList ); a_ddrList.clear(); QStringList l = s.split( ',' ); for ( QStringList::Iterator it=l.begin(); it != l.end(); ++it ) a_ddrList.append( new AddressField( p_arent, (*it), cs ) ); e_ncCS=cachedCharset(cs); } QString To::asUnicodeString() { if ( a_ddrList.isEmpty() ) return QString(); QString ret; ObsAddressList::Iterator it = a_ddrList.begin(); if ( *it ) ret += (*it)->asUnicodeString(); for ( ++it; it != a_ddrList.end(); ++it ) ret += "," + (*it)->asUnicodeString(); return ret; } void To::addAddress(const AddressField &a) { AddressField *add=new AddressField(a); add->setParent(p_arent); a_ddrList.append( add ); } QList To::emails() const { QList l; for ( ObsAddressList::ConstIterator it = a_ddrList.begin(); it != a_ddrList.end(); ++it ) if ( (*it)->hasEmail() ) l.append( (*it)->email() ); return l; } //---------------------------------------- #endif //--------------------------------- -void Newsgroups::from7BitString(const Q3CString &s) +void Newsgroups::from7BitString(const QByteArray &s) { g_roups=s; e_ncCS=cachedCharset("UTF-8"); } -Q3CString Newsgroups::as7BitString(bool incType) +QByteArray Newsgroups::as7BitString(bool incType) { if(incType) return (typeIntro()+g_roups); else return g_roups; } -void Newsgroups::fromUnicodeString(const QString &s, const Q3CString&) +void Newsgroups::fromUnicodeString(const QString &s, const QByteArray&) { - g_roups=s.utf8(); + g_roups=s.toUtf8(); e_ncCS=cachedCharset("UTF-8"); } QString Newsgroups::asUnicodeString() { return QString::fromUtf8(g_roups); } -Q3CString Newsgroups::firstGroup() +QByteArray Newsgroups::firstGroup() { int pos=0; if(!g_roups.isEmpty()) { - pos=g_roups.find(','); + pos=g_roups.indexOf(','); if(pos==-1) return g_roups; else return g_roups.left(pos); } else - return Q3CString(); + return QByteArray(); } QStringList Newsgroups::getGroups() { - QStringList temp = QStringList::split(',', g_roups); + QList temp = g_roups.split(','); QStringList ret; QString s; - for (QStringList::Iterator it = temp.begin(); it != temp.end(); ++it ) { - s = (*it).simplified(); - ret.append(s); + foreach ( QByteArray group, temp ) { + ret.append( group.simplified() ); } return ret; } //-------------------------------- //-------------------------------------- -void Lines::from7BitString(const Q3CString &s) +void Lines::from7BitString(const QByteArray &s) { l_ines=s.toInt(); e_ncCS=cachedCharset(Latin1); } -Q3CString Lines::as7BitString(bool incType) +QByteArray Lines::as7BitString(bool incType) { - Q3CString num; + QByteArray num; num.setNum(l_ines); if(incType) return ( typeIntro()+num ); else return num; } -void Lines::fromUnicodeString(const QString &s, const Q3CString&) +void Lines::fromUnicodeString(const QString &s, const QByteArray&) { l_ines=s.toInt(); e_ncCS=cachedCharset(Latin1); } QString Lines::asUnicodeString() { QString num; num.setNum(l_ines); return num; } //------------------------------------- #if !defined(KMIME_NEW_STYLE_CLASSTREE) //--------------------------------- -void References::from7BitString(const Q3CString &s) +void References::from7BitString(const QByteArray &s) { r_ef=s; e_ncCS=cachedCharset(Latin1); } -Q3CString References::as7BitString(bool incType) +QByteArray References::as7BitString(bool incType) { if(incType) return ( typeIntro()+r_ef ); else return r_ef; } -void References::fromUnicodeString(const QString &s, const Q3CString&) +void References::fromUnicodeString(const QString &s, const QByteArray&) { - r_ef=s.latin1(); + r_ef=s.toLatin1(); e_ncCS=cachedCharset(Latin1); } QString References::asUnicodeString() { return QString::fromLatin1(r_ef); } int References::count() { int cnt1=0, cnt2=0; unsigned int r_efLen=r_ef.length(); char *dataPtr=r_ef.data(); for(unsigned int i=0; i') cnt2++; } if(cnt1', p_os); + pos2=r_ef.lastIndexOf('>', p_os); p_os=0; if(pos2!=-1) { - pos1=r_ef.findRev('<', pos2); + pos1=r_ef.lastIndexOf('<', pos2); if(pos1!=-1) { ret=r_ef.mid(pos1, pos2-pos1+1); p_os=pos1; } } } return ret; } -Q3CString References::at(unsigned int i) +QByteArray References::at(unsigned int i) { - Q3CString ret; + QByteArray ret; int pos1=0, pos2=0; unsigned int cnt=0; while(pos1!=-1 && cnt < i+1) { pos2=pos1-1; - pos1=r_ef.findRev('<', pos2); + pos1=r_ef.lastIndexOf('<', pos2); cnt++; } if(pos1!=-1) { - pos2=r_ef.find('>', pos1); + pos2=r_ef.indexOf('>', pos1); if(pos2!=-1) ret=r_ef.mid(pos1, pos2-pos1+1); } return ret; } -void References::append(const Q3CString &s) +void References::append(const QByteArray &s) { QString temp=r_ef.data(); temp += " "; temp += s.data(); - QStringList lst=QStringList::split(' ',temp); + QStringList lst = temp.split(' '); QRegExp exp("^<.+@.+>$"); // remove bogus references QStringList::Iterator it = lst.begin(); while (it != lst.end()) { - if (-1==(*it).find(exp)) - it = lst.remove(it); + if (-1==(*it).indexOf(exp)) + it = lst.erase(it); else it++; } if (lst.isEmpty()) { - r_ef = s.copy(); // shouldn't happen... + r_ef = s; // shouldn't happen... return; } else r_ef = ""; temp = lst.first(); // include the first id - r_ef = temp.latin1(); - lst.remove(temp); // avoids duplicates + r_ef = temp.toLatin1(); + lst.removeAll(temp); // avoids duplicates int insPos = r_ef.length(); for (int i=1;i<=3;i++) { // include the last three ids if (!lst.isEmpty()) { temp = lst.last(); - r_ef.insert(insPos,(QString(" %1").arg(temp)).latin1()); - lst.remove(temp); + r_ef.insert(insPos,(QString(" %1").arg(temp)).toLatin1()); + lst.removeAll(temp); } else break; } while (!lst.isEmpty()) { // now insert the rest, up to 1000 characters temp = lst.last(); if ((15+r_ef.length()+temp.length())<1000) { - r_ef.insert(insPos,(QString(" %1").arg(temp)).latin1()); - lst.remove(temp); + r_ef.insert(insPos,(QString(" %1").arg(temp)).toLatin1()); + lst.removeAll(temp); } else return; } } //-------------------------------- #endif //---------------------------------- -void UserAgent::from7BitString(const Q3CString &s) +void UserAgent::from7BitString(const QByteArray &s) { u_agent=s; e_ncCS=cachedCharset(Latin1); } -Q3CString UserAgent::as7BitString(bool incType) +QByteArray UserAgent::as7BitString(bool incType) { if(incType) return ( typeIntro()+u_agent ); else return u_agent; } -void UserAgent::fromUnicodeString(const QString &s, const Q3CString&) +void UserAgent::fromUnicodeString(const QString &s, const QByteArray&) { - u_agent=s.latin1(); + u_agent=s.toLatin1(); e_ncCS=cachedCharset(Latin1); } QString UserAgent::asUnicodeString() { return QString::fromLatin1(u_agent); } //--------------------------------- #if !defined(KMIME_NEW_STYLE_CLASSTREE) //------------------------------- -void ContentType::from7BitString(const Q3CString &s) +void ContentType::from7BitString(const QByteArray &s) { - int pos=s.find(';'); + int pos=s.indexOf(';'); if(pos==-1) m_imeType=s.simplified(); else { m_imeType=s.left(pos).simplified(); p_arams=s.mid(pos, s.length()-pos).simplified(); } if(isMultipart()) c_ategory=CCcontainer; else c_ategory=CCsingle; e_ncCS=cachedCharset(Latin1); } -Q3CString ContentType::as7BitString(bool incType) +QByteArray ContentType::as7BitString(bool incType) { if(incType) return (typeIntro()+m_imeType+p_arams); else return (m_imeType+p_arams); } -void ContentType::fromUnicodeString(const QString &s, const Q3CString&) +void ContentType::fromUnicodeString(const QString &s, const QByteArray&) { - from7BitString( Q3CString(s.latin1()) ); + from7BitString( s.toLatin1() ); } QString ContentType::asUnicodeString() { return QString::fromLatin1(as7BitString(false)); } -Q3CString ContentType::mediaType() +QByteArray ContentType::mediaType() { - int pos=m_imeType.find('/'); + int pos=m_imeType.indexOf('/'); if(pos==-1) return m_imeType; else return m_imeType.left(pos); } -Q3CString ContentType::subType() +QByteArray ContentType::subType() { - int pos=m_imeType.find('/'); + int pos=m_imeType.indexOf('/'); if(pos==-1) - return Q3CString(); + return QByteArray(); else return m_imeType.mid(pos, m_imeType.length()-pos); } -void ContentType::setMimeType(const Q3CString &s) +void ContentType::setMimeType(const QByteArray &s) { p_arams.resize(0); m_imeType=s; if(isMultipart()) c_ategory=CCcontainer; else c_ategory=CCsingle; } bool ContentType::isMediatype(const char *s) { return ( strncasecmp(m_imeType.data(), s, strlen(s)) ); } bool ContentType::isSubtype(const char *s) { char *c=strchr(m_imeType.data(), '/'); if( (c==0) || (*(c+1)=='\0') ) return false; else return ( strcasecmp(c+1, s)==0 ); } bool ContentType::isText() { return (strncasecmp(m_imeType.data(), "text", 4)==0); } bool ContentType::isPlainText() { return (strcasecmp(m_imeType.data(), "text/plain")==0); } bool ContentType::isHTMLText() { return (strcasecmp(m_imeType.data(), "text/html")==0); } bool ContentType::isImage() { return (strncasecmp(m_imeType.data(), "image", 5)==0); } bool ContentType::isMultipart() { return (strncasecmp(m_imeType.data(), "multipart", 9)==0); } bool ContentType::isPartial() { return (strcasecmp(m_imeType.data(), "message/partial")==0); } -Q3CString ContentType::charset() +QByteArray ContentType::charset() { - Q3CString ret=getParameter("charset"); + QByteArray ret=getParameter("charset"); if( ret.isEmpty() || forceCS() ) { //we return the default-charset if necessary ret=defaultCS(); } return ret; } -void ContentType::setCharset(const Q3CString &s) +void ContentType::setCharset(const QByteArray &s) { setParameter("charset", s); } -Q3CString ContentType::boundary() +QByteArray ContentType::boundary() { return getParameter("boundary"); } -void ContentType::setBoundary(const Q3CString &s) +void ContentType::setBoundary(const QByteArray &s) { setParameter("boundary", s, true); } QString ContentType::name() { - const char *dummy=0; - return ( decodeRFC2047String(getParameter("name"), &dummy, defaultCS(), forceCS()) ); + QByteArray dummy; + return ( decodeRFC2047String(getParameter("name"), dummy, defaultCS(), forceCS()) ); } -void ContentType::setName(const QString &s, const Q3CString &cs) +void ContentType::setName(const QString &s, const QByteArray &cs) { e_ncCS=cs; if (isUsAscii(s)) { - Q3CString tmp(s.latin1()); + QByteArray tmp = s.toLatin1(); addQuotes(tmp, true); setParameter("name", tmp, false); } else { // FIXME: encoded words can't be enclosed in quotes!! setParameter("name", encodeRFC2047String(s, cs), true); } } -Q3CString ContentType::id() +QByteArray ContentType::id() { return (getParameter("id")); } -void ContentType::setId(const Q3CString &s) +void ContentType::setId(const QByteArray &s) { setParameter("id", s, true); } int ContentType::partialNumber() { - Q3CString p=getParameter("number"); + QByteArray p=getParameter("number"); if(!p.isEmpty()) return p.toInt(); else return -1; } int ContentType::partialCount() { - Q3CString p=getParameter("total"); + QByteArray p=getParameter("total"); if(!p.isEmpty()) return p.toInt(); else return -1; } void ContentType::setPartialParams(int total, int number) { - Q3CString num; + QByteArray num; num.setNum(number); setParameter("number", num); num.setNum(total); setParameter("total", num); } -Q3CString ContentType::getParameter(const char *name) +QByteArray ContentType::getParameter(const char *name) { - Q3CString ret; + QByteArray ret; int pos1=0, pos2=0; pos1=QString(p_arams).indexOf(name, 0, Qt::CaseInsensitive); if(pos1!=-1) { if( (pos2=p_arams.indexOf(';', pos1))==-1 ) pos2=p_arams.length(); pos1+=strlen(name)+1; ret=p_arams.mid(pos1, pos2-pos1); removeQuots(ret); } return ret; } -void ContentType::setParameter(const Q3CString &name, const Q3CString &value, bool doubleQuotes) +void ContentType::setParameter(const QByteArray &name, const QByteArray &value, bool doubleQuotes) { int pos1=0, pos2=0; QByteArray param; if(doubleQuotes) param=name+"=\""+value+"\""; else param=name+"="+value; pos1=QString(p_arams).indexOf(name, 0, Qt::CaseInsensitive); if(pos1==-1) { p_arams+="; "+param; } else { - pos2=p_arams.find(';', pos1); + pos2=p_arams.indexOf(';', pos1); if(pos2==-1) pos2=p_arams.length(); p_arams.remove(pos1, pos2-pos1); p_arams.insert(pos1, param); } } //------------------------------ //--------------------------------- typedef struct { const char *s; int e; } encTableType; static const encTableType encTable[] = { { "7Bit", CE7Bit }, { "8Bit", CE8Bit }, { "quoted-printable", CEquPr }, { "base64", CEbase64 }, { "x-uuencode", CEuuenc }, { "binary", CEbinary }, { 0, 0} }; -void CTEncoding::from7BitString(const Q3CString &s) +void CTEncoding::from7BitString(const QByteArray &s) { - Q3CString stripped(s.simplified()); + QByteArray stripped = s.simplified(); c_te=CE7Bit; for(int i=0; encTable[i].s!=0; i++) if(strcasecmp(stripped.data(), encTable[i].s)==0) { c_te=(contentEncoding)encTable[i].e; break; } d_ecoded=( c_te==CE7Bit || c_te==CE8Bit ); e_ncCS=cachedCharset(Latin1); } -Q3CString CTEncoding::as7BitString(bool incType) +QByteArray CTEncoding::as7BitString(bool incType) { - Q3CString str; + QByteArray str; for(int i=0; encTable[i].s!=0; i++) if(c_te==encTable[i].e) { str=encTable[i].s; break; } if(incType) return ( typeIntro()+str ); else return str; } -void CTEncoding::fromUnicodeString(const QString &s, const Q3CString&) +void CTEncoding::fromUnicodeString(const QString &s, const QByteArray&) { - from7BitString( Q3CString(s.latin1()) ); + from7BitString( s.toLatin1() ); } QString CTEncoding::asUnicodeString() { return QString::fromLatin1(as7BitString(false)); } //-------------------------------- //------------------------------- -void CDisposition::from7BitString(const Q3CString &s) +void CDisposition::from7BitString(const QByteArray &s) { if(strncasecmp(s.data(), "attachment", 10)==0) d_isp=CDattachment; else d_isp=CDinline; int pos=QString(s).indexOf("filename=", 0, Qt::CaseInsensitive); - Q3CString fn; + QByteArray fn; if(pos>-1) { pos+=9; fn=s.mid(pos, s.length()-pos); removeQuots(fn); - f_ilename=decodeRFC2047String(fn, &e_ncCS, defaultCS(), forceCS()); + f_ilename=decodeRFC2047String(fn, e_ncCS, defaultCS(), forceCS()); } } -Q3CString CDisposition::as7BitString(bool incType) +QByteArray CDisposition::as7BitString(bool incType) { - Q3CString ret; + QByteArray ret; if(d_isp==CDattachment) ret="attachment"; else ret="inline"; if(!f_ilename.isEmpty()) { if (isUsAscii(f_ilename)) { - Q3CString tmp(f_ilename.latin1()); + QByteArray tmp = f_ilename.toLatin1(); addQuotes(tmp, true); ret+="; filename="+tmp; } else { // FIXME: encoded words can't be enclosed in quotes!! ret+="; filename=\""+encodeRFC2047String(f_ilename, e_ncCS)+"\""; } } if(incType) return ( typeIntro()+ret ); else return ret; } -void CDisposition::fromUnicodeString(const QString &s, const Q3CString &cs) +void CDisposition::fromUnicodeString(const QString &s, const QByteArray &cs) { - if(strncasecmp(s.latin1(), "attachment", 10)==0) + if(strncasecmp(s.toLatin1(), "attachment", 10)==0) d_isp=CDattachment; else d_isp=CDinline; - int pos=s.find("filename=", 0, false); + int pos=s.indexOf("filename=", 0, Qt::CaseInsensitive); if(pos>-1) { pos+=9; f_ilename=s.mid(pos, s.length()-pos); removeQuots(f_ilename); } e_ncCS=cachedCharset(cs); } QString CDisposition::asUnicodeString() { QString ret; if(d_isp==CDattachment) ret="attachment"; else ret="inline"; if(!f_ilename.isEmpty()) ret+="; filename=\""+f_ilename+"\""; return ret; } //------------------------------ #endif } // namespace Headers } // namespace KMime diff --git a/kmime/kmime_headers.h b/kmime/kmime_headers.h index b81d73f6e..5e523db52 100644 --- a/kmime/kmime_headers.h +++ b/kmime/kmime_headers.h @@ -1,862 +1,860 @@ /* -*- c++ -* kmime_headers.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001-2002 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.0 as published by the Free Software Foundation. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifndef __KMIME_HEADERS_H__ #define __KMIME_HEADERS_H__ // Content: // // - header's base class defining the common interface // - generic base classes for different types of fields // - incompatible, GStructured-based field classes // - compatible, GUnstructured-based field classes #include "kmime_header_parsing.h" #include #include #include #include -#include #include -//Added by qt3to4: #include -#include +#include #include #include namespace KMime { //forward declaration class Content; namespace Headers { enum contentCategory { CCsingle, CCcontainer, CCmixedPart, CCalternativePart }; enum contentEncoding { CE7Bit, CE8Bit, CEquPr, CEbase64, CEuuenc, CEbinary }; enum contentDisposition { CDinline, CDattachment, CDparallel }; //often used charset static const QByteArray Latin1("ISO-8859-1"); #define mk_trivial_subclass_with_name( subclass, subclassName, baseclass ) \ class subclass : public Generics::baseclass { \ public: \ subclass() : Generics::baseclass() {} \ subclass( Content * p ) : Generics::baseclass( p ) {} \ - subclass( Content * p, const Q3CString & s ) \ + subclass( Content * p, const QByteArray & s ) \ : Generics::baseclass( p ) { from7BitString( s ); } \ - subclass( Content * p, const QString & s, const Q3CString & cs ) \ + subclass( Content * p, const QString & s, const QByteArray & cs ) \ : Generics::baseclass( p ) { fromUnicodeString( s, cs ); } \ ~subclass() {} \ \ const char * type() const { return #subclassName; } \ } #define mk_trivial_subclass( subclass, baseclass ) \ mk_trivial_subclass_with_name( subclass, subclass, baseclass ) #define mk_parsing_subclass_with_name( subclass, subclassName, baseclass ) \ class subclass : public Generics::baseclass { \ public: \ subclass() : Generics::baseclass() {} \ subclass( Content * p ) : Generics::baseclass( p ) {} \ - subclass( Content * p, const Q3CString & s ) \ + subclass( Content * p, const QByteArray & s ) \ : Generics::baseclass( p ) { from7BitString( s ); } \ - subclass( Content * p, const QString & s, const Q3CString & cs ) \ + subclass( Content * p, const QString & s, const QByteArray & cs ) \ : Generics::baseclass( p ) { fromUnicodeString( s, cs ); } \ ~subclass() {} \ \ const char * type() const { return #subclassName; } \ protected: \ bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); \ } #define mk_parsing_subclass( subclass, baseclass ) \ mk_parsing_subclass_with_name( subclass, subclass, baseclass ) // // // HEADER'S BASE CLASS. DEFINES THE COMMON INTERFACE // // /** Baseclass of all header-classes. It represents a header-field as described in RFC-822. */ class KDE_EXPORT Base { public: typedef QList List; /** Create an empty header. */ Base() : e_ncCS(0), p_arent(0) {} /** Create an empty header with a parent-content. */ Base(KMime::Content *parent) : e_ncCS(0), p_arent(parent) {} /** Destructor */ virtual ~Base() {} /** Return the parent of this header. */ KMime::Content* parent() { return p_arent; } /** Set the parent for this header. */ void setParent(KMime::Content *p) { p_arent=p; } /** Parse the given string. Take care of RFC2047-encoded strings. A default charset is given. If the last parameter is true the default charset is used in any case */ - virtual void from7BitString(const Q3CString&) {} + virtual void from7BitString(const QByteArray&) {} /** Return the encoded header. The parameter specifies whether the header-type should be included. */ - virtual Q3CString as7BitString(bool=true) { return Q3CString(); } + virtual QByteArray as7BitString(bool=true) { return QByteArray(); } /** Return the charset that is used for RFC2047-encoding */ - Q3CString rfc2047Charset(); + QByteArray rfc2047Charset(); /** Set the charset for RFC2047-encoding */ - void setRFC2047Charset(const Q3CString &cs); + void setRFC2047Charset(const QByteArray &cs); /** Return the default charset */ QByteArray defaultCS(); /** Return if the default charset is mandatory */ bool forceCS(); /** Parse the given string and set the charset. */ - virtual void fromUnicodeString(const QString&, const Q3CString&) {} + virtual void fromUnicodeString(const QString&, const QByteArray&) {} /** Return the decoded content of the header without the header-type. */ virtual QString asUnicodeString() { return QString(); } /** Delete */ virtual void clear() {} /** Do we have data? */ virtual bool isEmpty() { return false; } /** Return the type of this header (e.g. "From") */ virtual const char* type() { return ""; } /** Check if this header is of type t. */ bool is(const char* t) { return (strcasecmp(t, type())==0); } /** Check if this header is a MIME header */ bool isMimeHeader() { return (strncasecmp(type(), "Content-", 8)==0); } /** Check if this header is a X-Header */ bool isXHeader() { return (strncmp(type(), "X-", 2)==0); } protected: - Q3CString typeIntro() { return (Q3CString(type())+": "); } + QByteArray typeIntro() { return (QByteArray(type())+": "); } - const char *e_ncCS; + QByteArray e_ncCS; Content *p_arent; }; // // // GENERIC BASE CLASSES FOR DIFFERENT TYPES OF FIELDS // // namespace Generics { /** Abstract base class for unstructured header fields (e.g. "Subject", "Comment", "Content-description"). Features: Decodes the header according to RFC2047, incl. RFC2231 extensions to encoded-words. Subclasses need only re-implement @p const @p char* @p type(). A macro to automate this is named \code MK_TRIVIAL_GUnstructured_SUBCLASS(classname,headername); \endcode The ContentDescription class then reads: \code MK_TRIVIAL_GUnstructured_SUBCLASS(ContentDescription,Content-Description); \endcode */ // known issues: // - uses old decodeRFC2047String function, instead of our own... class KDE_EXPORT GUnstructured : public Base { public: GUnstructured() : Base() {} GUnstructured( Content * p ) : Base( p ) {} - GUnstructured( Content * p, const Q3CString & s ) + GUnstructured( Content * p, const QByteArray & s ) : Base( p ) { from7BitString(s); } - GUnstructured( Content * p, const QString & s, const Q3CString & cs ) + GUnstructured( Content * p, const QString & s, const QByteArray & cs ) : Base( p ) { fromUnicodeString( s, cs ); } ~GUnstructured() {} - virtual void from7BitString( const Q3CString& str ); - virtual Q3CString as7BitString( bool withHeaderType=true ); + virtual void from7BitString( const QByteArray& str ); + virtual QByteArray as7BitString( bool withHeaderType=true ); virtual void fromUnicodeString( const QString & str, - const Q3CString & suggestedCharset); + const QByteArray & suggestedCharset); virtual QString asUnicodeString(); virtual void clear() { d_ecoded.truncate(0); } virtual bool isEmpty() { return (d_ecoded.isEmpty()); } private: QString d_ecoded; }; /** This is the base class for all structured header fields. It contains parsing methods for all basic token types found in rfc2822. @section Parsing At the basic level, there are tokens & tspecials (rfc2045), atoms & specials, quoted-strings, domain-literals (all rfc822) and encoded-words (rfc2047). As a special token, we have the comment. It is one of the basic tokens defined in rfc822, but it's parsing relies in part on the basic token parsers (e.g. comments may contain encoded-words). Also, most upper-level parsers (notably those for phrase and dot-atom) choose to ignore any comment when parsing. Then there are the real composite tokens, which are made up of one or more of the basic tokens (and semantically invisible comments): phrases (rfc822 with rfc2047) and dot-atoms (rfc2822). This finishes the list of supported token types. Subclasses will provide support for more higher-level tokens, where necessary, using these parsers. @short Base class for structured header fields. @author Marc Mutz */ class KDE_EXPORT GStructured : public Base { public: GStructured() : Base() {} GStructured( Content * p ) : Base( p ) {} - GStructured( Content * p, const Q3CString & s ) + GStructured( Content * p, const QByteArray & s ) : Base( p ) { from7BitString(s); } - GStructured( Content * p, const QString & s, const Q3CString & cs ) + GStructured( Content * p, const QString & s, const QByteArray & cs ) : Base( p ) { fromUnicodeString( s, cs ); } ~GStructured() {} protected: #if 0 // the assembly squad: bool writeAtom( char* & dcursor, const char * const dend, const QString & input ); bool writeAtom( char* & dcursor, const char * const dend, const QPair & input ); bool writeToken( char* & dcursor, const char * const dend, const QString & input ); bool writeToken( char* & dcursor, const char * const dend, const QPair & input ); bool writeGenericQuotedString( char* & dcursor, const char * const dend, const QString & input, bool withCRLF=false ); bool writeComment( char* & dcursor, const char * const dend, const QString & input, bool withCRLF=false ); bool writePhrase( char* & dcursor, const char * const dend, const QString & input, bool withCRLF=false ); bool writeDotAtom( char* & dcursor, const char * const dend, const QString & input, bool withCRLF=false ); #endif }; class KDE_EXPORT GAddress : public GStructured { public: GAddress() : GStructured() {} GAddress( Content * p ) : GStructured( p ) {} - GAddress( Content * p, const Q3CString & s ) + GAddress( Content * p, const QByteArray & s ) : GStructured( p ) { from7BitString(s); } - GAddress( Content * p, const QString & s, const Q3CString & cs ) + GAddress( Content * p, const QString & s, const QByteArray & cs ) : GStructured( p ) { fromUnicodeString( s, cs ); } ~GAddress() {} protected: }; /** Base class for headers that deal with (possibly multiple) addresses, but don't allow groups: */ class KDE_EXPORT MailboxList : public GAddress { public: MailboxList() : GAddress() {} MailboxList( Content * p ) : GAddress( p ) {} - MailboxList( Content * p, const Q3CString & s ) + MailboxList( Content * p, const QByteArray & s ) : GAddress( p ) { from7BitString(s); } - MailboxList( Content * p, const QString & s, const Q3CString & cs ) + MailboxList( Content * p, const QString & s, const QByteArray & cs ) : GAddress( p ) { fromUnicodeString( s, cs ); } ~MailboxList() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); /** The list of mailboxes */ QList mMailboxList; }; /** Base class for headers that deal with exactly one mailbox (e.g. Sender) */ mk_parsing_subclass(SingleMailbox,MailboxList); /** Base class for headers that deal with (possibly multiple) addresses, allowing groups. */ class KDE_EXPORT AddressList : public GAddress { public: AddressList() : GAddress() {} AddressList( Content * p ) : GAddress( p ) {} - AddressList( Content * p, const Q3CString & s ) + AddressList( Content * p, const QByteArray & s ) : GAddress( p ) { from7BitString(s); } - AddressList( Content * p, const QString & s, const Q3CString & cs ) + AddressList( Content * p, const QString & s, const QByteArray & cs ) : GAddress( p ) { fromUnicodeString( s, cs ); } ~AddressList() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); /** The list of addresses */ QList mAddressList; }; /** Base class for headers which deal with a list of msg-id's */ class KDE_EXPORT GIdent : public GAddress { public: GIdent() : GAddress() {} GIdent( Content * p ) : GAddress( p ) {} - GIdent( Content * p, const Q3CString & s ) + GIdent( Content * p, const QByteArray & s ) : GAddress( p ) { from7BitString(s); } - GIdent( Content * p, const QString & s, const Q3CString & cs ) + GIdent( Content * p, const QString & s, const QByteArray & cs ) : GAddress( p ) { fromUnicodeString( s, cs ); } ~GIdent() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); /** The list of msg-id's */ QList mMsgIdList; }; /** Base class for headers which deal with a list of msg-id's */ mk_parsing_subclass(GSingleIdent,GIdent); /** Base class for headers which deal with a single atom. */ class KDE_EXPORT GToken : public GStructured { public: GToken() : GStructured() {} GToken( Content * p ) : GStructured( p ) {} - GToken( Content * p, const Q3CString & s ) + GToken( Content * p, const QByteArray & s ) : GStructured( p ) { from7BitString(s); } - GToken( Content * p, const QString & s, const Q3CString & cs ) + GToken( Content * p, const QString & s, const QByteArray & cs ) : GStructured( p ) { fromUnicodeString( s, cs ); } ~GToken() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); - Q3CString mToken; + QByteArray mToken; }; class KDE_EXPORT GPhraseList : public GStructured { public: GPhraseList() : GStructured() {} GPhraseList( Content * p ) : GStructured( p ) {} - GPhraseList( Content * p, const Q3CString & s ) + GPhraseList( Content * p, const QByteArray & s ) : GStructured( p ) { from7BitString(s); } - GPhraseList( Content * p, const QString & s, const Q3CString & cs ) + GPhraseList( Content * p, const QString & s, const QByteArray & cs ) : GStructured( p ) { fromUnicodeString( s, cs ); } ~GPhraseList() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); QStringList mPhraseList; }; class KDE_EXPORT GDotAtom : public GStructured { public: GDotAtom() : GStructured() {} GDotAtom( Content * p ) : GStructured( p ) {} - GDotAtom( Content * p, const Q3CString & s ) + GDotAtom( Content * p, const QByteArray & s ) : GStructured( p ) { from7BitString(s); } - GDotAtom( Content * p, const QString & s, const Q3CString & cs ) + GDotAtom( Content * p, const QString & s, const QByteArray & cs ) : GStructured( p ) { fromUnicodeString( s, cs ); } ~GDotAtom() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); QString mDotAtom; }; class KDE_EXPORT GParametrized : public GStructured { public: GParametrized() : GStructured() {} GParametrized( Content * p ) : GStructured( p ) {} - GParametrized( Content * p, const Q3CString & s ) + GParametrized( Content * p, const QByteArray & s ) : GStructured( p ) { from7BitString(s); } - GParametrized( Content * p, const QString & s, const Q3CString & cs ) + GParametrized( Content * p, const QString & s, const QByteArray & cs ) : GStructured( p ) { fromUnicodeString( s, cs ); } ~GParametrized() {} protected: QMap mParameterHash; private: }; class KDE_EXPORT GContentType : public GParametrized { public: GContentType() : GParametrized() {} GContentType( Content * p ) : GParametrized( p ) {} - GContentType( Content * p, const Q3CString & s ) + GContentType( Content * p, const QByteArray & s ) : GParametrized( p ) { from7BitString(s); } - GContentType( Content * p, const QString & s, const Q3CString & cs ) + GContentType( Content * p, const QString & s, const QByteArray & cs ) : GParametrized( p ) { fromUnicodeString( s, cs ); } ~GContentType() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); - Q3CString mMimeType; - Q3CString mMimeSubType; + QByteArray mMimeType; + QByteArray mMimeSubType; }; class KDE_EXPORT GCISTokenWithParameterList : public GParametrized { public: GCISTokenWithParameterList() : GParametrized() {} GCISTokenWithParameterList( Content * p ) : GParametrized( p ) {} - GCISTokenWithParameterList( Content * p, const Q3CString & s ) + GCISTokenWithParameterList( Content * p, const QByteArray & s ) : GParametrized( p ) { from7BitString(s); } - GCISTokenWithParameterList( Content * p, const QString & s, const Q3CString & cs ) + GCISTokenWithParameterList( Content * p, const QString & s, const QByteArray & cs ) : GParametrized( p ) { fromUnicodeString( s, cs ); } ~GCISTokenWithParameterList() {} protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); - Q3CString mToken; + QByteArray mToken; }; } // namespace Generics // // // INCOMPATIBLE, GSTRUCTURED-BASED FIELDS: // // /** Represents the Return-Path header field. */ class KDE_EXPORT ReturnPath : public Generics::GAddress { public: ReturnPath() : Generics::GAddress() {} ReturnPath( Content * p ) : Generics::GAddress( p ) {} - ReturnPath( Content * p, const Q3CString & s ) + ReturnPath( Content * p, const QByteArray & s ) : Generics::GAddress( p ) { from7BitString(s); } - ReturnPath( Content * p, const QString & s, const Q3CString & cs ) + ReturnPath( Content * p, const QString & s, const QByteArray & cs ) : Generics::GAddress( p ) { fromUnicodeString( s, cs ); } ~ReturnPath() {} const char * type() const { return "Return-Path"; } protected: bool parse( const char* & scursor, const char * const send, bool isCRLF=false ); }; #if defined(KMIME_NEW_STYLE_CLASSTREE) // classes whose names collide with earlier ones: // GAddress et al.: // rfc(2)822 headers: mk_trivial_subclass(From,MailboxList); mk_trivial_subclass(Sender,SingleMailbox); mk_trivial_subclass_with_name(ReplyTo,Reply-To,AddressList); mk_trivial_subclass(Cc,AddressList); mk_trivial_subclass(Bcc,AddressList); // usefor headers: mk_trivial_subclass_with_name(MailCopiesTo,Mail-Copies-To,AddressList); // GToken: mk_trivial_subclass_with_name(ContentTransferEncoding, Content-Transfer-Encoding,GToken); // GPhraseList: mk_trivial_subclass(Keywords,GPhraseList); // GDotAtom: mk_trivial_subclass_with_name(MIMEVersion,MIME-Version,GDotAtom); // GIdent: mk_trivial_subclass_with_name(MessageID,Message-ID,GSingleIdent); mk_trivial_subclass_with_name(ContentID,Content-ID,GSingleIdent); mk_trivial_subclass(Supersedes,GSingleIdent); mk_trivial_subclass_with_name(InReplyTo,In-Reply-To,GIdent); mk_trivial_subclass(References,GIdent); // GContentType: mk_trivial_subclass_with_name(ContentType,ContentType,GContentType); // GCISTokenWithParameterList: mk_trivial_subclass_with_name(ContentDisposition,Content-Disposition, GCISTokenWithParameterList); #endif // // // COMPATIBLE GUNSTRUCTURED-BASED FIELDS: // // /** Represents an arbitrary header, that can contain any header-field. Adds a type over GUnstructured. @see GUnstructured */ class KDE_EXPORT Generic : public Generics::GUnstructured { public: Generic() : Generics::GUnstructured(), t_ype(0) {} Generic(const char *t) : Generics::GUnstructured(), t_ype(0) { setType(t); } Generic(const char *t, Content *p) : Generics::GUnstructured( p ), t_ype(0) { setType(t); } - Generic(const char *t, Content *p, const Q3CString &s) + Generic(const char *t, Content *p, const QByteArray &s) : Generics::GUnstructured( p, s ), t_ype(0) { setType(t); } - Generic(const char *t, Content *p, const QString &s, const Q3CString &cs) + Generic(const char *t, Content *p, const QString &s, const QByteArray &cs) : Generics::GUnstructured( p, s, cs ), t_ype(0) { setType(t); } ~Generic() { delete[] t_ype; } virtual void clear() { delete[] t_ype; GUnstructured::clear(); } virtual bool isEmpty() { return (t_ype==0 || GUnstructured::isEmpty()); } virtual const char* type() { return t_ype; } void setType(const char *type); protected: char *t_ype; }; /** Represents a "Subject" header */ class KDE_EXPORT Subject : public Generics::GUnstructured { public: Subject() : Generics::GUnstructured() {} Subject( Content * p ) : Generics::GUnstructured( p ) {} - Subject( Content * p, const Q3CString & s ) + Subject( Content * p, const QByteArray & s ) : Generics::GUnstructured( p, s ) {} - Subject( Content * p, const QString & s, const Q3CString & cs ) + Subject( Content * p, const QString & s, const QByteArray & cs ) : Generics::GUnstructured( p, s, cs ) {} ~Subject() {} virtual const char* type() { return "Subject"; } bool isReply() { - return ( asUnicodeString().find( QString("Re:"), 0, false ) == 0 ); + return ( asUnicodeString().indexOf( "Re:", 0, Qt::CaseInsensitive ) == 0 ); } }; /** Represents a "Organization" header */ class KDE_EXPORT Organization : public Generics::GUnstructured { public: Organization() : Generics::GUnstructured() {} Organization( Content * p ) : Generics::GUnstructured( p ) {} - Organization( Content * p, const Q3CString & s ) + Organization( Content * p, const QByteArray & s ) : Generics::GUnstructured( p, s ) {}; - Organization( Content * p, const QString & s, const Q3CString & cs) + Organization( Content * p, const QString & s, const QByteArray & cs) : Generics::GUnstructured( p, s, cs ) {} ~Organization() {} virtual const char* type() { return "Organization"; } }; // // // NOT YET CONVERTED STUFF BELOW: // // /** Represents a "Control" header */ class KDE_EXPORT Control : public Base { public: Control() : Base() {} Control(Content *p) : Base(p) {} - Control(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + Control(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } Control(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~Control() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { c_trlMsg.truncate(0); } virtual bool isEmpty() { return (c_trlMsg.isEmpty()); } virtual const char* type() { return "Control"; } bool isCancel() { return QString(c_trlMsg).contains("cancel", Qt::CaseInsensitive); } protected: - Q3CString c_trlMsg; + QByteArray c_trlMsg; }; /** Represents a "Date" header */ class KDE_EXPORT Date : public Base { public: Date() : Base(), t_ime(0) {} Date(Content *p) : Base(p), t_ime(0) {} Date(Content *p, time_t t) : Base(p), t_ime(t) {} - Date(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + Date(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } Date(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~Date() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { t_ime=0; } virtual bool isEmpty() { return (t_ime==0); } virtual const char* type() { return "Date"; } time_t unixTime() { return t_ime; } void setUnixTime(time_t t) { t_ime=t; } void setUnixTime() { t_ime=time(0); } QDateTime qdt(); int ageInDays(); protected: time_t t_ime; }; /** Represents a "Newsgroups" header */ class KDE_EXPORT Newsgroups : public Base { public: Newsgroups() : Base() {} Newsgroups(Content *p) : Base(p) {} - Newsgroups(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + Newsgroups(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } Newsgroups(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~Newsgroups() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); - virtual void clear() { g_roups.resize(0); } + virtual void clear() { g_roups.clear(); } virtual bool isEmpty() { return g_roups.isEmpty(); } virtual const char* type() { return "Newsgroups"; } - Q3CString firstGroup(); - bool isCrossposted() { return ( g_roups.find(',')>-1 ); } + QByteArray firstGroup(); + bool isCrossposted() { return g_roups.contains(','); } QStringList getGroups(); protected: - Q3CString g_roups; + QByteArray g_roups; }; /** Represents a "Followup-To" header */ class KDE_EXPORT FollowUpTo : public Newsgroups { public: FollowUpTo() : Newsgroups() {} FollowUpTo(Content *p) : Newsgroups(p) {} - FollowUpTo(Content *p, const Q3CString &s) : Newsgroups(p,s) {} + FollowUpTo(Content *p, const QByteArray &s) : Newsgroups(p,s) {} FollowUpTo(Content *p, const QString &s) : Newsgroups(p,s) {} ~FollowUpTo() {} virtual const char* type() { return "Followup-To"; } }; /** Represents a "Lines" header */ class KDE_EXPORT Lines : public Base { public: Lines() : Base(),l_ines(-1) {} Lines(Content *p) : Base(p),l_ines(-1) {} Lines(Content *p, unsigned int i) : Base(p),l_ines(i) {} - Lines(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + Lines(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } Lines(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~Lines() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { l_ines=-1; } virtual bool isEmpty() { return (l_ines==-1); } virtual const char* type() { return "Lines"; } int numberOfLines() { return l_ines; } void setNumberOfLines(int i) { l_ines=i; } protected: int l_ines; }; /** Represents a "User-Agent" header */ class KDE_EXPORT UserAgent : public Base { public: UserAgent() : Base() {} UserAgent(Content *p) : Base(p) {} - UserAgent(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + UserAgent(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } UserAgent(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~UserAgent() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { u_agent.resize(0); } virtual bool isEmpty() { return (u_agent.isEmpty()); } virtual const char* type() { return "User-Agent"; } protected: - Q3CString u_agent; + QByteArray u_agent; }; #if !defined(KMIME_NEW_STYLE_CLASSTREE) #include "kmime_headers_obs.h" #endif } //namespace Headers #if 0 typedef Headers::Base* (*headerCreator)(void); /** This is a factory for KMime::Headers. You can create new header objects by type with @ref create and @ref upgrade an existing @ref Headers::Generic to a specialized header object. If you are a header class author, you can register your class (let's call it Foo) so:
 
     
@short Factory for KMime::Headers @author Marc Mutz @see KMime::Headers::Base KMime::Headers::Generic */ class HeaderFactory : public Q3AsciiDict { private: HeaderFactory(); ~HeaderFactory() {} static Q3AsciiDict public: /** Create a new header object of type @p aType, or a fitting generic substitute, if available and known */ static Headers::Base* create( const char* aType ) { if (!s_elf) s_elf = new HeaderFactory; headerCreator * hc = (*s_elf)[aType]; if ( !hc ) return 0; else return (*hc)(); } /** This is a wrapper around the above function, provided for convenience. It differs from the above only in what arguments it takes. */ - static Headers::Base* create( const Q3CString& aType ) + static Headers::Base* create( const QByteArray& aType ) { return create( aType.data() ); } /** Consume @p aType and build a header object that corresponds to the type that @p aType->type() returns. @param aType generic header to upgrade. This will be deleted if necessary, so don't use references to it after calling this function. @return A corresponding header object (if available), or a generic object for this kind of header (if known), or @p aType (else). @see Headers::Generic create */ static Headers::Base* upgrade( Headers::Generic* aType ) { (void)aType; return new Headers::Base; } }; #endif } //namespace KMime #endif // __KMIME_HEADERS_H__ diff --git a/kmime/kmime_headers_obs.h b/kmime/kmime_headers_obs.h index 5c118508b..fffaaa4f6 100644 --- a/kmime/kmime_headers_obs.h +++ b/kmime/kmime_headers_obs.h @@ -1,371 +1,370 @@ /* kmime_headers.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifndef __KMIME_HEADERS_OBS_H__ #define __KMIME_HEADERS_OBS_H__ #if defined(KMIME_NEW_STYLE_CLASSTREE) #error You cannot use this file with the new header classes! #endif #include -//Added by qt3to4: -#include +#include /** Represents a "Message-Id" header */ class KDE_EXPORT MessageID : public Base { public: MessageID() : Base() {} MessageID(Content *p) : Base(p) {} - MessageID(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + MessageID(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } MessageID(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~MessageID() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { m_id.resize(0); } virtual bool isEmpty() { return (m_id.isEmpty()); } virtual const char* type() { return "Message-Id"; } - void generate(const Q3CString &fqdn); + void generate(const QByteArray &fqdn); protected: - Q3CString m_id; + QByteArray m_id; }; /** Represents a "Supersedes" header */ class KDE_EXPORT Supersedes : public MessageID { public: Supersedes() : MessageID() {} Supersedes(Content *p) : MessageID(p) {} - Supersedes(Content *p, const Q3CString &s) : MessageID(p,s) {} + Supersedes(Content *p, const QByteArray &s) : MessageID(p,s) {} Supersedes(Content *p, const QString &s) : MessageID(p,s) {} ~Supersedes() {} virtual const char* type() { return "Supersedes"; } }; /** This class encapsulates an address-field, containing an email-address and a real name */ class KDE_EXPORT AddressField : public Base { public: AddressField() : Base() {} AddressField(Content *p) : Base(p) {} - AddressField(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } - AddressField(Content *p, const QString &s, const Q3CString &cs) : Base(p) { fromUnicodeString(s, cs); } - AddressField(const AddressField &a): Base(a.p_arent) { n_ame=a.n_ame; e_mail=a.e_mail.copy(); e_ncCS=a.e_ncCS; } + AddressField(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } + AddressField(Content *p, const QString &s, const QByteArray &cs) : Base(p) { fromUnicodeString(s, cs); } + AddressField(const AddressField &a): Base(a.p_arent) { n_ame=a.n_ame; e_mail=a.e_mail; e_ncCS=a.e_ncCS; } ~AddressField() {} - AddressField& operator=(const AddressField &a) { n_ame=a.n_ame; e_mail=a.e_mail.copy(); e_ncCS=a.e_ncCS; return (*this); } + AddressField& operator=(const AddressField &a) { n_ame=a.n_ame; e_mail=a.e_mail; e_ncCS=a.e_ncCS; return (*this); } - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString &cs); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray &cs); virtual QString asUnicodeString(); virtual void clear() { n_ame.truncate(0); e_mail.resize(0); } virtual bool isEmpty() { return (e_mail.isEmpty() && n_ame.isEmpty()); } bool hasName() { return ( !n_ame.isEmpty() ); } bool hasEmail() { return ( !e_mail.isEmpty() ); } QString name() { return n_ame; } - Q3CString nameAs7Bit(); - Q3CString email() { return e_mail; } + QByteArray nameAs7Bit(); + QByteArray email() { return e_mail; } void setName(const QString &s) { n_ame=s; } - void setNameFrom7Bit(const Q3CString &s); - void setEmail(const Q3CString &s) { e_mail=s; } + void setNameFrom7Bit(const QByteArray &s); + void setEmail(const QByteArray &s) { e_mail=s; } protected: QString n_ame; - Q3CString e_mail; + QByteArray e_mail; }; typedef QList ObsAddressList; /** Represent a "From" header */ class KDE_EXPORT From : public AddressField { public: From() : AddressField() {} From(Content *p) : AddressField(p) {} - From(Content *p, const Q3CString &s) : AddressField(p,s) {} - From(Content *p, const QString &s, const Q3CString &cs) : AddressField(p,s,cs) {} + From(Content *p, const QByteArray &s) : AddressField(p,s) {} + From(Content *p, const QString &s, const QByteArray &cs) : AddressField(p,s,cs) {} ~From() {} virtual const char* type() { return "From"; } }; /** Represents a "Reply-To" header */ class KDE_EXPORT ReplyTo : public AddressField { public: ReplyTo() : AddressField() {} ReplyTo(Content *p) : AddressField(p) {} - ReplyTo(Content *p, const Q3CString &s) : AddressField(p,s) {} - ReplyTo(Content *p, const QString &s, const Q3CString &cs) : AddressField(p,s,cs) {} + ReplyTo(Content *p, const QByteArray &s) : AddressField(p,s) {} + ReplyTo(Content *p, const QString &s, const QByteArray &cs) : AddressField(p,s,cs) {} ~ReplyTo() {} virtual const char* type() { return "Reply-To"; } }; /** Represents a "Mail-Copies-To" header http://www.newsreaders.com/misc/mail-copies-to.html */ class KDE_EXPORT MailCopiesTo : public AddressField { public: MailCopiesTo() : AddressField() {} MailCopiesTo(Content *p) : AddressField(p) {} - MailCopiesTo(Content *p, const Q3CString &s) : AddressField(p,s) {} - MailCopiesTo(Content *p, const QString &s, const Q3CString &cs) : AddressField(p,s,cs) {} + MailCopiesTo(Content *p, const QByteArray &s) : AddressField(p,s) {} + MailCopiesTo(Content *p, const QString &s, const QByteArray &cs) : AddressField(p,s,cs) {} ~MailCopiesTo() {} bool isValid(); bool alwaysCopy(); bool neverCopy(); virtual const char* type() { return "Mail-Copies-To"; } }; /** Represents a "To" header */ class KDE_EXPORT To : public Base { public: To() : Base() {} To(Content *p) : Base(p) {} - To(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } - To(Content *p, const QString &s, const Q3CString &cs) : Base(p) { fromUnicodeString(s,cs); } + To(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } + To(Content *p, const QString &s, const QByteArray &cs) : Base(p) { fromUnicodeString(s,cs); } ~To() { qDeleteAll( a_ddrList ); a_ddrList.clear(); } - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString &cs); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray &cs); virtual QString asUnicodeString(); virtual void clear() { qDeleteAll( a_ddrList ); a_ddrList.clear(); } virtual bool isEmpty() { return a_ddrList.isEmpty() || a_ddrList.first()->isEmpty(); } virtual const char* type() { return "To"; } void addAddress(const AddressField &a); QList emails() const; protected: ObsAddressList a_ddrList; }; /** Represents a "CC" header */ class KDE_EXPORT CC : public To { public: CC() : To() {} CC(Content *p) : To(p) {} - CC(Content *p, const Q3CString &s) : To(p,s) {} - CC(Content *p, const QString &s, const Q3CString &cs) : To(p,s,cs) {} + CC(Content *p, const QByteArray &s) : To(p,s) {} + CC(Content *p, const QString &s, const QByteArray &cs) : To(p,s,cs) {} ~CC() {} virtual const char* type() { return "CC"; } }; /** Represents a "BCC" header */ class KDE_EXPORT BCC : public To { public: BCC() : To() {} BCC(Content *p) : To(p) {} - BCC(Content *p, const Q3CString &s) : To(p,s) {} - BCC(Content *p, const QString &s, const Q3CString &cs) : To(p,s,cs) {} + BCC(Content *p, const QByteArray &s) : To(p,s) {} + BCC(Content *p, const QString &s, const QByteArray &cs) : To(p,s,cs) {} ~BCC() {} virtual const char* type() { return "BCC"; } }; /** Represents a "References" header */ class KDE_EXPORT References : public Base { public: References() : Base(),p_os(-1) {} References(Content *p) : Base(p),p_os(-1) {} - References(Content *p, const Q3CString &s) : Base(p),p_os(-1) { from7BitString(s); } + References(Content *p, const QByteArray &s) : Base(p),p_os(-1) { from7BitString(s); } References(Content *p, const QString &s) : Base(p),p_os(-1) { fromUnicodeString(s, Latin1); } ~References() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { r_ef.resize(0); p_os=0; } virtual bool isEmpty() { return (r_ef.isEmpty()); } virtual const char* type() { return "References"; } int count(); - Q3CString first(); - Q3CString next(); - Q3CString at(unsigned int i); - void append(const Q3CString &s); + QByteArray first(); + QByteArray next(); + QByteArray at(unsigned int i); + void append(const QByteArray &s); protected: - Q3CString r_ef; + QByteArray r_ef; int p_os; }; /** Represents a "Content-Type" header */ class KDE_EXPORT ContentType : public Base { public: ContentType() : Base(),m_imeType("invalid/invalid"),c_ategory(CCsingle) {} ContentType(Content *p) : Base(p),m_imeType("invalid/invalid"),c_ategory(CCsingle) {} - ContentType(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + ContentType(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } ContentType(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~ContentType() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { m_imeType.resize(0); p_arams.resize(0); } virtual bool isEmpty() { return (m_imeType.isEmpty()); } virtual const char* type() { return "Content-Type"; } //mime-type handling - Q3CString mimeType() { return m_imeType; } - Q3CString mediaType(); - Q3CString subType(); - void setMimeType(const Q3CString &s); + QByteArray mimeType() { return m_imeType; } + QByteArray mediaType(); + QByteArray subType(); + void setMimeType(const QByteArray &s); bool isMediatype(const char *s); bool isSubtype(const char *s); bool isText(); bool isPlainText(); bool isHTMLText(); bool isImage(); bool isMultipart(); bool isPartial(); //parameter handling - Q3CString charset(); - void setCharset(const Q3CString &s); - Q3CString boundary(); - void setBoundary(const Q3CString &s); + QByteArray charset(); + void setCharset(const QByteArray &s); + QByteArray boundary(); + void setBoundary(const QByteArray &s); QString name(); - void setName(const QString &s, const Q3CString &cs); - Q3CString id(); - void setId(const Q3CString &s); + void setName(const QString &s, const QByteArray &cs); + QByteArray id(); + void setId(const QByteArray &s); int partialNumber(); int partialCount(); void setPartialParams(int total, int number); //category contentCategory category() { return c_ategory; } void setCategory(contentCategory c) { c_ategory=c; } protected: - Q3CString getParameter(const char *name); - void setParameter(const Q3CString &name, const Q3CString &value, bool doubleQuotes=false); + QByteArray getParameter(const char *name); + void setParameter(const QByteArray &name, const QByteArray &value, bool doubleQuotes=false); QByteArray m_imeType, p_arams; contentCategory c_ategory; }; /** Represents a "Content-Transfer-Encoding" header */ class KDE_EXPORT CTEncoding : public Base { public: CTEncoding() : Base(),c_te(CE7Bit),d_ecoded(true) {} CTEncoding(Content *p) : Base(p),c_te(CE7Bit),d_ecoded(true) {} - CTEncoding(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } + CTEncoding(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } CTEncoding(Content *p, const QString &s) : Base(p) { fromUnicodeString(s, Latin1); } ~CTEncoding() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString&); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray&); virtual QString asUnicodeString(); virtual void clear() { d_ecoded=true; c_te=CE7Bit; } virtual const char* type() { return "Content-Transfer-Encoding"; } contentEncoding cte() { return c_te; } void setCte(contentEncoding e) { c_te=e; } bool decoded() { return d_ecoded; } void setDecoded(bool d=true) { d_ecoded=d; } bool needToEncode() { return (d_ecoded && (c_te==CEquPr || c_te==CEbase64)); } protected: contentEncoding c_te; bool d_ecoded; }; /** Represents a "Content-Disposition" header */ class KDE_EXPORT CDisposition : public Base { public: CDisposition() : Base(),d_isp(CDinline) {} CDisposition(Content *p) : Base(p),d_isp(CDinline) {} - CDisposition(Content *p, const Q3CString &s) : Base(p) { from7BitString(s); } - CDisposition(Content *p, const QString &s, const Q3CString &cs) : Base(p) { fromUnicodeString(s, cs); } + CDisposition(Content *p, const QByteArray &s) : Base(p) { from7BitString(s); } + CDisposition(Content *p, const QString &s, const QByteArray &cs) : Base(p) { fromUnicodeString(s, cs); } ~CDisposition() {} - virtual void from7BitString(const Q3CString &s); - virtual Q3CString as7BitString(bool incType=true); - virtual void fromUnicodeString(const QString &s, const Q3CString &cs); + virtual void from7BitString(const QByteArray &s); + virtual QByteArray as7BitString(bool incType=true); + virtual void fromUnicodeString(const QString &s, const QByteArray &cs); virtual QString asUnicodeString(); virtual void clear() { f_ilename.truncate(0); d_isp=CDinline; } virtual const char* type() { return "Content-Disposition"; } contentDisposition disposition() { return d_isp; } void setDisposition(contentDisposition d) { d_isp=d; } bool isAttachment() { return (d_isp==CDattachment); } QString filename() { return f_ilename; } void setFilename(const QString &s) { f_ilename=s; } protected: contentDisposition d_isp; QString f_ilename; }; /** Represents a "Content-Description" header */ class KDE_EXPORT CDescription : public Generics::GUnstructured { public: CDescription() : Generics::GUnstructured() {} CDescription( Content * p ) : Generics::GUnstructured( p ) {} - CDescription( Content * p, const Q3CString & s ) + CDescription( Content * p, const QByteArray & s ) : Generics::GUnstructured( p, s ) {}; - CDescription( Content * p, const QString & s, const Q3CString & cs ) + CDescription( Content * p, const QString & s, const QByteArray & cs ) : Generics::GUnstructured( p, s, cs ) {} ~CDescription() {} virtual const char* type() { return "Content-Description"; } }; #endif // __KMIME_HEADERS_OBS_H__ diff --git a/kmime/kmime_mdn.cpp b/kmime/kmime_mdn.cpp index 51124e077..373a19b16 100644 --- a/kmime/kmime_mdn.cpp +++ b/kmime/kmime_mdn.cpp @@ -1,264 +1,263 @@ /* -*- c++ -*- kmime_mdn.cpp This file is part of KMime, the KDE internet mail/usenet news message library. Copyright (c) 2002 Marc Mutz KMime is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMime is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this library with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "kmime_mdn.h" #include "kmime_version.h" #include "kmime_util.h" #include #include -#include -//Added by qt3to4: +#include #include #include // gethostname namespace KMime { namespace MDN { static const struct { DispositionType dispositionType; const char * string; const char * description; } dispositionTypes[] = { { Displayed, "displayed", I18N_NOOP("The message sent on ${date} to ${to} with subject " "\"${subject}\" has been displayed. This is no guarantee that " "the message has been read or understood.") }, { Deleted, "deleted", I18N_NOOP("The message sent on ${date} to ${to} with subject " "\"${subject}\" has been deleted unseen. This is no guarantee " "that the message will not be \"undeleted\" and nonetheless " "read later on.") }, { Dispatched, "dispatched", I18N_NOOP("The message sent on ${date} to ${to} with subject " "\"${subject}\" has been dispatched. This is no guarantee " "that the message will not be read later on.") }, { Processed, "processed", I18N_NOOP("The message sent on ${date} to ${to} with subject " "\"${subject}\" has been processed by some automatic means.") }, { Denied, "denied", I18N_NOOP("The message sent on ${date} to ${to} with subject " "\"${subject}\" has been acted upon. The sender does not wish " "to disclose more details to you than that.") }, { Failed, "failed", I18N_NOOP("Generation of a Message Disposition Notification for the " "message sent on ${date} to ${to} with subject \"${subject}\" " "failed. Reason is given in the Failure: header field below.") } }; static const int numDispositionTypes = sizeof dispositionTypes / sizeof *dispositionTypes; static const char * stringFor( DispositionType d ) { for ( int i = 0 ; i < numDispositionTypes ; ++i ) if ( dispositionTypes[i].dispositionType == d ) return dispositionTypes[i].string; return 0; } // // disposition-modifier // static const struct { DispositionModifier dispositionModifier; const char * string; } dispositionModifiers[] = { { Error, "error" }, { Warning, "warning" }, { Superseded, "superseded" }, { Expired, "expired" }, { MailboxTerminated, "mailbox-terminated" } }; static const int numDispositionModifiers = sizeof dispositionModifiers / sizeof * dispositionModifiers; static const char * stringFor( DispositionModifier m ) { for ( int i = 0 ; i < numDispositionModifiers ; ++i ) if ( dispositionModifiers[i].dispositionModifier == m ) return dispositionModifiers[i].string; return 0; } // // action-mode (part of disposition-mode) // static const struct { ActionMode actionMode; const char * string; } actionModes[] = { { ManualAction, "manual-action" }, { AutomaticAction, "automatic-action" } }; static const int numActionModes = sizeof actionModes / sizeof *actionModes; static const char * stringFor( ActionMode a ) { for ( int i = 0 ; i < numActionModes ; ++i ) if ( actionModes[i].actionMode == a ) return actionModes[i].string; return 0; } // // sending-mode (part of disposition-mode) // static const struct { SendingMode sendingMode; const char * string; } sendingModes[] = { { SentManually, "MDN-sent-manually" }, { SentAutomatically, "MDN-sent-automatically" } }; static const int numSendingModes = sizeof sendingModes / sizeof *sendingModes; static const char * stringFor( SendingMode s ) { for ( int i = 0 ; i < numSendingModes ; ++i ) if ( sendingModes[i].sendingMode == s ) return sendingModes[i].string; return 0; } static QByteArray dispositionField( DispositionType d, ActionMode a, SendingMode s, const QList & m ) { // mandatory parts: Disposition: foo/baz; bar QByteArray result = "Disposition: "; result += stringFor( a ); result += "/"; result += stringFor( s ); result += "; "; result += stringFor( d ); // optional parts: Disposition: foo/baz; bar/mod1,mod2,mod3 bool first = true; for ( QList::const_iterator mt = m.begin() ; mt != m.end() ; ++mt ) { if ( first ) { result += "/"; first = false; } else { result += ","; } result += stringFor( *mt ); } return result + "\n"; } static QByteArray finalRecipient( const QString & recipient ) { if ( recipient.isEmpty() ) return QByteArray(); else return "Final-Recipient: rfc822; " + encodeRFC2047String( recipient, "utf-8" ) + "\n"; } static QByteArray orginalRecipient( const QByteArray & recipient ) { if ( recipient.isEmpty() ) return QByteArray(); else return "Original-Recipient: " + recipient + "\n"; } static QByteArray originalMessageID( const QByteArray & msgid ) { if ( msgid.isEmpty() ) return QByteArray(); else return "Original-Message-ID: " + msgid + "\n"; } static QByteArray reportingUAField() { char hostName[256]; if ( gethostname( hostName, 255 ) ) hostName[0] = '\0'; // gethostname failed: pretend empty string else hostName[255] = '\0'; // gethostname may have returned 255 chars (man page) return QByteArray("Reporting-UA: ") + QByteArray( hostName ) + QByteArray( "; KMime " KMIME_VERSION_STRING "\n" ); } QByteArray dispositionNotificationBodyContent( const QString & r, const QByteArray & o, const QByteArray & omid, DispositionType d, ActionMode a, SendingMode s, const QList & m, const QString & special ) { // in Perl: chomp(special) QString spec; if ( special.endsWith("\n") ) spec = special.left( special.length() - 1 ); else spec = special; // std headers: QByteArray result = reportingUAField(); result += orginalRecipient( o ); result += finalRecipient( r ); result += originalMessageID( omid ); result += dispositionField( d, a, s, m ); // headers that are only present for certain disposition {types,modifiers}: if ( d == Failed ) result += "Failure: " + encodeRFC2047String( spec, "utf-8" ) + "\n"; else if ( m.contains( Error ) ) result += "Error: " + encodeRFC2047String( spec, "utf-8" ) + "\n"; else if ( m.contains( Warning ) ) result += "Warning: " + encodeRFC2047String( spec, "utf-8" ) + "\n"; return result; } QString descriptionFor( DispositionType d, const QList & ) { for ( int i = 0 ; i < numDispositionTypes ; ++i ) if ( dispositionTypes[i].dispositionType == d ) return i18n( dispositionTypes[i].description ); kdWarning() << "KMime::MDN::descriptionFor(): No such disposition type: " << (int)d << endl; return QString(); } } // namespace MDN } // namespace KMime diff --git a/kmime/kmime_mdn.h b/kmime/kmime_mdn.h index 24a25634a..e52b5b465 100644 --- a/kmime/kmime_mdn.h +++ b/kmime/kmime_mdn.h @@ -1,203 +1,202 @@ /* -*- c++ -*- kmime_mdn.h This file is part of KMime, the KDE internet mail/usenet news message library. Copyright (c) 2002 Marc Mutz KMime is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMime is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this library with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KMIME_MDN_H__ #define __KMIME_MDN_H__ #include -//Added by qt3to4: -#include +//#include #include #include class QByteArray; namespace KMime { namespace MDN { /** The following disposition-types are defined: @li Displayed The message has been displayed by the UA to someone reading the recipient's mailbox. There is no guarantee that the content has been read or understood. @li Dispatched The message has been sent somewhere in some manner (e.g., printed, faxed, forwarded) without necessarily having been previously displayed to the user. The user may or may not see the message later. @li Processed The message has been processed in some manner (i.e., by some sort of rules or server) without being displayed to the user. The user may or may not see the message later, or there may not even be a human user associated with the mailbox. @li Deleted The message has been deleted. The recipient may or may not have seen the message. The recipient might "undelete" the message at a later time and read the message. @li Denied The recipient does not wish the sender to be informed of the message's disposition. A UA may also siliently ignore message disposition requests in this situation. @li Failed A failure occurred that prevented the proper generation of an MDN. More information about the cause of the failure may be contained in a Failure field. The "failed" disposition type is not to be used for the situation in which there is is some problem in processing the message other than interpreting the request for an MDN. The "processed" or other disposition type with appropriate disposition modifiers is to be used in such situations. IOW: @p Displayed when - well -displayed @p Dispatched when forwarding unseen ( == new ) @p Processed (maybe) when piping unseen, but probably never used @p Deleted when deleting unseen @p Denied on user command @p Failed on Disposition-Notification-Options containing unknown required options. ( == @em any required options ) @p Failed needs a description in the @p special parameter. **/ enum DispositionType { Displayed, Read = Displayed, Deleted, Dispatched, Forwarded = Dispatched, Processed, Denied, Failed }; /** The following disposition modifiers are defined: @li Error An error of some sort occurred that prevented successful processing of the message. Further information is contained in an Error field. @li Warning The message was successfully processed but some sort of exceptional condition occurred. Further information is contained in a Warning field. @li Superseded The message has been automatically rendered obsolete by another message received. The recipient may still access and read the message later. @li Expired The message has reached its expiration date and has been automatically removed from the recipient's mailbox. @li MailboxTerminated The recipient's mailbox has been terminated and all message in it automatically removed. **/ enum DispositionModifier { Error, Warning, Superseded, Expired, MailboxTerminated }; \ /** The following disposition modes are defined: @li ManualAction The disposition described by the disposition type was a result of an explicit instruction by the user rather than some sort of automatically performed action. @li AutomaticAction The disposition described by the disposition type was a result of an automatic action, rather than an explicit instruction by the user for this message. IOW: @p ManualAction for user-driven actions, @p AutomanticAction for filtering. **/ enum ActionMode { ManualAction, AutomaticAction }; /** @li SentManually The user explicitly gave permission for this particular MDN to be sent. @li SentAutomatically The MDN was sent because the MUA had previously been configured to do so automatically. IOW: @p SentManually for when we have asked the user @p SentAutomatically when we use the default specified by the user **/ enum SendingMode { SentManually, SentAutomatically }; /** Main function. Generates the content of the message/disposition-notification body part. */ KDE_EXPORT extern QByteArray dispositionNotificationBodyContent( const QString & finalRecipient, const QByteArray & originalRecipient, const QByteArray & originalMsgID, DispositionType disposition, ActionMode actionMode, SendingMode sendingMode, const QList & dispositionModifers =QList(), const QString & special=QString() ); KDE_EXPORT extern QString descriptionFor( DispositionType d, const QList & m =QList() ); enum ReturnContent { Nothing, All, HeadersOnly }; } // namespace MDN } // namespace KMime #endif // __KMIME_MDN_H__ diff --git a/kmime/kmime_message.cpp b/kmime/kmime_message.cpp index e9939ceb3..729027525 100644 --- a/kmime/kmime_message.cpp +++ b/kmime/kmime_message.cpp @@ -1,159 +1,159 @@ /* kmime_message.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include "kmime_message.h" using namespace KMime; namespace KMime { Message::Message() { s_ubject.setParent(this); d_ate.setParent(this); } Message::~Message() {} void Message::parse() { Content::parse(); QByteArray raw; if( !(raw=rawHeader(s_ubject.type())).isEmpty() ) s_ubject.from7BitString(raw); if( !(raw=rawHeader(d_ate.type())).isEmpty() ) d_ate.from7BitString(raw); } void Message::assemble() { Headers::Base *h; QByteArray newHead=""; //Message-ID if( (h=messageID(false))!=0 ) newHead+=h->as7BitString()+"\n"; //From h=from(); // "From" is mandatory newHead+=h->as7BitString()+"\n"; //Subject h=subject(); // "Subject" is mandatory newHead+=h->as7BitString()+"\n"; //To if( (h=to(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Reply-To if( (h=replyTo(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Date h=date(); // "Date" is mandatory newHead+=h->as7BitString()+"\n"; //References if( (h=references(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Organization if( (h=organization(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Mime-Version newHead+="MIME-Version: 1.0\n"; //Content-Type newHead+=contentType()->as7BitString()+"\n"; //Content-Transfer-Encoding newHead+=contentTransferEncoding()->as7BitString()+"\n"; //X-Headers - int pos=h_ead.find("\nX-"); + int pos=h_ead.indexOf("\nX-"); if(pos>-1) //we already have some x-headers => "recycle" them newHead+=h_ead.mid(pos+1, h_ead.length()-pos-1); else foreach ( Headers::Base *h, h_eaders ) { if ( h->isXHeader() && ( strncasecmp( h->type(), "X-KNode", 7 ) != 0 ) ) newHead += h->as7BitString() + '\n'; } h_ead=newHead; } void Message::clear() { s_ubject.clear(); d_ate.clear(); f_lags.clear(); Content::clear(); } Headers::Base* Message::getHeaderByType(const char *type) { if(strcasecmp("Subject", type)==0) { if(s_ubject.isEmpty()) return 0; else return &s_ubject; } else if(strcasecmp("Date", type)==0){ if(d_ate.isEmpty()) return 0; else return &d_ate; } else return Content::getHeaderByType(type); } void Message::setHeader(Headers::Base *h) { bool del=true; if(h->is("Subject")) s_ubject.fromUnicodeString(h->asUnicodeString(), h->rfc2047Charset()); else if(h->is("Date")) d_ate.setUnixTime( (static_cast(h))->unixTime() ); else { del=false; Content::setHeader(h); } if(del) delete h; } bool Message::removeHeader(const char *type) { if(strcasecmp("Subject", type)==0) s_ubject.clear(); else if(strcasecmp("Date", type)==0) d_ate.clear(); else return Content::removeHeader(type); return true; } } // namespace KMime diff --git a/kmime/kmime_newsarticle.cpp b/kmime/kmime_newsarticle.cpp index 0d3877dfb..c206c47aa 100644 --- a/kmime/kmime_newsarticle.cpp +++ b/kmime/kmime_newsarticle.cpp @@ -1,158 +1,158 @@ /* kmime_newsarticle.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include "kmime_newsarticle.h" using namespace KMime; namespace KMime { void NewsArticle::parse() { Message::parse(); QByteArray raw; if( !(raw=rawHeader(l_ines.type())).isEmpty() ) l_ines.from7BitString(raw); } void NewsArticle::assemble() { Headers::Base *h; QByteArray newHead=""; //Message-ID if( (h=messageID(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Control if( (h=control(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Supersedes if( (h=supersedes(false))!=0 ) newHead+=h->as7BitString()+"\n"; //From h=from(); // "From" is mandatory newHead+=h->as7BitString()+"\n"; //Subject h=subject(); // "Subject" is mandatory newHead+=h->as7BitString()+"\n"; //To if( (h=to(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Newsgroups if( (h=newsgroups(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Followup-To if( (h=followUpTo(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Reply-To if( (h=replyTo(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Mail-Copies-To if( (h=mailCopiesTo(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Date h=date(); // "Date" is mandatory newHead+=h->as7BitString()+"\n"; //References if( (h=references(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Lines h=lines(); // "Lines" is mandatory newHead+=h->as7BitString()+"\n"; //Organization if( (h=organization(false))!=0 ) newHead+=h->as7BitString()+"\n"; //User-Agent if( (h=userAgent(false))!=0 ) newHead+=h->as7BitString()+"\n"; //Mime-Version newHead+="MIME-Version: 1.0\n"; //Content-Type newHead+=contentType()->as7BitString()+"\n"; //Content-Transfer-Encoding newHead+=contentTransferEncoding()->as7BitString()+"\n"; //X-Headers - int pos=h_ead.find("\nX-"); + int pos=h_ead.indexOf("\nX-"); if(pos>-1) //we already have some x-headers => "recycle" them newHead+=h_ead.mid(pos+1, h_ead.length()-pos); else foreach( Headers::Base *h, h_eaders ) if ( h->isXHeader() && ( strncasecmp( h->type(), "X-KNode", 7 ) != 0 ) ) newHead += h->as7BitString() + '\n'; h_ead=newHead; } void NewsArticle::clear() { l_ines.clear(); Message::clear(); } Headers::Base * NewsArticle::getHeaderByType(const char * type) { if(strcasecmp("Lines", type)==0) { if(l_ines.isEmpty()) return 0; else return &l_ines; } else return Message::getHeaderByType(type); } void NewsArticle::setHeader(Headers::Base *h) { bool del=true; if(h->is("Lines")) l_ines.setNumberOfLines( (static_cast(h))->numberOfLines() ); else { del=false; Message::setHeader(h); } if(del) delete h; } bool NewsArticle::removeHeader(const char *type) { if(strcasecmp("Lines", type)==0) l_ines.clear(); else return Message::removeHeader(type); return true; } } // namespace KMime diff --git a/kmime/kmime_parsers.cpp b/kmime/kmime_parsers.cpp index 99c74d1ed..2be3c39fe 100644 --- a/kmime/kmime_parsers.cpp +++ b/kmime/kmime_parsers.cpp @@ -1,468 +1,468 @@ /* kmime_parsers.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include "kmime_parsers.h" #include -//Added by qt3to4: #include using namespace KMime::Parser; namespace KMime { namespace Parser { MultiPart::MultiPart(const QByteArray &src, const QByteArray &boundary) { s_rc=src; b_oundary=boundary; } bool MultiPart::parse() { QByteArray b="--"+b_oundary, part; int pos1=0, pos2=0, blen=b.length(); p_arts.clear(); //find the first valid boundary while(1) { - if( (pos1=s_rc.find(b, pos1))==-1 || pos1==0 || s_rc[pos1-1]=='\n' ) //valid boundary found or no boundary at all + if( (pos1=s_rc.indexOf(b, pos1))==-1 || pos1==0 || s_rc[pos1-1]=='\n' ) //valid boundary found or no boundary at all break; pos1+=blen; //boundary found but not valid => skip it; } if(pos1>-1) { pos1+=blen; if(s_rc[pos1]=='-' && s_rc[pos1+1]=='-') // the only valid boundary is the end-boundary - this message is *really* broken pos1=-1; //we give up else if( (pos1-blen)>1 ) //preamble present p_reamble=s_rc.left(pos1-blen); } while(pos1>-1 && pos2>-1) { //skip the rest of the line for the first boundary - the message-part starts here - if( (pos1=s_rc.find('\n', pos1))>-1 ) { //now search the next linebreak + if( (pos1=s_rc.indexOf('\n', pos1))>-1 ) { //now search the next linebreak //now find the next valid boundary pos2=++pos1; //pos1 and pos2 point now to the beginning of the next line after the boundary while(1) { - if( (pos2=s_rc.find(b, pos2))==-1 || s_rc[pos2-1]=='\n' ) //valid boundary or no more boundaries found + if( (pos2=s_rc.indexOf(b, pos2))==-1 || s_rc[pos2-1]=='\n' ) //valid boundary or no more boundaries found break; pos2+=blen; //boundary is invalid => skip it; } if(pos2==-1) { // no more boundaries found part=s_rc.mid(pos1, s_rc.length()-pos1); //take the rest of the string p_arts.append(part); pos1=-1; pos2=-1; //break; } else { part=s_rc.mid(pos1, pos2-pos1); p_arts.append(part); pos2+=blen; //pos2 points now to the first charakter after the boundary if(s_rc[pos2]=='-' && s_rc[pos2+1]=='-') { //end-boundary pos1=pos2+2; //pos1 points now to the character directly after the end-boundary - if( (pos1=s_rc.find('\n', pos1))>-1 ) //skipt the rest of this line + if( (pos1=s_rc.indexOf('\n', pos1))>-1 ) //skipt the rest of this line e_pilouge=s_rc.mid(pos1+1, s_rc.length()-pos1-1); //everything after the end-boundary is considered as the epilouge pos1=-1; pos2=-1; //break } else { pos1=pos2; //the search continues ... } } } } return (!p_arts.isEmpty()); } //============================================================================================ NonMimeParser::NonMimeParser(const QByteArray &src) : s_rc(src), p_artNr(-1), t_otalNr(-1) {} /** * try to guess the mimetype from the file-extension */ QByteArray NonMimeParser::guessMimeType(const QByteArray& fileName) { QByteArray tmp, mimeType; int pos; if(!fileName.isEmpty()) { - pos=fileName.findRev('.'); + pos=fileName.lastIndexOf('.'); if(pos++ != -1) { tmp=fileName.mid(pos, fileName.length()-pos).toUpper(); if(tmp=="JPG" || tmp=="JPEG") mimeType="image/jpeg"; else if(tmp=="GIF") mimeType="image/gif"; else if(tmp=="PNG") mimeType="image/png"; else if(tmp=="TIFF" || tmp=="TIF") mimeType="image/tiff"; else if(tmp=="XPM") mimeType="image/x-xpm"; else if(tmp=="XBM") mimeType="image/x-xbm"; else if(tmp=="BMP") mimeType="image/x-bmp"; else if(tmp=="TXT" || tmp=="ASC" || tmp=="H" || tmp=="C" || tmp=="CC" || tmp=="CPP") mimeType="text/plain"; else if(tmp=="HTML" || tmp=="HTM") mimeType="text/html"; else mimeType="application/octet-stream"; } else mimeType="application/octet-stream"; } else mimeType="application/octet-stream"; return mimeType; } //============================================================================================ UUEncoded::UUEncoded(const QByteArray &src, const QByteArray &subject) : NonMimeParser(src), s_ubject(subject) {} bool UUEncoded::parse() { int currentPos=0; bool success=true, firstIteration=true; while (success) { int beginPos=currentPos, uuStart=currentPos, endPos=0, lineCount=0, MCount=0, pos=0, len=0; bool containsBegin=false, containsEnd=false; QByteArray tmp,fileName; - if( (beginPos=QString(s_rc).find(QRegExp("begin [0-9][0-9][0-9]"),currentPos))>-1 && (beginPos==0 || s_rc.at(beginPos-1)=='\n') ) { + if( (beginPos=QString(s_rc).indexOf(QRegExp("begin [0-9][0-9][0-9]"),currentPos))>-1 && (beginPos==0 || s_rc.at(beginPos-1)=='\n') ) { containsBegin=true; - uuStart=s_rc.find('\n', beginPos); + uuStart=s_rc.indexOf('\n', beginPos); if(uuStart==-1) {//no more line breaks found, we give up success = false; break; } else uuStart++; //points now at the beginning of the next line } else beginPos=currentPos; - if ( (endPos=s_rc.find("\nend",(uuStart>0)? uuStart-1:0))==-1 ) + if ( (endPos=s_rc.indexOf("\nend",(uuStart>0)? uuStart-1:0))==-1 ) endPos=s_rc.length(); //no end found else containsEnd=true; if ((containsBegin && containsEnd) || firstIteration) { //printf("beginPos=%d , uuStart=%d , endPos=%d\n", beginPos, uuStart, endPos); //all lines in a uuencoded text start with 'M' for(int idx=uuStart; idx10 || ((!containsBegin || !containsEnd) && (MCount<15)) ) { // harder check for splitted-articles success = false; break; //too many "non-M-Lines" found, we give up } if( (!containsBegin || !containsEnd) && !s_ubject.isNull()) { // message may be split up => parse subject QRegExp rx("[0-9]+/[0-9]+"); - pos=rx.search(QString(s_ubject), 0); + pos=rx.indexIn(QString(s_ubject), 0); len=rx.matchedLength(); if(pos!=-1) { tmp=s_ubject.mid(pos, len); - pos=tmp.find('/'); + pos=tmp.indexOf('/'); p_artNr=tmp.left(pos).toInt(); t_otalNr=tmp.right(tmp.length()-pos-1).toInt(); } else { success = false; break; //no "part-numbers" found in the subject, we give up } } //everything before "begin" is text if(beginPos>0) t_ext.append(s_rc.mid(currentPos,beginPos-currentPos)); if(containsBegin) fileName = s_rc.mid(beginPos+10, uuStart-beginPos-11); //everything between "begin ### " and the next LF is considered as the filename else fileName = ""; f_ilenames.append(fileName); b_ins.append(s_rc.mid(uuStart, endPos-uuStart+1)); //everything beetween "begin" and "end" is uuencoded m_imeTypes.append(guessMimeType(fileName)); firstIteration=false; - int next = s_rc.find('\n', endPos+1); + int next = s_rc.indexOf('\n', endPos+1); if(next==-1) { //no more line breaks found, we give up success = false; break; } else next++; //points now at the beginning of the next line currentPos = next; } else { success = false; } } // append trailing text part of the article t_ext.append(s_rc.right(s_rc.length()-currentPos)); return ((b_ins.count()>0) || isPartial()); } //============================================================================================ YENCEncoded::YENCEncoded(const QByteArray &src) : NonMimeParser(src) {} bool YENCEncoded::yencMeta(QByteArray& src, const QByteArray& name, int* value) { bool found = false; QByteArray sought=name + "="; - int iPos=src.find( sought); + int iPos=src.indexOf( sought); if (iPos>-1) { - int pos1=src.find(' ', iPos); - int pos2=src.find('\r', iPos); - int pos3=src.find('\t', iPos); - int pos4=src.find('\n', iPos); + int pos1=src.indexOf(' ', iPos); + int pos2=src.indexOf('\r', iPos); + int pos3=src.indexOf('\t', iPos); + int pos4=src.indexOf('\n', iPos); if (pos2>=0 && (pos1<0 || pos1>pos2)) pos1=pos2; if (pos3>=0 && (pos1<0 || pos1>pos3)) pos1=pos3; if (pos4>=0 && (pos1<0 || pos1>pos4)) pos1=pos4; - iPos=src.findRev( '=', pos1)+1; + iPos=src.lastIndexOf( '=', pos1)+1; if (iPos='0' && c<='9') { found=true; *value=src.mid( iPos, pos1-iPos).toInt(); } } } return found; } bool YENCEncoded::parse() { int currentPos=0; bool success=true; while (success) { int beginPos=currentPos, yencStart=currentPos; bool containsPart=false; QByteArray fileName,mimeType; - if ((beginPos=s_rc.find("=ybegin ", currentPos))>-1 && ( beginPos==0 || s_rc.at( beginPos-1)=='\n') ) { - yencStart=s_rc.find( '\n', beginPos); + if ((beginPos=s_rc.indexOf("=ybegin ", currentPos))>-1 && ( beginPos==0 || s_rc.at( beginPos-1)=='\n') ) { + yencStart=s_rc.indexOf( '\n', beginPos); if (yencStart==-1) { // no more line breaks found, give up success = false; break; } else { yencStart++; - if (s_rc.find("=ypart", yencStart)==yencStart) { + if (s_rc.indexOf("=ypart", yencStart)==yencStart) { containsPart=true; - yencStart=s_rc.find( '\n', yencStart); + yencStart=s_rc.indexOf( '\n', yencStart); if ( yencStart== -1) { success=false; break; } yencStart++; } } // Try to identify yenc meta data // Filenames can contain any embedded chars until end of line QByteArray meta=s_rc.mid(beginPos, yencStart-beginPos); - int namePos=meta.find("name="); + int namePos=meta.indexOf("name="); if (namePos== -1) { success=false; break; } - int eolPos=meta.find('\r', namePos); + int eolPos=meta.indexOf('\r', namePos); if (eolPos== -1) - eolPos=meta.find('\n', namePos); + eolPos=meta.indexOf('\n', namePos); if (eolPos== -1) { success=false; break; } fileName=meta.mid(namePos+5, eolPos-(namePos+5)); // Other metadata is integer int yencLine; if (!yencMeta(meta, "line", ¥cLine)) { success=false; break; } int yencSize; if (!yencMeta( meta, "size", ¥cSize)) { success=false; break; } int partBegin, partEnd; if (containsPart) { if (!yencMeta(meta, "part", &p_artNr)) { success=false; break; } if (!yencMeta(meta, "begin", &partBegin) || ! yencMeta(meta, "end", &partEnd)) { success=false; break; } if (!yencMeta(meta, "total", &t_otalNr)) t_otalNr=p_artNr+1; if (yencSize==partEnd-partBegin+1) t_otalNr=1; else yencSize=partEnd-partBegin+1; } // We have a valid yenc header; now we extract the binary data int totalSize=0; int pos=yencStart; int len=s_rc.length(); bool lineStart=true; int lineLength=0; bool containsEnd=false; - QByteArray binary = QByteArray(yencSize); + QByteArray binary; + binary.resize(yencSize); while (pos=yencSize) break; binary[totalSize++]=ch; lineLength++; } else break; } else { ch-=42; if (ch<0) ch+=256; if (totalSize>=yencSize) break; binary[totalSize++]=ch; lineLength++; pos++; } lineStart=false; } } if (!containsEnd) { success=false; break; } if (totalSize!=yencSize) { success=false; break; } // pos now points to =yend; get end data - eolPos=s_rc.find('\n', pos); + eolPos=s_rc.indexOf('\n', pos); if (eolPos== -1) { success=false; break; } meta=s_rc.mid(pos, eolPos-pos); if (!yencMeta(meta, "size", &totalSize)) { success=false; break; } if (totalSize!=yencSize) { success=false; break; } f_ilenames.append(fileName); m_imeTypes.append(guessMimeType( fileName)); b_ins.append(binary); //everything before "begin" is text if(beginPos>0) t_ext.append(s_rc.mid(currentPos,beginPos-currentPos)); currentPos = eolPos+1; } else { success = false; } } // append trailing text part of the article t_ext.append(s_rc.right(s_rc.length()-currentPos)); return b_ins.count()>0; } } // namespace Parser } // namespace KMime diff --git a/kmime/kmime_util.cpp b/kmime/kmime_util.cpp index 424c9a974..e952f4b43 100644 --- a/kmime/kmime_util.cpp +++ b/kmime/kmime_util.cpp @@ -1,807 +1,799 @@ /* kmime_util.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifdef HAVE_CONFIG_H #include #endif #include "kmime_util.h" #include // for KCodec::{quotedPrintableDe,base64{En,De}}code #include #include #include #include #include #include #include -//Added by qt3to4: -#include +#include #include #include #include // for time() #include // for getpid() using namespace KMime; namespace KMime { QList c_harsetCache; QList l_anguageCache; QByteArray cachedCharset( const QByteArray &name ) { foreach ( QByteArray charset, c_harsetCache ) if ( qstricmp( name.data(), charset.data() ) == 0 ) return charset; c_harsetCache.append( name.toUpper() ); //kdDebug() << "KNMimeBase::cachedCharset() number of cs " << c_harsetCache.count() << endl; return c_harsetCache.last(); } QByteArray cachedLanguage( const QByteArray &name ) { foreach ( QByteArray language, l_anguageCache ) if ( qstricmp( name.data(), language.data() ) == 0 ) return language; l_anguageCache.append( name.toUpper() ); //kdDebug() << "KNMimeBase::cachedCharset() number of cs " << c_harsetCache.count() << endl; return l_anguageCache.last(); } bool isUsAscii(const QString &s) { uint sLength = s.length(); for (uint i=0; i@[\] const uchar specialsMap[16] = { 0x00, 0x00, 0x00, 0x00, // CTLs 0x20, 0xCA, 0x00, 0x3A, // SPACE ... '?' 0x80, 0x00, 0x00, 0x1C, // '@' ... '_' 0x00, 0x00, 0x00, 0x00 // '`' ... DEL }; // "(),:;<>@[\]/=? const uchar tSpecialsMap[16] = { 0x00, 0x00, 0x00, 0x00, // CTLs 0x20, 0xC9, 0x00, 0x3F, // SPACE ... '?' 0x80, 0x00, 0x00, 0x1C, // '@' ... '_' 0x00, 0x00, 0x00, 0x00 // '`' ... DEL }; // all except specials, CTLs, SPACE. const uchar aTextMap[16] = { 0x00, 0x00, 0x00, 0x00, 0x5F, 0x35, 0xFF, 0xC5, 0x7F, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFE }; // all except tspecials, CTLs, SPACE. const uchar tTextMap[16] = { 0x00, 0x00, 0x00, 0x00, 0x5F, 0x36, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFE }; // none except a-zA-Z0-9!*+-/ const uchar eTextMap[16] = { 0x00, 0x00, 0x00, 0x00, 0x40, 0x35, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xE0 }; #if defined(_AIX) && defined(truncate) #undef truncate #endif -QString decodeRFC2047String(const Q3CString &src, const char **usedCS, - const Q3CString &defaultCS, bool forceCS) +QString decodeRFC2047String( const QByteArray &src, QByteArray &usedCS, + const QByteArray &defaultCS, bool forceCS ) { - Q3CString result, str; - Q3CString declaredCS; + QByteArray result, str; + QByteArray declaredCS; int pos = 0, dest = 0, beg = 0, end = 0, mid = 0, endOfLastEncWord = 0; char encoding = '\0'; bool valid, onlySpacesSinceLastWord=false; const int maxLen=400; int i; if ( !src.contains( "=?" ) ) - result = src.copy(); + result = src; else { - result.truncate(src.length()); - for (pos = 0, dest = 0; pos < src.size(); pos++) + result.resize( src.length() ); + for (pos = 0, dest = 0; pos < src.length(); pos++) { if ( src[pos] != '=' || src[pos + 1] != '?' ) { result[dest++] = src[pos]; if (onlySpacesSinceLastWord) onlySpacesSinceLastWord = (src[pos]==' ' || src[pos]=='\t'); continue; } beg = pos+2; end = beg; valid = true; // parse charset name - declaredCS=""; + declaredCS.clear(); for ( i = 2, pos += 2; i < maxLen && (src[pos] != '?' && (ispunct(src[pos]) || isalnum(src[pos]))); i++ ) { declaredCS += src[pos]; pos++; } - if ( src[pos] != '?' || i < 4 || i >= maxLen) valid = false; + if ( src[pos] != '?' || i < 4 || i >= maxLen) + valid = false; else { // get encoding and check delimiting question marks encoding = toupper(src[pos+1]); if ( src[pos+2] != '?' || (encoding != 'Q' && encoding != 'B')) valid = false; pos += 3; i+=3; } if (valid) { mid = pos; // search for end of encoded part - while ( i < maxLen && pos < src.size() && ! ( src[pos] == '?' && src[pos + 1] == '=' ) ) + while ( i < maxLen && pos < src.length() && ! ( src[pos] == '?' && src[pos + 1] == '=' ) ) { i++; pos++; } end = pos+2;//end now points to the first char after the encoded string - if ( i >= maxLen || src.size() <= pos ) valid = false; + if ( i >= maxLen || src.length() <= pos ) + valid = false; } if (valid) { // cut all linear-white space between two encoded words if (onlySpacesSinceLastWord) dest=endOfLastEncWord; if (mid < pos) { str = src.mid( mid, (int)(pos - mid + 1) ); if (encoding == 'Q') { // decode quoted printable text for (i=str.length()-1; i>=0; i--) if (str[i]=='_') str[i]=' '; str = KCodecs::quotedPrintableDecode(str); } else { str = KCodecs::base64Decode(str); } for (i=0; str[i]; i++) { result[dest++] = str[i]; } } endOfLastEncWord=dest; onlySpacesSinceLastWord=true; pos = end -1; } else { pos = beg - 2; result[dest++] = src[pos++]; result[dest++] = src[pos]; } } result.resize( dest ); } //find suitable QTextCodec QTextCodec *codec=0; bool ok=true; if (forceCS || declaredCS.isEmpty()) { codec=KGlobal::charsets()->codecForName(defaultCS); - (*usedCS)=cachedCharset(defaultCS); + usedCS=cachedCharset(defaultCS); } else { codec=KGlobal::charsets()->codecForName(declaredCS, ok); if(!ok) { //no suitable codec found => use default charset codec=KGlobal::charsets()->codecForName(defaultCS); - (*usedCS)=cachedCharset(defaultCS); + usedCS=cachedCharset(defaultCS); } else - (*usedCS)=cachedCharset(declaredCS); + usedCS=cachedCharset(declaredCS); } - return codec->toUnicode(result.data(), result.length()); + return codec->toUnicode( result.data(), result.length() ); } -Q3CString encodeRFC2047String(const QString &src, const char *charset, - bool addressHeader, bool allow8BitHeaders) +QByteArray encodeRFC2047String( const QString &src, const QByteArray &charset, + bool addressHeader, bool allow8BitHeaders ) { - Q3CString encoded8Bit, result, usedCS; + QByteArray encoded8Bit, result, usedCS; int start=0,end=0; bool nonAscii=false, ok=true, useQEncoding=false; QTextCodec *codec=0; usedCS=charset; codec=KGlobal::charsets()->codecForName(usedCS, ok); if(!ok) { //no codec available => try local8Bit and hope the best ;-) usedCS=KGlobal::locale()->encoding(); codec=KGlobal::charsets()->codecForName(usedCS, ok); } - if (usedCS.find("8859-")>=0) // use "B"-Encoding for non iso-8859-x charsets + if ( usedCS.contains("8859-") ) // use "B"-Encoding for non iso-8859-x charsets useQEncoding=true; encoded8Bit=codec->fromUnicode(src); if(allow8BitHeaders) return encoded8Bit; uint encoded8BitLength = encoded8Bit.length(); for (unsigned int i=0; i@,.;:\\[]=",encoded8Bit[i])!=0))) { end = start; // non us-ascii char found, now we determine where to stop encoding nonAscii=true; break; } } if (nonAscii) { while ((end@,.;:\\[]=",encoded8Bit[x])!=0))) { end = encoded8Bit.length(); // we found another non-ascii word while ((end='a')&&(c<='z'))|| // paranoid mode, we encode *all* special characters to avoid problems ((c>='A')&&(c<='Z'))|| // with "From" & "To" headers ((c>='0')&&(c<='9'))) result+=c; else { - result += "="; // "stolen" from KMail ;-) + result += '='; // "stolen" from KMail ;-) hexcode = ((c & 0xF0) >> 4) + 48; if (hexcode >= 58) hexcode += 7; result += hexcode; hexcode = (c & 0x0F) + 48; if (hexcode >= 58) hexcode += 7; result += hexcode; } } } else { result += "?B?"+KCodecs::base64Encode(encoded8Bit.mid(start,end-start), false); } result +="?="; result += encoded8Bit.right(encoded8Bit.length()-end); } else result = encoded8Bit; return result; } -Q3CString uniqueString() +QByteArray uniqueString() { static char chars[] = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; time_t now; - Q3CString ret; char p[11]; int pos, ran; unsigned int timeval; p[10]='\0'; now=time(0); ran=1+(int) (1000.0*rand()/(RAND_MAX+1.0)); timeval=(now/ran)+getpid(); for(int i=0; i<10; i++){ pos=(int) (61.0*rand()/(RAND_MAX+1.0)); //kdDebug(5003) << pos << endl; p[i]=chars[pos]; } - ret.sprintf("%d.%s", timeval, p); + + QByteArray ret; + ret.setNum( timeval ); + ret += '.'; + ret += p; return ret; } QByteArray multiPartBoundary() { - QByteArray ret; - ret="nextPart"+uniqueString(); - return ret; + return "nextPart" + uniqueString(); } -Q3CString extractHeader(const Q3CString &src, const char *name) +QByteArray extractHeader( const QByteArray &src, const QByteArray &name ) { - Q3CString n=Q3CString(name)+": "; - int pos1=-1, pos2=0, len=src.length()-1; - bool folded(false); + QByteArray n = name + ": "; + int pos1=-1; - if (n.toLower() == src.left(n.length()).toLower()) { + if ( qstrnicmp( n.data(), src.data(), n.length() ) == 0 ) { pos1 = 0; } else { - n.prepend("\n"); + n.prepend('\n'); + // TODO Possible optimization: Avoid using QString pos1 = QString(src).indexOf(n,0,Qt::CaseInsensitive); } if (pos1>-1) { //there is a header with the given name pos1+=n.length(); //skip the name - pos2=pos1; + int pos2=pos1; + int len=src.length()-1; + bool folded = false; if (src[pos2]!='\n') { // check if the header is not empty - while(1) { - pos2=src.find("\n", pos2+1); + while ( true ) { + pos2=src.indexOf('\n', pos2+1); if(pos2==-1 || pos2==len || ( src[pos2+1]!=' ' && src[pos2+1]!='\t') ) //break if we reach the end of the string, honor folded lines break; else folded = true; } } if(pos2<0) pos2=len+1; //take the rest of the string if (!folded) return src.mid(pos1, pos2-pos1); else { + // TODO Optimization: Copy to new string instead of removing in-string QByteArray hdrValue = src.mid( pos1, pos2 - pos1 ); // unfold header int beg = 0, mid = 0, end = 0; while ( (mid = hdrValue.indexOf( '\n' )) >= 0 ) { beg = end = mid; while ( beg > 0 ) { if ( !QChar( hdrValue[beg] ).isSpace() ) break; --beg; } while ( end < hdrValue.length() - 1 ) { if ( !QChar( hdrValue[end] ).isSpace() ) break; ++end; } hdrValue.remove( beg, end - beg ); } return hdrValue; } } else { - return Q3CString(0); //header not found + return QByteArray(); //header not found } } -Q3CString CRLFtoLF(const Q3CString &s) +QByteArray CRLFtoLF( const QByteArray &s ) { - Q3CString ret=s.copy(); + QByteArray ret = s; ret.replace( "\r\n", "\n" ); return ret; } -Q3CString CRLFtoLF(const char *s) +QByteArray LFtoCRLF( const QByteArray &s ) { - Q3CString ret=s; - ret.replace( "\r\n", "\n"); + QByteArray ret = s; + ret.replace( "\n", "\r\n" ); return ret; } -Q3CString LFtoCRLF(const Q3CString &s) -{ - Q3CString ret=s.copy(); - ret.replace( "\n", "\r\n"); - return ret; +namespace { + template < typename T > void removeQuotesGeneric( T & str ) + { + bool inQuote = false; + for ( int i = 0; i < str.length(); ++i ) { + if ( str[i] == '"' ) { + str.remove( i, 1 ); + i--; + inQuote = !inQuote; + } else { + if ( inQuote && ( str[i] == '\\' ) ) + str.remove( i, 1 ); + } + } + } } - -void removeQuots(Q3CString &str) +void removeQuots( QByteArray &str ) { - bool inQuote=false; - - for (int i=0; i < (int)str.length(); i++) { - if (str[i] == '"') { - str.remove(i,1); - i--; - inQuote = !inQuote; - } else { - if (inQuote && (str[i] == '\\')) - str.remove(i,1); - } - } + removeQuotesGeneric( str ); } -void removeQuots(QString &str) +void removeQuots( QString &str ) { - bool inQuote=false; - - for (int i=0; i < (int)str.length(); i++) { - if (str[i] == '"') { - str.remove(i,1); - i--; - inQuote = !inQuote; - } else { - if (inQuote && (str[i] == '\\')) - str.remove(i,1); - } - } + removeQuotesGeneric( str ); } void addQuotes(QByteArray &str, bool forceQuotes) { bool needsQuotes=false; for (int i=0; i < str.length(); i++) { if (strchr("()<>@,.;:[]=\\\"",str[i])!=0) needsQuotes = true; if (str[i]=='\\' || str[i]=='\"') { str.insert(i, '\\'); i++; } } if (needsQuotes || forceQuotes) { str.insert(0,'\"'); str.append("\""); } } int DateFormatter::mDaylight = -1; DateFormatter::DateFormatter(FormatType fType) : mFormat( fType ), mCurrentTime( 0 ) { } DateFormatter::~DateFormatter() {/*empty*/} DateFormatter::FormatType DateFormatter::getFormat() const { return mFormat; } void DateFormatter::setFormat( FormatType t ) { mFormat = t; } QString DateFormatter::dateString( time_t otime , const QString& lang , bool shortFormat, bool includeSecs ) const { switch ( mFormat ) { case Fancy: return fancy( otime ); break; case Localized: return localized( otime, shortFormat, includeSecs, lang ); break; case CTime: return cTime( otime ); break; case Iso: return isoDate( otime ); break; case Custom: return custom( otime ); break; } return QString(); } QString DateFormatter::dateString(const QDateTime& dtime, const QString& lang, bool shortFormat, bool includeSecs ) const { return DateFormatter::dateString( qdateToTimeT(dtime), lang, shortFormat, includeSecs ); } -Q3CString +QByteArray DateFormatter::rfc2822(time_t otime) const { QDateTime tmp; - Q3CString ret; + QByteArray ret; tmp.setTime_t(otime); ret = tmp.toString("ddd, dd MMM yyyy hh:mm:ss ").toLatin1(); ret += zone(otime); return ret; } QString DateFormatter::custom(time_t t) const { if ( mCustomFormat.isEmpty() ) return QString(); - int z = mCustomFormat.find("Z"); + int z = mCustomFormat.indexOf('Z'); QDateTime d; QString ret = mCustomFormat; d.setTime_t(t); if ( z != -1 ) { ret.replace(z,1,zone(t)); } ret = d.toString(ret); return ret; } void DateFormatter::setCustomFormat(const QString& format) { mCustomFormat = format; mFormat = Custom; } QString DateFormatter::getCustomFormat() const { return mCustomFormat; } -Q3CString +QByteArray DateFormatter::zone(time_t otime) const { - Q3CString ret; #if defined(HAVE_TIMEZONE) || defined(HAVE_TM_GMTOFF) struct tm *local = localtime( &otime ); #endif #if defined(HAVE_TIMEZONE) //hmm, could make hours & mins static int secs = abs(timezone); int neg = (timezone>0)?1:0; int hours = secs/3600; int mins = (secs - hours*3600)/60; // adjust to daylight if ( local->tm_isdst > 0 ) { mDaylight = 1; if ( neg ) --hours; else ++hours; } else mDaylight = 0; - ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins); - #elif defined(HAVE_TM_GMTOFF) int secs = abs( local->tm_gmtoff ); int neg = (local->tm_gmtoff<0)?1:0; //no, I don't know why it's backwards :o int hours = secs/3600; int mins = (secs - hours*3600)/60; if ( local->tm_isdst > 0 ) mDaylight = 1; else mDaylight = 0; - ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins); - #else QDateTime d1 = QDateTime::fromString( asctime(gmtime(&otime)) ); QDateTime d2 = QDateTime::fromString( asctime(localtime(&otime)) ); int secs = d1.secsTo(d2); int neg = (secs<0)?1:0; secs = abs(secs); int hours = secs/3600; int mins = (secs - hours*3600)/60; // daylight should be already taken care of here - ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins); #endif /* HAVE_TIMEZONE */ + QByteArray ret; + QTextStream s( &ret, QIODevice::WriteOnly ); + s << ( neg ? '-' : '+' ) + << qSetFieldWidth(2) << qSetPadChar('0') << right << hours << mins; + //old code: ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins); + return ret; } time_t DateFormatter::qdateToTimeT(const QDateTime& dt) const { QDateTime epoch( QDate(1970, 1,1), QTime(00,00,00) ); time_t otime; time( &otime ); QDateTime d1 = QDateTime::fromString( asctime(gmtime(&otime)) ); QDateTime d2 = QDateTime::fromString( asctime(localtime(&otime)) ); time_t drf = epoch.secsTo( dt ) - d1.secsTo( d2 ); return drf; } QString DateFormatter::fancy(time_t otime) const { KLocale *locale = KGlobal::locale(); if ( otime <= 0 ) return i18n( "unknown" ); if ( !mCurrentTime ) { time( &mCurrentTime ); mDate.setTime_t( mCurrentTime ); } QDateTime old; old.setTime_t( otime ); // not more than an hour in the future if ( mCurrentTime + 60 * 60 >= otime ) { time_t diff = mCurrentTime - otime; if ( diff < 24 * 60 * 60 ) { if ( old.date().year() == mDate.date().year() && old.date().dayOfYear() == mDate.date().dayOfYear() ) return i18n( "Today %1" ).arg( locale-> formatTime( old.time(), true ) ); } if ( diff < 2 * 24 * 60 * 60 ) { QDateTime yesterday( mDate.addDays( -1 ) ); if ( old.date().year() == yesterday.date().year() && old.date().dayOfYear() == yesterday.date().dayOfYear() ) return i18n( "Yesterday %1" ).arg( locale-> formatTime( old.time(), true) ); } for ( int i = 3; i < 7; i++ ) if ( diff < i * 24 * 60 * 60 ) { QDateTime weekday( mDate.addDays( -i + 1 ) ); if ( old.date().year() == weekday.date().year() && old.date().dayOfYear() == weekday.date().dayOfYear() ) return i18n( "1. weekday, 2. time", "%1 %2" ). arg( locale->calendar()->weekDayName( old.date() ) ). arg( locale->formatTime( old.time(), true) ); } } return locale->formatDateTime( old ); } QString DateFormatter::localized(time_t otime, bool shortFormat, bool includeSecs, const QString& localeLanguage ) const { QDateTime tmp; QString ret; KLocale *locale = KGlobal::locale(); tmp.setTime_t( otime ); if ( !localeLanguage.isEmpty() ) { locale=new KLocale(localeLanguage); locale->setLanguage(localeLanguage); locale->setCountry(localeLanguage); ret = locale->formatDateTime( tmp, shortFormat, includeSecs ); delete locale; } else { ret = locale->formatDateTime( tmp, shortFormat, includeSecs ); } return ret; } QString DateFormatter::cTime(time_t otime) const { - return QString::fromLatin1( ctime( &otime ) ).trimmed() ; + return QString::fromLatin1( ctime( &otime ) ).trimmed(); } QString DateFormatter::isoDate(time_t otime) const { char cstr[64]; strftime( cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&otime) ); return QString( cstr ); } void DateFormatter::reset() { mCurrentTime = 0; } QString DateFormatter::formatDate(DateFormatter::FormatType t, time_t otime, const QString& data, bool shortFormat, bool includeSecs ) { DateFormatter f( t ); if ( t == DateFormatter::Custom ) { f.setCustomFormat( data ); } return f.dateString( otime, data, shortFormat, includeSecs ); } QString DateFormatter::formatCurrentDate( DateFormatter::FormatType t, const QString& data, bool shortFormat, bool includeSecs ) { DateFormatter f( t ); if ( t == DateFormatter::Custom ) { f.setCustomFormat( data ); } return f.dateString( time(0), data, shortFormat, includeSecs ); } -Q3CString +QByteArray DateFormatter::rfc2822FormatDate( time_t t ) { DateFormatter f; return f.rfc2822( t ); } bool DateFormatter::isDaylight() { if ( mDaylight == -1 ) { time_t ntime = time( 0 ); struct tm *local = localtime( &ntime ); if ( local->tm_isdst > 0 ) { mDaylight = 1; return true; } else { mDaylight = 0; return false; } } else if ( mDaylight != 0 ) return true; else return false; } } // namespace KMime diff --git a/kmime/kmime_util.h b/kmime/kmime_util.h index 9aebe591d..e1adc4ad7 100644 --- a/kmime/kmime_util.h +++ b/kmime/kmime_util.h @@ -1,330 +1,329 @@ /* -*- c++ -*- kmime_util.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #ifndef __KMIME_UTIL_H__ #define __KMIME_UTIL_H__ #include "qdatetime.h" #include "qstring.h" -#include "q3cstring.h" #include "time.h" #include namespace KMime { /** Consult the charset cache. Only used for reducing mem usage by keeping strings in a common repository.*/ extern QByteArray cachedCharset( const QByteArray &name ) KDE_EXPORT; /** Consult the language cache. Only used for reducing mem usage by keeping strings in a common repository.*/ extern QByteArray cachedLanguage( const QByteArray &name ) KDE_EXPORT; /** checks whether @p s contains any non-us-ascii characters */ extern bool isUsAscii(const QString &s) KDE_EXPORT; inline bool isOfSet(const uchar map[16], unsigned char ch) { Q_ASSERT( ch < 128 ); return ( map[ ch/8 ] & 0x80 >> ch%8 ); } extern const uchar specialsMap[16]; extern const uchar tSpecialsMap[16]; extern const uchar aTextMap[16]; extern const uchar tTextMap[16]; extern const uchar eTextMap[16]; inline bool isSpecial(char ch) { return isOfSet( specialsMap, ch ); } inline bool isTSpecial(char ch) { return isOfSet( tSpecialsMap, ch ); } inline bool isAText(char ch) { return isOfSet( aTextMap, ch ); } inline bool isTText(char ch) { return isOfSet( tTextMap, ch ); } inline bool isEText(char ch) { return isOfSet( eTextMap, ch ); } /** Decode string @p src according to RFC2047 (ie. the =?charset?[qb]?encoded?= construct). @param src source string. @param usedCS the detected charset is returned here @param defaultCS the charset to use in case the detected one isn't known to us. @param forceCS force the use of the default charset. @return the decoded string. */ - extern QString decodeRFC2047String(const Q3CString &src, const char **usedCS, - const Q3CString &defaultCS, bool forceCS) KDE_EXPORT; + extern QString decodeRFC2047String(const QByteArray &src, QByteArray &usedCS, + const QByteArray &defaultCS, bool forceCS) KDE_EXPORT; /** Encode string @p src according to RFC2047 using charset @p charset. @param src source string. @param charset charset to use. @param addressheader if this flag is true, all special chars like <,>,[,],... will be encoded, too. @param allow8BitHeaders if this flag is true, 8Bit headers are allowed. @return the encoded string. */ - extern Q3CString encodeRFC2047String(const QString &src, const char *charset, - bool addressHeader=false, bool allow8bitHeaders=false) KDE_EXPORT; + extern QByteArray encodeRFC2047String(const QString &src, const QByteArray &charset, + bool addressHeader=false, bool allow8bitHeaders=false) KDE_EXPORT; /** Uses current time, pid and random numbers to construct a string that aims to be unique on a per-host basis (ie. for the local part of a message-id or for multipart boundaries. @return the unique string. @see multiPartBoundary */ - extern Q3CString uniqueString() KDE_EXPORT; + extern QByteArray uniqueString() KDE_EXPORT; /** Constructs a random string (sans leading/trailing "--") that can be used as a multipart delimiter (ie. as @p boundary parameter to a multipart/... content-type). @return the randomized string. @see uniqueString */ extern QByteArray multiPartBoundary() KDE_EXPORT; /** Tries to extract the header with name @p name from the string @p src, unfolding it if necessary. @param src the source string. @param name the name of the header to search for. @return the first instance of the header @p name in @p src or a null QCString if no such header was found. */ - extern Q3CString extractHeader(const Q3CString &src, const char *name) KDE_EXPORT; + extern QByteArray extractHeader(const QByteArray &src, const QByteArray &name) KDE_EXPORT; /** Converts all occurrences of "\r\n" (CRLF) in @p s to "\n" (LF). This function is expensive and should be used only if the mail will be stored locally. All decode functions can cope with both line endings. @param s source string containing CRLF's @return the string with CRLF's substitued for LF's @see CRLFtoLF(const char*) LFtoCRLF */ - extern Q3CString CRLFtoLF(const Q3CString &s) KDE_EXPORT; + extern QByteArray CRLFtoLF(const QByteArray &s) KDE_EXPORT; /** Converts all occurrences of "\r\n" (CRLF) in @p s to "\n" (LF). This function is expensive and should be used only if the mail will be stored locally. All decode functions can cope with both line endings. @param s source string containing CRLF's @return the string with CRLF's substitued for LF's @see CRLFtoLF(const QCString&) LFtoCRLF */ - extern Q3CString CRLFtoLF(const char *s) KDE_EXPORT; + extern QByteArray CRLFtoLF(const char *s) KDE_EXPORT; /** Converts all occurrences of "\n" (LF) in @p s to "\r\n" (CRLF). This function is expensive and should be used only if the mail will be transmitted as an RFC822 message later. All decode functions can cope with and all encode functions can optionally produce both line endings, which is much faster. @param s source string containing CRLF's @return the string with CRLF's substitued for LF's @see CRLFtoLF(const QCString&) LFtoCRLF */ - extern Q3CString LFtoCRLF(const Q3CString &s) KDE_EXPORT; + extern QByteArray LFtoCRLF(const QByteArray &s) KDE_EXPORT; /** Removes quote (DQUOTE) characters and decodes "quoted-pairs" (ie. backslash-escaped characters) @param str the string to work on. @see addQuotes */ - KDE_EXPORT extern void removeQuots(Q3CString &str); + KDE_EXPORT extern void removeQuots(QByteArray &str); /** Removes quote (DQUOTE) characters and decodes "quoted-pairs" (ie. backslash-escaped characters) @param str the string to work on. @see addQuotes */ KDE_EXPORT extern void removeQuots(QString &str); /** Converts the given string into a quoted-string if the string contains any special characters (ie. one of ()<>@,.;:[]=\"). @param str us-ascii string to work on. @param forceQuotes if @p true, always add quote characters. */ KDE_EXPORT extern void addQuotes(QByteArray &str, bool forceQuotes); /** * @short class abstracting date formatting * * DateFormatter deals with different kinds of date * display formats. The formats supported by the class include: *
    *
  • fancy "Today 02:08:35" *
  • ctime "Sun Mar 31 02:08:35 2002" *
  • localized "2002-03-31 02:08" *
  • iso "2002-03-31 02:08:35" *
  • rfc2822 "Sun, 31 Mar 2002 02:08:35 -0500" *
  • custom "whatever you like" *
* * */ class KDE_EXPORT DateFormatter { public: enum FormatType { CTime, //< ctime "Sun Mar 31 02:08:35 2002" Localized, //< localized "2002-03-31 02:08" Fancy, //< fancy "Today 02:08:35" Iso, //< iso "2002-03-31 02:08:35" Custom //< custom "whatever you like" }; /** * constructor * @param fType default format used by the class */ DateFormatter(FormatType fType = DateFormatter::Fancy); ~DateFormatter(); /** * returns the currently set format */ FormatType getFormat() const; /** * sets the currently used format */ void setFormat(FormatType t); /** * returns formatted date string in a currently * set format. * @param otime time to format * @param lang used only by the Localized format, sets the used language * @param shortFormat used only by the Localized format, is passed to KLocale::formatDateTime * @param includeSecs used only by the Localized format, is passed to KLocale::formatDateTime */ QString dateString(time_t otime, const QString& lang = QString(), bool shortFormat = true, bool includeSecs=false) const; /** * overloaded, does exactly what #dateString does (it's slower) */ QString dateString(const QDateTime& dtime, const QString& lang = QString(), bool shortFormat = true, bool includeSecs=false) const; /** * makes the class use the custom format for * date to string conversions. * Method accepts the same arguments * as QDateTime::toString method and adds * "Z" expression which is substituted with the * RFC-822 style numeric timezone (-0500) * @param format the custom format */ void setCustomFormat(const QString& format); QString getCustomFormat() const; /** * returns rfc2822 formatted string * @param otime time to use for formatting */ - Q3CString rfc2822(time_t otime) const; + QByteArray rfc2822(time_t otime) const; /** * resets the internal clock */ void reset(); //statics /** convenience function dateString * @param t specifies the FormatType to use * @param time time to format * @param data is either the format when FormatType is Custom, or language * when FormatType is Localized * @param shortFormat used only by the Localized format, is passed to KLocale::formatDateTime * @param includeSecs used only by the Localized format, is passed to KLocale::formatDateTime */ static QString formatDate( DateFormatter::FormatType t, time_t time, const QString& data = QString(), bool shortFormat = true, bool includeSecs=false); /** convenience function, same as #formatDate * but returns the current time formatted * @param t specifies the FormatType to use * @param data is either the format when FormatType is Custom, or language * when FormatType is Localized * @param shortFormat used only by the Localized format, is passed to KLocale::formatDateTime * @param includeSecs used only by the Localized format, is passed to KLocale::formatDateTime */ static QString formatCurrentDate( DateFormatter::FormatType t, const QString& data = QString(), bool shortFormat = true, bool includeSecs=false); /** convenience function, same as #rfc2822 */ - static Q3CString rfc2822FormatDate( time_t time ); + static QByteArray rfc2822FormatDate( time_t time ); static bool isDaylight(); protected: /** * returns fancy formatted date string * @param otime time to format * @internal */ QString fancy(time_t otime) const ; /** * returns localized formatted date string * @param otime time to format * @param shortFormat * @param includeSecs * @param localeLanguage language used for formatting * @internal */ QString localized(time_t otime, bool shortFormat = true, bool includeSecs = false, const QString& localeLanguage=QString() ) const; /** * returns string as formatted with ctime function * @internal */ QString cTime(time_t otime) const; /** * returns a string in the "%Y-%m-%d %H:%M:%S" format * @internal */ QString isoDate(time_t otime) const; /** * returns date formatted with the earlier * given custom format * @param t time used for formatting * @internal */ QString custom(time_t t) const; /** * returns a string identifying the timezone (eg."-0500") * @internal */ - Q3CString zone(time_t otime) const; + QByteArray zone(time_t otime) const; time_t qdateToTimeT(const QDateTime& dt) const; private: FormatType mFormat; mutable time_t mCurrentTime; mutable QDateTime mDate; QString mCustomFormat; static int mDaylight; }; } // namespace KMime #endif /* __KMIME_UTIL_H__ */ diff --git a/kmime/kmime_version.h b/kmime/kmime_version.h index fbab89a3f..9873a2d53 100644 --- a/kmime/kmime_version.h +++ b/kmime/kmime_version.h @@ -1,10 +1,10 @@ #ifndef __KMIME_VERSION_H__ #define __KMIME_VERSION_H__ #define KMIME_MAJOR 0; -#define KMIME_MINOR 1; +#define KMIME_MINOR 2; #define KMIME_PATCHLEVEL 0; #define KMIME_VERSION (KMIME_MAJOR * 100 + KMIME_MINOR * 10 + KMIME_PATCHLEVEL) -#define KMIME_VERSION_STRING "0.1.0" +#define KMIME_VERSION_STRING "0.2.0" #endif // __KMIME_VERSION_H__ diff --git a/kmime/kqcstringsplitter.cpp b/kmime/kqcstringsplitter.cpp deleted file mode 100644 index f31fcda51..000000000 --- a/kmime/kqcstringsplitter.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - kqcstringsplitter.cpp - - KNode, the KDE newsreader - Copyright (c) 1999-2001 the KNode authors. - See file AUTHORS for details - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US -*/ - -#include "kqcstringsplitter.h" -//Added by qt3to4: - -KQCStringSplitter::KQCStringSplitter() -{ - reset(); -} - - - - -KQCStringSplitter::~KQCStringSplitter() -{ -} - - - -void KQCStringSplitter::init(const QByteArray &str, const char *s) -{ - sep=s; - src=str; -} - - - -void KQCStringSplitter::init(const char *str, const char *s) -{ - sep=s; - src=str; -} - -bool KQCStringSplitter::first() -{ - /*int plus; - if(incSep) plus=sep.length(); - else plus=0; */ - - start=0; - - end=src.find(sep, start); - - if(end!=-1) { - dst=src.mid(start, end); - return true; - } - else { - start=src.length(); - end=start; - return false; - } - -} - - - -bool KQCStringSplitter::last() -{ - /*int startplus, endplus; - - if(incSep) { - startplus=0; - endplus=sep.length(); - } - else { - startplus=sep.length(); - endplus=0; - }*/ - - end=src.length(); - - start=src.findRev(sep,end); - - if(start!=-1) { - dst=src.mid(start, end-start); - return true; - } - else return false; - - -} - - - -bool KQCStringSplitter::next() -{ - /*int plus; - if(incSep) plus=sep.length(); - else plus=0;*/ - - start=end+1; - - if(start< (int) src.length()) { - - end=src.find(sep, start); - - if(end!=-1) { - dst=src.mid(start, end-start); - } - else { - dst=src.mid(start, src.length()-start); - start=src.length(); - end=src.length(); - } - - return true; - } - else return false; - -} - - - -bool KQCStringSplitter::prev() -{ - /*int startplus, endplus; - - if(incSep) { - startplus=0; - endplus=sep.length(); - } - else { - startplus=sep.length(); - endplus=0; - }*/ - - end=start-1; - - if(end>0) { - - start=src.findRev(sep,end); - - if(start!=-1) - dst=src.mid(start, end-start); - - else { - dst=src.mid(0, end+1); - end=0; - start=0; - } - - return true; - } - else return false; - -} - diff --git a/kmime/kqcstringsplitter.h b/kmime/kqcstringsplitter.h deleted file mode 100644 index fa2e05399..000000000 --- a/kmime/kqcstringsplitter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - kqcstringsplitter.h - - KNode, the KDE newsreader - Copyright (c) 1999-2001 the KNode authors. - See file AUTHORS for details - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US -*/ - -#ifndef KQCSTRINGSPLITTER_H -#define KQCSTRINGSPLITTER_H - -#include - -#include - -class KDE_EXPORT KQCStringSplitter { - - public: - KQCStringSplitter(); - ~KQCStringSplitter(); - - void reset() { start=0; end=0; sep=""; incSep=false;} - - void init(const QByteArray &str, const char *s); - void init(const char *str, const char *s); - void setIncludeSep(bool inc) { incSep=inc; } - - bool first(); - bool last(); - - bool next(); - bool prev(); - - QByteArray& string() { return dst; } - const QByteArray& source() { return src; } - - private: - QByteArray src, dst, sep; - int start,end; - bool incSep; - -}; - -#endif