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