1 /* UIxMailPartHTMLViewer.m - this file is part of SOGo
3 * Copyright (C) 2007 Inverse groupe conseil
5 * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This file is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 #import <Foundation/NSArray.h>
24 #import <SaxObjC/SaxAttributes.h>
25 #import <SaxObjC/SaxContentHandler.h>
26 #import <SaxObjC/SaxLexicalHandler.h>
27 #import <SaxObjC/SaxXMLReader.h>
28 #import <SaxObjC/SaxXMLReaderFactory.h>
29 #import <NGExtensions/NSString+misc.h>
30 #import <NGObjWeb/SoObjects.h>
32 #import "UIxMailPartHTMLViewer.h"
34 #define showWhoWeAre() NSLog(@"invoked '%@'", NSStringFromSelector(_cmd))
36 @interface _UIxHTMLMailContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
38 NSMutableString *result;
40 NSDictionary *attachmentIds;
44 BOOL inCSSDeclaration;
46 NSMutableArray *crumb;
49 - (NSString *) result;
53 @implementation _UIxHTMLMailContentHandler
57 if ((self = [super init]))
79 - (void) setAttachmentIds: (NSDictionary *) newAttachmentIds
81 attachmentIds = newAttachmentIds;
86 return [[css copy] autorelease];
91 return [[result copy] autorelease];
94 /* SaxContentHandler */
95 - (void) startDocument
104 result = [NSMutableString new];
105 css = [NSMutableString new];
106 crumb = [NSMutableArray new];
110 inCSSDeclaration = NO;
116 unsigned int count, max;
120 for (count = max - 1; count > -1; count--)
122 [result appendFormat: @"</%@>", [crumb objectAtIndex: count]];
123 [crumb removeObjectAtIndex: count];
127 - (void) startPrefixMapping: (NSString *)_prefix
128 uri: (NSString *)_uri
133 - (void) endPrefixMapping: (NSString *)_prefix
138 - (void) _appendStyle: (unichar *) _chars
142 unichar *start, *currentChar;
146 for (count = 0; count < _len; count++)
148 currentChar = _chars + count;
149 if (inCSSDeclaration)
151 if (*(char *) currentChar == '}')
153 inCSSDeclaration = NO;
159 if (*(char *) currentChar == '{')
160 inCSSDeclaration = YES;
161 if (*(char *) currentChar == ',')
163 else if (!hasEmbeddedCSS)
165 if (*(char *) currentChar == '@')
166 hasEmbeddedCSS = YES;
168 if (*(char *) currentChar > 32)
170 [css appendString: [NSString stringWithCharacters: start
171 length: (currentChar - start)]];
172 [css appendString: @".SOGoHTMLMail-CSS-Delimiter "];
173 hasEmbeddedCSS = YES;
179 [css appendString: [NSString stringWithCharacters: start
180 length: (currentChar - start)]];
183 - (void) startElement: (NSString *) _localName
184 namespace: (NSString *) _ns
185 rawName: (NSString *) _rawName
186 attributes: (id <SaxAttributes>) _attributes
188 unsigned int count, max;
189 NSString *name, *value;
190 NSMutableString *resultPart;
193 if (inStyle || inScript)
195 else if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame)
197 else if ([_localName caseInsensitiveCompare: @"script"] == NSOrderedSame)
199 else if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame)
203 resultPart = [NSMutableString new];
204 [resultPart appendFormat: @"<%@", _rawName];
206 max = [_attributes count];
207 for (count = 0; count < max; count++)
210 name = [_attributes nameAtIndex: count];
211 if ([[name lowercaseString] hasPrefix: @"on"])
213 else if ([name caseInsensitiveCompare: @"src"] == NSOrderedSame)
215 value = [_attributes valueAtIndex: count];
216 if ([value hasPrefix: @"cid:"])
218 value = [attachmentIds
219 objectForKey: [value substringFromIndex: 4]];
220 skipAttribute = (value == nil);
226 value = [_attributes valueAtIndex: count];
228 [resultPart appendFormat: @" %@=\"%@\"",
229 name, [value stringByReplacingString: @"\""
230 withString: @"\\\""]];
233 [resultPart appendString: @">"];
234 [result appendString: resultPart];
238 - (void) endElement: (NSString *) _localName
239 namespace: (NSString *) _ns
240 rawName: (NSString *) _rawName
244 if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame)
247 inCSSDeclaration = NO;
251 inScript = ([_localName caseInsensitiveCompare: @"script"] != NSOrderedSame);
254 if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame)
257 [result appendFormat: @"</%@>", _localName];
261 - (void) characters: (unichar *) _chars
269 [self _appendStyle: _chars length: _len];
272 tmpString = [NSString stringWithCharacters: _chars length: _len];
273 [result appendString: [tmpString stringByEscapingHTMLString]];
278 - (void) ignorableWhitespace: (unichar *) _chars
284 - (void) processingInstruction: (NSString *) _pi
285 data: (NSString *) _data
290 - (void) setDocumentLocator: (id <NSObject, SaxLocator>) _locator
295 - (void) skippedEntity: (NSString *) _entityName
300 /* SaxLexicalHandler */
301 - (void) comment: (unichar *) _chars
305 [self _appendStyle: _chars length: _len];
308 - (void) startDTD: (NSString *) _name
309 publicId: (NSString *) _pub
310 systemId: (NSString *) _sys
320 - (void) startEntity: (NSString *) _name
325 - (void) endEntity: (NSString *) _name
342 @interface NSDictionary (SOGoDebug)
348 @implementation NSDictionary (SOGoDebug)
354 NSMutableString *dump;
356 dump = [NSMutableString new];
357 [dump appendFormat: @"\nNSDictionary dump (%@):\n", self];
358 keys = [[self allKeys] objectEnumerator];
359 key = [keys nextObject];
362 [dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]];
363 key = [keys nextObject];
365 [dump appendFormat: @"--- end ---\n"];
373 @implementation UIxMailPartHTMLViewer
375 - (void) _convertReferencesForPart: (NSDictionary *) part
376 withCount: (unsigned int) count
377 andBaseURL: (NSString *) url
378 intoDictionary: (NSMutableDictionary *) attachmentIds
382 bodyId = [part objectForKey: @"bodyId"];
383 if ([bodyId length] > 0)
386 if ([bodyId hasPrefix: @"<"])
387 bodyId = [bodyId substringFromIndex: 1];
388 if ([bodyId hasSuffix: @">"])
389 bodyId = [bodyId substringToIndex: [bodyId length] - 1];
390 [attachmentIds setObject: [url stringByAppendingFormat: @"/%d", count]
395 - (NSDictionary *) _attachmentIds
397 NSMutableDictionary *attachmentIds;
398 UIxMailPartViewer *parent;
399 unsigned int count, max;
403 attachmentIds = [NSMutableDictionary new];
404 [attachmentIds autorelease];
406 parent = [self parent];
407 if ([NSStringFromClass ([parent class])
408 isEqualToString: @"UIxMailPartAlternativeViewer"])
410 baseURL = [[self clientObject] baseURLInContext: context];
411 parts = [[parent bodyInfo] objectForKey: @"parts"];
413 for (count = 0; count < max; count++)
414 [self _convertReferencesForPart: [parts objectAtIndex: count]
417 intoDictionary: attachmentIds];
420 NSLog(@"attc: '%@'", attachmentIds);
421 return attachmentIds;
424 - (NSString *) flatContentAsString
426 id <NSObject, SaxXMLReader> parser;
427 _UIxHTMLMailContentHandler *handler;
428 NSString *preparsedContent, *content, *css;
430 preparsedContent = [super flatContentAsString];
431 parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
432 createXMLReaderForMimeType: @"text/html"];
434 handler = [_UIxHTMLMailContentHandler new];
435 [handler setAttachmentIds: [self _attachmentIds]];
436 [parser setContentHandler: handler];
437 [parser setProperty: @"http://xml.org/sax/properties/lexical-handler"
439 [parser parseFromSource: preparsedContent];
444 = [NSString stringWithFormat: @"<style type=\"text/css\">%@</style>%@",
445 css, [handler result]];
447 content = [handler result];