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