/* Copyright (C) 2000-2005 SKYRIX Software AG This file is part of SOPE. SOPE is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. SOPE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "imCommon.h" @interface EOQualifier(PrivateMethodes) - (NSString *)qualifierDescription; - (NSException *)invalidImap4SearchQualifier:(NSString *)_reason; - (NSException *)appendToImap4SearchString:(NSMutableString *)_search insertNot:(BOOL)_insertNot; @end @implementation EOQualifier(IMAPAdditions) - (BOOL)isImap4UnseenQualifier { return NO; } /* building search qualifiers */ static NSArray *FlagKeyWords = nil; static NSArray *OtherKeyWords = nil; static BOOL debugOn = NO; - (void)_initImap4SearchCategory { NSUserDefaults *ud; if (FlagKeyWords) return; ud = [NSUserDefaults standardUserDefaults]; FlagKeyWords = [[NSArray alloc] initWithObjects: @"answered", @"deleted", @"draft", @"flagged", @"new", @"old", @"recent", @"seen", @"unanswered", @"undeleted", @"undraft", @"unflagged", @"unseen", nil]; OtherKeyWords = [[NSArray alloc] initWithObjects: @"bcc", @"body", @"cc", @"from", @"subject", @"text", @"to", @"keyword", @"unkeyword", nil]; debugOn = [ud boolForKey:@"ImapDebugQualifierGeneration"]; } - (NSException *)invalidImap4SearchQualifier:(NSString *)_reason { if (_reason == nil) _reason = @"unknown reason"; return [NSException exceptionWithName:@"NGImap4SearchQualifierException" reason:_reason userInfo:nil]; } - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction { return nil; } - (BOOL)isImap4NotQualifier { return NO; } - (BOOL)isImap4KeyValueQualifier { return NO; } - (NSException *)appendToImap4SearchString:(NSMutableString *)_search insertNot:(BOOL)_insertNot { return [self invalidImap4SearchQualifier:@"expected key/value qualifier"]; } - (NSException *)appendToImap4SearchString:(NSMutableString *)_search { return [self appendToImap4SearchString:_search insertNot:NO]; } - (id)imap4SearchString { /* returns exception on fail */ // TODO: split up method BOOL disjunction = NO; /* OR */ NSEnumerator *quals; id qualifier; NSMutableString *search; [self _initImap4SearchCategory]; if ([self isImap4UnseenQualifier]) { if (debugOn) [self logWithFormat:@"is unseen: %@ (%@)", self, [self class]]; return @" unseen"; } if (debugOn) [self logWithFormat:@"generate IMAP4 expression for qualifier: %@", self]; search = [NSMutableString stringWithCapacity:256]; quals = nil; if ((quals = [self subqualifiersForImap4SearchString:&disjunction]) == nil) { if (debugOn) [self logWithFormat:@" got no subqualifiers .."]; return (id)[self invalidImap4SearchQualifier:@"unexpected qualifier 1"]; } if (disjunction) [search appendString:@" or"]; while ((qualifier = [quals nextObject])) { NSException *error; if (debugOn) [self logWithFormat:@" append subqualifier: %@", qualifier]; if ((error = [qualifier appendToImap4SearchString:search])) return error; } if (debugOn) [self logWithFormat:@" generated: '%@'", search]; return search; } @end /* EOQualifier(IMAPAdditions) */ @implementation EOAndQualifier(IMAPAdditions) - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction { if (_isDisjunction) *_isDisjunction = NO; return [[self qualifiers] objectEnumerator]; } @end /* EOAndQualifier(IMAPAdditions) */ @implementation EOOrQualifier(IMAPAdditions) - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction { if (_isDisjunction) *_isDisjunction = YES; return [[self qualifiers] objectEnumerator]; } @end /* EOOrQualifier(IMAPAdditions) */ @implementation EOKeyValueQualifier(IMAPAdditions) - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction { if (_isDisjunction) *_isDisjunction = NO; return [[NSArray arrayWithObject:self] objectEnumerator]; } - (BOOL)isImap4KeyValueQualifier { return YES; } - (BOOL)isImap4UnseenQualifier { // TODO: this is rather weird: flags suggests an array value! if (![[self key] isEqualToString:@"flags"]) return NO; return [[self value] isEqualToString:@"unseen"]; } - (NSException *)appendFlagsCheckToImap4SearchString:(NSMutableString *)search insertNot:(BOOL)insertNot { NSEnumerator *enumerator = nil; id lvalue; SEL lselector; lvalue = [self value]; lselector = [self selector]; if (sel_eq(lselector, EOQualifierOperatorEqual)) { lvalue = [NSArray arrayWithObject:lvalue]; } else if (!sel_eq(lselector, EOQualifierOperatorContains)) { return [self invalidImap4SearchQualifier: @"unexpected EOKeyValueQualifier selector"]; } if (![lvalue isKindOfClass:[NSArray class]]) { return [self invalidImap4SearchQualifier: @"expected an array in contains-qualifier"]; } enumerator = [lvalue objectEnumerator]; while ((lvalue = [enumerator nextObject])) { lvalue = [lvalue lowercaseString]; if ([FlagKeyWords containsObject:lvalue]) { [search appendString:insertNot ? @" not " : @" "]; [search appendString:lvalue]; } else { return [self invalidImap4SearchQualifier: @"unexpected keyword for EOKeyValueQualifier"]; } } return nil; } - (NSString *)imap4OperatorForDateComparisonSelector:(SEL)lselector { if (sel_eq(lselector, EOQualifierOperatorEqual)) return @" senton "; if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) return @" sentsince "; if (sel_eq(lselector, EOQualifierOperatorLessThan)) return @" sentbefore "; return nil; } - (NSException *)appendToImap4SearchString:(NSMutableString *)search insertNot:(BOOL)insertNot { /* returns exception on fail */ NSString *lkey; id lvalue; SEL lselector; lkey = [[self key] lowercaseString]; lvalue = [self value]; lselector = [self selector]; if ([lkey isEqualToString:@"flags"]) { return [self appendFlagsCheckToImap4SearchString:search insertNot:insertNot]; } /* not a flag */ if (insertNot) [search appendString:@" not"]; if ([lkey isEqualToString:@"date"]) { NSString *s; if (![lvalue isKindOfClass:[NSCalendarDate class]]) { return [self invalidImap4SearchQualifier: @"expected a NSDate as value"]; } if ((s = [self imap4OperatorForDateComparisonSelector:lselector]) == nil) return [self invalidImap4SearchQualifier:@"unexpected selector"]; // TODO: much faster without descriptionWithCalendarFormat:?! s = [lvalue descriptionWithCalendarFormat:@"%d-%b-%Y"]; [search appendString:s]; } else if ([lkey isEqualToString:@"uid"]) { if (!sel_eq(lselector, EOQualifierOperatorEqual)) return [self invalidImap4SearchQualifier:@"unexpected qualifier 2"]; [search appendString:@" uid "]; [search appendString:[lvalue stringValue]]; } else if ([lkey isEqualToString:@"size"]) { if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) [search appendString:@" larger "]; else if (sel_eq(lselector, EOQualifierOperatorLessThan)) [search appendString:@" smaller "]; else return [self invalidImap4SearchQualifier:@"unexpected qualifier 3"]; [search appendString:[lvalue stringValue]]; } else if ([OtherKeyWords containsObject:lkey]) { if (!sel_eq(lselector, EOQualifierOperatorEqual)) { [self logWithFormat:@"SELECTOR: got: %@, allowed: %@", NSStringFromSelector(lselector), NSStringFromSelector(EOQualifierOperatorEqual)]; return [self invalidImap4SearchQualifier: @"unexpected qualifier, disallowed comparison on " @"OtherKeyWords)"]; } [search appendString:@" "]; [search appendString:lkey]; [search appendString:@" \""]; [search appendString:[lvalue stringValue]]; [search appendString:@"\""]; } else { if (!sel_eq(lselector, EOQualifierOperatorEqual)) return [self invalidImap4SearchQualifier:@"unexpected qualifier 5"]; [search appendString:@" header "]; [search appendString:lkey]; [search appendString:@" \""]; [search appendString:[lvalue stringValue]]; [search appendString:@"\""]; } return nil; } @end /* EOKeyValueQualifier(IMAPAdditions) */ @implementation EONotQualifier(IMAPAdditions) - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction { if (_isDisjunction) *_isDisjunction = NO; return [[NSArray arrayWithObject:self] objectEnumerator]; } - (BOOL)isImap4NotQualifier { return YES; } - (NSException *)appendToImap4SearchString:(NSMutableString *)_search { return [[self qualifier] appendToImap4SearchString:_search insertNot:YES]; } @end /* EONotQualifier(IMAPAdditions) */