kmail Library API Documentation

kmmessage.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*- 00002 // kmmessage.cpp 00003 00004 // if you do not want GUI elements in here then set ALLOW_GUI to 0. 00005 #include <config.h> 00006 00007 #define ALLOW_GUI 1 00008 #include "kmmessage.h" 00009 #include "mailinglist-magic.h" 00010 #include "messageproperty.h" 00011 using KMail::MessageProperty; 00012 #include "objecttreeparser.h" 00013 using KMail::ObjectTreeParser; 00014 #include "kmfolderindex.h" 00015 #include "undostack.h" 00016 #include "kmversion.h" 00017 #include "kmidentity.h" 00018 #include "identitymanager.h" 00019 #include "kmkernel.h" 00020 #include "headerstrategy.h" 00021 using KMail::HeaderStrategy; 00022 #include "kmaddrbook.h" 00023 00024 #include <cryptplugwrapperlist.h> 00025 #include <kpgpblock.h> 00026 00027 #include <kapplication.h> 00028 #include <kglobalsettings.h> 00029 #include <kdebug.h> 00030 #include <kconfig.h> 00031 #include <khtml_part.h> 00032 #if KDE_IS_VERSION( 3, 1, 92 ) 00033 #include <kuser.h> 00034 #else 00035 #include <pwd.h> 00036 #endif 00037 00038 #include <qcursor.h> 00039 #include <qtextcodec.h> 00040 #include <qmessagebox.h> 00041 #include <kmime_util.h> 00042 #include <kmime_charfreq.h> 00043 00044 #include <kmime_header_parsing.h> 00045 using KMime::HeaderParsing::parseAddressList; 00046 using namespace KMime::Types; 00047 00048 #include <mimelib/body.h> 00049 #include <mimelib/field.h> 00050 #include <mimelib/mimepp.h> 00051 #include <mimelib/string.h> 00052 #include <assert.h> 00053 #include <sys/time.h> 00054 #include <time.h> 00055 #include <klocale.h> 00056 #include <stdlib.h> 00057 #include <unistd.h> 00058 00059 #if ALLOW_GUI 00060 #include <kmessagebox.h> 00061 #endif 00062 00063 // needed temporarily until KMime is replacing the partNode helper class: 00064 #include "partNode.h" 00065 00066 using namespace KMime; 00067 00068 static DwString emptyString(""); 00069 00070 // Values that are set from the config file with KMMessage::readConfig() 00071 static QString sReplyLanguage, sReplyStr, sReplyAllStr, sIndentPrefixStr; 00072 static bool sSmartQuote, sReplaceSubjPrefix, sReplaceForwSubjPrefix, 00073 sWordWrap; 00074 static int sWrapCol; 00075 static QStringList sReplySubjPrefixes, sForwardSubjPrefixes; 00076 static QStringList sPrefCharsets; 00077 00078 QString KMMessage::sForwardStr; 00079 const HeaderStrategy * KMMessage::sHeaderStrategy = HeaderStrategy::rich(); 00080 00081 //----------------------------------------------------------------------------- 00082 KMMessage::KMMessage(DwMessage* aMsg) 00083 : mMsg(aMsg), 00084 mNeedsAssembly(true), 00085 mDecodeHTML(false), 00086 mOverrideCodec(0), 00087 mFolderOffset( 0 ), 00088 mMsgSize(0), 00089 mMsgLength( 0 ), 00090 mDate( 0 ), 00091 mEncryptionState( KMMsgEncryptionStateUnknown ), 00092 mSignatureState( KMMsgSignatureStateUnknown ), 00093 mMDNSentState( KMMsgMDNStateUnknown ), 00094 mUnencryptedMsg(0), 00095 mLastUpdated( 0 ) 00096 { 00097 } 00098 00099 //----------------------------------------------------------------------------- 00100 KMMessage::KMMessage(KMFolderIndex* parent): KMMsgBase(parent) 00101 { 00102 mNeedsAssembly = FALSE; 00103 mMsg = new DwMessage; 00104 mOverrideCodec = 0; 00105 mDecodeHTML = FALSE; 00106 mMsgSize = 0; 00107 mMsgLength = 0; 00108 mFolderOffset = 0; 00109 mStatus = KMMsgStatusNew; 00110 mEncryptionState = KMMsgEncryptionStateUnknown; 00111 mSignatureState = KMMsgSignatureStateUnknown; 00112 mMDNSentState = KMMsgMDNStateUnknown; 00113 mDate = 0; 00114 mUnencryptedMsg = 0; 00115 mLastUpdated = 0; 00116 } 00117 00118 00119 //----------------------------------------------------------------------------- 00120 KMMessage::KMMessage(KMMsgInfo& msgInfo): KMMsgBase() 00121 { 00122 mNeedsAssembly = FALSE; 00123 mMsg = new DwMessage; 00124 mOverrideCodec = 0; 00125 mDecodeHTML = FALSE; 00126 mMsgSize = msgInfo.msgSize(); 00127 mMsgLength = 0; 00128 mFolderOffset = msgInfo.folderOffset(); 00129 mStatus = msgInfo.status(); 00130 mEncryptionState = msgInfo.encryptionState(); 00131 mSignatureState = msgInfo.signatureState(); 00132 mMDNSentState = msgInfo.mdnSentState(); 00133 mDate = msgInfo.date(); 00134 mFileName = msgInfo.fileName(); 00135 KMMsgBase::assign(&msgInfo); 00136 mUnencryptedMsg = 0; 00137 mLastUpdated = 0; 00138 } 00139 00140 00141 //----------------------------------------------------------------------------- 00142 KMMessage::KMMessage(const KMMessage& other) : 00143 KMMsgBase( other ), 00144 ISubject(), 00145 mMsg(0) 00146 { 00147 mUnencryptedMsg = 0; 00148 mLastUpdated = 0; 00149 assign( other ); 00150 } 00151 00152 void KMMessage::assign( const KMMessage& other ) 00153 { 00154 MessageProperty::forget( this ); 00155 delete mMsg; 00156 delete mUnencryptedMsg; 00157 00158 mNeedsAssembly = true;//other.mNeedsAssembly; 00159 if( other.mMsg ) 00160 mMsg = new DwMessage( *(other.mMsg) ); 00161 mOverrideCodec = other.mOverrideCodec; 00162 mDecodeHTML = other.mDecodeHTML; 00163 Q_UINT32 otherTransfer = MessageProperty::transferInProgress( &other ); 00164 MessageProperty::setTransferInProgress( this, otherTransfer ); 00165 mMsgSize = other.mMsgSize; 00166 mMsgLength = other.mMsgLength; 00167 mFolderOffset = other.mFolderOffset; 00168 mStatus = other.mStatus; 00169 mEncryptionState = other.mEncryptionState; 00170 mSignatureState = other.mSignatureState; 00171 mMDNSentState = other.mMDNSentState; 00172 mDate = other.mDate; 00173 if( other.hasUnencryptedMsg() ) 00174 mUnencryptedMsg = new KMMessage( *other.unencryptedMsg() ); 00175 else 00176 mUnencryptedMsg = 0; 00177 //mFileName = ""; // we might not want to copy the other messages filename (?) 00178 //KMMsgBase::assign( &other ); 00179 } 00180 00181 //----------------------------------------------------------------------------- 00182 KMMessage::~KMMessage() 00183 { 00184 delete mMsg; 00185 kmkernel->undoStack()->msgDestroyed( this ); 00186 } 00187 00188 00189 //----------------------------------------------------------------------------- 00190 void KMMessage::setReferences(const QCString& aStr) 00191 { 00192 if (!aStr) return; 00193 mMsg->Headers().References().FromString(aStr); 00194 mNeedsAssembly = TRUE; 00195 } 00196 00197 00198 //----------------------------------------------------------------------------- 00199 QCString KMMessage::id() const 00200 { 00201 DwHeaders& header = mMsg->Headers(); 00202 if (header.HasMessageId()) 00203 return header.MessageId().AsString().c_str(); 00204 else 00205 return ""; 00206 } 00207 00208 00209 //----------------------------------------------------------------------------- 00210 void KMMessage::setMsgSerNum(unsigned long newMsgSerNum) 00211 { 00212 MessageProperty::setSerialCache( this, newMsgSerNum ); 00213 } 00214 00215 00216 //----------------------------------------------------------------------------- 00217 bool KMMessage::isMessage() const 00218 { 00219 return TRUE; 00220 } 00221 00222 bool KMMessage::isUrgent() const { 00223 return headerField( "Priority" ).contains( "urgent", false ) 00224 || headerField( "X-Priority" ).startsWith( "2" ); 00225 } 00226 00227 //----------------------------------------------------------------------------- 00228 void KMMessage::setUnencryptedMsg( KMMessage* unencrypted ) 00229 { 00230 delete mUnencryptedMsg; 00231 mUnencryptedMsg = unencrypted; 00232 } 00233 00234 //----------------------------------------------------------------------------- 00235 const DwString& KMMessage::asDwString() const 00236 { 00237 if (mNeedsAssembly) 00238 { 00239 mNeedsAssembly = FALSE; 00240 mMsg->Assemble(); 00241 } 00242 return mMsg->AsString(); 00243 } 00244 00245 //----------------------------------------------------------------------------- 00246 const DwMessage *KMMessage::asDwMessage() 00247 { 00248 if (mNeedsAssembly) 00249 { 00250 mNeedsAssembly = FALSE; 00251 mMsg->Assemble(); 00252 } 00253 return mMsg; 00254 } 00255 00256 //----------------------------------------------------------------------------- 00257 QCString KMMessage::asString() const { 00258 return asDwString().c_str(); 00259 } 00260 00261 00262 QCString KMMessage::asSendableString() const 00263 { 00264 KMMessage msg; 00265 msg.fromString(asString()); 00266 msg.removePrivateHeaderFields(); 00267 msg.removeHeaderField("Bcc"); 00268 return msg.asString(); 00269 } 00270 00271 QCString KMMessage::headerAsSendableString() const 00272 { 00273 KMMessage msg; 00274 msg.fromString(asString()); 00275 msg.removePrivateHeaderFields(); 00276 msg.removeHeaderField("Bcc"); 00277 return msg.headerAsString().latin1(); 00278 } 00279 00280 void KMMessage::removePrivateHeaderFields() { 00281 removeHeaderField("Status"); 00282 removeHeaderField("X-Status"); 00283 removeHeaderField("X-KMail-EncryptionState"); 00284 removeHeaderField("X-KMail-SignatureState"); 00285 removeHeaderField("X-KMail-MDN-Sent"); 00286 removeHeaderField("X-KMail-Transport"); 00287 removeHeaderField("X-KMail-Identity"); 00288 removeHeaderField("X-KMail-Fcc"); 00289 removeHeaderField("X-KMail-Redirect-From"); 00290 removeHeaderField("X-KMail-Link-Message"); 00291 removeHeaderField("X-KMail-Link-Type"); 00292 } 00293 00294 //----------------------------------------------------------------------------- 00295 void KMMessage::setStatusFields() 00296 { 00297 char str[2] = { 0, 0 }; 00298 00299 setHeaderField("Status", status() & KMMsgStatusNew ? "R" : "RO"); 00300 setHeaderField("X-Status", statusToStr(status())); 00301 00302 str[0] = (char)encryptionState(); 00303 setHeaderField("X-KMail-EncryptionState", str); 00304 00305 str[0] = (char)signatureState(); 00306 //kdDebug(5006) << "Setting SignatureState header field to " << str[0] << endl; 00307 setHeaderField("X-KMail-SignatureState", str); 00308 00309 str[0] = static_cast<char>( mdnSentState() ); 00310 setHeaderField("X-KMail-MDN-Sent", str); 00311 00312 // We better do the assembling ourselves now to prevent the 00313 // mimelib from changing the message *body*. (khz, 10.8.2002) 00314 mNeedsAssembly = false; 00315 mMsg->Headers().Assemble(); 00316 mMsg->Assemble( mMsg->Headers(), 00317 mMsg->Body() ); 00318 } 00319 00320 00321 //---------------------------------------------------------------------------- 00322 QString KMMessage::headerAsString() const 00323 { 00324 DwHeaders& header = mMsg->Headers(); 00325 header.Assemble(); 00326 if(header.AsString() != "") 00327 return header.AsString().c_str(); 00328 return ""; 00329 } 00330 00331 00332 //----------------------------------------------------------------------------- 00333 DwMediaType& KMMessage::dwContentType() 00334 { 00335 return mMsg->Headers().ContentType(); 00336 } 00337 00338 void KMMessage::fromByteArray( const QByteArray & ba, bool setStatus ) { 00339 return fromDwString( DwString( ba.data(), ba.size() ), setStatus ); 00340 } 00341 00342 void KMMessage::fromString( const QCString & str, bool aSetStatus ) { 00343 return fromDwString( DwString( str.data() ), aSetStatus ); 00344 } 00345 00346 void KMMessage::fromDwString(const DwString& str, bool aSetStatus) 00347 { 00348 delete mMsg; 00349 mMsg = new DwMessage; 00350 mMsg->FromString( str ); 00351 mMsg->Parse(); 00352 00353 if (aSetStatus) { 00354 setStatus(headerField("Status").latin1(), headerField("X-Status").latin1()); 00355 setEncryptionStateChar( headerField("X-KMail-EncryptionState").at(0) ); 00356 setSignatureStateChar( headerField("X-KMail-SignatureState").at(0) ); 00357 setMDNSentState( static_cast<KMMsgMDNSentState>( headerField("X-KMail-MDN-Sent").at(0).latin1() ) ); 00358 } 00359 00360 mNeedsAssembly = FALSE; 00361 mDate = date(); 00362 } 00363 00364 00365 //----------------------------------------------------------------------------- 00366 QString KMMessage::formatString(const QString& aStr) const 00367 { 00368 QString result, str; 00369 QChar ch; 00370 uint j; 00371 00372 if (aStr.isEmpty()) 00373 return aStr; 00374 00375 for (uint i=0; i<aStr.length();) { 00376 ch = aStr[i++]; 00377 if (ch == '%') { 00378 ch = aStr[i++]; 00379 switch ((char)ch) { 00380 case 'D': 00381 /* I'm not too sure about this change. Is it not possible 00382 to have a long form of the date used? I don't 00383 like this change to a short XX/XX/YY date format. 00384 At least not for the default. -sanders */ 00385 result += KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized, 00386 date(), sReplyLanguage, false ); 00387 break; 00388 case 'e': 00389 result += from(); 00390 break; 00391 case 'F': 00392 result += fromStrip(); 00393 break; 00394 case 'f': 00395 str = fromStrip(); 00396 00397 for (j=0; str[j]>' '; j++) 00398 ; 00399 for (; j < str.length() && str[j] <= ' '; j++) 00400 ; 00401 result += str[0]; 00402 if (str[j]>' ') 00403 result += str[j]; 00404 else 00405 if (str[1]>' ') 00406 result += str[1]; 00407 break; 00408 case 'T': 00409 result += toStrip(); 00410 break; 00411 case 't': 00412 result += to(); 00413 break; 00414 case 'C': 00415 result += ccStrip(); 00416 break; 00417 case 'c': 00418 result += cc(); 00419 break; 00420 case 'S': 00421 result += subject(); 00422 break; 00423 case '_': 00424 result += ' '; 00425 break; 00426 case 'L': 00427 result += "\n"; 00428 break; 00429 case '%': 00430 result += '%'; 00431 break; 00432 default: 00433 result += '%'; 00434 result += ch; 00435 break; 00436 } 00437 } else 00438 result += ch; 00439 } 00440 return result; 00441 } 00442 00443 static void removeTrailingSpace( QString &line ) 00444 { 00445 int i = line.length()-1; 00446 while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t'))) 00447 i--; 00448 line.truncate( i+1); 00449 } 00450 00451 static QString splitLine( QString &line) 00452 { 00453 removeTrailingSpace( line ); 00454 int i = 0; 00455 int j = -1; 00456 int l = line.length(); 00457 00458 // TODO: Replace tabs with spaces first. 00459 00460 while(i < l) 00461 { 00462 QChar c = line[i]; 00463 if ((c == '>') || (c == ':') || (c == '|')) 00464 j = i+1; 00465 else if ((c != ' ') && (c != '\t')) 00466 break; 00467 i++; 00468 } 00469 00470 if ( j <= 0 ) 00471 { 00472 return ""; 00473 } 00474 if ( i == l ) 00475 { 00476 QString result = line.left(j); 00477 line = QString::null; 00478 return result; 00479 } 00480 00481 QString result = line.left(j); 00482 line = line.mid(j); 00483 return result; 00484 } 00485 00486 static QString flowText(QString &text, const QString& indent, int maxLength) 00487 { 00488 maxLength--; 00489 if (text.isEmpty()) 00490 { 00491 return indent+"<NULL>\n"; 00492 } 00493 QString result; 00494 while (1) 00495 { 00496 int i; 00497 if ((int) text.length() > maxLength) 00498 { 00499 i = maxLength; 00500 while( (i >= 0) && (text[i] != ' ')) 00501 i--; 00502 if (i <= 0) 00503 { 00504 // Couldn't break before maxLength. 00505 i = maxLength; 00506 // while( (i < (int) text.length()) && (text[i] != ' ')) 00507 // i++; 00508 } 00509 } 00510 else 00511 { 00512 i = text.length(); 00513 } 00514 00515 QString line = text.left(i); 00516 if (i < (int) text.length()) 00517 text = text.mid(i); 00518 else 00519 text = QString::null; 00520 00521 result += indent + line + '\n'; 00522 00523 if (text.isEmpty()) 00524 return result; 00525 } 00526 } 00527 00528 static bool flushPart(QString &msg, QStringList &part, 00529 const QString &indent, int maxLength) 00530 { 00531 maxLength -= indent.length(); 00532 if (maxLength < 20) maxLength = 20; 00533 00534 // Remove empty lines at end of quote 00535 while ((part.begin() != part.end()) && part.last().isEmpty()) 00536 { 00537 part.remove(part.fromLast()); 00538 } 00539 00540 QString text; 00541 for(QStringList::Iterator it2 = part.begin(); 00542 it2 != part.end(); 00543 it2++) 00544 { 00545 QString line = (*it2); 00546 00547 if (line.isEmpty()) 00548 { 00549 if (!text.isEmpty()) 00550 msg += flowText(text, indent, maxLength); 00551 msg += indent + '\n'; 00552 } 00553 else 00554 { 00555 if (text.isEmpty()) 00556 text = line; 00557 else 00558 text += ' '+line.stripWhiteSpace(); 00559 00560 if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10))) 00561 msg += flowText(text, indent, maxLength); 00562 } 00563 } 00564 if (!text.isEmpty()) 00565 msg += flowText(text, indent, maxLength); 00566 00567 bool appendEmptyLine = true; 00568 if (!part.count()) 00569 appendEmptyLine = false; 00570 00571 part.clear(); 00572 return appendEmptyLine; 00573 } 00574 00575 static QString stripSignature( const QString & msg, bool clearSigned ) { 00576 if ( clearSigned ) 00577 return msg.left( msg.findRev( QRegExp( "\n--\\s?\n" ) ) ); 00578 else 00579 return msg.left( msg.findRev( "\n-- \n" ) ); 00580 } 00581 00582 static QString smartQuote( const QString & msg, int maxLength ) 00583 { 00584 QStringList part; 00585 QString oldIndent; 00586 bool firstPart = true; 00587 00588 00589 const QStringList lines = QStringList::split('\n', msg, true); 00590 00591 QString result; 00592 for(QStringList::const_iterator it = lines.begin(); 00593 it != lines.end(); 00594 ++it) 00595 { 00596 QString line = *it; 00597 00598 const QString indent = splitLine( line ); 00599 00600 if ( line.isEmpty()) 00601 { 00602 if (!firstPart) 00603 part.append(QString::null); 00604 continue; 00605 }; 00606 00607 if (firstPart) 00608 { 00609 oldIndent = indent; 00610 firstPart = false; 00611 } 00612 00613 if (oldIndent != indent) 00614 { 00615 QString fromLine; 00616 // Search if the last non-blank line could be "From" line 00617 if (part.count() && (oldIndent.length() < indent.length())) 00618 { 00619 QStringList::Iterator it2 = part.fromLast(); 00620 while( (it2 != part.end()) && (*it2).isEmpty()) 00621 --it2; 00622 00623 if ((it2 != part.end()) && ((*it2).endsWith(":"))) 00624 { 00625 fromLine = oldIndent + (*it2) + '\n'; 00626 part.remove(it2); 00627 } 00628 } 00629 if (flushPart( result, part, oldIndent, maxLength)) 00630 { 00631 if (oldIndent.length() > indent.length()) 00632 result += indent + '\n'; 00633 else 00634 result += oldIndent + '\n'; 00635 } 00636 if (!fromLine.isEmpty()) 00637 { 00638 result += fromLine; 00639 } 00640 oldIndent = indent; 00641 } 00642 part.append(line); 00643 } 00644 flushPart( result, part, oldIndent, maxLength); 00645 return result; 00646 } 00647 00648 00649 //----------------------------------------------------------------------------- 00650 void KMMessage::parseTextStringFromDwPart( DwBodyPart * mainBody, 00651 DwBodyPart * firstBodyPart, 00652 QCString& parsedString, 00653 const QTextCodec*& codec, 00654 bool& isHTML ) const 00655 { 00656 // get a valid CryptPlugList 00657 CryptPlugWrapperList cryptPlugList; 00658 KConfig *config = KMKernel::config(); 00659 cryptPlugList.loadFromConfig( config ); 00660 00661 isHTML = false; 00662 int mainType = type(); 00663 int mainSubType = subtype(); 00664 if( (DwMime::kTypeNull == mainType) 00665 || (DwMime::kTypeUnknown == mainType) ){ 00666 mainType = DwMime::kTypeText; 00667 mainSubType = DwMime::kSubtypePlain; 00668 } 00669 partNode rootNode( mainBody, mainType, mainSubType); 00670 if ( firstBodyPart ) { 00671 partNode * curNode = new partNode( firstBodyPart ); 00672 rootNode.setFirstChild( curNode ); 00673 curNode->buildObjectTree(); 00674 } 00675 // initialy parse the complete message to decrypt any encrypted parts 00676 { 00677 ObjectTreeParser otp( 0, 0, true, false, true ); 00678 otp.parseObjectTree( &rootNode ); 00679 } 00680 partNode * curNode = rootNode.findType( DwMime::kTypeText, 00681 DwMime::kSubtypeUnknown, 00682 true, 00683 false ); 00684 kdDebug(5006) << "\n\n======= KMMessage::parseTextStringFromDwPart() - " 00685 << ( curNode ? "text part found!\n" : "sorry, no text node!\n" ) << endl; 00686 if( curNode ) { 00687 isHTML = DwMime::kSubtypeHtml == curNode->subType(); 00688 // now parse the TEXT message part we want to quote 00689 ObjectTreeParser otp( 0, 0, true, false, true ); 00690 otp.parseObjectTree( curNode ); 00691 parsedString = otp.rawReplyString(); 00692 codec = curNode->msgPart().codec(); 00693 } 00694 } 00695 00696 //----------------------------------------------------------------------------- 00697 00698 QString KMMessage::asPlainText( bool aStripSignature, bool allowDecryption ) const { 00699 QCString parsedString; 00700 bool isHTML = false; 00701 const QTextCodec * codec = 0; 00702 00703 if ( numBodyParts() == 0 ) { 00704 DwBodyPart * mainBody = 0; 00705 DwBodyPart * firstBodyPart = getFirstDwBodyPart(); 00706 if ( !firstBodyPart ) { 00707 mainBody = new DwBodyPart( asDwString(), 0 ); 00708 mainBody->Parse(); 00709 } 00710 parseTextStringFromDwPart( mainBody, firstBodyPart, parsedString, codec, 00711 isHTML ); 00712 } else { 00713 DwBodyPart * dwPart = getFirstDwBodyPart(); 00714 if ( dwPart ) 00715 parseTextStringFromDwPart( 0, dwPart, parsedString, codec, isHTML ); 00716 } 00717 00718 if ( mOverrideCodec || !codec ) 00719 codec = this->codec(); 00720 00721 if ( parsedString.isEmpty() ) 00722 return QString::null; 00723 00724 bool clearSigned = false; 00725 QString result; 00726 00727 // decrypt 00728 if ( allowDecryption ) { 00729 QPtrList<Kpgp::Block> pgpBlocks; 00730 QStrList nonPgpBlocks; 00731 if ( Kpgp::Module::prepareMessageForDecryption( parsedString, 00732 pgpBlocks, 00733 nonPgpBlocks ) ) { 00734 // Only decrypt/strip off the signature if there is only one OpenPGP 00735 // block in the message 00736 if ( pgpBlocks.count() == 1 ) { 00737 Kpgp::Block * block = pgpBlocks.first(); 00738 if ( block->type() == Kpgp::PgpMessageBlock || 00739 block->type() == Kpgp::ClearsignedBlock ) { 00740 if ( block->type() == Kpgp::PgpMessageBlock ) { 00741 // try to decrypt this OpenPGP block 00742 block->decrypt(); 00743 } else { 00744 // strip off the signature 00745 block->verify(); 00746 clearSigned = true; 00747 } 00748 00749 result = codec->toUnicode( nonPgpBlocks.first() ) 00750 + codec->toUnicode( block->text() ) 00751 + codec->toUnicode( nonPgpBlocks.last() ); 00752 } 00753 } 00754 } 00755 } 00756 00757 if ( result.isEmpty() ) { 00758 result = codec->toUnicode( parsedString ); 00759 if ( result.isEmpty() ) 00760 return result; 00761 } 00762 00763 // html -> plaintext conversion, if necessary: 00764 if ( isHTML && mDecodeHTML ) { 00765 KHTMLPart htmlPart; 00766 htmlPart.setOnlyLocalReferences( true ); 00767 htmlPart.setMetaRefreshEnabled( false ); 00768 htmlPart.setPluginsEnabled( false ); 00769 htmlPart.setJScriptEnabled( false ); 00770 htmlPart.setJavaEnabled( false ); 00771 htmlPart.begin(); 00772 htmlPart.write( result ); 00773 htmlPart.end(); 00774 htmlPart.selectAll(); 00775 result = htmlPart.selectedText(); 00776 } 00777 00778 // strip the signature (footer): 00779 if ( aStripSignature ) 00780 return stripSignature( result, clearSigned ); 00781 else 00782 return result; 00783 } 00784 00785 QString KMMessage::asQuotedString( const QString& aHeaderStr, 00786 const QString& aIndentStr, 00787 const QString& selection /* = QString::null */, 00788 bool aStripSignature /* = true */, 00789 bool allowDecryption /* = true */) const 00790 { 00791 QString content = selection.isEmpty() ? 00792 asPlainText( aStripSignature, allowDecryption ) : selection ; 00793 00794 // Remove blank lines at the beginning: 00795 const int firstNonWS = content.find( QRegExp( "\\S" ) ); 00796 const int lineStart = content.findRev( '\n', firstNonWS ); 00797 if ( lineStart >= 0 ) 00798 content.remove( 0, static_cast<unsigned int>( lineStart ) ); 00799 00800 const QString indentStr = formatString( aIndentStr ); 00801 00802 content.replace( '\n', '\n' + indentStr ); 00803 content.prepend( indentStr ); 00804 content += '\n'; 00805 00806 const QString headerStr = formatString( aHeaderStr ); 00807 if ( sSmartQuote && sWordWrap ) 00808 return headerStr + smartQuote( content, sWrapCol ); 00809 else 00810 return headerStr + content; 00811 } 00812 00813 //----------------------------------------------------------------------------- 00814 // static 00815 QString KMMessage::stripOffPrefixes( const QString& str ) 00816 { 00817 return replacePrefixes( str, sReplySubjPrefixes + sForwardSubjPrefixes, 00818 true, QString::null ).stripWhiteSpace(); 00819 } 00820 00821 //----------------------------------------------------------------------------- 00822 // static 00823 QString KMMessage::replacePrefixes( const QString& str, 00824 const QStringList& prefixRegExps, 00825 bool replace, 00826 const QString& newPrefix ) 00827 { 00828 bool recognized = false; 00829 // construct a big regexp that 00830 // 1. is anchored to the beginning of str (sans whitespace) 00831 // 2. matches at least one of the part regexps in prefixRegExps 00832 QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*") 00833 .arg( prefixRegExps.join(")|(?:") ); 00834 QRegExp rx( bigRegExp, false /*case insens.*/ ); 00835 if ( !rx.isValid() ) { 00836 kdWarning(5006) << "KMMessage::replacePrefixes(): bigRegExp = \"" 00837 << bigRegExp << "\"\n" 00838 << "prefix regexp is invalid!" << endl; 00839 // try good ole Re/Fwd: 00840 recognized = str.startsWith( newPrefix ); 00841 } else { // valid rx 00842 QString tmp = str; 00843 if ( rx.search( tmp ) == 0 ) { 00844 recognized = true; 00845 if ( replace ) 00846 return tmp.replace( 0, rx.matchedLength(), newPrefix + ' ' ); 00847 } 00848 } 00849 if ( !recognized ) 00850 return newPrefix + ' ' + str; 00851 else 00852 return str; 00853 } 00854 00855 //----------------------------------------------------------------------------- 00856 QString KMMessage::cleanSubject() const 00857 { 00858 return cleanSubject( sReplySubjPrefixes + sForwardSubjPrefixes, 00859 true, QString::null ).stripWhiteSpace(); 00860 } 00861 00862 //----------------------------------------------------------------------------- 00863 QString KMMessage::cleanSubject( const QStringList & prefixRegExps, 00864 bool replace, 00865 const QString & newPrefix ) const 00866 { 00867 return KMMessage::replacePrefixes( subject(), prefixRegExps, replace, 00868 newPrefix ); 00869 } 00870 00871 //----------------------------------------------------------------------------- 00872 KMMessage* KMMessage::createReply( KMail::ReplyStrategy replyStrategy, 00873 QString selection /* = QString::null */, 00874 bool noQuote /* = false */, 00875 bool allowDecryption /* = true */, 00876 bool selectionIsBody /* = false */) 00877 { 00878 KMMessage* msg = new KMMessage; 00879 QString str, replyStr, mailingListStr, replyToStr, toStr; 00880 QStringList mailingListAddresses; 00881 QCString refStr, headerName; 00882 00883 msg->initFromMessage(this); 00884 00885 KMMLInfo::name(this, headerName, mailingListStr); 00886 replyToStr = replyTo(); 00887 00888 msg->setCharset("utf-8"); 00889 00890 // determine the mailing list posting address 00891 if ( parent() && parent()->isMailingList() && 00892 !parent()->mailingListPostAddress().isEmpty() ) { 00893 mailingListAddresses << parent()->mailingListPostAddress(); 00894 } 00895 if ( headerField("List-Post").find( "mailto:", 0, false ) != -1 ) { 00896 QString listPost = headerField("List-Post"); 00897 QRegExp rx( "<mailto:([^@>]+)@([^>]+)>", false ); 00898 if ( rx.search( listPost, 0 ) != -1 ) // matched 00899 mailingListAddresses << rx.cap(1) + '@' + rx.cap(2); 00900 } 00901 00902 // use the "On ... Joe User wrote:" header by default 00903 replyStr = sReplyAllStr; 00904 00905 switch( replyStrategy ) { 00906 case KMail::ReplySmart : { 00907 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) { 00908 toStr = headerField( "Mail-Followup-To" ); 00909 } 00910 else if ( !replyToStr.isEmpty() ) { 00911 // assume a Reply-To header mangling mailing list 00912 toStr = replyToStr; 00913 } 00914 else if ( !mailingListAddresses.isEmpty() ) { 00915 toStr = mailingListAddresses[0]; 00916 } 00917 else { 00918 // doesn't seem to be a mailing list, reply to From: address 00919 toStr = from(); 00920 replyStr = sReplyStr; // reply to author, so use "On ... you wrote:" 00921 } 00922 // strip all my addresses from the list of recipients 00923 QStringList recipients = splitEmailAddrList( toStr ); 00924 toStr = stripMyAddressesFromAddressList( recipients ).join(", "); 00925 // ... unless the list contains only my addresses (reply to self) 00926 if ( toStr.isEmpty() && !recipients.isEmpty() ) 00927 toStr = recipients[0]; 00928 00929 break; 00930 } 00931 case KMail::ReplyList : { 00932 if ( !headerField( "Mail-Followup-To" ).isEmpty() ) { 00933 toStr = headerField( "Mail-Followup-To" ); 00934 } 00935 else if ( !mailingListAddresses.isEmpty() ) { 00936 toStr = mailingListAddresses[0]; 00937 } 00938 else if ( !replyToStr.isEmpty() ) { 00939 // assume a Reply-To header mangling mailing list 00940 toStr = replyToStr; 00941 } 00942 // strip all my addresses from the list of recipients 00943 QStringList recipients = splitEmailAddrList( toStr ); 00944 toStr = stripMyAddressesFromAddressList( recipients ).join(", "); 00945 00946 break; 00947 } 00948 case KMail::ReplyAll : { 00949 QStringList recipients; 00950 QStringList ccRecipients; 00951 00952 // add addresses from the Reply-To header to the list of recipients 00953 if( !replyToStr.isEmpty() ) { 00954 recipients += splitEmailAddrList( replyToStr ); 00955 // strip all possible mailing list addresses from the list of Reply-To 00956 // addresses 00957 for ( QStringList::const_iterator it = mailingListAddresses.begin(); 00958 it != mailingListAddresses.end(); 00959 ++it ) { 00960 recipients = stripAddressFromAddressList( *it, recipients ); 00961 } 00962 } 00963 00964 if ( !mailingListAddresses.isEmpty() ) { 00965 // this is a mailing list message 00966 if ( recipients.isEmpty() && !from().isEmpty() ) { 00967 // The sender didn't set a Reply-to address, so we add the From 00968 // address to the list of CC recipients. 00969 ccRecipients += from(); 00970 kdDebug(5006) << "Added " << from() << " to the list of CC recipients" 00971 << endl; 00972 } 00973 // if it is a mailing list, add the posting address 00974 recipients.prepend( mailingListAddresses[0] ); 00975 } 00976 else { 00977 // this is a normal message 00978 if ( recipients.isEmpty() && !from().isEmpty() ) { 00979 // in case of replying to a normal message only then add the From 00980 // address to the list of recipients if there was no Reply-to address 00981 recipients += from(); 00982 kdDebug(5006) << "Added " << from() << " to the list of recipients" 00983 << endl; 00984 } 00985 } 00986 00987 // strip all my addresses from the list of recipients 00988 toStr = stripMyAddressesFromAddressList( recipients ).join(", "); 00989 00990 // merge To header and CC header into a list of CC recipients 00991 if( !cc().isEmpty() || !to().isEmpty() ) { 00992 QStringList list; 00993 if (!to().isEmpty()) 00994 list += splitEmailAddrList(to()); 00995 if (!cc().isEmpty()) 00996 list += splitEmailAddrList(cc()); 00997 for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { 00998 if( !addressIsInAddressList( *it, recipients ) 00999 && !addressIsInAddressList( *it, ccRecipients ) ) { 01000 ccRecipients += *it; 01001 kdDebug(5006) << "Added " << *it << " to the list of CC recipients" 01002 << endl; 01003 } 01004 } 01005 } 01006 01007 if ( !ccRecipients.isEmpty() ) { 01008 // strip all my addresses from the list of CC recipients 01009 ccRecipients = stripMyAddressesFromAddressList( ccRecipients ); 01010 01011 // in case of a reply to self toStr might be empty. if that's the case 01012 // then propagate a cc recipient to To: (if there is any). 01013 if ( toStr.isEmpty() && !ccRecipients.isEmpty() ) { 01014 toStr = ccRecipients[0]; 01015 ccRecipients.pop_front(); 01016 } 01017 01018 msg->setCc( ccRecipients.join(", ") ); 01019 } 01020 01021 if ( toStr.isEmpty() && !recipients.isEmpty() ) { 01022 // reply to self without other recipients 01023 toStr = recipients[0]; 01024 } 01025 break; 01026 } 01027 case KMail::ReplyAuthor : { 01028 if ( !replyToStr.isEmpty() ) { 01029 QStringList recipients = splitEmailAddrList( replyToStr ); 01030 // strip the mailing list post address from the list of Reply-To 01031 // addresses since we want to reply in private 01032 for ( QStringList::const_iterator it = mailingListAddresses.begin(); 01033 it != mailingListAddresses.end(); 01034 ++it ) { 01035 recipients = stripAddressFromAddressList( *it, recipients ); 01036 } 01037 if ( !recipients.isEmpty() ) { 01038 toStr = recipients.join(", "); 01039 } 01040 else { 01041 // there was only the mailing list post address in the Reply-To header, 01042 // so use the From address instead 01043 toStr = from(); 01044 } 01045 } 01046 else if ( !from().isEmpty() ) { 01047 toStr = from(); 01048 } 01049 replyStr = sReplyStr; // reply to author, so use "On ... you wrote:" 01050 break; 01051 } 01052 case KMail::ReplyNone : { 01053 // the addressees will be set by the caller 01054 } 01055 } 01056 01057 msg->setTo(toStr); 01058 01059 refStr = getRefStr(); 01060 if (!refStr.isEmpty()) 01061 msg->setReferences(refStr); 01062 //In-Reply-To = original msg-id 01063 msg->setReplyToId(msgId()); 01064 01065 if (!noQuote) { 01066 if( selectionIsBody ){ 01067 QCString cStr = selection.latin1(); 01068 msg->setBody( cStr ); 01069 }else{ 01070 msg->setBody(asQuotedString(replyStr + "\n", sIndentPrefixStr, selection, 01071 sSmartQuote, allowDecryption).utf8()); 01072 } 01073 } 01074 01075 msg->setSubject(cleanSubject(sReplySubjPrefixes, sReplaceSubjPrefix, "Re:")); 01076 01077 // setStatus(KMMsgStatusReplied); 01078 msg->link(this, KMMsgStatusReplied); 01079 01080 // replies to an encrypted message should be encrypted as well 01081 if ( encryptionState() == KMMsgPartiallyEncrypted || 01082 encryptionState() == KMMsgFullyEncrypted ) { 01083 msg->setEncryptionState( KMMsgFullyEncrypted ); 01084 } 01085 01086 return msg; 01087 } 01088 01089 01090 //----------------------------------------------------------------------------- 01091 QCString KMMessage::getRefStr() const 01092 { 01093 QCString firstRef, lastRef, refStr, retRefStr; 01094 int i, j; 01095 01096 refStr = headerField("References").stripWhiteSpace().latin1(); 01097 01098 if (refStr.isEmpty()) 01099 return headerField("Message-Id").latin1(); 01100 01101 i = refStr.find('<'); 01102 j = refStr.find('>'); 01103 firstRef = refStr.mid(i, j-i+1); 01104 if (!firstRef.isEmpty()) 01105 retRefStr = firstRef + ' '; 01106 01107 i = refStr.findRev('<'); 01108 j = refStr.findRev('>'); 01109 01110 lastRef = refStr.mid(i, j-i+1); 01111 if (!lastRef.isEmpty() && lastRef != firstRef) 01112 retRefStr += lastRef + ' '; 01113 01114 retRefStr += headerField("Message-Id").latin1(); 01115 return retRefStr; 01116 } 01117 01118 01119 KMMessage* KMMessage::createRedirect() 01120 { 01121 KMMessage* msg = new KMMessage; 01122 KMMessagePart msgPart; 01123 int i; 01124 01125 msg->initFromMessage(this); 01126 01130 01131 QString st = asQuotedString("", "", QString::null, false, false); 01132 QCString encoding = autoDetectCharset(charset(), sPrefCharsets, st); 01133 if (encoding.isEmpty()) encoding = "utf-8"; 01134 QCString str = codecForName(encoding)->fromUnicode(st); 01135 01136 msg->setCharset(encoding); 01137 msg->setBody(str); 01138 01139 if (numBodyParts() > 0) 01140 { 01141 msgPart.setBody(str); 01142 msgPart.setTypeStr("text"); 01143 msgPart.setSubtypeStr("plain"); 01144 msgPart.setCharset(encoding); 01145 msg->addBodyPart(&msgPart); 01146 01147 for (i = 0; i < numBodyParts(); i++) 01148 { 01149 bodyPart(i, &msgPart); 01150 if ((qstricmp(msgPart.contentDisposition(),"inline")!=0 && i > 0) || 01151 (qstricmp(msgPart.typeStr(),"text")!=0 && 01152 qstricmp(msgPart.typeStr(),"message")!=0)) 01153 { 01154 msg->addBodyPart(&msgPart); 01155 } 01156 } 01157 } 01158 01159 //TODO: insert sender here 01160 msg->setHeaderField("X-KMail-Redirect-From", from()); 01161 msg->setSubject(subject()); 01162 msg->setFrom(from()); 01163 msg->cleanupHeader(); 01164 01165 // setStatus(KMMsgStatusForwarded); 01166 msg->link(this, KMMsgStatusForwarded); 01167 01168 return msg; 01169 } 01170 01171 #if ALLOW_GUI 01172 KMMessage* KMMessage::createBounce( bool withUI ) 01173 #else 01174 KMMessage* KMMessage::createBounce( bool ) 01175 #endif 01176 { 01177 QString fromStr, bodyStr, senderStr; 01178 int atIdx, i; 01179 01180 const char* fromFields[] = { "Errors-To", "Return-Path", "Resent-From", 01181 "Resent-Sender", "From", "Sender", 0 }; 01182 01183 // Find email address of sender 01184 for (i=0; fromFields[i]; i++) 01185 { 01186 senderStr = headerField(fromFields[i]); 01187 if (!senderStr.isEmpty()) break; 01188 } 01189 if (senderStr.isEmpty()) 01190 { 01191 #if ALLOW_GUI 01192 if ( withUI ) 01193 KMessageBox::sorry(0 /*app-global modal*/, 01194 i18n("The message has no sender set"), 01195 i18n("Bounce Message")); 01196 #endif 01197 return 0; 01198 } 01199 01200 QString receiver = headerField("Received"); 01201 int a = -1, b = -1; 01202 a = receiver.find("from"); 01203 if (a != -1) a = receiver.find("by", a); 01204 if (a != -1) a = receiver.find("for", a); 01205 if (a != -1) a = receiver.find('<', a); 01206 if (a != -1) b = receiver.find('>', a); 01207 if (a != -1 && b != -1) receiver = receiver.mid(a+1, b-a-1); 01208 else receiver = getEmailAddr(to()); 01209 01210 #if ALLOW_GUI 01211 if ( withUI ) 01212 // No composer appears. So better ask before sending. 01213 if (KMessageBox::warningContinueCancel(0 /*app-global modal*/, 01214 i18n("Return the message to the sender as undeliverable?\n" 01215 "This will only work if the email address of the sender, " 01216 "%1, is valid.\n" 01217 "The failing address will be reported to be %2.") 01218 .arg(senderStr).arg(receiver), 01219 i18n("Bounce Message"), KStdGuiItem::cont()) == KMessageBox::Cancel) 01220 { 01221 return 0; 01222 } 01223 #endif 01224 01225 KMMessage *msg = new KMMessage; 01226 msg->initFromMessage(this, FALSE); 01227 msg->setTo( senderStr ); 01228 msg->setDateToday(); 01229 msg->setSubject( "mail failed, returning to sender" ); 01230 01231 fromStr = receiver; 01232 atIdx = fromStr.find('@'); 01233 msg->setFrom( fromStr.replace( 0, atIdx, "MAILER-DAEMON" ) ); 01234 msg->setReferences( id() ); 01235 01236 bodyStr = "|------------------------- Message log follows: -------------------------|\n" 01237 "no valid recipients were found for this message\n" 01238 "|------------------------- Failed addresses follow: ---------------------|\n"; 01239 bodyStr += receiver; 01240 bodyStr += "\n|------------------------- Message text follows: ------------------------|\n"; 01241 bodyStr += asSendableString(); 01242 01243 msg->setBody( bodyStr.latin1() ); 01244 msg->cleanupHeader(); 01245 01246 return msg; 01247 } 01248 01249 01250 //----------------------------------------------------------------------------- 01251 QCString KMMessage::createForwardBody() 01252 { 01253 QString s; 01254 QCString str; 01255 01256 if (sHeaderStrategy == HeaderStrategy::all()) { 01257 s = "\n\n---------- " + sForwardStr + " ----------\n\n"; 01258 s += headerAsString(); 01259 str = asQuotedString(s, "", QString::null, false, false).utf8(); 01260 str += "\n-------------------------------------------------------\n"; 01261 } else { 01262 s = "\n\n---------- " + sForwardStr + " ----------\n\n"; 01263 s += "Subject: " + subject() + "\n"; 01264 s += "Date: " 01265 + KMime::DateFormatter::formatDate( KMime::DateFormatter::Localized, 01266 date(), sReplyLanguage, false ) 01267 + "\n"; 01268 s += "From: " + from() + "\n"; 01269 s += "To: " + to() + "\n"; 01270 if (!cc().isEmpty()) s += "Cc: " + cc() + "\n"; 01271 s += "\n"; 01272 str = asQuotedString(s, "", QString::null, false, false).utf8(); 01273 str += "\n-------------------------------------------------------\n"; 01274 } 01275 01276 return str; 01277 } 01278 01279 //----------------------------------------------------------------------------- 01280 KMMessage* KMMessage::createForward() 01281 { 01282 KMMessage* msg = new KMMessage; 01283 KMMessagePart msgPart; 01284 QString id; 01285 int i; 01286 01287 msg->initFromMessage(this); 01288 01289 QString st = QString::fromUtf8(createForwardBody()); 01290 QCString encoding = autoDetectCharset(charset(), sPrefCharsets, st); 01291 if (encoding.isEmpty()) encoding = "utf-8"; 01292 QCString str = codecForName(encoding)->fromUnicode(st); 01293 01294 msg->setCharset(encoding); 01295 msg->setBody(str); 01296 01297 if (numBodyParts() > 0) 01298 { 01299 msgPart.setTypeStr("text"); 01300 msgPart.setSubtypeStr("plain"); 01301 msgPart.setCharset(encoding); 01302 msgPart.setBody(str); 01303 msg->addBodyPart(&msgPart); 01304 01305 for (i = 0; i < numBodyParts(); i++) 01306 { 01307 bodyPart(i, &msgPart); 01308 QCString mimeType = msgPart.typeStr().lower() + '/' 01309 + msgPart.subtypeStr().lower(); 01310 // don't add the detached signature as attachment when forwarding a 01311 // PGP/MIME signed message inline 01312 if( mimeType != "application/pgp-signature" ) { 01313 if (i > 0 || qstricmp(msgPart.typeStr(),"text") != 0) 01314 msg->addBodyPart(&msgPart); 01315 } 01316 } 01317 } 01318 01319 msg->setSubject(cleanSubject(sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:")); 01320 01321 msg->cleanupHeader(); 01322 01323 // setStatus(KMMsgStatusForwarded); 01324 msg->link(this, KMMsgStatusForwarded); 01325 01326 return msg; 01327 } 01328 01329 static const struct { 01330 const char * dontAskAgainID; 01331 bool canDeny; 01332 const char * text; 01333 } mdnMessageBoxes[] = { 01334 { "mdnNormalAsk", true, 01335 I18N_NOOP("This message contains a request to send a disposition " 01336 "notification.\n" 01337 "You can either ignore the request or let KMail send a " 01338 "\"denied\" or normal response.") }, 01339 { "mdnUnknownOption", false, 01340 I18N_NOOP("This message contains a request to send a disposition " 01341 "notification.\n" 01342 "It contains a processing instruction that is marked as " 01343 "\"required\", but which is unknown to KMail.\n" 01344 "You can either ignore the request or let KMail send a " 01345 "\"failed\" response.") }, 01346 { "mdnMultipleAddressesInReceiptTo", true, 01347 I18N_NOOP("This message contains a request to send a disposition " 01348 "notification,\n" 01349 "but it is requested to send the notification to more " 01350 "than one address.\n" 01351 "You can either ignore the request or let KMail send a " 01352 "\"denied\" or normal response.") }, 01353 { "mdnReturnPathEmpty", true, 01354 I18N_NOOP("This message contains a request to send a disposition " 01355 "notification,\n" 01356 "but there is no return-path set.\n" 01357 "You can either ignore the request or let KMail send a " 01358 "\"denied\" or normal response.") }, 01359 { "mdnReturnPathNotInReceiptTo", true, 01360 I18N_NOOP("This message contains a request to send a disposition " 01361 "notification,\n" 01362 "but the return-path address differs from the address " 01363 "the notification was requested to be sent to.\n" 01364 "You can either ignore the request or let KMail send a " 01365 "\"denied\" or normal response.") }, 01366 }; 01367 01368 static const int numMdnMessageBoxes 01369 = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes; 01370 01371 01372 static int requestAdviceOnMDN( const char * what ) { 01373 for ( int i = 0 ; i < numMdnMessageBoxes ; ++i ) 01374 if ( !qstrcmp( what, mdnMessageBoxes[i].dontAskAgainID ) ) 01375 if ( mdnMessageBoxes[i].canDeny ) { 01376 int answer = QMessageBox::information( 0, 01377 i18n("Message Disposition Notification Request"), 01378 i18n( mdnMessageBoxes[i].text ), 01379 i18n("&Ignore"), i18n("Send \"&denied\""), i18n("&Send") ); 01380 return answer ? answer + 1 : 0 ; // map to "mode" in createMDN 01381 } else { 01382 int answer = QMessageBox::information( 0, 01383 i18n("Message Disposition Notification Request"), 01384 i18n( mdnMessageBoxes[i].text ), 01385 i18n("&Ignore"), i18n("&Send") ); 01386 return answer ? answer + 2 : 0 ; // map to "mode" in createMDN 01387 } 01388 kdWarning(5006) << "didn't find data for message box \"" 01389 << what << "\"" << endl; 01390 return 0; 01391 } 01392 01393 KMMessage* KMMessage::createMDN( MDN::ActionMode a, 01394 MDN::DispositionType d, 01395 bool allowGUI, 01396 QValueList<MDN::DispositionModifier> m ) 01397 { 01398 // RFC 2298: At most one MDN may be issued on behalf of each 01399 // particular recipient by their user agent. That is, once an MDN 01400 // has been issued on behalf of a recipient, no further MDNs may be 01401 // issued on behalf of that recipient, even if another disposition 01402 // is performed on the message. 01403 //#define MDN_DEBUG 1 01404 #ifndef MDN_DEBUG 01405 if ( mdnSentState() != KMMsgMDNStateUnknown && 01406 mdnSentState() != KMMsgMDNNone ) 01407 return 0; 01408 #else 01409 char st[2]; st[0] = (char)mdnSentState(); st[1] = 0; 01410 kdDebug(5006) << "mdnSentState() == '" << st << "'" << endl; 01411 #endif 01412 01413 // RFC 2298: An MDN MUST NOT be generated in response to an MDN. 01414 if ( findDwBodyPart( DwMime::kTypeMessage, 01415 DwMime::kSubtypeDispositionNotification ) ) { 01416 setMDNSentState( KMMsgMDNIgnore ); 01417 return 0; 01418 } 01419 01420 // extract where to send to: 01421 QString receiptTo = headerField("Disposition-Notification-To"); 01422 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0; 01423 receiptTo.remove( '\n' ); 01424 01425 01426 MDN::SendingMode s = MDN::SentAutomatically; // set to manual if asked user 01427 QString special; // fill in case of error, warning or failure 01428 KConfigGroup mdnConfig( KGlobal::config(), "MDN" ); 01429 01430 // default: 01431 int mode = mdnConfig.readNumEntry( "default-policy", 0 ); 01432 if ( !mode || mode < 0 || mode > 3 ) { 01433 // early out for ignore: 01434 setMDNSentState( KMMsgMDNIgnore ); 01435 return 0; 01436 } 01437 01438 // RFC 2298: An importance of "required" indicates that 01439 // interpretation of the parameter is necessary for proper 01440 // generation of an MDN in response to this request. If a UA does 01441 // not understand the meaning of the parameter, it MUST NOT generate 01442 // an MDN with any disposition type other than "failed" in response 01443 // to the request. 01444 QString notificationOptions = headerField("Disposition-Notification-Options"); 01445 if ( notificationOptions.contains( "required", false ) ) { 01446 // ### hacky; should parse... 01447 // There is a required option that we don't understand. We need to 01448 // ask the user what we should do: 01449 if ( !allowGUI ) return 0; // don't setMDNSentState here! 01450 mode = requestAdviceOnMDN( "mdnUnknownOption" ); 01451 s = MDN::SentManually; 01452 01453 special = i18n("Header \"Disposition-Notification-Options\" contained " 01454 "required, but unknown parameter"); 01455 d = MDN::Failed; 01456 m.clear(); // clear modifiers 01457 } 01458 01459 // RFC 2298: [ Confirmation from the user SHOULD be obtained (or no 01460 // MDN sent) ] if there is more than one distinct address in the 01461 // Disposition-Notification-To header. 01462 kdDebug(5006) << "splitEmailAddrList(receiptTo): " 01463 << splitEmailAddrList(receiptTo).join("\n") << endl; 01464 if ( splitEmailAddrList(receiptTo).count() > 1 ) { 01465 if ( !allowGUI ) return 0; // don't setMDNSentState here! 01466 mode = requestAdviceOnMDN( "mdnMultipleAddressesInReceiptTo" ); 01467 s = MDN::SentManually; 01468 } 01469 01470 // RFC 2298: MDNs SHOULD NOT be sent automatically if the address in 01471 // the Disposition-Notification-To header differs from the address 01472 // in the Return-Path header. [...] Confirmation from the user 01473 // SHOULD be obtained (or no MDN sent) if there is no Return-Path 01474 // header in the message [...] 01475 AddrSpecList returnPathList = extractAddrSpecs("Return-Path"); 01476 QString returnPath = returnPathList.isEmpty() ? QString::null 01477 : returnPathList.front().localPart + '@' + returnPathList.front().domain ; 01478 kdDebug(5006) << "clean return path: " << returnPath << endl; 01479 if ( returnPath.isEmpty() || !receiptTo.contains( returnPath, false ) ) { 01480 if ( !allowGUI ) return 0; // don't setMDNSentState here! 01481 mode = requestAdviceOnMDN( returnPath.isEmpty() ? 01482 "mdnReturnPathEmpty" : 01483 "mdnReturnPathNotInReceiptTo" ); 01484 s = MDN::SentManually; 01485 } 01486 01487 if ( mode == 1 ) { // ask 01488 if ( !allowGUI ) return 0; // don't setMDNSentState here! 01489 mode = requestAdviceOnMDN( "mdnNormalAsk" ); 01490 s = MDN::SentManually; // asked user 01491 } 01492 01493 switch ( mode ) { 01494 case 0: // ignore: 01495 setMDNSentState( KMMsgMDNIgnore ); 01496 return 0; 01497 default: 01498 case 1: 01499 kdFatal(5006) << "KMMessage::createMDN(): The \"ask\" mode should " 01500 << "never appear here!" << endl; 01501 break; 01502 case 2: // deny 01503 d = MDN::Denied; 01504 m.clear(); 01505 break; 01506 case 3: 01507 break; 01508 } 01509 01510 01511 // extract where to send from: 01512 QString finalRecipient = kmkernel->identityManager() 01513 ->identityForUoidOrDefault( identityUoid() ).fullEmailAddr(); 01514 01515 // 01516 // Generate message: 01517 // 01518 01519 KMMessage * receipt = new KMMessage(); 01520 receipt->initFromMessage( this ); 01521 receipt->removeHeaderField("Content-Type"); 01522 receipt->removeHeaderField("Content-Transfer-Encoding"); 01523 // Modify the ContentType directly (replaces setAutomaticFields(true)) 01524 DwHeaders & header = receipt->mMsg->Headers(); 01525 header.MimeVersion().FromString("1.0"); 01526 DwMediaType & contentType = receipt->dwContentType(); 01527 contentType.SetType( DwMime::kTypeMultipart ); 01528 contentType.SetSubtype( DwMime::kSubtypeReport ); 01529 contentType.CreateBoundary(0); 01530 receipt->mNeedsAssembly = true; 01531 receipt->setContentTypeParam( "report-type", "disposition-notification" ); 01532 01533 QString description = replaceHeadersInString( MDN::descriptionFor( d, m ) ); 01534 01535 // text/plain part: 01536 KMMessagePart firstMsgPart; 01537 firstMsgPart.setTypeStr( "text" ); 01538 firstMsgPart.setSubtypeStr( "plain" ); 01539 firstMsgPart.setBodyFromUnicode( description ); 01540 receipt->addBodyPart( &firstMsgPart ); 01541 01542 // message/disposition-notification part: 01543 KMMessagePart secondMsgPart; 01544 secondMsgPart.setType( DwMime::kTypeMessage ); 01545 secondMsgPart.setSubtype( DwMime::kSubtypeDispositionNotification ); 01546 //secondMsgPart.setCharset( "us-ascii" ); 01547 //secondMsgPart.setCteStr( "7bit" ); 01548 secondMsgPart.setBodyEncoded( MDN::dispositionNotificationBodyContent( 01549 finalRecipient, 01550 rawHeaderField("Original-Recipient"), 01551 id(), /* Message-ID */ 01552 d, a, s, m, special ) ); 01553 receipt->addBodyPart( &secondMsgPart ); 01554 01555 // message/rfc822 or text/rfc822-headers body part: 01556 int num = mdnConfig.readNumEntry( "quote-message", 0 ); 01557 if ( num < 0 || num > 2 ) num = 0; 01558 MDN::ReturnContent returnContent = static_cast<MDN::ReturnContent>( num ); 01559 01560 KMMessagePart thirdMsgPart; 01561 switch ( returnContent ) { 01562 case MDN::All: 01563 thirdMsgPart.setTypeStr( "message" ); 01564 thirdMsgPart.setSubtypeStr( "rfc822" ); 01565 thirdMsgPart.setBody( asSendableString() ); 01566 receipt->addBodyPart( &thirdMsgPart ); 01567 break; 01568 case MDN::HeadersOnly: 01569 thirdMsgPart.setTypeStr( "text" ); 01570 thirdMsgPart.setSubtypeStr( "rfc822-headers" ); 01571 thirdMsgPart.setBody( headerAsSendableString() ); 01572 receipt->addBodyPart( &thirdMsgPart ); 01573 break; 01574 case MDN::Nothing: 01575 default: 01576 break; 01577 }; 01578 01579 receipt->setTo( receiptTo ); 01580 receipt->setSubject( "Message Disposition Notification" ); 01581 receipt->setReplyToId( msgId() ); 01582 receipt->setReferences( getRefStr() ); 01583 01584 receipt->cleanupHeader(); 01585 01586 kdDebug(5006) << "final message:\n" + receipt->asString() << endl; 01587 01588 // 01589 // Set "MDN sent" status: 01590 // 01591 KMMsgMDNSentState state = KMMsgMDNStateUnknown; 01592 switch ( d ) { 01593 case MDN::Displayed: state = KMMsgMDNDisplayed; break; 01594 case MDN::Deleted: state = KMMsgMDNDeleted; break; 01595 case MDN::Dispatched: state = KMMsgMDNDispatched; break; 01596 case MDN::Processed: state = KMMsgMDNProcessed; break; 01597 case MDN::Denied: state = KMMsgMDNDenied; break; 01598 case MDN::Failed: state = KMMsgMDNFailed; break; 01599 }; 01600 setMDNSentState( state ); 01601 01602 return receipt; 01603 } 01604 01605 QString KMMessage::replaceHeadersInString( const QString & s ) const { 01606 QString result = s; 01607 QRegExp rx( "\\$\\{([a-z0-9-]+)\\}", false ); 01608 Q_ASSERT( rx.isValid() ); 01609 int idx = 0; 01610 while ( ( idx = rx.search( result, idx ) ) != -1 ) { 01611 QString replacement = headerField( rx.cap(1).latin1() ); 01612 result.replace( idx, rx.matchedLength(), replacement ); 01613 idx += replacement.length(); 01614 } 01615 return result; 01616 } 01617 01618 QString KMMessage::forwardSubject() const { 01619 return cleanSubject( sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:" ); 01620 } 01621 01622 QString KMMessage::replySubject() const { 01623 return cleanSubject( sReplySubjPrefixes, sReplaceSubjPrefix, "Re:" ); 01624 } 01625 01626 KMMessage* KMMessage::createDeliveryReceipt() const 01627 { 01628 QString str, receiptTo; 01629 KMMessage *receipt; 01630 01631 receiptTo = headerField("Disposition-Notification-To"); 01632 if ( receiptTo.stripWhiteSpace().isEmpty() ) return 0; 01633 receiptTo.remove( '\n' ); 01634 01635 receipt = new KMMessage; 01636 receipt->initFromMessage(this); 01637 receipt->setTo(receiptTo); 01638 receipt->setSubject(i18n("Receipt: ") + subject()); 01639 01640 str = "Your message was successfully delivered."; 01641 str += "\n\n---------- Message header follows ----------\n"; 01642 str += headerAsString(); 01643 str += "--------------------------------------------\n"; 01644 // Conversion to latin1 is correct here as Mail headers should contain 01645 // ascii only 01646 receipt->setBody(str.latin1()); 01647 receipt->setAutomaticFields(); 01648 01649 return receipt; 01650 } 01651 01652 //----------------------------------------------------------------------------- 01653 void KMMessage::initHeader( uint id ) 01654 { 01655 const KMIdentity & ident = 01656 kmkernel->identityManager()->identityForUoidOrDefault( id ); 01657 01658 if(ident.fullEmailAddr().isEmpty()) 01659 setFrom(""); 01660 else 01661 setFrom(ident.fullEmailAddr()); 01662 01663 if(ident.replyToAddr().isEmpty()) 01664 setReplyTo(""); 01665 else 01666 setReplyTo(ident.replyToAddr()); 01667 01668 if(ident.bcc().isEmpty()) 01669 setBcc(""); 01670 else 01671 setBcc(ident.bcc()); 01672 01673 if (ident.organization().isEmpty()) 01674 removeHeaderField("Organization"); 01675 else 01676 setHeaderField("Organization", ident.organization()); 01677 01678 if (ident.isDefault()) 01679 removeHeaderField("X-KMail-Identity"); 01680 else 01681 setHeaderField("X-KMail-Identity", QString::number( ident.uoid() )); 01682 01683 if (ident.transport().isEmpty()) 01684 removeHeaderField("X-KMail-Transport"); 01685 else 01686 setHeaderField("X-KMail-Transport", ident.transport()); 01687 01688 if (ident.fcc().isEmpty()) 01689 setFcc( QString::null ); 01690 else 01691 setFcc( ident.fcc() ); 01692 01693 if (ident.drafts().isEmpty()) 01694 setDrafts( QString::null ); 01695 else 01696 setDrafts( ident.drafts() ); 01697 01698 setTo(""); 01699 setSubject(""); 01700 setDateToday(); 01701 01702 setHeaderField("User-Agent", "KMail/" KMAIL_VERSION ); 01703 // This will allow to change Content-Type: 01704 setHeaderField("Content-Type","text/plain"); 01705 } 01706 01707 uint KMMessage::identityUoid() const { 01708 QString idString = headerField("X-KMail-Identity").stripWhiteSpace(); 01709 bool ok = false; 01710 int id = idString.toUInt( &ok ); 01711 01712 if ( !ok || id == 0 ) 01713 id = kmkernel->identityManager()->identityForAddress( to() + cc() ).uoid(); 01714 if ( id == 0 && parent() ) 01715 id = parent()->identity(); 01716 01717 return id; 01718 } 01719 01720 01721 //----------------------------------------------------------------------------- 01722 void KMMessage::initFromMessage(const KMMessage *msg, bool idHeaders) 01723 { 01724 uint id = msg->identityUoid(); 01725 01726 if ( idHeaders ) initHeader(id); 01727 else setHeaderField("X-KMail-Identity", QString::number(id)); 01728 if (!msg->headerField("X-KMail-Transport").isEmpty()) 01729 setHeaderField("X-KMail-Transport", msg->headerField("X-KMail-Transport")); 01730 } 01731 01732 01733 //----------------------------------------------------------------------------- 01734 void KMMessage::cleanupHeader() 01735 { 01736 DwHeaders& header = mMsg->Headers(); 01737 DwField* field = header.FirstField(); 01738 DwField* nextField; 01739 01740 if (mNeedsAssembly) mMsg->Assemble(); 01741 mNeedsAssembly = FALSE; 01742 01743 while (field) 01744 { 01745 nextField = field->Next(); 01746 if (field->FieldBody()->AsString().empty()) 01747 { 01748 header.RemoveField(field); 01749 mNeedsAssembly = TRUE; 01750 } 01751 field = nextField; 01752 } 01753 } 01754 01755 01756 //----------------------------------------------------------------------------- 01757 void KMMessage::setAutomaticFields(bool aIsMulti) 01758 { 01759 DwHeaders& header = mMsg->Headers(); 01760 header.MimeVersion().FromString("1.0"); 01761 01762 if (aIsMulti || numBodyParts() > 1) 01763 { 01764 // Set the type to 'Multipart' and the subtype to 'Mixed' 01765 DwMediaType& contentType = dwContentType(); 01766 contentType.SetType( DwMime::kTypeMultipart); 01767 contentType.SetSubtype(DwMime::kSubtypeMixed ); 01768 01769 // Create a random printable string and set it as the boundary parameter 01770 contentType.CreateBoundary(0); 01771 } 01772 mNeedsAssembly = TRUE; 01773 } 01774 01775 01776 //----------------------------------------------------------------------------- 01777 QString KMMessage::dateStr() const 01778 { 01779 KConfigGroup general( KMKernel::config(), "General" ); 01780 DwHeaders& header = mMsg->Headers(); 01781 time_t unixTime; 01782 01783 if (!header.HasDate()) return ""; 01784 unixTime = header.Date().AsUnixTime(); 01785 01786 //kdDebug(5006)<<"#### Date = "<<header.Date().AsString().c_str()<<endl; 01787 01788 return KMime::DateFormatter::formatDate( 01789 static_cast<KMime::DateFormatter::FormatType>(general.readNumEntry( "dateFormat", KMime::DateFormatter::Fancy )), 01790 unixTime, general.readEntry( "customDateFormat" )); 01791 } 01792 01793 01794 //----------------------------------------------------------------------------- 01795 QCString KMMessage::dateShortStr() const 01796 { 01797 DwHeaders& header = mMsg->Headers(); 01798 time_t unixTime; 01799 01800 if (!header.HasDate()) return ""; 01801 unixTime = header.Date().AsUnixTime(); 01802 01803 QCString result = ctime(&unixTime); 01804 01805 if (result[result.length()-1]=='\n') 01806 result.truncate(result.length()-1); 01807 01808 return result; 01809 } 01810 01811 01812 //----------------------------------------------------------------------------- 01813 QString KMMessage::dateIsoStr() const 01814 { 01815 DwHeaders& header = mMsg->Headers(); 01816 time_t unixTime; 01817 01818 if (!header.HasDate()) return ""; 01819 unixTime = header.Date().AsUnixTime(); 01820 01821 char cstr[64]; 01822 strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&unixTime)); 01823 return QString(cstr); 01824 } 01825 01826 01827 //----------------------------------------------------------------------------- 01828 time_t KMMessage::date() const 01829 { 01830 time_t res = ( time_t )-1; 01831 DwHeaders& header = mMsg->Headers(); 01832 if (header.HasDate()) 01833 res = header.Date().AsUnixTime(); 01834 return res; 01835 } 01836 01837 01838 //----------------------------------------------------------------------------- 01839 void KMMessage::setDateToday() 01840 { 01841 struct timeval tval; 01842 gettimeofday(&tval, 0); 01843 setDate((time_t)tval.tv_sec); 01844 } 01845 01846 01847 //----------------------------------------------------------------------------- 01848 void KMMessage::setDate(time_t aDate) 01849 { 01850 mDate = aDate; 01851 mMsg->Headers().Date().FromCalendarTime(aDate); 01852 mMsg->Headers().Date().Assemble(); 01853 mNeedsAssembly = TRUE; 01854 mDirty = TRUE; 01855 } 01856 01857 01858 //----------------------------------------------------------------------------- 01859 void KMMessage::setDate(const QCString& aStr) 01860 { 01861 DwHeaders& header = mMsg->Headers(); 01862 01863 header.Date().FromString(aStr); 01864 header.Date().Parse(); 01865 mNeedsAssembly = TRUE; 01866 mDirty = TRUE; 01867 01868 if (header.HasDate()) 01869 mDate = header.Date().AsUnixTime(); 01870 } 01871 01872 01873 //----------------------------------------------------------------------------- 01874 QString KMMessage::to() const 01875 { 01876 return headerField("To"); 01877 } 01878 01879 01880 //----------------------------------------------------------------------------- 01881 void KMMessage::setTo(const QString& aStr) 01882 { 01883 setHeaderField("To", aStr); 01884 } 01885 01886 //----------------------------------------------------------------------------- 01887 QString KMMessage::toStrip() const 01888 { 01889 return decodeRFC2047String( stripEmailAddr( rawHeaderField("To") ) ); 01890 } 01891 01892 //----------------------------------------------------------------------------- 01893 QString KMMessage::replyTo() const 01894 { 01895 return headerField("Reply-To"); 01896 } 01897 01898 01899 //----------------------------------------------------------------------------- 01900 void KMMessage::setReplyTo(const QString& aStr) 01901 { 01902 setHeaderField("Reply-To", aStr); 01903 } 01904 01905 01906 //----------------------------------------------------------------------------- 01907 void KMMessage::setReplyTo(KMMessage* aMsg) 01908 { 01909 setHeaderField("Reply-To", aMsg->from()); 01910 } 01911 01912 01913 //----------------------------------------------------------------------------- 01914 QString KMMessage::cc() const 01915 { 01916 // get the combined contents of all Cc headers (as workaround for invalid 01917 // messages with multiple Cc headers) 01918 return allHeaderFields("Cc"); 01919 } 01920 01921 01922 //----------------------------------------------------------------------------- 01923 void KMMessage::setCc(const QString& aStr) 01924 { 01925 setHeaderField("Cc",aStr); 01926 } 01927 01928 01929 //----------------------------------------------------------------------------- 01930 QString KMMessage::ccStrip() const 01931 { 01932 return decodeRFC2047String( stripEmailAddr( rawHeaderField("Cc") ) ); 01933 } 01934 01935 01936 //----------------------------------------------------------------------------- 01937 QString KMMessage::bcc() const 01938 { 01939 return headerField("Bcc"); 01940 } 01941 01942 01943 //----------------------------------------------------------------------------- 01944 void KMMessage::setBcc(const QString& aStr) 01945 { 01946 setHeaderField("Bcc", aStr); 01947 } 01948 01949 //----------------------------------------------------------------------------- 01950 QString KMMessage::fcc() const 01951 { 01952 return headerField( "X-KMail-Fcc" ); 01953 } 01954 01955 01956 //----------------------------------------------------------------------------- 01957 void KMMessage::setFcc(const QString& aStr) 01958 { 01959 setHeaderField( "X-KMail-Fcc", aStr ); 01960 } 01961 01962 //----------------------------------------------------------------------------- 01963 void KMMessage::setDrafts(const QString& aStr) 01964 { 01965 mDrafts = aStr; 01966 kdDebug(5006) << "KMMessage::setDrafts " << aStr << endl; 01967 } 01968 01969 //----------------------------------------------------------------------------- 01970 QString KMMessage::who() const 01971 { 01972 if (mParent) 01973 return headerField(mParent->whoField().utf8()); 01974 return headerField("From"); 01975 } 01976 01977 01978 //----------------------------------------------------------------------------- 01979 QString KMMessage::from() const 01980 { 01981 return headerField("From"); 01982 } 01983 01984 01985 //----------------------------------------------------------------------------- 01986 void KMMessage::setFrom(const QString& bStr) 01987 { 01988 QString aStr = bStr; 01989 if (aStr.isNull()) 01990 aStr = ""; 01991 setHeaderField("From", aStr); 01992 mDirty = TRUE; 01993 } 01994 01995 01996 //----------------------------------------------------------------------------- 01997 QString KMMessage::fromStrip() const 01998 { 01999 return decodeRFC2047String( stripEmailAddr( rawHeaderField("From") ) ); 02000 } 02001 02002 //----------------------------------------------------------------------------- 02003 QCString KMMessage::fromEmail() const 02004 { 02005 return getEmailAddr(headerField("From")); 02006 } 02007 02008 //----------------------------------------------------------------------------- 02009 QString KMMessage::sender() const { 02010 AddrSpecList asl = extractAddrSpecs( "Sender" ); 02011 if ( asl.empty() ) 02012 asl = extractAddrSpecs( "From" ); 02013 if ( asl.empty() ) 02014 return QString::null; 02015 return asl.front().asString(); 02016 } 02017 02018 //----------------------------------------------------------------------------- 02019 QString KMMessage::subject() const 02020 { 02021 return headerField("Subject"); 02022 } 02023 02024 02025 //----------------------------------------------------------------------------- 02026 void KMMessage::setSubject(const QString& aStr) 02027 { 02028 setHeaderField("Subject",aStr); 02029 mDirty = TRUE; 02030 } 02031 02032 02033 //----------------------------------------------------------------------------- 02034 QString KMMessage::xmark() const 02035 { 02036 return headerField("X-KMail-Mark"); 02037 } 02038 02039 02040 //----------------------------------------------------------------------------- 02041 void KMMessage::setXMark(const QString& aStr) 02042 { 02043 setHeaderField("X-KMail-Mark", aStr); 02044 mDirty = TRUE; 02045 } 02046 02047 02048 //----------------------------------------------------------------------------- 02049 QString KMMessage::replyToId() const 02050 { 02051 int leftAngle, rightAngle; 02052 QString replyTo, references; 02053 02054 replyTo = headerField("In-Reply-To"); 02055 // search the end of the (first) message id in the In-Reply-To header 02056 rightAngle = replyTo.find( '>' ); 02057 if (rightAngle != -1) 02058 replyTo.truncate( rightAngle + 1 ); 02059 // now search the start of the message id 02060 leftAngle = replyTo.findRev( '<' ); 02061 if (leftAngle != -1) 02062 replyTo = replyTo.mid( leftAngle ); 02063 02064 // if we have found a good message id we can return immediately 02065 // We ignore mangled In-Reply-To headers which are created by a 02066 // misconfigured Mutt. They look like this <"from foo"@bar.baz>, i.e. 02067 // they contain double quotes and spaces. We only check for '"'. 02068 if (!replyTo.isEmpty() && (replyTo[0] == '<') && 02069 ( -1 == replyTo.find( '"' ) ) ) 02070 return replyTo; 02071 02072 references = headerField("References"); 02073 leftAngle = references.findRev( '<' ); 02074 if (leftAngle != -1) 02075 references = references.mid( leftAngle ); 02076 rightAngle = references.find( '>' ); 02077 if (rightAngle != -1) 02078 references.truncate( rightAngle + 1 ); 02079 02080 // if we found a good message id in the References header return it 02081 if (!references.isEmpty() && references[0] == '<') 02082 return references; 02083 // else return the broken message id we found in the In-Reply-To header 02084 else 02085 return replyTo; 02086 } 02087 02088 02089 //----------------------------------------------------------------------------- 02090 QString KMMessage::replyToIdMD5() const { 02091 return base64EncodedMD5( replyToId() ); 02092 } 02093 02094 //----------------------------------------------------------------------------- 02095 QString KMMessage::references() const 02096 { 02097 int leftAngle, rightAngle; 02098 QString references = headerField( "References" ); 02099 02100 // keep the last two entries for threading 02101 leftAngle = references.findRev( '<' ); 02102 leftAngle = references.findRev( '<', leftAngle - 1 ); 02103 if( leftAngle != -1 ) 02104 references = references.mid( leftAngle ); 02105 rightAngle = references.findRev( '>' ); 02106 if( rightAngle != -1 ) 02107 references.truncate( rightAngle + 1 ); 02108 02109 if( !references.isEmpty() && references[0] == '<' ) 02110 return references; 02111 else 02112 return QString::null; 02113 } 02114 02115 //----------------------------------------------------------------------------- 02116 QString KMMessage::replyToAuxIdMD5() const 02117 { 02118 QString result = references(); 02119 // references contains two items, use the first one 02120 // (the second to last reference) 02121 const int rightAngle = result.find( '>' ); 02122 if( rightAngle != -1 ) 02123 result.truncate( rightAngle + 1 ); 02124 02125 return base64EncodedMD5( result ); 02126 } 02127 02128 //----------------------------------------------------------------------------- 02129 QString KMMessage::strippedSubjectMD5() const { 02130 return base64EncodedMD5( stripOffPrefixes( subject() ), true /*utf8*/ ); 02131 } 02132 02133 //----------------------------------------------------------------------------- 02134 QString KMMessage::subjectMD5() const { 02135 return base64EncodedMD5( subject(), true /*utf8*/ ); 02136 } 02137 02138 //----------------------------------------------------------------------------- 02139 bool KMMessage::subjectIsPrefixed() const { 02140 return subjectMD5() != strippedSubjectMD5(); 02141 } 02142 02143 //----------------------------------------------------------------------------- 02144 void KMMessage::setReplyToId(const QString& aStr) 02145 { 02146 setHeaderField("In-Reply-To", aStr); 02147 mDirty = TRUE; 02148 } 02149 02150 02151 //----------------------------------------------------------------------------- 02152 QString KMMessage::msgId() const 02153 { 02154 QString msgId = headerField("Message-Id"); 02155 02156 // search the end of the message id 02157 const int rightAngle = msgId.find( '>' ); 02158 if (rightAngle != -1) 02159 msgId.truncate( rightAngle + 1 ); 02160 // now search the start of the message id 02161 const int leftAngle = msgId.findRev( '<' ); 02162 if (leftAngle != -1) 02163 msgId = msgId.mid( leftAngle ); 02164 return msgId; 02165 } 02166 02167 02168 //----------------------------------------------------------------------------- 02169 QString KMMessage::msgIdMD5() const { 02170 return base64EncodedMD5( msgId() ); 02171 } 02172 02173 02174 //----------------------------------------------------------------------------- 02175 void KMMessage::setMsgId(const QString& aStr) 02176 { 02177 setHeaderField("Message-Id", aStr); 02178 mDirty = TRUE; 02179 } 02180 02181 02182 //----------------------------------------------------------------------------- 02183 AddressList KMMessage::headerAddrField( const QCString & aName ) const { 02184 const QCString header = rawHeaderField( aName ); 02185 AddressList result; 02186 const char * scursor = header.begin(); 02187 if ( !scursor ) 02188 return AddressList(); 02189 const char * const send = header.begin() + header.length(); 02190 if ( !parseAddressList( scursor, send, result ) ) 02191 kdDebug(5006) << "Error in address splitting: parseAddressList returned false!" 02192 << endl; 02193 return result; 02194 } 02195 02196 AddrSpecList KMMessage::extractAddrSpecs( const QCString & header ) const { 02197 AddressList al = headerAddrField( header ); 02198 AddrSpecList result; 02199 for ( AddressList::const_iterator ait = al.begin() ; ait != al.end() ; ++ait ) 02200 for ( MailboxList::const_iterator mit = (*ait).mailboxList.begin() ; mit != (*ait).mailboxList.end() ; ++mit ) 02201 result.push_back( (*mit).addrSpec ); 02202 return result; 02203 } 02204 02205 QCString KMMessage::rawHeaderField( const QCString & name ) const { 02206 if ( name.isEmpty() ) return QCString(); 02207 02208 DwHeaders & header = mMsg->Headers(); 02209 DwField * field = header.FindField( name ); 02210 02211 if ( !field ) return QCString(); 02212 02213 return header.FieldBody( name.data() ).AsString().c_str(); 02214 } 02215 02216 QString KMMessage::headerField(const QCString& aName) const 02217 { 02218 if ( aName.isEmpty() ) 02219 return QString::null; 02220 02221 if ( !mMsg->Headers().FindField( aName ) ) 02222 return QString::null; 02223 02224 return decodeRFC2047String( mMsg->Headers().FieldBody( aName.data() ).AsString().c_str() ); 02225 } 02226 02227 02228 QString KMMessage::allHeaderFields(const QCString& aName) const 02229 { 02230 if ( aName.isEmpty() ) 02231 return QString::null; 02232 02233 if ( !mMsg->Headers().FindField( aName ) ) 02234 return QString::null; 02235 02236 return decodeRFC2047String( mMsg->Headers().AllFieldBodiesAsString( aName.data() ).c_str() ); 02237 } 02238 02239 02240 //----------------------------------------------------------------------------- 02241 void KMMessage::removeHeaderField(const QCString& aName) 02242 { 02243 DwHeaders & header = mMsg->Headers(); 02244 DwField * field = header.FindField(aName); 02245 if (!field) return; 02246 02247 header.RemoveField(field); 02248 mNeedsAssembly = TRUE; 02249 } 02250 02251 02252 //----------------------------------------------------------------------------- 02253 void KMMessage::setHeaderField(const QCString& aName, const QString& bValue) 02254 { 02255 if (aName.isEmpty()) return; 02256 02257 DwHeaders& header = mMsg->Headers(); 02258 02259 DwString str; 02260 DwField* field; 02261 QCString aValue = ""; 02262 if (!bValue.isEmpty()) 02263 { 02264 QCString encoding = autoDetectCharset(charset(), sPrefCharsets, bValue); 02265 if (encoding.isEmpty()) 02266 encoding = "utf-8"; 02267 aValue = encodeRFC2047String(bValue, encoding); 02268 } 02269 str = aName; 02270 if (str[str.length()-1] != ':') str += ": "; 02271 else str += ' '; 02272 str += aValue; 02273 if (str[str.length()-1] != '\n') str += '\n'; 02274 02275 field = new DwField(str, mMsg); 02276 field->Parse(); 02277 02278 header.AddOrReplaceField(field); 02279 mNeedsAssembly = TRUE; 02280 } 02281 02282 02283 //----------------------------------------------------------------------------- 02284 QCString KMMessage::typeStr() const 02285 { 02286 DwHeaders& header = mMsg->Headers(); 02287 if (header.HasContentType()) return header.ContentType().AsString().c_str(); 02288 else return ""; 02289 } 02290 02291 02292 //----------------------------------------------------------------------------- 02293 int KMMessage::type() const 02294 { 02295 DwHeaders& header = mMsg->Headers(); 02296 if (header.HasContentType()) return header.ContentType().Type(); 02297 else return DwMime::kTypeNull; 02298 } 02299 02300 02301 //----------------------------------------------------------------------------- 02302 void KMMessage::setTypeStr(const QCString& aStr) 02303 { 02304 dwContentType().SetTypeStr(DwString(aStr)); 02305 dwContentType().Parse(); 02306 mNeedsAssembly = TRUE; 02307 } 02308 02309 02310 //----------------------------------------------------------------------------- 02311 void KMMessage::setType(int aType) 02312 { 02313 dwContentType().SetType(aType); 02314 dwContentType().Assemble(); 02315 mNeedsAssembly = TRUE; 02316 } 02317 02318 02319 02320 //----------------------------------------------------------------------------- 02321 QCString KMMessage::subtypeStr() const 02322 { 02323 DwHeaders& header = mMsg->Headers(); 02324 if (header.HasContentType()) return header.ContentType().SubtypeStr().c_str(); 02325 else return ""; 02326 } 02327 02328 02329 //----------------------------------------------------------------------------- 02330 int KMMessage::subtype() const 02331 { 02332 DwHeaders& header = mMsg->Headers(); 02333 if (header.HasContentType()) return header.ContentType().Subtype(); 02334 else return DwMime::kSubtypeNull; 02335 } 02336 02337 02338 //----------------------------------------------------------------------------- 02339 void KMMessage::setSubtypeStr(const QCString& aStr) 02340 { 02341 dwContentType().SetSubtypeStr(DwString(aStr)); 02342 dwContentType().Parse(); 02343 mNeedsAssembly = TRUE; 02344 } 02345 02346 02347 //----------------------------------------------------------------------------- 02348 void KMMessage::setSubtype(int aSubtype) 02349 { 02350 dwContentType().SetSubtype(aSubtype); 02351 dwContentType().Assemble(); 02352 mNeedsAssembly = TRUE; 02353 } 02354 02355 02356 //----------------------------------------------------------------------------- 02357 void KMMessage::setDwMediaTypeParam( DwMediaType &mType, 02358 const QCString& attr, 02359 const QCString& val ) 02360 { 02361 mType.Parse(); 02362 DwParameter *param = mType.FirstParameter(); 02363 while(param) { 02364 if (!qstricmp(param->Attribute().c_str(), attr)) 02365 break; 02366 else 02367 param = param->Next(); 02368 } 02369 if (!param){ 02370 param = new DwParameter; 02371 param->SetAttribute(DwString( attr )); 02372 mType.AddParameter( param ); 02373 } 02374 else 02375 mType.SetModified(); 02376 param->SetValue(DwString( val )); 02377 mType.Assemble(); 02378 } 02379 02380 02381 //----------------------------------------------------------------------------- 02382 void KMMessage::setContentTypeParam(const QCString& attr, const QCString& val) 02383 { 02384 if (mNeedsAssembly) mMsg->Assemble(); 02385 mNeedsAssembly = FALSE; 02386 setDwMediaTypeParam( dwContentType(), attr, val ); 02387 mNeedsAssembly = TRUE; 02388 } 02389 02390 02391 //----------------------------------------------------------------------------- 02392 QCString KMMessage::contentTransferEncodingStr() const 02393 { 02394 DwHeaders& header = mMsg->Headers(); 02395 if (header.HasContentTransferEncoding()) 02396 return header.ContentTransferEncoding().AsString().c_str(); 02397 else return ""; 02398 } 02399 02400 02401 //----------------------------------------------------------------------------- 02402 int KMMessage::contentTransferEncoding() const 02403 { 02404 DwHeaders& header = mMsg->Headers(); 02405 if (header.HasContentTransferEncoding()) 02406 return header.ContentTransferEncoding().AsEnum(); 02407 else return DwMime::kCteNull; 02408 } 02409 02410 02411 //----------------------------------------------------------------------------- 02412 void KMMessage::setContentTransferEncodingStr(const QCString& aStr) 02413 { 02414 mMsg->Headers().ContentTransferEncoding().FromString(aStr); 02415 mMsg->Headers().ContentTransferEncoding().Parse(); 02416 mNeedsAssembly = TRUE; 02417 } 02418 02419 02420 //----------------------------------------------------------------------------- 02421 void KMMessage::setContentTransferEncoding(int aCte) 02422 { 02423 mMsg->Headers().ContentTransferEncoding().FromEnum(aCte); 02424 mNeedsAssembly = TRUE; 02425 } 02426 02427 02428 //----------------------------------------------------------------------------- 02429 DwHeaders& KMMessage::headers() const 02430 { 02431 return mMsg->Headers(); 02432 } 02433 02434 02435 //----------------------------------------------------------------------------- 02436 void KMMessage::setNeedsAssembly() 02437 { 02438 mNeedsAssembly = true; 02439 } 02440 02441 02442 //----------------------------------------------------------------------------- 02443 QCString KMMessage::body() const 02444 { 02445 DwString body = mMsg->Body().AsString(); 02446 QCString str = body.c_str(); 02447 kdWarning( str.length() != body.length(), 5006 ) 02448 << "KMMessage::body(): body is binary but used as text!" << endl; 02449 return str; 02450 } 02451 02452 02453 //----------------------------------------------------------------------------- 02454 QByteArray KMMessage::bodyDecodedBinary() const 02455 { 02456 DwString dwstr; 02457 DwString dwsrc = mMsg->Body().AsString(); 02458 02459 switch (cte()) 02460 { 02461 case DwMime::kCteBase64: 02462 DwDecodeBase64(dwsrc, dwstr); 02463 break; 02464 case DwMime::kCteQuotedPrintable: 02465 DwDecodeQuotedPrintable(dwsrc, dwstr); 02466 break; 02467 default: 02468 dwstr = dwsrc; 02469 break; 02470 } 02471 02472 int len = dwstr.size(); 02473 QByteArray ba(len); 02474 memcpy(ba.data(),dwstr.data(),len); 02475 return ba; 02476 } 02477 02478 02479 //----------------------------------------------------------------------------- 02480 QCString KMMessage::bodyDecoded() const 02481 { 02482 DwString dwstr; 02483 DwString dwsrc = mMsg->Body().AsString(); 02484 02485 switch (cte()) 02486 { 02487 case DwMime::kCteBase64: 02488 DwDecodeBase64(dwsrc, dwstr); 02489 break; 02490 case DwMime::kCteQuotedPrintable: 02491 DwDecodeQuotedPrintable(dwsrc, dwstr); 02492 break; 02493 default: 02494 dwstr = dwsrc; 02495 break; 02496 } 02497 02498 unsigned int len = dwstr.size(); 02499 QCString result(len+1); 02500 memcpy(result.data(),dwstr.data(),len); 02501 result[len] = 0; 02502 kdWarning(result.length() != len, 5006) 02503 << "KMMessage::bodyDecoded(): body is binary but used as text!" << endl; 02504 return result; 02505 } 02506 02507 02508 //----------------------------------------------------------------------------- 02509 QValueList<int> KMMessage::determineAllowedCtes( const CharFreq& cf, 02510 bool allow8Bit, 02511 bool willBeSigned ) 02512 { 02513 QValueList<int> allowedCtes; 02514 02515 switch ( cf.type() ) { 02516 case CharFreq::SevenBitText: 02517 allowedCtes << DwMime::kCte7bit; 02518 case CharFreq::EightBitText: 02519 if ( allow8Bit ) 02520 allowedCtes << DwMime::kCte8bit; 02521 case CharFreq::SevenBitData: 02522 if ( cf.printableRatio() > 5.0/6.0 ) { 02523 // let n the length of data and p the number of printable chars. 02524 // Then base64 \approx 4n/3; qp \approx p + 3(n-p) 02525 // => qp < base64 iff p > 5n/6. 02526 allowedCtes << DwMime::kCteQp; 02527 allowedCtes << DwMime::kCteBase64; 02528 } else { 02529 allowedCtes << DwMime::kCteBase64; 02530 allowedCtes << DwMime::kCteQp; 02531 } 02532 break; 02533 case CharFreq::EightBitData: 02534 allowedCtes << DwMime::kCteBase64; 02535 break; 02536 case CharFreq::None: 02537 default: 02538 // just nothing (avoid compiler warning) 02539 ; 02540 } 02541 02542 // In the following cases only QP and Base64 are allowed: 02543 // - the buffer will be OpenPGP/MIME signed and it contains trailing 02544 // whitespace (cf. RFC 3156) 02545 // - a line starts with "From " 02546 if ( ( willBeSigned && cf.hasTrailingWhitespace() ) || 02547 cf.hasLeadingFrom() ) { 02548 allowedCtes.remove( DwMime::kCte8bit ); 02549 allowedCtes.remove( DwMime::kCte7bit ); 02550 } 02551 02552 return allowedCtes; 02553 } 02554 02555 02556 //----------------------------------------------------------------------------- 02557 void KMMessage::setBodyAndGuessCte( const QByteArray& aBuf, 02558 QValueList<int> & allowedCte, 02559 bool allow8Bit, 02560 bool willBeSigned ) 02561 { 02562 CharFreq cf( aBuf ); // it's safe to pass null arrays 02563 02564 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned ); 02565 02566 #ifndef NDEBUG 02567 DwString dwCte; 02568 DwCteEnumToStr(allowedCte[0], dwCte); 02569 kdDebug(5006) << "CharFreq returned " << cf.type() << "/" 02570 << cf.printableRatio() << " and I chose " 02571 << dwCte.c_str() << endl; 02572 #endif 02573 02574 setCte( allowedCte[0] ); // choose best fitting 02575 setBodyEncodedBinary( aBuf ); 02576 } 02577 02578 02579 //----------------------------------------------------------------------------- 02580 void KMMessage::setBodyAndGuessCte( const QCString& aBuf, 02581 QValueList<int> & allowedCte, 02582 bool allow8Bit, 02583 bool willBeSigned ) 02584 { 02585 CharFreq cf( aBuf.data(), aBuf.length() ); // it's safe to pass null strings 02586 02587 allowedCte = determineAllowedCtes( cf, allow8Bit, willBeSigned ); 02588 02589 #ifndef NDEBUG 02590 DwString dwCte; 02591 DwCteEnumToStr(allowedCte[0], dwCte); 02592 kdDebug(5006) << "CharFreq returned " << cf.type() << "/" 02593 << cf.printableRatio() << " and I chose " 02594 << dwCte.c_str() << endl; 02595 #endif 02596 02597 setCte( allowedCte[0] ); // choose best fitting 02598 setBodyEncoded( aBuf ); 02599 } 02600 02601 02602 //----------------------------------------------------------------------------- 02603 void KMMessage::setBodyEncoded(const QCString& aStr) 02604 { 02605 DwString dwSrc(aStr.data(), aStr.size()-1 /* not the trailing NUL */); 02606 DwString dwResult; 02607 02608 switch (cte()) 02609 { 02610 case DwMime::kCteBase64: 02611 DwEncodeBase64(dwSrc, dwResult); 02612 break; 02613 case DwMime::kCteQuotedPrintable: 02614 DwEncodeQuotedPrintable(dwSrc, dwResult); 02615 break; 02616 default: 02617 dwResult = dwSrc; 02618 break; 02619 } 02620 02621 mMsg->Body().FromString(dwResult); 02622 mNeedsAssembly = TRUE; 02623 } 02624 02625 //----------------------------------------------------------------------------- 02626 void KMMessage::setBodyEncodedBinary(const QByteArray& aStr) 02627 { 02628 DwString dwSrc(aStr.data(), aStr.size()); 02629 DwString dwResult; 02630 02631 switch (cte()) 02632 { 02633 case DwMime::kCteBase64: 02634 DwEncodeBase64(dwSrc, dwResult); 02635 break; 02636 case DwMime::kCteQuotedPrintable: 02637 DwEncodeQuotedPrintable(dwSrc, dwResult); 02638 break; 02639 default: 02640 dwResult = dwSrc; 02641 break; 02642 } 02643 02644 mMsg->Body().FromString(dwResult); 02645 mNeedsAssembly = TRUE; 02646 } 02647 02648 02649 //----------------------------------------------------------------------------- 02650 void KMMessage::setBody(const QCString& aStr) 02651 { 02652 mMsg->Body().FromString(aStr.data()); 02653 mNeedsAssembly = TRUE; 02654 } 02655 02656 void KMMessage::setMultiPartBody( const QCString & aStr ) { 02657 setBody( aStr ); 02658 mMsg->Body().Parse(); 02659 mNeedsAssembly = true; 02660 } 02661 02662 02663 // Patched by Daniel Moisset <dmoisset@grulic.org.ar> 02664 // modified numbodyparts, bodypart to take nested body parts as 02665 // a linear sequence. 02666 // third revision, Sep 26 2000 02667 02668 // this is support structure for traversing tree without recursion 02669 02670 //----------------------------------------------------------------------------- 02671 int KMMessage::numBodyParts() const 02672 { 02673 int count = 0; 02674 DwBodyPart* part = getFirstDwBodyPart(); 02675 QPtrList< DwBodyPart > parts; 02676 02677 while (part) 02678 { 02679 //dive into multipart messages 02680 while ( part 02681 && part->hasHeaders() 02682 && part->Headers().HasContentType() 02683 && part->Body().FirstBodyPart() 02684 && (DwMime::kTypeMultipart == part->Headers().ContentType().Type()) ) 02685 { 02686 parts.append( part ); 02687 part = part->Body().FirstBodyPart(); 02688 } 02689 // this is where currPart->msgPart contains a leaf message part 02690 count++; 02691 // go up in the tree until reaching a node with next 02692 // (or the last top-level node) 02693 while (part && !(part->Next()) && !(parts.isEmpty())) 02694 { 02695 part = parts.getLast(); 02696 parts.removeLast(); 02697 } 02698 02699 if (part->Body().Message() && 02700 part->Body().Message()->Body().FirstBodyPart()) 02701 { 02702 part = part->Body().Message()->Body().FirstBodyPart(); 02703 } else if (part) { 02704 part = part->Next(); 02705 } 02706 } 02707 02708 return count; 02709 } 02710 02711 02712 //----------------------------------------------------------------------------- 02713 DwBodyPart * KMMessage::getFirstDwBodyPart() const 02714 { 02715 return mMsg->Body().FirstBodyPart(); 02716 } 02717 02718 02719 //----------------------------------------------------------------------------- 02720 int KMMessage::partNumber( DwBodyPart * aDwBodyPart ) const 02721 { 02722 DwBodyPart *curpart; 02723 QPtrList< DwBodyPart > parts; 02724 int curIdx = 0; 02725 int idx = 0; 02726 // Get the DwBodyPart for this index 02727 02728 curpart = getFirstDwBodyPart(); 02729 02730 while (curpart && !idx) { 02731 //dive into multipart messages 02732 while( curpart 02733 && curpart->hasHeaders() 02734 && curpart->Headers().HasContentType() 02735 && curpart->Body().FirstBodyPart() 02736 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) 02737 { 02738 parts.append( curpart ); 02739 curpart = curpart->Body().FirstBodyPart(); 02740 } 02741 // this is where currPart->msgPart contains a leaf message part 02742 if (curpart == aDwBodyPart) 02743 idx = curIdx; 02744 curIdx++; 02745 // go up in the tree until reaching a node with next 02746 // (or the last top-level node) 02747 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) 02748 { 02749 curpart = parts.getLast(); 02750 parts.removeLast(); 02751 } ; 02752 if (curpart) 02753 curpart = curpart->Next(); 02754 } 02755 return idx; 02756 } 02757 02758 02759 //----------------------------------------------------------------------------- 02760 DwBodyPart * KMMessage::dwBodyPart( int aIdx ) const 02761 { 02762 DwBodyPart *part, *curpart; 02763 QPtrList< DwBodyPart > parts; 02764 int curIdx = 0; 02765 // Get the DwBodyPart for this index 02766 02767 curpart = getFirstDwBodyPart(); 02768 part = 0; 02769 02770 while (curpart && !part) { 02771 //dive into multipart messages 02772 while( curpart 02773 && curpart->hasHeaders() 02774 && curpart->Headers().HasContentType() 02775 && curpart->Body().FirstBodyPart() 02776 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) 02777 { 02778 parts.append( curpart ); 02779 curpart = curpart->Body().FirstBodyPart(); 02780 } 02781 // this is where currPart->msgPart contains a leaf message part 02782 if (curIdx==aIdx) 02783 part = curpart; 02784 curIdx++; 02785 // go up in the tree until reaching a node with next 02786 // (or the last top-level node) 02787 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) 02788 { 02789 curpart = parts.getLast(); 02790 parts.removeLast(); 02791 } 02792 if (curpart) 02793 curpart = curpart->Next(); 02794 } 02795 return part; 02796 } 02797 02798 02799 //----------------------------------------------------------------------------- 02800 DwBodyPart * KMMessage::findDwBodyPart( int type, int subtype ) const 02801 { 02802 DwBodyPart *part, *curpart; 02803 QPtrList< DwBodyPart > parts; 02804 // Get the DwBodyPart for this index 02805 02806 curpart = getFirstDwBodyPart(); 02807 part = 0; 02808 02809 while (curpart && !part) { 02810 //dive into multipart messages 02811 while(curpart 02812 && curpart->hasHeaders() 02813 && curpart->Headers().HasContentType() 02814 && curpart->Body().FirstBodyPart() 02815 && (DwMime::kTypeMultipart == curpart->Headers().ContentType().Type()) ) { 02816 parts.append( curpart ); 02817 curpart = curpart->Body().FirstBodyPart(); 02818 } 02819 // this is where curPart->msgPart contains a leaf message part 02820 02821 // pending(khz): Find out WHY this look does not travel down *into* an 02822 // embedded "Message/RfF822" message containing a "Multipart/Mixed" 02823 if (curpart && curpart->hasHeaders() ) { 02824 kdDebug(5006) << curpart->Headers().ContentType().TypeStr().c_str() 02825 << " " << curpart->Headers().ContentType().SubtypeStr().c_str() << endl; 02826 } 02827 02828 if (curpart && 02829 curpart->hasHeaders() && 02830 curpart->Headers().ContentType().Type() == type && 02831 curpart->Headers().ContentType().Subtype() == subtype) { 02832 part = curpart; 02833 } else { 02834 // go up in the tree until reaching a node with next 02835 // (or the last top-level node) 02836 while (curpart && !(curpart->Next()) && !(parts.isEmpty())) { 02837 curpart = parts.getLast(); 02838 parts.removeLast(); 02839 } ; 02840 if (curpart) 02841 curpart = curpart->Next(); 02842 } 02843 } 02844 return part; 02845 } 02846 02847 02848 //----------------------------------------------------------------------------- 02849 void KMMessage::bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart, 02850 bool withBody) 02851 { 02852 if( aPart ) { 02853 if( aDwBodyPart && aDwBodyPart->hasHeaders() ) { 02854 // This must not be an empty string, because we'll get a 02855 // spurious empty Subject: line in some of the parts. 02856 aPart->setName(" "); 02857 // partSpecifier 02858 QString partId( aDwBodyPart->partId() ); 02859 aPart->setPartSpecifier( partId ); 02860 02861 DwHeaders& headers = aDwBodyPart->Headers(); 02862 // Content-type 02863 QCString additionalCTypeParams; 02864 if (headers.HasContentType()) 02865 { 02866 DwMediaType& ct = headers.ContentType(); 02867 aPart->setOriginalContentTypeStr( ct.AsString().c_str() ); 02868 aPart->setTypeStr(ct.TypeStr().c_str()); 02869 aPart->setSubtypeStr(ct.SubtypeStr().c_str()); 02870 DwParameter *param = ct.FirstParameter(); 02871 while(param) 02872 { 02873 if (!qstricmp(param->Attribute().c_str(), "charset")) 02874 aPart->setCharset(QCString(param->Value().c_str()).lower()); 02875 else if (param->Attribute().c_str()=="name*") 02876 aPart->setName(KMMsgBase::decodeRFC2231String( 02877 param->Value().c_str())); 02878 else { 02879 additionalCTypeParams += ';'; 02880 additionalCTypeParams += param->AsString().c_str(); 02881 } 02882 param=param->Next(); 02883 } 02884 } 02885 else 02886 { 02887 aPart->setTypeStr("text"); // Set to defaults 02888 aPart->setSubtypeStr("plain"); 02889 } 02890 aPart->setAdditionalCTypeParamStr( additionalCTypeParams ); 02891 // Modification by Markus 02892 if (aPart->name().isEmpty() || aPart->name() == " ") 02893 { 02894 if (!headers.ContentType().Name().empty()) { 02895 aPart->setName(KMMsgBase::decodeRFC2047String(headers. 02896 ContentType().Name().c_str()) ); 02897 } else if (!headers.Subject().AsString().empty()) { 02898 aPart->setName( KMMsgBase::decodeRFC2047String(headers. 02899 Subject().AsString().c_str()) ); 02900 } 02901 } 02902 02903 // Content-transfer-encoding 02904 if (headers.HasContentTransferEncoding()) 02905 aPart->setCteStr(headers.ContentTransferEncoding().AsString().c_str()); 02906 else 02907 aPart->setCteStr("7bit"); 02908 02909 // Content-description 02910 if (headers.HasContentDescription()) 02911 aPart->setContentDescription(headers.ContentDescription().AsString().c_str()); 02912 else 02913 aPart->setContentDescription(""); 02914 02915 // Content-disposition 02916 if (headers.HasContentDisposition()) 02917 aPart->setContentDisposition(headers.ContentDisposition().AsString().c_str()); 02918 else 02919 aPart->setContentDisposition(""); 02920 02921 // Body 02922 if (withBody) 02923 aPart->setBody( aDwBodyPart->Body().AsString().c_str() ); 02924 else 02925 aPart->setBody( "" ); 02926 02927 } 02928 // If no valid body part was given, 02929 // set all MultipartBodyPart attributes to empty values. 02930 else 02931 { 02932 aPart->setTypeStr(""); 02933 aPart->setSubtypeStr(""); 02934 aPart->setCteStr(""); 02935 // This must not be an empty string, because we'll get a 02936 // spurious empty Subject: line in some of the parts. 02937 aPart->setName(" "); 02938 aPart->setContentDescription(""); 02939 aPart->setContentDisposition(""); 02940 aPart->setBody(""); 02941 } 02942 } 02943 } 02944 02945 02946 //----------------------------------------------------------------------------- 02947 void KMMessage::bodyPart(int aIdx, KMMessagePart* aPart) const 02948 { 02949 if ( !aPart ) 02950 return; 02951 02952 // If the DwBodyPart was found get the header fields and body 02953 if ( DwBodyPart *part = dwBodyPart( aIdx ) ) { 02954 KMMessage::bodyPart(part, aPart); 02955 if( aPart->name().isEmpty() ) 02956 aPart->setName( i18n("Attachment: %1").arg( aIdx ) ); 02957 } 02958 } 02959 02960 02961 //----------------------------------------------------------------------------- 02962 void KMMessage::deleteBodyParts() 02963 { 02964 mMsg->Body().DeleteBodyParts(); 02965 } 02966 02967 02968 //----------------------------------------------------------------------------- 02969 DwBodyPart* KMMessage::createDWBodyPart(const KMMessagePart* aPart) 02970 { 02971 DwBodyPart* part = DwBodyPart::NewBodyPart(emptyString, 0); 02972 02973 if ( !aPart ) 02974 return part; 02975 02976 QCString charset = aPart->charset(); 02977 QCString type = aPart->typeStr(); 02978 QCString subtype = aPart->subtypeStr(); 02979 QCString cte = aPart->cteStr(); 02980 QCString contDesc = aPart->contentDescriptionEncoded(); 02981 QCString contDisp = aPart->contentDisposition(); 02982 QCString encoding = autoDetectCharset(charset, sPrefCharsets, aPart->name()); 02983 if (encoding.isEmpty()) encoding = "utf-8"; 02984 QCString name = KMMsgBase::encodeRFC2231String(aPart->name(), encoding); 02985 bool RFC2231encoded = aPart->name() != QString(name); 02986 QCString paramAttr = aPart->parameterAttribute(); 02987 02988 DwHeaders& headers = part->Headers(); 02989 02990 DwMediaType& ct = headers.ContentType(); 02991 if (!type.isEmpty() && !subtype.isEmpty()) 02992 { 02993 ct.SetTypeStr(type.data()); 02994 ct.SetSubtypeStr(subtype.data()); 02995 if (!charset.isEmpty()){ 02996 DwParameter *param; 02997 param=new DwParameter; 02998 param->SetAttribute("charset"); 02999 param->SetValue(charset.data()); 03000 ct.AddParameter(param); 03001 } 03002 } 03003 03004 QCString additionalParam = aPart->additionalCTypeParamStr(); 03005 if( !additionalParam.isEmpty() ) 03006 { 03007 QCString parAV; 03008 DwString parA, parV; 03009 int iL, i1, i2, iM; 03010 iL = additionalParam.length(); 03011 i1 = 0; 03012 i2 = additionalParam.find(';', i1, false); 03013 while ( i1 < iL ) 03014 { 03015 if( -1 == i2 ) 03016 i2 = iL; 03017 if( i1+1 < i2 ) { 03018 parAV = additionalParam.mid( i1, (i2-i1) ); 03019 iM = parAV.find('='); 03020 if( -1 < iM ) 03021 { 03022 parA = parAV.left( iM ); 03023 parV = parAV.right( parAV.length() - iM - 1 ); 03024 if( ('"' == parV.at(0)) && ('"' == parV.at(parV.length()-1)) ) 03025 { 03026 parV.erase( 0, 1); 03027 parV.erase( parV.length()-1 ); 03028 } 03029 } 03030 else 03031 { 03032 parA = parAV; 03033 parV = ""; 03034 } 03035 DwParameter *param; 03036 param = new DwParameter; 03037 param->SetAttribute( parA ); 03038 param->SetValue( parV ); 03039 ct.AddParameter( param ); 03040 } 03041 i1 = i2+1; 03042 i2 = additionalParam.find(';', i1, false); 03043 } 03044 } 03045 03046 if (RFC2231encoded) 03047 { 03048 DwParameter *nameParam; 03049 nameParam = new DwParameter; 03050 nameParam->SetAttribute("name*"); 03051 nameParam->SetValue(name.data(),true); 03052 ct.AddParameter(nameParam); 03053 } else { 03054 if(!name.isEmpty()) 03055 ct.SetName(name.data()); 03056 } 03057 03058 if (!paramAttr.isEmpty()) 03059 { 03060 QCString encoding = autoDetectCharset(charset, sPrefCharsets, 03061 aPart->parameterValue()); 03062 if (encoding.isEmpty()) encoding = "utf-8"; 03063 QCString paramValue; 03064 paramValue = KMMsgBase::encodeRFC2231String(aPart->parameterValue(), 03065 encoding); 03066 DwParameter *param = new DwParameter; 03067 if (aPart->parameterValue() != QString(paramValue)) 03068 { 03069 param->SetAttribute((paramAttr + '*').data()); 03070 param->SetValue(paramValue.data(),true); 03071 } else { 03072 param->SetAttribute(paramAttr.data()); 03073 param->SetValue(paramValue.data()); 03074 } 03075 ct.AddParameter(param); 03076 } 03077 03078 if (!cte.isEmpty()) 03079 headers.Cte().FromString(cte); 03080 03081 if (!contDesc.isEmpty()) 03082 headers.ContentDescription().FromString(contDesc); 03083 03084 if (!contDisp.isEmpty()) 03085 headers.ContentDisposition().FromString(contDisp); 03086 03087 if (!aPart->body().isNull()) 03088 part->Body().FromString(aPart->body()); 03089 else 03090 part->Body().FromString(""); 03091 03092 if (!aPart->partSpecifier().isNull()) 03093 part->SetPartId( aPart->partSpecifier().latin1() ); 03094 03095 if (aPart->decodedSize() > 0) 03096 part->SetBodySize( aPart->decodedSize() ); 03097 03098 return part; 03099 } 03100 03101 03102 //----------------------------------------------------------------------------- 03103 void KMMessage::addDwBodyPart(DwBodyPart * aDwPart) 03104 { 03105 mMsg->Body().AddBodyPart( aDwPart ); 03106 mNeedsAssembly = TRUE; 03107 } 03108 03109 03110 //----------------------------------------------------------------------------- 03111 void KMMessage::addBodyPart(const KMMessagePart* aPart) 03112 { 03113 DwBodyPart* part = createDWBodyPart( aPart ); 03114 addDwBodyPart( part ); 03115 } 03116 03117 03118 //----------------------------------------------------------------------------- 03119 QString KMMessage::generateMessageId( const QString& addr ) 03120 { 03121 QDateTime datetime = QDateTime::currentDateTime(); 03122 QString msgIdStr; 03123 03124 msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" ); 03125 03126 QString msgIdSuffix; 03127 KConfigGroup general( KMKernel::config(), "General" ); 03128 03129 if( general.readBoolEntry( "useCustomMessageIdSuffix", false ) ) 03130 msgIdSuffix = general.readEntry( "myMessageIdSuffix" ); 03131 03132 if( !msgIdSuffix.isEmpty() ) 03133 msgIdStr += '@' + msgIdSuffix; 03134 else 03135 msgIdStr += '.' + addr; 03136 03137 msgIdStr += '>'; 03138 03139 return msgIdStr; 03140 } 03141 03142 03143 //----------------------------------------------------------------------------- 03144 QCString KMMessage::html2source( const QCString & src ) 03145 { 03146 QCString result( 1 + 6*src.length() ); // maximal possible length 03147 03148 QCString::ConstIterator s = src.begin(); 03149 QCString::Iterator d = result.begin(); 03150 while ( *s ) { 03151 switch ( *s ) { 03152 case '<': { 03153 *d++ = '&'; 03154 *d++ = 'l'; 03155 *d++ = 't'; 03156 *d++ = ';'; 03157 ++s; 03158 } 03159 break; 03160 case '\r': { 03161 ++s; 03162 } 03163 break; 03164 case '\n': { 03165 *d++ = '<'; 03166 *d++ = 'b'; 03167 *d++ = 'r'; 03168 *d++ = '>'; 03169 ++s; 03170 } 03171 break; 03172 case '>': { 03173 *d++ = '&'; 03174 *d++ = 'g'; 03175 *d++ = 't'; 03176 *d++ = ';'; 03177 ++s; 03178 } 03179 break; 03180 case '&': { 03181 *d++ = '&'; 03182 *d++ = 'a'; 03183 *d++ = 'm'; 03184 *d++ = 'p'; 03185 *d++ = ';'; 03186 ++s; 03187 } 03188 break; 03189 case '"': { 03190 *d++ = '&'; 03191 *d++ = 'q'; 03192 *d++ = 'u'; 03193 *d++ = 'o'; 03194 *d++ = 't'; 03195 *d++ = ';'; 03196 ++s; 03197 } 03198 break; 03199 case '\'': { 03200 *d++ = '&'; 03201 *d++ = 'a'; 03202 *d++ = 'p'; 03203 *d++ = 's'; 03204 *d++ = ';'; 03205 ++s; 03206 } 03207 break; 03208 default: 03209 *d++ = *s++; 03210 } 03211 } 03212 result.truncate( d - result.begin() ); // adds trailing NUL 03213 return result; 03214 } 03215 03216 03217 //----------------------------------------------------------------------------- 03218 QCString KMMessage::lf2crlf( const QCString & src ) 03219 { 03220 QCString result( 1 + 2*src.length() ); // maximal possible length 03221 03222 QCString::ConstIterator s = src.begin(); 03223 QCString::Iterator d = result.begin(); 03224 // we use cPrev to make sure we insert '\r' only there where it is missing 03225 char cPrev = '?'; 03226 while ( *s ) { 03227 if ( ('\n' == *s) && ('\r' != cPrev) ) 03228 *d++ = '\r'; 03229 cPrev = *s; 03230 *d++ = *s++; 03231 } 03232 result.truncate( d - result.begin() ); // adds trailing NUL 03233 return result; 03234 } 03235 03236 03237 //----------------------------------------------------------------------------- 03238 QString KMMessage::encodeMailtoUrl( const QString& str ) 03239 { 03240 QString result; 03241 result = QString::fromLatin1( KMMsgBase::encodeRFC2047String( str, 03242 "utf-8" ) ); 03243 result = KURL::encode_string( result ); 03244 return result; 03245 } 03246 03247 03248 //----------------------------------------------------------------------------- 03249 QString KMMessage::decodeMailtoUrl( const QString& url ) 03250 { 03251 QString result; 03252 result = KURL::decode_string( url ); 03253 result = KMMsgBase::decodeRFC2047String( result.latin1() ); 03254 return result; 03255 } 03256 03257 03258 //----------------------------------------------------------------------------- 03259 QCString KMMessage::stripEmailAddr( const QCString& aStr ) 03260 { 03261 //kdDebug(5006) << "KMMessage::stripEmailAddr( " << aStr << " )" << endl; 03262 03263 if ( aStr.isEmpty() ) 03264 return QCString(); 03265 03266 QCString result; 03267 03268 // The following is a primitive parser for a mailbox-list (cf. RFC 2822). 03269 // The purpose is to extract a displayable string from the mailboxes. 03270 // Comments in the addr-spec are not handled. No error checking is done. 03271 03272 QCString name; 03273 QCString comment; 03274 QCString angleAddress; 03275 enum { TopLevel, InComment, InAngleAddress } context = TopLevel; 03276 bool inQuotedString = false; 03277 int commentLevel = 0; 03278 03279 for ( char* p = aStr.data(); *p; ++p ) { 03280 switch ( context ) { 03281 case TopLevel : { 03282 switch ( *p ) { 03283 case '"' : inQuotedString = !inQuotedString; 03284 break; 03285 case '(' : if ( !inQuotedString ) { 03286 context = InComment; 03287 commentLevel = 1; 03288 } 03289 else 03290 name += *p; 03291 break; 03292 case '<' : if ( !inQuotedString ) { 03293 context = InAngleAddress; 03294 } 03295 else 03296 name += *p; 03297 break; 03298 case '\\' : // quoted character 03299 ++p; // skip the '\' 03300 if ( *p ) 03301 name += *p; 03302 break; 03303 case ',' : if ( !inQuotedString ) { 03304 // next email address 03305 if ( !result.isEmpty() ) 03306 result += ", "; 03307 name = name.stripWhiteSpace(); 03308 comment = comment.stripWhiteSpace(); 03309 angleAddress = angleAddress.stripWhiteSpace(); 03310 /* 03311 kdDebug(5006) << "Name : \"" << name 03312 << "\"" << endl; 03313 kdDebug(5006) << "Comment : \"" << comment 03314 << "\"" << endl; 03315 kdDebug(5006) << "Address : \"" << angleAddress 03316 << "\"" << endl; 03317 */ 03318 if ( angleAddress.isEmpty() && !comment.isEmpty() ) { 03319 // handle Outlook-style addresses like 03320 // john.doe@invalid (John Doe) 03321 result += comment; 03322 } 03323 else if ( !name.isEmpty() ) { 03324 result += name; 03325 } 03326 else if ( !comment.isEmpty() ) { 03327 result += comment; 03328 } 03329 else if ( !angleAddress.isEmpty() ) { 03330 result += angleAddress; 03331 } 03332 name = QCString(); 03333 comment = QCString(); 03334 angleAddress = QCString(); 03335 } 03336 else 03337 name += *p; 03338 break; 03339 default : name += *p; 03340 } 03341 break; 03342 } 03343 case InComment : { 03344 switch ( *p ) { 03345 case '(' : ++commentLevel; 03346 comment += *p; 03347 break; 03348 case ')' : --commentLevel; 03349 if ( commentLevel == 0 ) { 03350 context = TopLevel; 03351 comment += ' '; // separate the text of several comments 03352 } 03353 else 03354 comment += *p; 03355 break; 03356 case '\\' : // quoted character 03357 ++p; // skip the '\' 03358 if ( *p ) 03359 comment += *p; 03360 break; 03361 default : comment += *p; 03362 } 03363 break; 03364 } 03365 case InAngleAddress : { 03366 switch ( *p ) { 03367 case '"' : inQuotedString = !inQuotedString; 03368 angleAddress += *p; 03369 break; 03370 case '>' : if ( !inQuotedString ) { 03371 context = TopLevel; 03372 } 03373 else 03374 angleAddress += *p; 03375 break; 03376 case '\\' : // quoted character 03377 ++p; // skip the '\' 03378 if ( *p ) 03379 angleAddress += *p; 03380 break; 03381 default : angleAddress += *p; 03382 } 03383 break; 03384 } 03385 } // switch ( context ) 03386 } 03387 if ( !result.isEmpty() ) 03388 result += ", "; 03389 name = name.stripWhiteSpace(); 03390 comment = comment.stripWhiteSpace(); 03391 angleAddress = angleAddress.stripWhiteSpace(); 03392 /* 03393 kdDebug(5006) << "Name : \"" << name << "\"" << endl; 03394 kdDebug(5006) << "Comment : \"" << comment << "\"" << endl; 03395 kdDebug(5006) << "Address : \"" << angleAddress << "\"" << endl; 03396 */ 03397 if ( angleAddress.isEmpty() && !comment.isEmpty() ) { 03398 // handle Outlook-style addresses like 03399 // john.doe@invalid (John Doe) 03400 result += comment; 03401 } 03402 else if ( !name.isEmpty() ) { 03403 result += name; 03404 } 03405 else if ( !comment.isEmpty() ) { 03406 result += comment; 03407 } 03408 else if ( !angleAddress.isEmpty() ) { 03409 result += angleAddress; 03410 } 03411 03412 //kdDebug(5006) << "KMMessage::stripEmailAddr(...) returns \"" << result 03413 // << "\"" << endl; 03414 return result; 03415 } 03416 03417 //----------------------------------------------------------------------------- 03418 QString KMMessage::stripEmailAddr( const QString& aStr ) 03419 { 03420 //kdDebug(5006) << "KMMessage::stripEmailAddr( " << aStr << " )" << endl; 03421 03422 if ( aStr.isEmpty() ) 03423 return QString::null; 03424 03425 QString result; 03426 03427 // The following is a primitive parser for a mailbox-list (cf. RFC 2822). 03428 // The purpose is to extract a displayable string from the mailboxes. 03429 // Comments in the addr-spec are not handled. No error checking is done. 03430 03431 QString name; 03432 QString comment; 03433 QString angleAddress; 03434 enum { TopLevel, InComment, InAngleAddress } context = TopLevel; 03435 bool inQuotedString = false; 03436 int commentLevel = 0; 03437 03438 QChar ch; 03439 for ( uint index = 0; index < aStr.length(); ++index ) { 03440 ch = aStr[index]; 03441 switch ( context ) { 03442 case TopLevel : { 03443 switch ( ch.latin1() ) { 03444 case '"' : inQuotedString = !inQuotedString; 03445 break; 03446 case '(' : if ( !inQuotedString ) { 03447 context = InComment; 03448 commentLevel = 1; 03449 } 03450 else 03451 name += ch; 03452 break; 03453 case '<' : if ( !inQuotedString ) { 03454 context = InAngleAddress; 03455 } 03456 else 03457 name += ch; 03458 break; 03459 case '\\' : // quoted character 03460 ++index; // skip the '\' 03461 if ( index < aStr.length() ) 03462 name += aStr[index]; 03463 break; 03464 case ',' : if ( !inQuotedString ) { 03465 // next email address 03466 if ( !result.isEmpty() ) 03467 result += ", "; 03468 name = name.stripWhiteSpace(); 03469 comment = comment.stripWhiteSpace(); 03470 angleAddress = angleAddress.stripWhiteSpace(); 03471 /* 03472 kdDebug(5006) << "Name : \"" << name 03473 << "\"" << endl; 03474 kdDebug(5006) << "Comment : \"" << comment 03475 << "\"" << endl; 03476 kdDebug(5006) << "Address : \"" << angleAddress 03477 << "\"" << endl; 03478 */ 03479 if ( angleAddress.isEmpty() && !comment.isEmpty() ) { 03480 // handle Outlook-style addresses like 03481 // john.doe@invalid (John Doe) 03482 result += comment; 03483 } 03484 else if ( !name.isEmpty() ) { 03485 result += name; 03486 } 03487 else if ( !comment.isEmpty() ) { 03488 result += comment; 03489 } 03490 else if ( !angleAddress.isEmpty() ) { 03491 result += angleAddress; 03492 } 03493 name = QString::null; 03494 comment = QString::null; 03495 angleAddress = QString::null; 03496 } 03497 else 03498 name += ch; 03499 break; 03500 default : name += ch; 03501 } 03502 break; 03503 } 03504 case InComment : { 03505 switch ( ch.latin1() ) { 03506 case '(' : ++commentLevel; 03507 comment += ch; 03508 break; 03509 case ')' : --commentLevel; 03510 if ( commentLevel == 0 ) { 03511 context = TopLevel; 03512 comment += ' '; // separate the text of several comments 03513 } 03514 else 03515 comment += ch; 03516 break; 03517 case '\\' : // quoted character 03518 ++index; // skip the '\' 03519 if ( index < aStr.length() ) 03520 comment += aStr[index]; 03521 break; 03522 default : comment += ch; 03523 } 03524 break; 03525 } 03526 case InAngleAddress : { 03527 switch ( ch.latin1() ) { 03528 case '"' : inQuotedString = !inQuotedString; 03529 angleAddress += ch; 03530 break; 03531 case '>' : if ( !inQuotedString ) { 03532 context = TopLevel; 03533 } 03534 else 03535 angleAddress += ch; 03536 break; 03537 case '\\' : // quoted character 03538 ++index; // skip the '\' 03539 if ( index < aStr.length() ) 03540 angleAddress += aStr[index]; 03541 break; 03542 default : angleAddress += ch; 03543 } 03544 break; 03545 } 03546 } // switch ( context ) 03547 } 03548 if ( !result.isEmpty() ) 03549 result += ", "; 03550 name = name.stripWhiteSpace(); 03551 comment = comment.stripWhiteSpace(); 03552 angleAddress = angleAddress.stripWhiteSpace(); 03553 /* 03554 kdDebug(5006) << "Name : \"" << name << "\"" << endl; 03555 kdDebug(5006) << "Comment : \"" << comment << "\"" << endl; 03556 kdDebug(5006) << "Address : \"" << angleAddress << "\"" << endl; 03557 */ 03558 if ( angleAddress.isEmpty() && !comment.isEmpty() ) { 03559 // handle Outlook-style addresses like 03560 // john.doe@invalid (John Doe) 03561 result += comment; 03562 } 03563 else if ( !name.isEmpty() ) { 03564 result += name; 03565 } 03566 else if ( !comment.isEmpty() ) { 03567 result += comment; 03568 } 03569 else if ( !angleAddress.isEmpty() ) { 03570 result += angleAddress; 03571 } 03572 03573 //kdDebug(5006) << "KMMessage::stripEmailAddr(...) returns \"" << result 03574 // << "\"" << endl; 03575 return result; 03576 } 03577 03578 //----------------------------------------------------------------------------- 03579 QCString KMMessage::getEmailAddr(const QString& aStr) 03580 { 03581 int a, i, j, len, found = 0; 03582 QChar c; 03583 // Find the '@' in the email address: 03584 a = aStr.find('@'); 03585 if (a<0) return aStr.latin1(); 03586 // Loop backwards until we find '<', '(', ' ', or beginning of string. 03587 for (i = a - 1; i >= 0; i--) { 03588 c = aStr[i]; 03589 if (c == '<' || c == '(' || c == ' ') found = 1; 03590 if (found) break; 03591 } 03592 // Reset found for next loop. 03593 found = 0; 03594 // Loop forwards until we find '>', ')', ' ', or end of string. 03595 for (j = a + 1; j < (int)aStr.length(); j++) { 03596 c = aStr[j]; 03597 if (c == '>' || c == ')' || c == ' ') found = 1; 03598 if (found) break; 03599 } 03600 // Calculate the length and return the result. 03601 len = j - (i + 1); 03602 return aStr.mid(i+1,len).latin1(); 03603 } 03604 03605 //----------------------------------------------------------------------------- 03606 QString KMMessage::quoteHtmlChars( const QString& str, bool removeLineBreaks ) 03607 { 03608 QString result; 03609 result.reserve( 6*str.length() ); // maximal possible length 03610 03611 for( unsigned int i = 0; i < str.length(); ++i ) 03612 switch ( str[i].latin1() ) { 03613 case '<': 03614 result += "&lt;"; 03615 break; 03616 case '>': 03617 result += "&gt;"; 03618 break; 03619 case '&': 03620 result += "&amp;"; 03621 break; 03622 case '"': 03623 result += "&quot;"; 03624 break; 03625 case '\n': 03626 if ( !removeLineBreaks ) 03627 result += "<br>"; 03628 break; 03629 case '\r': 03630 // ignore CR 03631 break; 03632 default: 03633 result += str[i]; 03634 } 03635 03636 result.squeeze(); 03637 return result; 03638 } 03639 03640 //----------------------------------------------------------------------------- 03641 QString KMMessage::emailAddrAsAnchor(const QString& aEmail, bool stripped) 03642 { 03643 if( aEmail.isEmpty() ) 03644 return aEmail; 03645 03646 QStringList addressList = KMMessage::splitEmailAddrList( aEmail ); 03647 03648 QString result; 03649 03650 for( QStringList::ConstIterator it = addressList.begin(); 03651 ( it != addressList.end() ); 03652 ++it ) { 03653 if( !(*it).isEmpty() ) { 03654 QString address = *it; 03655 result += "<a href=\"mailto:" 03656 + KMMessage::encodeMailtoUrl( address ) 03657 + "\">"; 03658 if( stripped ) 03659 address = KMMessage::stripEmailAddr( address ); 03660 result += KMMessage::quoteHtmlChars( address, true ); 03661 result += "</a>, "; 03662 } 03663 } 03664 // cut of the trailing ", " 03665 result.truncate( result.length() - 2 ); 03666 03667 kdDebug(5006) << "KMMessage::emailAddrAsAnchor('" << aEmail 03668 << "') returns:\n-->" << result << "<--" << endl; 03669 return result; 03670 } 03671 03672 03673 //----------------------------------------------------------------------------- 03674 QStringList KMMessage::splitEmailAddrList(const QString& aStr) 03675 { 03676 // Features: 03677 // - always ignores quoted characters 03678 // - ignores everything (including parentheses and commas) 03679 // inside quoted strings 03680 // - supports nested comments 03681 // - ignores everything (including double quotes and commas) 03682 // inside comments 03683 03684 QStringList list; 03685 03686 if (aStr.isEmpty()) 03687 return list; 03688 03689 QString addr; 03690 uint addrstart = 0; 03691 int commentlevel = 0; 03692 bool insidequote = false; 03693 03694 for (uint index=0; index<aStr.length(); index++) { 03695 // the following conversion to latin1 is o.k. because 03696 // we can safely ignore all non-latin1 characters 03697 switch (aStr[index].latin1()) { 03698 case '"' : // start or end of quoted string 03699 if (commentlevel == 0) 03700 insidequote = !insidequote; 03701 break; 03702 case '(' : // start of comment 03703 if (!insidequote) 03704 commentlevel++; 03705 break; 03706 case ')' : // end of comment 03707 if (!insidequote) { 03708 if (commentlevel > 0) 03709 commentlevel--; 03710 else { 03711 kdDebug(5006) << "Error in address splitting: Unmatched ')'" 03712 << endl; 03713 return list; 03714 } 03715 } 03716 break; 03717 case '\\' : // quoted character 03718 index++; // ignore the quoted character 03719 break; 03720 case ',' : 03721 if (!insidequote && (commentlevel == 0)) { 03722 addr = aStr.mid(addrstart, index-addrstart); 03723 if (!addr.isEmpty()) 03724 list += addr.simplifyWhiteSpace(); 03725 addrstart = index+1; 03726 } 03727 break; 03728 } 03729 } 03730 // append the last address to the list 03731 if (!insidequote && (commentlevel == 0)) { 03732 addr = aStr.mid(addrstart, aStr.length()-addrstart); 03733 if (!addr.isEmpty()) 03734 list += addr.simplifyWhiteSpace(); 03735 } 03736 else 03737 kdDebug(5006) << "Error in address splitting: " 03738 << "Unexpected end of address list" 03739 << endl; 03740 03741 return list; 03742 } 03743 03744 03745 //----------------------------------------------------------------------------- 03746 //static 03747 QStringList KMMessage::stripAddressFromAddressList( const QString& address, 03748 const QStringList& list ) 03749 { 03750 QStringList addresses = list; 03751 QCString addrSpec = getEmailAddr( address ).lower(); 03752 for( QStringList::Iterator it = addresses.begin(); 03753 it != addresses.end(); ) { 03754 if( addrSpec == getEmailAddr( *it ).lower() ) { 03755 kdDebug(5006) << "Removing " << *it << " from the address list" 03756 << endl; 03757 it = addresses.remove( it ); 03758 } 03759 else 03760 ++it; 03761 } 03762 return addresses; 03763 } 03764 03765 03766 //----------------------------------------------------------------------------- 03767 //static 03768 QStringList KMMessage::stripMyAddressesFromAddressList( const QStringList& list ) 03769 { 03770 QStringList addresses = list; 03771 for( QStringList::Iterator it = addresses.begin(); 03772 it != addresses.end(); ) { 03773 kdDebug(5006) << "Check whether " << *it << " is one of my addresses" 03774 << endl; 03775 if( kmkernel->identityManager()->thatIsMe( getEmailAddr( *it ).lower() ) ) { 03776 kdDebug(5006) << "Removing " << *it << " from the address list" 03777 << endl; 03778 it = addresses.remove( it ); 03779 } 03780 else 03781 ++it; 03782 } 03783 return addresses; 03784 } 03785 03786 03787 //----------------------------------------------------------------------------- 03788 //static 03789 bool KMMessage::addressIsInAddressList( const QString& address, 03790 const QStringList& addresses ) 03791 { 03792 QCString addrSpec = getEmailAddr( address ).lower(); 03793 for( QStringList::ConstIterator it = addresses.begin(); 03794 it != addresses.end(); ++it ) { 03795 if( addrSpec == getEmailAddr( *it ).lower() ) 03796 return true; 03797 } 03798 return false; 03799 } 03800 03801 03802 //----------------------------------------------------------------------------- 03803 //static 03804 QString KMMessage::expandAliases( const QString& recipients ) 03805 { 03806 if ( recipients.isEmpty() ) 03807 return QString(); 03808 03809 QStringList recipientList = KMMessage::splitEmailAddrList( recipients ); 03810 03811 QString expandedRecipients; 03812 for ( QStringList::Iterator it = recipientList.begin(); 03813 it != recipientList.end(); ++it ) { 03814 if ( !expandedRecipients.isEmpty() ) 03815 expandedRecipients += ", "; 03816 QString receiver = (*it).stripWhiteSpace(); 03817 03818 // try to expand distribution list 03819 QString expandedList = KabcBridge::expandDistributionList( receiver ); 03820 if ( !expandedList.isEmpty() ) { 03821 expandedRecipients += expandedList; 03822 continue; 03823 } 03824 03825 // try to expand nick name 03826 QString expandedNickName = KabcBridge::expandNickName( receiver ); 03827 if ( !expandedNickName.isEmpty() ) { 03828 expandedRecipients += expandedNickName; 03829 continue; 03830 } 03831 03832 // check whether the address is missing the domain part 03833 // FIXME: looking for '@' might be wrong 03834 if ( receiver.find('@') == -1 ) { 03835 KConfigGroup general( KMKernel::config(), "General" ); 03836 QString defaultdomain = general.readEntry( "Default domain" ); 03837 if( !defaultdomain.isEmpty() ) { 03838 expandedRecipients += receiver + "@" + defaultdomain; 03839 } 03840 else { 03841 expandedRecipients += guessEmailAddressFromLoginName( receiver ); 03842 } 03843 } 03844 else 03845 expandedRecipients += receiver; 03846 } 03847 03848 return expandedRecipients; 03849 } 03850 03851 03852 //----------------------------------------------------------------------------- 03853 //static 03854 QString KMMessage::guessEmailAddressFromLoginName( const QString& loginName ) 03855 { 03856 if ( loginName.isEmpty() ) 03857 return QString(); 03858 03859 char hostnameC[256]; 03860 // null terminate this C string 03861 hostnameC[255] = '\0'; 03862 // set the string to 0 length if gethostname fails 03863 if ( gethostname( hostnameC, 255 ) ) 03864 hostnameC[0] = '\0'; 03865 QString address = loginName; 03866 address += '@'; 03867 address += QString::fromLocal8Bit( hostnameC ); 03868 03869 // try to determine the real name 03870 #if KDE_IS_VERSION( 3, 1, 92 ) 03871 const KUser user( loginName ); 03872 if ( user.isValid() ) { 03873 QString fullName = user.fullName(); 03874 #else 03875 if ( const struct passwd * pw = getpwnam( loginName.local8Bit() ) ) { 03876 QString fullName = QString::fromLocal8Bit( pw->pw_gecos ).simplifyWhiteSpace(); 03877 const int first_comma = fullName.find( ',' ); 03878 if ( first_comma > 0 ) 03879 fullName.truncate( first_comma ); 03880 #endif 03881 if ( fullName.find( QRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) != -1 ) 03882 address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" ) 03883 + "\" <" + address + '>'; 03884 else 03885 address = fullName + " <" + address + '>'; 03886 } 03887 03888 return address; 03889 } 03890 03891 //----------------------------------------------------------------------------- 03892 void KMMessage::readConfig() 03893 { 03894 KConfig *config=KMKernel::config(); 03895 KConfigGroupSaver saver(config, "General"); 03896 03897 config->setGroup("General"); 03898 03899 int languageNr = config->readNumEntry("reply-current-language",0); 03900 03901 { // area for config group "KMMessage #n" 03902 KConfigGroupSaver saver(config, QString("KMMessage #%1").arg(languageNr)); 03903 sReplyLanguage = config->readEntry("language",KGlobal::locale()->language()); 03904 sReplyStr = config->readEntry("phrase-reply", 03905 i18n("On %D, you wrote:")); 03906 sReplyAllStr = config->readEntry("phrase-reply-all", 03907 i18n("On %D, %F wrote:")); 03908 sForwardStr = config->readEntry("phrase-forward", 03909 i18n("Forwarded Message")); 03910 sIndentPrefixStr = config->readEntry("indent-prefix",">%_"); 03911 } 03912 03913 { // area for config group "Composer" 03914 KConfigGroupSaver saver(config, "Composer"); 03915 sReplySubjPrefixes = config->readListEntry("reply-prefixes", ','); 03916 if (sReplySubjPrefixes.count() == 0) 03917 sReplySubjPrefixes << "Re\\s*:" << "Re\\[\\d+\\]:" << "Re\\d+:"; 03918 sReplaceSubjPrefix = config->readBoolEntry("replace-reply-prefix", true); 03919 sForwardSubjPrefixes = config->readListEntry("forward-prefixes", ','); 03920 if (sForwardSubjPrefixes.count() == 0) 03921 sForwardSubjPrefixes << "Fwd:" << "FW:"; 03922 sReplaceForwSubjPrefix = config->readBoolEntry("replace-forward-prefix", true); 03923 03924 sSmartQuote = config->readBoolEntry("smart-quote", true); 03925 sWordWrap = config->readBoolEntry( "word-wrap", true ); 03926 sWrapCol = config->readNumEntry("break-at", 78); 03927 if ((sWrapCol == 0) || (sWrapCol > 78)) 03928 sWrapCol = 78; 03929 if (sWrapCol < 30) 03930 sWrapCol = 30; 03931 03932 sPrefCharsets = config->readListEntry("pref-charsets"); 03933 } 03934 03935 { // area for config group "Reader" 03936 KConfigGroupSaver saver(config, "Reader"); 03937 sHeaderStrategy = HeaderStrategy::create( config->readEntry( "header-set-displayed", "rich" ) ); 03938 } 03939 } 03940 03941 QCString KMMessage::defaultCharset() 03942 { 03943 QCString retval; 03944 03945 if (!sPrefCharsets.isEmpty()) 03946 retval = sPrefCharsets[0].latin1(); 03947 03948 if (retval.isEmpty() || (retval == "locale")) 03949 retval = QCString(kmkernel->networkCodec()->mimeName()).lower(); 03950 03951 if (retval == "jisx0208.1983-0") retval = "iso-2022-jp"; 03952 else if (retval == "ksc5601.1987-0") retval = "euc-kr"; 03953 return retval; 03954 } 03955 03956 const QStringList &KMMessage::preferredCharsets() 03957 { 03958 return sPrefCharsets; 03959 } 03960 03961 //----------------------------------------------------------------------------- 03962 QCString KMMessage::charset() const 03963 { 03964 DwMediaType &mType=mMsg->Headers().ContentType(); 03965 mType.Parse(); 03966 DwParameter *param=mType.FirstParameter(); 03967 while(param){ 03968 if (!qstricmp(param->Attribute().c_str(), "charset")) 03969 return param->Value().c_str(); 03970 else param=param->Next(); 03971 } 03972 return ""; // us-ascii, but we don't have to specify it 03973 } 03974 03975 //----------------------------------------------------------------------------- 03976 void KMMessage::setCharset(const QCString& bStr) 03977 { 03978 QCString aStr = bStr.lower(); 03979 if (aStr.isNull()) 03980 aStr = ""; 03981 DwMediaType &mType = dwContentType(); 03982 mType.Parse(); 03983 DwParameter *param=mType.FirstParameter(); 03984 while(param) 03985 // FIXME use the mimelib functions here for comparison. 03986 if (!qstricmp(param->Attribute().c_str(), "charset")) break; 03987 else param=param->Next(); 03988 if (!param){ 03989 param=new DwParameter; 03990 param->SetAttribute("charset"); 03991 mType.AddParameter(param); 03992 } 03993 else 03994 mType.SetModified(); 03995 param->SetValue(DwString(aStr)); 03996 mType.Assemble(); 03997 } 03998 03999 04000 //----------------------------------------------------------------------------- 04001 void KMMessage::setStatus(const KMMsgStatus aStatus, int idx) 04002 { 04003 if (mStatus == aStatus) 04004 return; 04005 KMMsgBase::setStatus(aStatus, idx); 04006 } 04007 04008 void KMMessage::setEncryptionState(const KMMsgEncryptionState s, int idx) 04009 { 04010 if( mEncryptionState == s ) 04011 return; 04012 mEncryptionState = s; 04013 mDirty = true; 04014 KMMsgBase::setEncryptionState(s, idx); 04015 } 04016 04017 void KMMessage::setSignatureState(KMMsgSignatureState s, int idx) 04018 { 04019 if( mSignatureState == s ) 04020 return; 04021 mSignatureState = s; 04022 mDirty = true; 04023 KMMsgBase::setSignatureState(s, idx); 04024 } 04025 04026 void KMMessage::setMDNSentState( KMMsgMDNSentState status, int idx ) { 04027 if ( mMDNSentState == status ) 04028 return; 04029 mMDNSentState = status; 04030 mDirty = true; 04031 KMMsgBase::setMDNSentState( status, idx ); 04032 } 04033 04034 //----------------------------------------------------------------------------- 04035 void KMMessage::link(const KMMessage *aMsg, KMMsgStatus aStatus) 04036 { 04037 Q_ASSERT(aStatus == KMMsgStatusReplied || aStatus == KMMsgStatusForwarded); 04038 04039 QString message = headerField("X-KMail-Link-Message"); 04040 if (!message.isEmpty()) 04041 message += ','; 04042 QString type = headerField("X-KMail-Link-Type"); 04043 if (!type.isEmpty()) 04044 type += ','; 04045 04046 message += QString::number(aMsg->getMsgSerNum()); 04047 if (aStatus == KMMsgStatusReplied) 04048 type += "reply"; 04049 else if (aStatus == KMMsgStatusForwarded) 04050 type += "forward"; 04051 04052 setHeaderField("X-KMail-Link-Message", message); 04053 setHeaderField("X-KMail-Link-Type", type); 04054 } 04055 04056 //----------------------------------------------------------------------------- 04057 void KMMessage::getLink(int n, ulong *retMsgSerNum, KMMsgStatus *retStatus) const 04058 { 04059 *retMsgSerNum = 0; 04060 *retStatus = KMMsgStatusUnknown; 04061 04062 QString message = headerField("X-KMail-Link-Message"); 04063 QString type = headerField("X-KMail-Link-Type"); 04064 message = message.section(',', n, n); 04065 type = type.section(',', n, n); 04066 04067 if (!message.isEmpty() && !type.isEmpty()) { 04068 *retMsgSerNum = message.toULong(); 04069 if (type == "reply") 04070 *retStatus = KMMsgStatusReplied; 04071 else if (type == "forward") 04072 *retStatus = KMMsgStatusForwarded; 04073 } 04074 } 04075 04076 //----------------------------------------------------------------------------- 04077 DwBodyPart* KMMessage::findDwBodyPart( DwBodyPart* part, const QString & partSpecifier ) 04078 { 04079 if ( !part ) return 0; 04080 DwBodyPart* current; 04081 04082 if ( part->partId() == partSpecifier ) 04083 return part; 04084 04085 // multipart 04086 if ( part->hasHeaders() && 04087 part->Headers().HasContentType() && 04088 part->Body().FirstBodyPart() && 04089 (DwMime::kTypeMultipart == part->Headers().ContentType().Type() ) && 04090 (current = findDwBodyPart( part->Body().FirstBodyPart(), partSpecifier )) ) 04091 { 04092 return current; 04093 } 04094 04095 // encapsulated message 04096 if ( part->Body().Message() && 04097 part->Body().Message()->Body().FirstBodyPart() && 04098 (current = findDwBodyPart( part->Body().Message()->Body().FirstBodyPart(), partSpecifier )) ) 04099 { 04100 return current; 04101 } 04102 04103 // next part 04104 return findDwBodyPart( part->Next(), partSpecifier ); 04105 } 04106 04107 //----------------------------------------------------------------------------- 04108 void KMMessage::updateBodyPart(const QString partSpecifier, const QByteArray & data) 04109 { 04110 DwString content( data.data(), data.size() ); 04111 if ( numBodyParts() > 0 && 04112 partSpecifier != "0" && 04113 partSpecifier != "TEXT" ) 04114 { 04115 QString specifier = partSpecifier; 04116 if ( partSpecifier.endsWith(".HEADER") || 04117 partSpecifier.endsWith(".MIME") ) { 04118 // get the parent bodypart 04119 specifier = partSpecifier.section( '.', 0, -2 ); 04120 } 04121 kdDebug(5006) << "KMMessage::updateBodyPart " << specifier << endl; 04122 04123 // search for the bodypart 04124 mLastUpdated = findDwBodyPart( getFirstDwBodyPart(), specifier ); 04125 if (!mLastUpdated) 04126 { 04127 kdWarning(5006) << "KMMessage::updateBodyPart - can not find part " 04128 << specifier << endl; 04129 return; 04130 } 04131 if ( partSpecifier.endsWith(".MIME") ) 04132 { 04133 // update headers 04134 // get rid of EOL 04135 content.resize( content.length()-2 ); 04136 // we have to delete the fields first as they might by created by an earlier 04137 // call to DwHeaders::FieldBody 04138 mLastUpdated->Headers().DeleteAllFields(); 04139 mLastUpdated->Headers().FromString( content ); 04140 mLastUpdated->Headers().Parse(); 04141 } else { 04142 // update body 04143 mLastUpdated->Body().FromString( content ); 04144 mLastUpdated->Body().Parse(); 04145 } 04146 04147 } else 04148 { 04149 // update text-only messages 04150 if ( partSpecifier == "TEXT" ) 04151 deleteBodyParts(); // delete empty parts first 04152 mMsg->Body().FromString( content ); 04153 mMsg->Body().Parse(); 04154 } 04155 mNeedsAssembly = true; 04156 if (!( partSpecifier.endsWith(".HEADER") || partSpecifier.endsWith(".MIME") )) 04157 { 04158 // notify observers 04159 notify(); 04160 } 04161 } 04162 04163 void KMMessage::setBodyFromUnicode( const QString & str ) { 04164 QCString encoding = KMMsgBase::autoDetectCharset( charset(), KMMessage::preferredCharsets(), str ); 04165 if ( encoding.isEmpty() ) 04166 encoding = "utf-8"; 04167 const QTextCodec * codec = KMMsgBase::codecForName( encoding ); 04168 assert( codec ); 04169 QValueList<int> dummy; 04170 setCharset( encoding ); 04171 setBodyAndGuessCte( codec->fromUnicode( str ), dummy, false /* no 8bit */ ); 04172 } 04173 04174 const QTextCodec * KMMessage::codec() const { 04175 const QTextCodec * c = mOverrideCodec; 04176 if ( !c ) 04177 // no override-codec set for this message, try the CT charset parameter: 04178 c = KMMsgBase::codecForName( charset() ); 04179 if ( !c ) 04180 // no charset means us-ascii (RFC 2045), so using local encoding should 04181 // be okay 04182 c = kmkernel->networkCodec(); 04183 assert( c ); 04184 return c; 04185 } 04186 04187 QString KMMessage::bodyToUnicode(const QTextCodec* codec) const { 04188 if ( !codec ) 04189 // No codec was given, so try the charset in the mail 04190 codec = this->codec(); 04191 assert( codec ); 04192 04193 return codec->toUnicode( bodyDecoded() ); 04194 } 04195
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:58:03 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003