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