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 <Foundation/NSDictionary.h>
25 #import <SaxObjC/SaxAttributes.h>
26 #import <SaxObjC/SaxContentHandler.h>
27 #import <SaxObjC/SaxLexicalHandler.h>
28 #import <SaxObjC/SaxXMLReader.h>
29 #import <SaxObjC/SaxXMLReaderFactory.h>
30 #import <NGExtensions/NSString+misc.h>
31 #import <NGObjWeb/SoObjects.h>
33 #import "UIxMailPartHTMLViewer.h"
35 #define showWhoWeAre() NSLog(@"invoked '%@'", NSStringFromSelector(_cmd))
37 @interface _UIxHTMLMailContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
39 NSMutableString *result;
41 NSDictionary *attachmentIds;
45 BOOL inCSSDeclaration;
47 NSMutableArray *crumb;
50 - (NSString *) result;
54 @implementation _UIxHTMLMailContentHandler
58 if ((self = [super init]))
80 - (void) setAttachmentIds: (NSDictionary *) newAttachmentIds
82 attachmentIds = newAttachmentIds;
87 return [[css copy] autorelease];
92 return [[result copy] autorelease];
95 /* SaxContentHandler */
96 - (void) startDocument
105 result = [NSMutableString new];
106 css = [NSMutableString new];
107 crumb = [NSMutableArray new];
111 inCSSDeclaration = NO;
117 unsigned int count, max;
121 for (count = max - 1; count > -1; count--)
123 [result appendFormat: @"</%@>", [crumb objectAtIndex: count]];
124 [crumb removeObjectAtIndex: count];
128 - (void) startPrefixMapping: (NSString *)_prefix
129 uri: (NSString *)_uri
134 - (void) endPrefixMapping: (NSString *)_prefix
139 - (void) _appendStyle: (unichar *) _chars
143 unichar *start, *currentChar;
147 for (count = 0; count < _len; count++)
149 currentChar = _chars + count;
150 if (inCSSDeclaration)
152 if (*(char *) currentChar == '}')
154 inCSSDeclaration = NO;
160 if (*(char *) currentChar == '{')
161 inCSSDeclaration = YES;
162 if (*(char *) currentChar == ',')
164 else if (!hasEmbeddedCSS)
166 if (*(char *) currentChar == '@')
167 hasEmbeddedCSS = YES;
169 if (*(char *) currentChar > 32)
171 [css appendString: [NSString stringWithCharacters: start
172 length: (currentChar - start)]];
173 [css appendString: @".SOGoHTMLMail-CSS-Delimiter "];
174 hasEmbeddedCSS = YES;
180 [css appendString: [NSString stringWithCharacters: start
181 length: (currentChar - start)]];
184 - (void) startElement: (NSString *) _localName
185 namespace: (NSString *) _ns
186 rawName: (NSString *) _rawName
187 attributes: (id <SaxAttributes>) _attributes
189 unsigned int count, max;
190 NSString *name, *value;
191 NSMutableString *resultPart;
194 if (inStyle || inScript)
196 else if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame)
198 else if ([_localName caseInsensitiveCompare: @"script"] == NSOrderedSame)
200 else if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame)
204 resultPart = [NSMutableString new];
205 [resultPart appendFormat: @"<%@", _rawName];
207 max = [_attributes count];
208 for (count = 0; count < max; count++)
211 name = [_attributes nameAtIndex: count];
212 if ([[name lowercaseString] hasPrefix: @"on"])
214 else if ([name caseInsensitiveCompare: @"src"] == NSOrderedSame)
216 value = [_attributes valueAtIndex: count];
217 if ([value hasPrefix: @"cid:"])
219 value = [attachmentIds
220 objectForKey: [value substringFromIndex: 4]];
221 skipAttribute = (value == nil);
227 value = [_attributes valueAtIndex: count];
229 [resultPart appendFormat: @" %@=\"%@\"",
230 name, [value stringByReplacingString: @"\""
231 withString: @"\\\""]];
234 [resultPart appendString: @">"];
235 [result appendString: resultPart];
239 - (void) endElement: (NSString *) _localName
240 namespace: (NSString *) _ns
241 rawName: (NSString *) _rawName
245 if ([_localName caseInsensitiveCompare: @"style"] == NSOrderedSame)
248 inCSSDeclaration = NO;
252 inScript = ([_localName caseInsensitiveCompare: @"script"] != NSOrderedSame);
255 if ([_localName caseInsensitiveCompare: @"body"] == NSOrderedSame)
258 [result appendFormat: @"</%@>", _localName];
262 - (void) characters: (unichar *) _chars
270 [self _appendStyle: _chars length: _len];
273 tmpString = [NSString stringWithCharacters: _chars length: _len];
274 [result appendString: [tmpString stringByEscapingHTMLString]];
279 - (void) ignorableWhitespace: (unichar *) _chars
285 - (void) processingInstruction: (NSString *) _pi
286 data: (NSString *) _data
291 - (void) setDocumentLocator: (id <NSObject, SaxLocator>) _locator
296 - (void) skippedEntity: (NSString *) _entityName
301 /* SaxLexicalHandler */
302 - (void) comment: (unichar *) _chars
306 [self _appendStyle: _chars length: _len];
309 - (void) startDTD: (NSString *) _name
310 publicId: (NSString *) _pub
311 systemId: (NSString *) _sys
321 - (void) startEntity: (NSString *) _name
326 - (void) endEntity: (NSString *) _name
343 @interface NSDictionary (SOGoDebug)
349 @implementation NSDictionary (SOGoDebug)
355 NSMutableString *dump;
357 dump = [NSMutableString new];
358 [dump appendFormat: @"\nNSDictionary dump (%@):\n", self];
359 keys = [[self allKeys] objectEnumerator];
360 key = [keys nextObject];
363 [dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]];
364 key = [keys nextObject];
366 [dump appendFormat: @"--- end ---\n"];
374 @implementation UIxMailPartHTMLViewer
376 - (void) _convertReferencesForPart: (NSDictionary *) part
377 withCount: (unsigned int) count
378 andBaseURL: (NSString *) url
379 intoDictionary: (NSMutableDictionary *) attachmentIds
383 bodyId = [part objectForKey: @"bodyId"];
384 if ([bodyId length] > 0)
387 if ([bodyId hasPrefix: @"<"])
388 bodyId = [bodyId substringFromIndex: 1];
389 if ([bodyId hasSuffix: @">"])
390 bodyId = [bodyId substringToIndex: [bodyId length] - 1];
391 [attachmentIds setObject: [url stringByAppendingFormat: @"/%d", count]
396 - (NSDictionary *) _attachmentIds
398 NSMutableDictionary *attachmentIds;
399 UIxMailPartViewer *parent;
400 unsigned int count, max;
401 NSMutableString *url;
405 attachmentIds = [NSMutableDictionary new];
406 [attachmentIds autorelease];
408 parent = [self parent];
409 if ([NSStringFromClass ([parent class])
410 isEqualToString: @"UIxMailPartAlternativeViewer"])
412 baseURL = [[self clientObject] baseURLInContext: context];
413 url = [NSMutableString new];
414 [url appendString: baseURL];
415 [url appendFormat: @"/%@", [partPath componentsJoinedByString: @"/"]];
416 [url deleteCharactersInRange: NSMakeRange([url length] - 3, 2)];
417 parts = [[parent bodyInfo] objectForKey: @"parts"];
419 for (count = 0; count < max; count++)
420 [self _convertReferencesForPart: [parts objectAtIndex: count]
423 intoDictionary: attachmentIds];
427 return attachmentIds;
430 - (NSString *) flatContentAsString
432 id <NSObject, SaxXMLReader> parser;
433 _UIxHTMLMailContentHandler *handler;
434 NSString *preparsedContent, *content, *css;
436 preparsedContent = [super flatContentAsString];
437 parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
438 createXMLReaderForMimeType: @"text/html"];
440 handler = [_UIxHTMLMailContentHandler new];
441 [handler setAttachmentIds: [self _attachmentIds]];
442 [parser setContentHandler: handler];
443 [parser setProperty: @"http://xml.org/sax/properties/lexical-handler"
445 [parser parseFromSource: preparsedContent];
450 = [NSString stringWithFormat: @"<style type=\"text/css\">%@</style>%@",
451 css, [handler result]];
453 content = [handler result];