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