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