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