]> err.no Git - sope/blob - sope-mime/NGMime/NGMimeBodyGenerator.m
minor code cleanups
[sope] / sope-mime / NGMime / NGMimeBodyGenerator.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 #import "NGMimeBodyGenerator.h"
24 #import "NGMimePartGenerator.h"
25 #import "NGMimeMultipartBody.h"
26 #import "NGMimeJoinedData.h"
27 #import "NGMimeFileData.h"
28 #import "common.h"
29 #include <unistd.h>
30
31 @implementation NGMimeBodyGenerator
32
33 + (int)version {
34   return 2;
35 }
36
37 - (NSData *)generateBodyOfPart:(id<NGMimePart>)_part
38   additionalHeaders:(NGMutableHashMap *)_addHeaders
39   delegate:(id)_delegate
40 {
41   return [self encodeData:[_part body]
42                forPart:_part
43                additionalHeaders:_addHeaders];
44 }
45
46 - (NSData *)encodeData:(NSData *)_data
47   forPart:(id<NGMimePart>)_part
48   additionalHeaders:(NGMutableHashMap *)_addHeaders
49 {
50   return _data;
51 }
52
53 - (BOOL)useMimeData {
54   return self->useMimeData;
55 }
56
57 - (void)setUseMimeData:(BOOL)_b {
58   self->useMimeData = _b;
59 }
60
61 @end /* NGMimeBodyGenerator */
62
63 @implementation NGMimeTextBodyGenerator
64
65 + (int)version {
66   return 2;
67 }
68 + (void)initialize {
69   NSAssert2([super version] == 2,
70             @"invalid superclass (%@) version %i !",
71             NSStringFromClass([self superclass]), [super version]);
72 }
73
74 - (NSData *)generateBodyOfPart:(id<NGMimePart>)_part
75   additionalHeaders:(NGMutableHashMap *)_addHeaders
76   delegate:(id)_delegate
77 {
78   NSStringEncoding encoding = [NSString defaultCStringEncoding];
79   NSData           *data    = nil;
80   id               body     = nil;
81   
82   body = [_part body];
83
84   if ([body isKindOfClass:[NSString class]]) {
85     data = [body dataUsingEncoding:encoding];
86   }
87   else
88     data = body;
89 #if 0  
90   else {
91     NSLog(@"WARNING: textBodyGenerator expect that body is"
92           @" kind of class NSString");
93   }
94 #endif  
95   if (data == nil) {
96     NSLog(@"WARNING(%s): generate empty body", __PRETTY_FUNCTION__);
97     data = [NSData data];
98   }
99   return [self encodeData:data forPart:_part additionalHeaders:_addHeaders];
100 }
101
102 @end /* NGMimeTextBodyGenerator */
103
104 @implementation NGMimeRfc822BodyGenerator
105
106 + (int)version {
107   return 2;
108 }
109 + (void)initialize {
110   NSAssert2([super version] == 2,
111             @"invalid superclass (%@) version %i !",
112             NSStringFromClass([self superclass]), [super version]);
113 }
114
115 - (id<NGMimePartGenerator>)generatorForPart:(id<NGMimePart>)_part {
116   id g;
117
118   g = [[[NGMimePartGenerator allocWithZone:[self zone]] init]
119                              autorelease];
120   [g setUseMimeData:self->useMimeData];
121   return g;
122 }
123
124 - (NSData *)generateBodyOfPart:(id<NGMimePart>)_part
125   additionalHeaders:(NGMutableHashMap *)_addHeaders
126   delegate:(id)_delegate
127 {
128   NSData              *data = nil;
129   NGMimePartGenerator *gen  = nil;
130   
131   gen = (NGMimePartGenerator *)[self generatorForPart:_part];
132   [gen setDelegate:_delegate];
133   data = [gen generateMimeFromPart:[_part body]];
134   return data;
135 }
136
137 @end /* NGMimeRfc822BodyGenerator */
138
139
140 @implementation NGMimeMultipartBodyGenerator
141
142 static Class NGMimeFileDataClass   = Nil;
143 static Class NGMimeJoinedDataClass = Nil;
144
145 + (int)version {
146   return 2;
147 }
148 + (void)initialize {
149   NSAssert2([super version] == 2,
150             @"invalid superclass (%@) version %i !",
151             NSStringFromClass([self superclass]), [super version]);
152
153   NGMimeFileDataClass   = [NGMimeFileData class];
154   NGMimeJoinedDataClass = [NGMimeJoinedData class];
155 }
156
157 + (NSString *)boundaryPrefix {
158   static NSString *BoundaryPrefix = nil;
159   
160   if (BoundaryPrefix == nil) {
161     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
162     BoundaryPrefix = 
163       [[ud stringForKey:@"NGMime_MultipartBoundaryPrefix"] copy];
164     if (BoundaryPrefix == nil)
165       BoundaryPrefix = @"--=_=-_OpenGroupware_org_NGMime";
166   }
167   return BoundaryPrefix;
168 }
169
170 static inline BOOL _isBoundaryInArray(NGMimeMultipartBodyGenerator *self,
171                                       NSString *_boundary,
172                                       NSArray *_data)
173 {
174   const unsigned char *boundary;
175   unsigned int length;
176   NSEnumerator *enumerator;
177   NSData       *data;
178   BOOL         wasFound;
179
180   boundary   = [_boundary cString];
181   length     = [_boundary length];
182   enumerator = [_data objectEnumerator];
183   data       = nil;
184   wasFound   = NO;
185   
186   while ((data = [enumerator nextObject])) {
187     const unsigned char *bytes;
188     unsigned int dataLen;
189     unsigned     cnt;
190     
191     if ([data isKindOfClass:NGMimeFileDataClass] ||
192         [data isKindOfClass:NGMimeJoinedDataClass])
193       continue;
194     
195     bytes   = [data bytes];
196     dataLen = [data length];
197     cnt     = 0;
198     
199     if (dataLen < length)
200       return NO;
201       
202     while ((cnt < dataLen) && ((dataLen - cnt) >= length)) {
203       if (bytes[cnt + 2] != '-') { // can`t be a boundary
204         cnt++;
205         continue;
206       }
207
208       if (bytes[cnt] == '\n') {// LF*-
209         if (bytes[cnt + 1] == '-') { // LF--
210           if (strncmp(boundary, bytes + cnt + 3, length) == 0) {
211             wasFound = YES;
212             break;
213           }
214         }
215       }
216       else if (bytes[cnt] == '\r') { //CR*-
217         if (bytes[cnt + 1] == '-') { //CR--
218           if (strncmp(boundary, bytes + cnt + 3, length) == 0) {
219             wasFound = YES;
220             break;
221           }
222         }
223         else if ((bytes[cnt + 1] == '\n') && (bytes[cnt + 3] == '-')) {
224           if (strncmp(boundary, bytes + cnt + 4, length) == 0) { // CRLF--
225             wasFound = YES;
226             break;
227           }
228         }
229       }
230       cnt++;
231     }
232   }
233   return wasFound;
234 }
235
236 - (NSString *)buildBoundaryForPart:(id<NGMimePart>)_part data:(NSArray *)_data
237   additionalHeaders:(NGMutableHashMap *)_addHeaders 
238 {
239   static   int       BoundaryUniqueCount = 0;
240   NSString *boundary = nil;
241   BOOL     isUnique  = NO;
242   unsigned pid;
243   
244   if ((boundary = [[_part contentType] valueOfParameter:@"boundary"]))
245     return boundary;
246   
247 #if defined(__WIN32__)
248   pid = GetCurrentProcessId();
249 #else
250   pid = getpid();
251 #endif
252   
253   boundary = [NSString stringWithFormat:
254                        @"--%@-%d-%f-%d------",
255                        [NGMimeMultipartBodyGenerator boundaryPrefix],
256                        pid, [[NSDate date] timeIntervalSince1970],
257                        BoundaryUniqueCount++];
258   while (!isUnique) {
259     isUnique = _isBoundaryInArray(self, boundary, _data) ? NO : YES;
260     if (!isUnique)
261       boundary = [NSString stringWithFormat:
262                            @"--%@-%d-%f-%d-----",
263                            [NGMimeMultipartBodyGenerator boundaryPrefix],
264                            pid, [[NSDate date] timeIntervalSince1970],
265                            BoundaryUniqueCount++];
266   }
267   { // setting content-type with boundary
268     NGMimeType *type = nil;
269
270     type = [_part contentType];
271     
272     if (type == nil) {
273       NSDictionary *d;
274
275       d = [[NSDictionary alloc] initWithObjectsAndKeys:
276                                   boundary, @"boundary", nil];
277       type = [NGMimeType mimeType:@"multipart" subType:@"mixed"
278                          parameters:d];
279       [d release];
280     }
281     else {
282       NSMutableDictionary *dict = nil;
283       
284       dict = [NSMutableDictionary dictionaryWithDictionary:
285                                     [type parametersAsDictionary]];
286       [dict setObject:boundary forKey:@"boundary"];
287       type = [NGMimeType mimeType:[type type] subType:[type subType]
288                          parameters:dict];
289     }
290     [_addHeaders setObject:type forKey:@"content-type"];
291   }
292   return boundary;
293 }
294
295 - (NSData *)buildDataWithBoundary:(NSString *)_boundary
296   partsData:(NSArray *)_parts
297 {
298   NSEnumerator  *enumerator;
299   NSData        *part;
300   NSMutableData *data;
301
302   data = (self->useMimeData)
303     ? [[[NGMimeJoinedData alloc] init] autorelease]
304     : [NSMutableData dataWithCapacity:4096];
305   
306   enumerator = [_parts objectEnumerator];
307   while ((part = [enumerator nextObject])) {
308     [data appendBytes:"--" length:2];
309     [data appendBytes:[_boundary cString] length:[_boundary length]];
310     [data appendBytes:"\n" length:1];
311     [data appendData:part];
312     [data appendBytes:"\n" length:1];
313   }
314   [data appendBytes:"--" length:2];
315   [data appendBytes:[_boundary cString] length:[_boundary length]];
316   [data appendBytes:"--\n" length:3];
317   return data;
318 }
319
320 - (NSData *)generateBodyOfPart:(id<NGMimePart>)_part
321   additionalHeaders:(NGMutableHashMap *)_addHeaders
322   delegate:(id)_delegate
323 {
324   // TODO: split up
325   NGMimeMultipartBody *body       = nil;
326   NSMutableData       *data       = nil;
327   id                  tmp         = nil;
328   NSArray             *parts      = nil;
329   id<NGMimePart>      part        = nil;
330   NSEnumerator        *enumerator = nil;
331   NSString            *boundary   = nil;
332   NSMutableArray      *partsData  = nil;
333   NSAutoreleasePool   *pool;
334
335   body = [_part body];
336
337   if (body == nil)
338     return [NSData data];
339
340   pool = [[NSAutoreleasePool alloc] init];
341   
342   NSAssert1([body isKindOfClass:[NGMimeMultipartBody class]],
343             @"NGMimeMultipartBodyGenerator expect a NGMimeMultipartBody "
344             @"as body of part\n part: %@\n", _part);
345
346   data = (self->useMimeData)
347     ? [[[NGMimeJoinedData alloc] init] autorelease]
348     : [NSMutableData dataWithCapacity:4096];
349
350   if ([_delegate respondsToSelector:
351                    @selector(multipartBodyGenerator:prefixForPart:)])
352     tmp = [_delegate multipartBodyGenerator:self prefixForPart:_part];
353   else 
354     tmp = [self multipartBodyGenerator:self prefixForPart:_part
355                 mimeMultipart:body];
356   if (tmp != nil) {
357     NSAssert([tmp isKindOfClass:[NSString class]],
358              @"prefix should be a NSString");
359     [data appendBytes:[tmp cString] length:[tmp length]];
360   }
361   
362   parts      = [body parts];
363   enumerator = [parts objectEnumerator];
364   partsData  = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:4];
365
366   while ((part = [enumerator nextObject])) {
367     id<NGMimePartGenerator> gen = nil;
368
369     if ([_delegate respondsToSelector:
370                    @selector(multipartBodyGenerator:generatorForPart:)]) {
371       gen = [_delegate multipartBodyGenerator:self generatorForPart:part];
372     }
373     else {
374       gen = [self multipartBodyGenerator:self generatorForPart:part];
375       [gen setDelegate:_delegate];
376       [(id)gen setUseMimeData:self->useMimeData];
377     }
378     if (gen == nil) {
379       NSLog(@"WARNING(%s): got no generator", __PRETTY_FUNCTION__);
380       continue;
381     }
382     tmp = [gen generateMimeFromPart:part];
383     if (tmp != nil) {
384       [partsData addObject:tmp];
385     }
386   }
387   boundary = [self buildBoundaryForPart:_part data:partsData
388                    additionalHeaders:_addHeaders];
389   tmp      = [self buildDataWithBoundary:boundary partsData:partsData];
390
391   if (tmp != nil) {
392     [data appendData:tmp];
393   }
394   else {
395     NSLog(@"WARNING(%s): couldn`t build multipart data", __PRETTY_FUNCTION__);
396   }
397   if ([_delegate respondsToSelector:
398                    @selector(multipartBodyGenerator:suffixForPart:)])
399     tmp = [_delegate multipartBodyGenerator:self suffixForPart:_part];
400   else 
401     tmp = [self multipartBodyGenerator:self suffixForPart:_part
402                 mimeMultipart:body];
403   if (tmp != nil) {
404     NSAssert([tmp isKindOfClass:[NSString class]],
405              @"suffix should be a NSString");
406     [data appendBytes:[tmp cString] length:[tmp length]];
407   }
408   [partsData release]; partsData = nil;
409   [data retain];
410   [pool release];
411   return [data autorelease];
412 }
413
414 - (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen
415   prefixForPart:(id<NGMimePart>)_part
416   mimeMultipart:(NGMimeMultipartBody *)_body {
417   
418   return @""; // [_body prefix];
419 }
420
421 - (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen
422   suffixForPart:(id<NGMimePart>)_part
423   mimeMultipart:(NGMimeMultipartBody *)_body {
424
425   return @""; //[_body suffix];
426 }
427
428 - (id<NGMimePartGenerator>)multipartBodyGenerator:(NGMimeBodyGenerator *)_gen
429   generatorForPart:(id<NGMimePart>)_part
430 {
431   id gen;
432   
433   gen = [[NGMimePartGenerator alloc] init];
434   [gen setUseMimeData:self->useMimeData];
435   return [gen autorelease];
436 }
437
438 @end /* NGMimeMultipartBodyGenerator */