]> err.no Git - sope/blob - sope-mime/NGImap4/EOQualifier+IMAPAdditions.m
qualifier/sortordering cleanups
[sope] / sope-mime / NGImap4 / EOQualifier+IMAPAdditions.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "imCommon.h"
23
24 @interface EOQualifier(PrivateMethodes)
25
26 - (NSString *)qualifierDescription;
27
28 - (NSException *)invalidImap4SearchQualifier:(NSString *)_reason;
29
30 - (NSException *)appendToImap4SearchString:(NSMutableString *)_search 
31   insertNot:(BOOL)_insertNot;
32
33 @end
34
35 @implementation EOQualifier(IMAPAdditions)
36
37 - (BOOL)isImap4UnseenQualifier {
38   return NO;
39 }
40
41 /* building search qualifiers */
42
43 static NSArray *FlagKeyWords = nil;
44 static NSArray *OtherKeyWords = nil;
45 static BOOL    debugOn = NO;
46
47 - (void)_initImap4SearchCategory {
48   NSUserDefaults *ud;
49   
50   if (FlagKeyWords) return;
51
52   ud = [NSUserDefaults standardUserDefaults];
53   FlagKeyWords = [[NSArray alloc] initWithObjects: @"answered", @"deleted",
54                             @"draft", @"flagged", @"new", @"old", @"recent",
55                             @"seen", @"unanswered", @"undeleted", @"undraft",
56                             @"unflagged", @"unseen", nil];
57   OtherKeyWords = [[NSArray alloc] initWithObjects:
58                              @"bcc", @"body", @"cc", @"from", @"subject",
59                              @"text", @"to", @"keyword", @"unkeyword", nil];
60   
61   debugOn = [ud boolForKey:@"ImapDebugQualifierGeneration"];
62 }
63
64 - (NSException *)invalidImap4SearchQualifier:(NSString *)_reason {
65   if (_reason == nil) _reason = @"unknown reason";
66   return [NSException exceptionWithName:@"NGImap4SearchQualifierException"
67                       reason:_reason
68                       userInfo:nil];
69 }
70
71 - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction {
72   return nil;
73 }
74 - (BOOL)isImap4NotQualifier {
75   return NO;
76 }
77 - (BOOL)isImap4KeyValueQualifier {
78   return NO;
79 }
80
81 - (NSException *)appendToImap4SearchString:(NSMutableString *)_search 
82   insertNot:(BOOL)_insertNot
83 {
84   return [self invalidImap4SearchQualifier:@"expected key/value qualifier"];
85 }
86 - (NSException *)appendToImap4SearchString:(NSMutableString *)_search { 
87   return [self appendToImap4SearchString:_search insertNot:NO];
88 }
89
90 - (id)imap4SearchString { /* returns exception on fail */
91   // TODO: split up method
92   BOOL            disjunction = NO; /* OR */
93   NSEnumerator    *quals;
94   id              qualifier;
95   NSMutableString *search;
96   
97   [self _initImap4SearchCategory];
98   
99   if ([self isImap4UnseenQualifier]) {
100     if (debugOn)
101       [self logWithFormat:@"is unseen: %@ (%@)", self, [self class]];
102     return @" unseen";
103   }
104   
105   if (debugOn)
106     [self logWithFormat:@"generate IMAP4 expression for qualifier: %@", self];
107   
108   search = [NSMutableString stringWithCapacity:256];
109   quals  = nil;
110   
111   if ((quals = [self subqualifiersForImap4SearchString:&disjunction]) == nil) {
112     if (debugOn)
113       [self logWithFormat:@"  got no subqualifiers .."];
114     
115     return (id)[self invalidImap4SearchQualifier:@"unexpected qualifier 1"];
116   }
117   
118   if (disjunction)
119     [search appendString:@" or"];
120   
121   while ((qualifier = [quals nextObject]) != nil) {
122     NSException *error;
123
124     if (debugOn)
125       [self logWithFormat:@"  append subqualifier: %@", qualifier];
126     
127     if ((error = [qualifier appendToImap4SearchString:search]))
128       return error;
129   }
130   
131   if (debugOn)
132     [self logWithFormat:@"  generated: '%@'", search];
133
134   return search;
135 }
136
137 @end /* EOQualifier(IMAPAdditions) */
138
139 @implementation EOAndQualifier(IMAPAdditions)
140
141 - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction {
142   if (_isDisjunction) *_isDisjunction = NO;
143   return [[self qualifiers] objectEnumerator];
144 }
145
146 @end /* EOAndQualifier(IMAPAdditions) */
147
148 @implementation EOOrQualifier(IMAPAdditions)
149
150 - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction {
151   if (_isDisjunction) *_isDisjunction = YES;
152   return [[self qualifiers] objectEnumerator];
153 }
154
155 @end /* EOOrQualifier(IMAPAdditions) */
156
157 @implementation EOKeyValueQualifier(IMAPAdditions)
158
159 - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction {
160   if (_isDisjunction) *_isDisjunction = NO;
161   return [[NSArray arrayWithObject:self] objectEnumerator];
162 }
163
164 - (BOOL)isImap4KeyValueQualifier {
165   return YES;
166 }
167
168 - (BOOL)isImap4UnseenQualifier {
169   // TODO: this is rather weird: flags suggests an array value!
170   if (![[self key] isEqualToString:@"flags"]) 
171     return NO;
172   return [[self value] isEqualToString:@"unseen"];
173 }
174
175 - (NSException *)appendFlagsCheckToImap4SearchString:(NSMutableString *)search 
176   insertNot:(BOOL)insertNot
177 {
178   NSEnumerator *enumerator = nil;
179   id       lvalue;
180   SEL      lselector;
181   
182   lvalue    = [self value];
183   lselector = [self selector];
184       
185       if (sel_eq(lselector, EOQualifierOperatorEqual)) {
186         lvalue = [NSArray arrayWithObject:lvalue];
187       }
188       else if (!sel_eq(lselector, EOQualifierOperatorContains)) {
189         return [self invalidImap4SearchQualifier:
190                        @"unexpected EOKeyValueQualifier selector"];
191       }
192       if (![lvalue isKindOfClass:[NSArray class]]) {
193         return [self invalidImap4SearchQualifier:
194                        @"expected an array in contains-qualifier"];
195       }
196       enumerator = [lvalue objectEnumerator];
197       while ((lvalue = [enumerator nextObject])) {
198         lvalue = [lvalue lowercaseString];
199         
200         if ([FlagKeyWords containsObject:lvalue]) {
201           [search appendString:insertNot ? @" not " : @" "];
202           [search appendString:lvalue];
203         }
204         else {
205           return [self invalidImap4SearchQualifier:
206                          @"unexpected keyword for EOKeyValueQualifier"];
207         }
208       }
209       return nil;
210 }
211
212 - (NSString *)imap4OperatorForDateComparisonSelector:(SEL)lselector {
213   if (sel_eq(lselector, EOQualifierOperatorEqual))
214     return @" senton ";
215   if (sel_eq(lselector, EOQualifierOperatorGreaterThan))
216     return @" sentsince ";
217   if (sel_eq(lselector, EOQualifierOperatorLessThan))
218     return @" sentbefore ";
219   
220   return nil;
221 }
222
223 - (NSException *)appendToImap4SearchString:(NSMutableString *)search 
224   insertNot:(BOOL)insertNot
225 {
226   /* returns exception on fail */
227   NSString *lkey;
228   id       lvalue;
229   SEL      lselector;
230   
231   lkey      = [[self key] lowercaseString];
232   lvalue    = [self value];
233   lselector = [self selector];
234     
235   if ([lkey isEqualToString:@"flags"]) {
236     return [self appendFlagsCheckToImap4SearchString:search 
237                  insertNot:insertNot];
238   }
239   
240   /* not a flag */
241   if (insertNot) 
242     [search appendString:@" not"];
243   
244   if ([lkey isEqualToString:@"date"]) {
245     NSString *s;
246     
247     if (![lvalue isKindOfClass:[NSCalendarDate class]]) {
248       return [self invalidImap4SearchQualifier:
249                      @"expected a NSDate as value"];
250     }
251     
252     if ((s = [self imap4OperatorForDateComparisonSelector:lselector]) == nil)
253       return [self invalidImap4SearchQualifier:@"unexpected selector"];
254     
255     // TODO: much faster without descriptionWithCalendarFormat:?!
256     s = [lvalue descriptionWithCalendarFormat:@"%d-%b-%Y"];
257     [search appendString:s];
258   }
259   else if ([lkey isEqualToString:@"uid"]) {
260     if (!sel_eq(lselector, EOQualifierOperatorEqual))
261       return [self invalidImap4SearchQualifier:@"unexpected qualifier 2"];
262     
263     [search appendString:@" uid "];
264     [search appendString:[lvalue stringValue]];
265   }
266   else if ([lkey isEqualToString:@"size"]) {
267     if (sel_eq(lselector, EOQualifierOperatorGreaterThan))
268       [search appendString:@" larger "];
269     else if (sel_eq(lselector, EOQualifierOperatorLessThan))
270       [search appendString:@" smaller "];
271     else
272       return [self invalidImap4SearchQualifier:@"unexpected qualifier 3"];
273         
274     [search appendString:[lvalue stringValue]];
275   }
276   else if ([OtherKeyWords containsObject:lkey]) {
277     // TODO: actually most keywords only allow for contains! Eg "subject abc"
278     //       is a contains query, not an equal query!
279     if (!sel_eq(lselector, EOQualifierOperatorEqual) &&
280         !sel_eq(lselector, EOQualifierOperatorContains)) {
281       [self logWithFormat:@"IMAP4 generation: got: %@, allowed: %@", 
282             NSStringFromSelector(lselector),
283             NSStringFromSelector(EOQualifierOperatorEqual)];
284       return [self invalidImap4SearchQualifier:
285                      @"unexpected qualifier, disallowed comparison on "
286                      @"OtherKeyWords)"];
287     }
288     
289     [search appendString:@" "];
290     [search appendString:lkey];
291     [search appendString:@" \""];
292     [search appendString:[lvalue stringValue]];
293     [search appendString:@"\""];
294   }
295   else {
296     if (!sel_eq(lselector, EOQualifierOperatorEqual))
297       return [self invalidImap4SearchQualifier:@"unexpected qualifier 5"];
298     
299     [search appendString:@" header "];
300     [search appendString:lkey];
301     [search appendString:@" \""];
302     [search appendString:[lvalue stringValue]];
303     [search appendString:@"\""];
304   }
305   return nil;
306 }
307
308 @end /* EOKeyValueQualifier(IMAPAdditions) */
309
310 @implementation EONotQualifier(IMAPAdditions)
311
312 - (NSEnumerator *)subqualifiersForImap4SearchString:(BOOL *)_isDisjunction {
313   if (_isDisjunction) *_isDisjunction = NO;
314   return [[NSArray arrayWithObject:self] objectEnumerator];
315 }
316
317 - (BOOL)isImap4NotQualifier {
318   return YES;
319 }
320
321 - (NSException *)appendToImap4SearchString:(NSMutableString *)_search { 
322   return [[self qualifier] appendToImap4SearchString:_search insertNot:YES];
323 }
324
325 @end /* EONotQualifier(IMAPAdditions) */