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