]> err.no Git - sope/blob - sope-mime/samples/Mime2XmlTool.m
replaced usage of getCString on MacOS >= 10.4
[sope] / sope-mime / samples / Mime2XmlTool.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 "Mime2XmlTool.h"
23 #import "common.h"
24 #import <NGStreams/NGStreams.h>
25 #import <NGMime/NGMime.h>
26 #import <NGMail/NGMail.h>
27
28 @interface Mime2XmlTool(Privates)
29 - (BOOL)_outputPartAsXML:(id<NGMimePart>)_part;
30 @end
31
32 @implementation Mime2XmlTool
33
34 - (NSUserDefaults *)userDefaults {
35   return [NSUserDefaults standardUserDefaults];
36 }
37
38 /* part generation */
39
40 - (void)indent {
41   unsigned i;
42   for (i = 0; i < self->outIndent; i++)
43     printf("  ");
44 }
45
46 - (BOOL)_outputMultipartBody:(NGMimeMultipartBody *)_body {
47   NSEnumerator *e;
48   id<NGMimePart> part;
49   
50   self->outIndent++;
51
52   e = [[_body parts] objectEnumerator];
53   while ((part = [e nextObject]))
54     [self _outputPartAsXML:part];
55   
56   self->outIndent--;
57   return YES;
58 }
59
60 - (BOOL)_outputStringBody:(NSString *)body {
61   [self indent];
62   printf("<body charlen='%i'>", [body length]);
63   if ([body length] > 256) {
64     body = [body substringToIndex:255];
65     printf("%s", [body cString]);
66   }
67   printf("</body>\n");
68   return YES;
69 }
70 - (BOOL)_outputDataBody:(NSData *)body {
71   [self indent];
72   printf("<body><data size='%i'>...</data></body>\n",
73          [body length]);
74   return YES;
75 }
76 - (BOOL)_outputPartBody:(id<NGMimePart>)body {
77   [self indent];
78   printf("<body>\n");
79   [self _outputPartAsXML:(id<NGMimePart>)body];
80   [self indent];
81   printf("</body>\n");
82   return YES;
83 }
84 - (BOOL)_outputMultiPartBody:(id<NGMimePart>)body {
85   [self indent];
86   printf("<body multipart='true'>\n");
87         
88   [self _outputMultipartBody:(id)body];
89         
90   [self indent];
91   printf("</body>\n");
92   return YES;
93 }
94
95 - (BOOL)_outputHeaderField:(NSString *)_field value:(id)_value {
96   Class    clazz;
97   NSString *s;
98   [self indent];
99   
100   clazz = [_value class];
101   printf("<h name=\"%s\"", [_field cString]);
102   printf(">");
103   
104   s = [_value stringValue];
105   s = [s stringByEscapingXMLString];
106   printf("%s", [s cString]);
107   
108   printf("</h>\n");
109   return YES;
110 }
111 - (BOOL)_outputPartHeaders:(id<NGMimePart>)_part {
112   NSEnumerator *headerFields;
113   NSString *headerField;
114   
115   headerFields = [_part headerFieldNames];
116   while ((headerField = [headerFields nextObject])) {
117     NSEnumerator *vals;
118     id value;
119     
120     vals = [_part valuesOfHeaderFieldWithName:headerField];
121     while ((value = [vals nextObject])) {
122       [self _outputHeaderField:headerField value:value];
123     }
124   }
125   return YES;
126 }
127
128 - (BOOL)_outputPartAsXML:(id<NGMimePart>)_part {
129   id tmp;
130   id body;
131   
132   if (_part == nil) {
133     NSLog(@"got no part ...");
134     return NO;
135   }
136   
137   [self indent];
138   printf("<part ptr='0x%p'", (unsigned int)_part);
139   
140   if ([_part conformsToProtocol:@protocol(NGMimePart)]) {
141     if ((tmp = [_part contentType]))
142       printf(" content-type='%s'", [[tmp stringValue] cString]);
143     if ((tmp = [_part encoding]))
144       printf(" encoding='%s'", [[tmp stringValue] cString]);
145     if ((tmp = [_part contentId]))
146       printf(" content-id='%s'", [[tmp stringValue] cString]);
147     if ((tmp = [_part contentLanguage]))
148       printf(" content-language='%s'", [[tmp stringValue] cString]);
149     if ((tmp = [_part contentMd5]))
150       printf(" content-md5='%s'", [[tmp stringValue] cString]);
151   }  
152   printf(">\n");
153   self->outIndent++;
154   {
155     /* output header */
156     [self _outputPartHeaders:_part];
157     
158     /* output body */
159     
160     if ((body = [_part body])) {
161       if ([body isKindOfClass:[NSString class]])
162         [self _outputStringBody:body];
163       else if ([body isKindOfClass:[NSData class]])
164         [self _outputDataBody:body];
165       else if ([body conformsToProtocol:@protocol(NGMimePart)]) {
166         /*
167           Note: an NSData is also a MIME part, because of this
168           a check for NSData needs to be done first ! 
169         */
170         [self _outputPartBody:body];
171       }
172       else if ([body isKindOfClass:[NGMimeMultipartBody class]]) {
173         [self _outputMultiPartBody:body];
174       }
175       else
176         NSLog(@"unknown body class: %@", NSStringFromClass(body));
177     }
178   }
179   self->outIndent--;
180   [self indent];
181   printf("</part>\n");
182   return YES;
183 }
184
185 - (BOOL)outputPartAsXML:(id<NGMimePart>)_part {
186   self->outIndent = 0;
187   printf("<?xml version='1.0' encoding='ISO-8859-1'?>\n");
188   return [self _outputPartAsXML:_part];
189 }
190
191 - (BOOL)outputPartAsMime:(id<NGMimePart>)_part {
192   return NO;
193 }
194
195 - (BOOL)outputPart:(id<NGMimePart>)_part {
196   NSUserDefaults *ud = [self userDefaults];
197   NSAutoreleasePool *pool;
198   NSString *out;
199   BOOL result;
200   
201   pool = [[NSAutoreleasePool alloc] init];
202   
203   out = [ud stringForKey:@"out"];
204   if ([out length] == 0)
205     result = YES;
206   else if ([out isEqualToString:@"xml"])
207     result = [self outputPartAsXML:_part];
208   else if ([out isEqualToString:@"mime"]) 
209     result = [self outputPartAsMime:_part];
210   else {
211     NSLog(@"unknown output module: %@", out);
212     result = NO;
213   }
214   [pool release];
215   return result;
216 }
217
218 /* MIME delegate */
219
220 #if 0
221 - (id)parser:(NGMimePartParser *)_parser
222   parseHeaderField:(NSString *)_name
223   data:(NSData *)_data
224 {
225   if (self->rejectAllFields) {
226     if ([_name hasPrefix:@"content-"]) {
227       NSString *s;
228       
229       s = [[NSString alloc] initWithData:_data 
230                             encoding:NSISOLatin1StringEncoding];
231       return [s autorelease];
232     }
233     return nil;
234   }
235   
236   return _data;
237 }
238 #endif
239
240 - (void)parser:(NGMimePartParser *)_parser
241   didParseHeader:(NGHashMap *)_header
242 {
243   //NSLog(@"got header: %@", _header);
244 }
245 - (BOOL)parser:(NGMimePartParser *)_parser
246   keepHeaderField:(NSString *)_name
247   value:(id)_value
248 {
249   if ([_name hasPrefix:@"content-"])
250     return YES;
251   
252   return self->rejectAllFields ? NO : YES;
253 }
254
255 /* MIME parsing */
256
257 - (id<NGMimePart>)parseFile:(NSString *)_path useStreams:(BOOL)_streams {
258   NSAutoreleasePool *pool;
259   NGMimePartParser  *parser;
260   id<NGMimePart>    part;
261   
262   pool = [[NSAutoreleasePool alloc] init];
263   {
264     id  input;
265     SEL sel;
266     
267     parser = [[[NGMimeMessageParser alloc] init] autorelease];
268     
269     if (self->useDelegate)
270       [parser setDelegate:self];
271     
272     /* create input object */
273     
274     if (_streams) {
275       NGFileStream *stream;
276       
277       stream = [[[NGFileStream alloc] initWithPath:_path] autorelease];
278       if (![stream openInMode:@"r"]) {
279         NSLog(@"could not open stream for reading: '%@'\n  exception: %@", 
280               _path, [stream lastException]);
281         return NO;
282       }
283       
284       sel   = @selector(parsePartFromStream:);
285       input = stream;
286     }
287     else {
288       NSData *data;
289     
290       if ((data = [NSData dataWithContentsOfMappedFile:_path]) == nil) {
291         NSLog(@"could not get data for path: '%@'", _path);
292         return NO;
293       }
294       
295       sel   = @selector(parsePartFromData:);
296       input = data;
297     }
298
299     /* do parsing */
300     
301     if ([[self userDefaults] boolForKey:@"noparse"])
302       part = nil;
303     else
304       part = [parser performSelector:sel withObject:input];
305     
306     part = [part retain];
307   }
308   [pool release];
309   return [part autorelease];
310 }
311
312 - (BOOL)processFile:(NSString *)_path {
313   NSProcessInfo     *pi = [NSProcessInfo processInfo];
314   NSUserDefaults    *ud = [self userDefaults];
315   id<NGMimePart>    part;
316   NSAutoreleasePool *pool;
317   
318   NSLog(@"process: %@", _path);
319
320   /* parsing */
321   
322   pool = [[NSAutoreleasePool alloc] init];
323   {
324     NSTimeInterval startTime, endTime;
325     unsigned int   startSize, endSize;
326     int i, preRepeatCount;
327     
328     /* first some useless parsing for profiling */
329     
330     preRepeatCount = self->preloops;
331     for (i = 0; i < preRepeatCount; i++) {
332       NSAutoreleasePool *pool;
333       
334       startTime = [[NSDate date] timeIntervalSince1970];
335       startSize = [pi virtualMemorySize];
336       
337       pool = [[NSAutoreleasePool alloc] init];
338       part = [self parseFile:_path useStreams:[ud boolForKey:@"streams"]];
339       [pool release];
340       
341       endSize = [pi virtualMemorySize];
342       endTime = [[NSDate date] timeIntervalSince1970];
343       
344       /* statistics */
345       
346       if ([ud boolForKey:@"statistics"]) {
347         fprintf(stderr, 
348                 "parsing time [%2i]: %.3fs, "
349                 "vmem-diff: %8i (%4iK,%4iM), vmem: %8i (%4iK,%4iM))\n", 
350                 i, (endTime-startTime), 
351                 (endSize - startSize), 
352                 (endSize - startSize) / 1024, 
353                 (endSize - startSize) / 1024 / 1024, 
354                 endSize, endSize/1024, endSize/1024/1024);
355       }
356     }
357
358     /* the "real" parser (remembers the part ...) */
359     
360     startTime = [[NSDate date] timeIntervalSince1970];
361     startSize = [pi virtualMemorySize];
362     part = [self parseFile:_path useStreams:[ud boolForKey:@"streams"]];
363     endSize = [pi virtualMemorySize];
364     endTime = [[NSDate date] timeIntervalSince1970];
365     
366     /* statistics */
367     
368     if ([ud boolForKey:@"statistics"]) {
369       fprintf(stderr, 
370               "parsing time: %.3fs, vmem-diff: %8i, vmem: %8i\n", 
371               (endTime-startTime), (endSize - startSize), endSize);
372     }
373   }
374   part = [part retain];
375   [pool release];
376   part = [part autorelease];
377   
378   if (part == nil) {
379     NSLog(@"could not parse a part from path: '%@'", _path);
380     return NO;
381   }
382   
383   /* generating */
384   [self outputPart:part];
385   return YES;
386 }
387
388 /* tool operation */
389
390 - (int)usage {
391   fprintf(stderr, "usage: mime2xml <file>*\n");
392   fprintf(stderr, "  -preloops    <n>\n");
393   fprintf(stderr, "  -statistics  YES|NO\n");
394   fprintf(stderr, "  -streams     YES|NO\n");
395   fprintf(stderr, "  -noparse     YES|NO\n");
396   fprintf(stderr, "  -delegate    YES|NO\n");
397   fprintf(stderr, "  -parsefields YES|NO\n");
398   return 1;
399 }
400
401 - (int)runWithArguments:(NSArray *)_args {
402   NSUserDefaults *ud = [self userDefaults];
403   NSEnumerator *e;
404   NSString *f;
405
406   self->parseFields     = [ud boolForKey:@"parsefields"];
407   self->useDelegate     = [ud boolForKey:@"delegate"];
408   self->preloops        = [ud integerForKey:@"preloops"];
409   self->rejectAllFields = [ud boolForKey:@"rejectAllFields"];
410   
411   _args = [_args subarrayWithRange:NSMakeRange(1, [_args count] - 1)];
412   if ([_args count] == 0)
413     return [self usage];
414   
415   e = [_args objectEnumerator];
416   while ((f = [e nextObject])) {
417     NSAutoreleasePool *pool;
418     
419     pool = [[NSAutoreleasePool alloc] init];
420     [self processFile:f];
421     [pool release];
422   }
423   
424   return 0;
425 }
426
427 @end /* Mime2XmlTool */