kmail Library API Documentation

kmsearchpattern.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*- 00002 // kmsearchpattern.cpp 00003 // Author: Marc Mutz <Marc@Mutz.com> 00004 // This code is under GPL! 00005 00006 #include <config.h> 00007 00008 #include "kmaddrbook.h" 00009 #include "kmsearchpattern.h" 00010 #include "kmmessage.h" 00011 #include "kmmsgindex.h" 00012 #include "kmmsgdict.h" 00013 #include "filterlog.h" 00014 using KMail::FilterLog; 00015 00016 #include <libkdepim/email.h> 00017 00018 #include <kglobal.h> 00019 #include <klocale.h> 00020 #include <kdebug.h> 00021 #include <kconfig.h> 00022 00023 #include <kabc/stdaddressbook.h> 00024 00025 #include <qregexp.h> 00026 00027 #include <mimelib/string.h> 00028 #include <mimelib/boyermor.h> 00029 00030 #include <assert.h> 00031 00032 static const char* funcConfigNames[] = 00033 { "contains", "contains-not", "equals", "not-equal", "regexp", 00034 "not-regexp", "greater", "less-or-equal", "less", "greater-or-equal", 00035 "is-in-addressbook", "is-not-in-addressbook" , "is-in-category", "is-not-in-category", 00036 "has-attachment", "has-no-attachment"}; 00037 static const int numFuncConfigNames = sizeof funcConfigNames / sizeof *funcConfigNames; 00038 00039 00040 //================================================== 00041 // 00042 // class KMSearchRule (was: KMFilterRule) 00043 // 00044 //================================================== 00045 00046 KMSearchRule::KMSearchRule( const QCString & field, Function func, const QString & contents ) 00047 : mField( field ), 00048 mFunction( func ), 00049 mContents( contents ) 00050 { 00051 } 00052 00053 KMSearchRule::KMSearchRule( const KMSearchRule & other ) 00054 : mField( other.mField ), 00055 mFunction( other.mFunction ), 00056 mContents( other.mContents ) 00057 { 00058 } 00059 00060 const KMSearchRule & KMSearchRule::operator=( const KMSearchRule & other ) { 00061 if ( this == &other ) 00062 return *this; 00063 00064 mField = other.mField; 00065 mFunction = other.mFunction; 00066 mContents = other.mContents; 00067 00068 return *this; 00069 } 00070 00071 KMSearchRule * KMSearchRule::createInstance( const QCString & field, 00072 Function func, 00073 const QString & contents ) 00074 { 00075 KMSearchRule *ret; 00076 if (field == "<status>") 00077 ret = new KMSearchRuleStatus( field, func, contents ); 00078 else if ( field == "<age in days>" || field == "<size>" ) 00079 ret = new KMSearchRuleNumerical( field, func, contents ); 00080 else 00081 ret = new KMSearchRuleString( field, func, contents ); 00082 00083 return ret; 00084 } 00085 00086 KMSearchRule * KMSearchRule::createInstance( const QCString & field, 00087 const char *func, 00088 const QString & contents ) 00089 { 00090 return ( createInstance( field, configValueToFunc( func ), contents ) ); 00091 } 00092 00093 KMSearchRule * KMSearchRule::createInstance( const KMSearchRule & other ) 00094 { 00095 return ( createInstance( other.field(), other.function(), other.contents() ) ); 00096 } 00097 00098 KMSearchRule * KMSearchRule::createInstanceFromConfig( const KConfig * config, int aIdx ) 00099 { 00100 const char cIdx = char( int('A') + aIdx ); 00101 00102 static const QString & field = KGlobal::staticQString( "field" ); 00103 static const QString & func = KGlobal::staticQString( "func" ); 00104 static const QString & contents = KGlobal::staticQString( "contents" ); 00105 00106 const QCString &field2 = config->readEntry( field + cIdx ).latin1(); 00107 Function func2 = configValueToFunc( config->readEntry( func + cIdx ).latin1() ); 00108 const QString & contents2 = config->readEntry( contents + cIdx ); 00109 00110 if ( field2 == "<To or Cc>" ) // backwards compat 00111 return KMSearchRule::createInstance( "<recipients>", func2, contents2 ); 00112 else 00113 return KMSearchRule::createInstance( field2, func2, contents2 ); 00114 } 00115 00116 KMSearchRule::Function KMSearchRule::configValueToFunc( const char * str ) { 00117 if ( !str ) 00118 return FuncNone; 00119 00120 for ( int i = 0 ; i < numFuncConfigNames ; ++i ) 00121 if ( qstricmp( funcConfigNames[i], str ) == 0 ) return (Function)i; 00122 00123 return FuncNone; 00124 } 00125 00126 QString KMSearchRule::functionToString( Function function ) 00127 { 00128 if ( function != FuncNone ) 00129 return funcConfigNames[int( function )]; 00130 else 00131 return "invalid"; 00132 } 00133 00134 void KMSearchRule::writeConfig( KConfig * config, int aIdx ) const { 00135 const char cIdx = char('A' + aIdx); 00136 static const QString & field = KGlobal::staticQString( "field" ); 00137 static const QString & func = KGlobal::staticQString( "func" ); 00138 static const QString & contents = KGlobal::staticQString( "contents" ); 00139 00140 config->writeEntry( field + cIdx, QString(mField) ); 00141 config->writeEntry( func + cIdx, functionToString( mFunction ) ); 00142 config->writeEntry( contents + cIdx, mContents ); 00143 } 00144 00145 bool KMSearchRule::matches( const DwString & aStr, KMMessage & msg, 00146 const DwBoyerMoore *, int ) const 00147 { 00148 if ( !msg.isComplete() ) { 00149 msg.fromDwString( aStr ); 00150 msg.setComplete( true ); 00151 } 00152 return matches( &msg ); 00153 } 00154 00155 const QString KMSearchRule::asString() const 00156 { 00157 QString result = "\"" + mField + "\" <"; 00158 result += functionToString( mFunction ); 00159 result += "> \"" + mContents + "\""; 00160 00161 return result; 00162 } 00163 00164 00165 00166 //================================================== 00167 // 00168 // class KMSearchRuleString 00169 // 00170 //================================================== 00171 00172 KMSearchRuleString::KMSearchRuleString( const QCString & field, 00173 Function func, const QString & contents ) 00174 : KMSearchRule(field, func, contents) 00175 { 00176 if ( field.isEmpty() || field[0] == '<' ) 00177 mBmHeaderField = 0; 00178 else //TODO handle the unrealistic case of the message starting with mField 00179 mBmHeaderField = new DwBoyerMoore(("\n" + field + ": ").data()); 00180 } 00181 00182 KMSearchRuleString::KMSearchRuleString( const KMSearchRuleString & other ) 00183 : KMSearchRule( other ), 00184 mBmHeaderField( 0 ) 00185 { 00186 if ( other.mBmHeaderField ) 00187 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField ); 00188 } 00189 00190 const KMSearchRuleString & KMSearchRuleString::operator=( const KMSearchRuleString & other ) 00191 { 00192 if ( this == &other ) 00193 return *this; 00194 00195 setField( other.field() ); 00196 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField ); 00197 setFunction( other.function() ); 00198 setContents( other.contents() ); 00199 delete mBmHeaderField; mBmHeaderField = 0; 00200 if ( other.mBmHeaderField ) 00201 mBmHeaderField = new DwBoyerMoore( *other.mBmHeaderField ); 00202 00203 return *this; 00204 } 00205 00206 KMSearchRuleString::~KMSearchRuleString() 00207 { 00208 delete mBmHeaderField; 00209 mBmHeaderField = 0; 00210 } 00211 00212 bool KMSearchRuleString::isEmpty() const 00213 { 00214 return field().stripWhiteSpace().isEmpty() || contents().isEmpty(); 00215 } 00216 00217 bool KMSearchRuleString::requiresBody() const 00218 { 00219 if (mBmHeaderField || (field() == "<recipients>" )) 00220 return false; 00221 return true; 00222 } 00223 00224 bool KMSearchRuleString::matches( const DwString & aStr, KMMessage & msg, 00225 const DwBoyerMoore * aHeaderField, int aHeaderLen ) const 00226 { 00227 if ( isEmpty() ) 00228 return false; 00229 00230 const DwBoyerMoore * headerField = aHeaderField ? aHeaderField : mBmHeaderField ; 00231 00232 const int headerLen = ( aHeaderLen > -1 ? aHeaderLen : field().length() ) + 2 ; // +1 for ': ' 00233 00234 if ( headerField ) { 00235 static const DwBoyerMoore lflf( "\n\n" ); 00236 static const DwBoyerMoore lfcrlf( "\n\r\n" ); 00237 00238 size_t endOfHeader = lflf.FindIn( aStr, 0 ); 00239 if ( endOfHeader == DwString::npos ) 00240 endOfHeader = lfcrlf.FindIn( aStr, 0 ); 00241 const DwString headers = ( endOfHeader == DwString::npos ) ? aStr : aStr.substr( 0, endOfHeader ); 00242 size_t start = headerField->FindIn( headers, 0, false ); 00243 // if the header field doesn't exist then return false for positive 00244 // functions and true for negated functions (e.g. "does not 00245 // contain"); note that all negated string functions correspond 00246 // to an odd value 00247 if ( start == DwString::npos ) 00248 return ( ( function() & 1 ) == 1 ); 00249 start += headerLen; 00250 size_t stop = aStr.find( '\n', start ); 00251 char ch = '\0'; 00252 while ( stop != DwString::npos && ( ch = aStr.at( stop + 1 ) ) == ' ' || ch == '\t' ) 00253 stop = aStr.find( '\n', stop + 1 ); 00254 const int len = stop == DwString::npos ? aStr.length() - start : stop - start ; 00255 const QCString codedValue( aStr.data() + start, len + 1 ); 00256 const QString msgContents = KMMsgBase::decodeRFC2047String( codedValue ).stripWhiteSpace(); // FIXME: This needs to be changed for IDN support. 00257 return matchesInternal( msgContents ); 00258 } else if ( field() == "<recipients>" ) { 00259 static const DwBoyerMoore to("\nTo: "); 00260 static const DwBoyerMoore cc("\nCc: "); 00261 static const DwBoyerMoore bcc("\nBcc: "); 00262 // <recipients> "contains" "foo" is true if any of the fields contains 00263 // "foo", while <recipients> "does not contain" "foo" is true if none 00264 // of the fields contains "foo" 00265 if ( ( function() & 1 ) == 0 ) { 00266 // positive function, e.g. "contains" 00267 return ( matches( aStr, msg, &to, 2 ) || 00268 matches( aStr, msg, &cc, 2 ) || 00269 matches( aStr, msg, &bcc, 3 ) ); 00270 } 00271 else { 00272 // negated function, e.g. "does not contain" 00273 return ( matches( aStr, msg, &to, 2 ) && 00274 matches( aStr, msg, &cc, 2 ) && 00275 matches( aStr, msg, &bcc, 3 ) ); 00276 } 00277 } 00278 return false; 00279 } 00280 00281 bool KMSearchRuleString::matches( const KMMessage * msg ) const 00282 { 00283 assert( msg ); 00284 00285 if ( isEmpty() ) 00286 return false; 00287 00288 QString msgContents; 00289 // Show the value used to compare the rules against in the log. 00290 // Overwrite the value for complete messages and all headers! 00291 bool logContents = true; 00292 00293 if( field() == "<message>" ) { 00294 msgContents = msg->asString(); 00295 logContents = false; 00296 } else if ( field() == "<body>" ) { 00297 msgContents = msg->bodyToUnicode(); 00298 logContents = false; 00299 } else if ( field() == "<any header>" ) { 00300 msgContents = msg->headerAsString(); 00301 logContents = false; 00302 } else if ( field() == "<recipients>" ) { 00303 // (mmutz 2001-11-05) hack to fix "<recipients> !contains foo" to 00304 // meet user's expectations. See FAQ entry in KDE 2.2.2's KMail 00305 // handbook 00306 if ( function() == FuncEquals || function() == FuncNotEqual ) 00307 // do we need to treat this case specially? Ie.: What shall 00308 // "equality" mean for recipients. 00309 return matchesInternal( msg->headerField("To") ) 00310 || matchesInternal( msg->headerField("Cc") ) 00311 || matchesInternal( msg->headerField("Bcc") ) 00312 // sometimes messages have multiple Cc headers 00313 || matchesInternal( msg->cc() ); 00314 00315 msgContents = msg->headerField("To"); 00316 if ( !msg->headerField("Cc").compare( msg->cc() ) ) 00317 msgContents += ", " + msg->headerField("Cc"); 00318 else 00319 msgContents += ", " + msg->cc(); 00320 msgContents += ", " + msg->headerField("Bcc"); 00321 } else { 00322 // make sure to treat messages with multiple header lines for 00323 // the same header correctly 00324 msgContents = msg->headerFields( field() ).join( " " ); 00325 } 00326 00327 if ( function() == FuncIsInAddressbook || 00328 function() == FuncIsNotInAddressbook ) { 00329 // I think only the "from"-field makes sense. 00330 msgContents = msg->headerField( field() ); 00331 if ( msgContents.isEmpty() ) 00332 return false; 00333 } 00334 00335 // these two functions need the kmmessage therefore they don't call matchesInternal 00336 if ( function() == FuncHasAttachment ) 00337 return ( msg->toMsgBase().attachmentState() == KMMsgHasAttachment ); 00338 if ( function() == FuncHasNoAttachment ) 00339 return ( ((KMMsgAttachmentState) msg->toMsgBase().attachmentState()) == KMMsgHasNoAttachment ); 00340 00341 bool rc = matchesInternal( msgContents ); 00342 if ( FilterLog::instance()->isLogging() ) { 00343 QString msg = ( rc ? "<font color=#00FF00>1 = </font>" 00344 : "<font color=#FF0000>0 = </font>" ); 00345 msg += FilterLog::recode( asString() ); 00346 // only log headers bcause messages and bodies can be pretty large 00347 if ( logContents ) 00348 msg += " (<i>" + FilterLog::recode( msgContents ) + "</i>)"; 00349 FilterLog::instance()->add( msg, FilterLog::ruleResult ); 00350 } 00351 return rc; 00352 } 00353 00354 // helper, does the actual comparing 00355 bool KMSearchRuleString::matchesInternal( const QString & msgContents ) const 00356 { 00357 switch ( function() ) { 00358 case KMSearchRule::FuncEquals: 00359 return ( QString::compare( msgContents.lower(), contents().lower() ) == 0 ); 00360 00361 case KMSearchRule::FuncNotEqual: 00362 return ( QString::compare( msgContents.lower(), contents().lower() ) != 0 ); 00363 00364 case KMSearchRule::FuncContains: 00365 return ( msgContents.find( contents(), 0, false ) >= 0 ); 00366 00367 case KMSearchRule::FuncContainsNot: 00368 return ( msgContents.find( contents(), 0, false ) < 0 ); 00369 00370 case KMSearchRule::FuncRegExp: 00371 { 00372 QRegExp regexp( contents(), false ); 00373 return ( regexp.search( msgContents ) >= 0 ); 00374 } 00375 00376 case KMSearchRule::FuncNotRegExp: 00377 { 00378 QRegExp regexp( contents(), false ); 00379 return ( regexp.search( msgContents ) < 0 ); 00380 } 00381 00382 case FuncIsGreater: 00383 return ( QString::compare( msgContents.lower(), contents().lower() ) > 0 ); 00384 00385 case FuncIsLessOrEqual: 00386 return ( QString::compare( msgContents.lower(), contents().lower() ) <= 0 ); 00387 00388 case FuncIsLess: 00389 return ( QString::compare( msgContents.lower(), contents().lower() ) < 0 ); 00390 00391 case FuncIsGreaterOrEqual: 00392 return ( QString::compare( msgContents.lower(), contents().lower() ) >= 0 ); 00393 00394 case FuncIsInAddressbook: { 00395 KABC::AddressBook *stdAb = KABC::StdAddressBook::self(); 00396 QStringList addressList = 00397 KPIM::splitEmailAddrList( msgContents.lower() ); 00398 for( QStringList::ConstIterator it = addressList.begin(); 00399 ( it != addressList.end() ); 00400 ++it ) { 00401 if ( !stdAb->findByEmail( KPIM::getEmailAddr( *it ) ).isEmpty() ) 00402 return true; 00403 } 00404 return false; 00405 } 00406 00407 case FuncIsNotInAddressbook: { 00408 KABC::AddressBook *stdAb = KABC::StdAddressBook::self(); 00409 QStringList addressList = 00410 KPIM::splitEmailAddrList( msgContents.lower() ); 00411 for( QStringList::ConstIterator it = addressList.begin(); 00412 ( it != addressList.end() ); 00413 ++it ) { 00414 if ( stdAb->findByEmail( KPIM::getEmailAddr( *it ) ).isEmpty() ) 00415 return true; 00416 } 00417 return false; 00418 } 00419 00420 case FuncIsInCategory: { 00421 QString category = contents(); 00422 QStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() ); 00423 KABC::AddressBook *stdAb = KABC::StdAddressBook::self(); 00424 00425 for( QStringList::ConstIterator it = addressList.begin(); 00426 it != addressList.end(); ++it ) { 00427 KABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddr( *it ) ); 00428 00429 for ( KABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd ) 00430 if ( (*itAd).hasCategory(category) ) 00431 return true; 00432 00433 } 00434 return false; 00435 } 00436 00437 case FuncIsNotInCategory: { 00438 QString category = contents(); 00439 QStringList addressList = KPIM::splitEmailAddrList( msgContents.lower() ); 00440 KABC::AddressBook *stdAb = KABC::StdAddressBook::self(); 00441 00442 for( QStringList::ConstIterator it = addressList.begin(); 00443 it != addressList.end(); ++it ) { 00444 KABC::Addressee::List addresses = stdAb->findByEmail( KPIM::getEmailAddr( *it ) ); 00445 00446 for ( KABC::Addressee::List::Iterator itAd = addresses.begin(); itAd != addresses.end(); ++itAd ) 00447 if ( (*itAd).hasCategory(category) ) 00448 return false; 00449 00450 } 00451 return true; 00452 } 00453 default: 00454 ; 00455 } 00456 00457 return false; 00458 } 00459 00460 00461 //================================================== 00462 // 00463 // class KMSearchRuleNumerical 00464 // 00465 //================================================== 00466 00467 KMSearchRuleNumerical::KMSearchRuleNumerical( const QCString & field, 00468 Function func, const QString & contents ) 00469 : KMSearchRule(field, func, contents) 00470 { 00471 } 00472 00473 bool KMSearchRuleNumerical::isEmpty() const 00474 { 00475 bool ok = false; 00476 contents().toInt( &ok ); 00477 00478 return !ok; 00479 } 00480 00481 00482 bool KMSearchRuleNumerical::matches( const KMMessage * msg ) const 00483 { 00484 00485 QString msgContents; 00486 int numericalMsgContents = 0; 00487 int numericalValue = 0; 00488 00489 if ( field() == "<size>" ) { 00490 numericalMsgContents = int( msg->msgLength() ); 00491 numericalValue = contents().toInt(); 00492 msgContents.setNum( numericalMsgContents ); 00493 } else if ( field() == "<age in days>" ) { 00494 QDateTime msgDateTime; 00495 msgDateTime.setTime_t( msg->date() ); 00496 numericalMsgContents = msgDateTime.daysTo( QDateTime::currentDateTime() ); 00497 numericalValue = contents().toInt(); 00498 msgContents.setNum( numericalMsgContents ); 00499 } 00500 bool rc = matchesInternal( numericalValue, numericalMsgContents, msgContents ); 00501 if ( FilterLog::instance()->isLogging() ) { 00502 QString msg = ( rc ? "<font color=#00FF00>1 = </font>" 00503 : "<font color=#FF0000>0 = </font>" ); 00504 msg += FilterLog::recode( asString() ); 00505 msg += " ( <i>" + QString::number( numericalMsgContents ) + "</i> )"; 00506 FilterLog::instance()->add( msg, FilterLog::ruleResult ); 00507 } 00508 return rc; 00509 } 00510 00511 bool KMSearchRuleNumerical::matchesInternal( long numericalValue, 00512 long numericalMsgContents, const QString & msgContents ) const 00513 { 00514 switch ( function() ) { 00515 case KMSearchRule::FuncEquals: 00516 return ( numericalValue == numericalMsgContents ); 00517 00518 case KMSearchRule::FuncNotEqual: 00519 return ( numericalValue != numericalMsgContents ); 00520 00521 case KMSearchRule::FuncContains: 00522 return ( msgContents.find( contents(), 0, false ) >= 0 ); 00523 00524 case KMSearchRule::FuncContainsNot: 00525 return ( msgContents.find( contents(), 0, false ) < 0 ); 00526 00527 case KMSearchRule::FuncRegExp: 00528 { 00529 QRegExp regexp( contents(), false ); 00530 return ( regexp.search( msgContents ) >= 0 ); 00531 } 00532 00533 case KMSearchRule::FuncNotRegExp: 00534 { 00535 QRegExp regexp( contents(), false ); 00536 return ( regexp.search( msgContents ) < 0 ); 00537 } 00538 00539 case FuncIsGreater: 00540 return ( numericalMsgContents > numericalValue ); 00541 00542 case FuncIsLessOrEqual: 00543 return ( numericalMsgContents <= numericalValue ); 00544 00545 case FuncIsLess: 00546 return ( numericalMsgContents < numericalValue ); 00547 00548 case FuncIsGreaterOrEqual: 00549 return ( numericalMsgContents >= numericalValue ); 00550 00551 case FuncIsInAddressbook: // since email-addresses are not numerical, I settle for false here 00552 return false; 00553 00554 case FuncIsNotInAddressbook: 00555 return false; 00556 00557 default: 00558 ; 00559 } 00560 00561 return false; 00562 } 00563 00564 00565 00566 //================================================== 00567 // 00568 // class KMSearchRuleStatus 00569 // 00570 //================================================== 00571 00572 00573 KMSearchRuleStatus::KMSearchRuleStatus( const QCString & field, 00574 Function func, const QString & aContents ) 00575 : KMSearchRule(field, func, aContents) 00576 { 00577 // the values are always in english, both from the conf file as well as 00578 // the patternedit gui 00579 mStatus = statusFromEnglishName( aContents ); 00580 } 00581 00582 KMMsgStatus KMSearchRuleStatus::statusFromEnglishName( 00583 const QString & aStatusString ) 00584 { 00585 KMMsgStatus status = 0; 00586 if ( ! aStatusString.compare("important") ) 00587 status = KMMsgStatusFlag; 00588 if ( ! aStatusString.compare("new") ) 00589 status = KMMsgStatusNew; 00590 if ( ! aStatusString.compare("unread") ) 00591 status = KMMsgStatusUnread | KMMsgStatusNew; 00592 if ( ! aStatusString.compare("read") ) 00593 status = KMMsgStatusRead; 00594 if ( ! aStatusString.compare("old") ) 00595 status = KMMsgStatusOld; 00596 if ( ! aStatusString.compare("deleted") ) 00597 status = KMMsgStatusDeleted; 00598 if ( ! aStatusString.compare("replied") ) 00599 status = KMMsgStatusReplied; 00600 if ( ! aStatusString.compare("forwarded") ) 00601 status = KMMsgStatusForwarded; 00602 if ( ! aStatusString.compare("queued") ) 00603 status = KMMsgStatusQueued; 00604 if ( ! aStatusString.compare("sent") ) 00605 status = KMMsgStatusSent; 00606 if ( ! aStatusString.compare("watched") ) 00607 status = KMMsgStatusWatched; 00608 if ( ! aStatusString.compare("ignored") ) 00609 status = KMMsgStatusIgnored; 00610 if ( ! aStatusString.compare("todo") ) 00611 status = KMMsgStatusTodo; 00612 if ( ! aStatusString.compare("spam") ) 00613 status = KMMsgStatusSpam; 00614 if ( ! aStatusString.compare("ham") ) 00615 status = KMMsgStatusHam; 00616 if ( ! aStatusString.compare("has an attachment") ) 00617 status = KMMsgStatusHasAttach; 00618 00619 return status; 00620 } 00621 00622 bool KMSearchRuleStatus::isEmpty() const 00623 { 00624 return field().stripWhiteSpace().isEmpty() || contents().isEmpty(); 00625 } 00626 00627 bool KMSearchRuleStatus::matches( const DwString &, KMMessage &, 00628 const DwBoyerMoore *, int ) const 00629 { 00630 assert( 0 ); 00631 return false; // don't warn 00632 } 00633 00634 bool KMSearchRuleStatus::matches( const KMMessage * msg ) const 00635 { 00636 00637 KMMsgStatus msgStatus = msg->status(); 00638 bool rc = false; 00639 00640 switch ( function() ) { 00641 case FuncEquals: // fallthrough. So that "<status> 'is' 'read'" works 00642 case FuncContains: 00643 if (msgStatus & mStatus) 00644 rc = true; 00645 break; 00646 case FuncNotEqual: // fallthrough. So that "<status> 'is not' 'read'" works 00647 case FuncContainsNot: 00648 if (! (msgStatus & mStatus) ) 00649 rc = true; 00650 break; 00651 // FIXME what about the remaining funcs, how can they make sense for 00652 // stati? 00653 default: 00654 break; 00655 } 00656 00657 if ( FilterLog::instance()->isLogging() ) { 00658 QString msg = ( rc ? "<font color=#00FF00>1 = </font>" 00659 : "<font color=#FF0000>0 = </font>" ); 00660 msg += FilterLog::recode( asString() ); 00661 FilterLog::instance()->add( msg, FilterLog::ruleResult ); 00662 } 00663 return rc; 00664 } 00665 00666 // ---------------------------------------------------------------------------- 00667 00668 //================================================== 00669 // 00670 // class KMSearchPattern 00671 // 00672 //================================================== 00673 00674 KMSearchPattern::KMSearchPattern( const KConfig * config ) 00675 : QPtrList<KMSearchRule>() 00676 { 00677 setAutoDelete( true ); 00678 if ( config ) 00679 readConfig( config ); 00680 else 00681 init(); 00682 } 00683 00684 KMSearchPattern::~KMSearchPattern() 00685 { 00686 } 00687 00688 bool KMSearchPattern::matches( const KMMessage * msg ) const 00689 { 00690 if ( isEmpty() ) 00691 return true; 00692 00693 QPtrListIterator<KMSearchRule> it( *this ); 00694 switch ( mOperator ) { 00695 case OpAnd: // all rules must match 00696 for ( it.toFirst() ; it.current() ; ++it ) 00697 if ( !(*it)->matches( msg ) ) 00698 return false; 00699 return true; 00700 case OpOr: // at least one rule must match 00701 for ( it.toFirst() ; it.current() ; ++it ) 00702 if ( (*it)->matches( msg ) ) 00703 return true; 00704 // fall through 00705 default: 00706 return false; 00707 } 00708 } 00709 00710 bool KMSearchPattern::matches( const DwString & aStr ) const 00711 { 00712 if ( isEmpty() ) 00713 return true; 00714 00715 KMMessage msg; 00716 QPtrListIterator<KMSearchRule> it( *this ); 00717 switch ( mOperator ) { 00718 case OpAnd: // all rules must match 00719 for ( it.toFirst() ; it.current() ; ++it ) 00720 if ( !(*it)->matches( aStr, msg ) ) 00721 return false; 00722 return true; 00723 case OpOr: // at least one rule must match 00724 for ( it.toFirst() ; it.current() ; ++it ) 00725 if ( (*it)->matches( aStr, msg ) ) 00726 return true; 00727 // fall through 00728 default: 00729 return false; 00730 } 00731 } 00732 00733 bool KMSearchPattern::matches( Q_UINT32 serNum ) const 00734 { 00735 if ( isEmpty() ) 00736 return true; 00737 00738 bool res; 00739 int idx = -1; 00740 KMFolder *folder = 0; 00741 kmkernel->msgDict()->getLocation(serNum, &folder, &idx); 00742 if (!folder || (idx == -1) || (idx >= folder->count())) { 00743 return false; 00744 } 00745 00746 bool opened = folder->isOpened(); 00747 if ( !opened ) 00748 folder->open(); 00749 KMMsgBase *msgBase = folder->getMsgBase(idx); 00750 if (requiresBody()) { 00751 bool unGet = !msgBase->isMessage(); 00752 KMMessage *msg = folder->getMsg(idx); 00753 res = matches( msg ); 00754 if (unGet) 00755 folder->unGetMsg(idx); 00756 } else { 00757 res = matches( folder->getDwString(idx) ); 00758 } 00759 if ( !opened ) 00760 folder->close(); 00761 return res; 00762 } 00763 00764 bool KMSearchPattern::requiresBody() const { 00765 QPtrListIterator<KMSearchRule> it( *this ); 00766 for ( it.toFirst() ; it.current() ; ++it ) 00767 if ( (*it)->requiresBody() ) 00768 return true; 00769 return false; 00770 } 00771 00772 void KMSearchPattern::purify() { 00773 QPtrListIterator<KMSearchRule> it( *this ); 00774 it.toLast(); 00775 while ( it.current() ) 00776 if ( (*it)->isEmpty() ) { 00777 #ifndef NDEBUG 00778 kdDebug(5006) << "KMSearchPattern::purify(): removing " << (*it)->asString() << endl; 00779 #endif 00780 remove( *it ); 00781 } else { 00782 --it; 00783 } 00784 } 00785 00786 void KMSearchPattern::readConfig( const KConfig * config ) { 00787 init(); 00788 00789 mName = config->readEntry("name"); 00790 if ( !config->hasKey("rules") ) { 00791 kdDebug(5006) << "KMSearchPattern::readConfig: found legacy config! Converting." << endl; 00792 importLegacyConfig( config ); 00793 return; 00794 } 00795 00796 mOperator = config->readEntry("operator") == "or" ? OpOr : OpAnd; 00797 00798 const int nRules = config->readNumEntry( "rules", 0 ); 00799 00800 for ( int i = 0 ; i < nRules ; i++ ) { 00801 KMSearchRule * r = KMSearchRule::createInstanceFromConfig( config, i ); 00802 if ( r->isEmpty() ) 00803 delete r; 00804 else 00805 append( r ); 00806 } 00807 } 00808 00809 void KMSearchPattern::importLegacyConfig( const KConfig * config ) { 00810 KMSearchRule * rule = KMSearchRule::createInstance( config->readEntry("fieldA").latin1(), 00811 config->readEntry("funcA").latin1(), 00812 config->readEntry("contentsA") ); 00813 if ( rule->isEmpty() ) { 00814 // if the first rule is invalid, 00815 // we really can't do much heuristics... 00816 delete rule; 00817 return; 00818 } 00819 append( rule ); 00820 00821 const QString sOperator = config->readEntry("operator"); 00822 if ( sOperator == "ignore" ) return; 00823 00824 rule = KMSearchRule::createInstance( config->readEntry("fieldB").latin1(), 00825 config->readEntry("funcB").latin1(), 00826 config->readEntry("contentsB") ); 00827 if ( rule->isEmpty() ) { 00828 delete rule; 00829 return; 00830 } 00831 append( rule ); 00832 00833 if ( sOperator == "or" ) { 00834 mOperator = OpOr; 00835 return; 00836 } 00837 // This is the interesting case... 00838 if ( sOperator == "unless" ) { // meaning "and not", ie we need to... 00839 // ...invert the function (e.g. "equals" <-> "doesn't equal") 00840 // We simply toggle the last bit (xor with 0x1)... This assumes that 00841 // KMSearchRule::Function's come in adjacent pairs of pros and cons 00842 KMSearchRule::Function func = last()->function(); 00843 unsigned int intFunc = (unsigned int)func; 00844 func = KMSearchRule::Function( intFunc ^ 0x1 ); 00845 00846 last()->setFunction( func ); 00847 } 00848 00849 // treat any other case as "and" (our default). 00850 } 00851 00852 void KMSearchPattern::writeConfig( KConfig * config ) const { 00853 config->writeEntry("name", mName); 00854 config->writeEntry("operator", (mOperator == KMSearchPattern::OpOr) ? "or" : "and" ); 00855 00856 int i = 0; 00857 for ( QPtrListIterator<KMSearchRule> it( *this ) ; it.current() && i < FILTER_MAX_RULES ; ++i , ++it ) 00858 // we could do this ourselves, but we want the rules to be extensible, 00859 // so we give the rule it's number and let it do the rest. 00860 (*it)->writeConfig( config, i ); 00861 00862 // save the total number of rules. 00863 config->writeEntry( "rules", i ); 00864 } 00865 00866 void KMSearchPattern::init() { 00867 clear(); 00868 mOperator = OpAnd; 00869 mName = '<' + i18n("name used for a virgin filter","unknown") + '>'; 00870 } 00871 00872 QString KMSearchPattern::asString() const { 00873 QString result; 00874 if ( mOperator == OpOr ) 00875 result = i18n("(match any of the following)"); 00876 else 00877 result = i18n("(match all of the following)"); 00878 00879 for ( QPtrListIterator<KMSearchRule> it( *this ) ; it.current() ; ++it ) 00880 result += "\n\t" + FilterLog::recode( (*it)->asString() ); 00881 00882 return result; 00883 } 00884 00885 const KMSearchPattern & KMSearchPattern::operator=( const KMSearchPattern & other ) { 00886 if ( this == &other ) 00887 return *this; 00888 00889 setOp( other.op() ); 00890 setName( other.name() ); 00891 00892 clear(); // ### 00893 for ( QPtrListIterator<KMSearchRule> it( other ) ; it.current() ; ++it ) 00894 append( KMSearchRule::createInstance( **it ) ); // deep copy 00895 00896 return *this; 00897 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 27 12:52:49 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003