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