1 /* NSString+Mail.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 <Foundation/NSObject.h>
26 #import <SaxObjC/SaxAttributes.h>
27 #import <SaxObjC/SaxContentHandler.h>
28 #import <SaxObjC/SaxLexicalHandler.h>
29 #import <SaxObjC/SaxXMLReader.h>
30 #import <SaxObjC/SaxXMLReaderFactory.h>
31 #import <NGExtensions/NGQuotedPrintableCoding.h>
32 #import <NGExtensions/NSString+misc.h>
33 #import <NGExtensions/NSObject+Logs.h>
35 #import "NSString+Mail.h"
38 #define showWhoWeAre() \
39 [self logWithFormat: @"invoked '%@'", NSStringFromSelector (_cmd)]
41 #define showWhoWeAre() {}
44 @interface _SOGoHTMLToTextContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
46 NSArray *ignoreContentTags;
47 NSArray *specialTreatmentTags;
52 unsigned int listCount;
54 NSMutableString *result;
57 + (id) htmlToTextContentHandler;
59 - (NSString *) result;
63 @implementation _SOGoHTMLToTextContentHandler
65 + (id) htmlToTextContentHandler
67 static id htmlToTextContentHandler;
69 if (!htmlToTextContentHandler)
70 htmlToTextContentHandler = [self new];
72 return htmlToTextContentHandler;
77 if ((self = [super init]))
79 ignoreContentTags = [NSArray arrayWithObjects: @"head", @"script",
81 specialTreatmentTags = [NSArray arrayWithObjects: @"body", @"p", @"ul",
82 @"li", @"table", @"tr", @"td", @"th",
83 @"br", @"hr", @"dt", @"dd", nil];
84 [ignoreContentTags retain];
85 [specialTreatmentTags retain];
100 [ignoreContentTags release];
101 [specialTreatmentTags release];
106 - (NSString *) result
110 newResult = [NSString stringWithString: result];
117 /* SaxContentHandler */
118 - (void) startDocument
123 result = [NSMutableString new];
133 - (void) startPrefixMapping: (NSString *) prefix
134 uri: (NSString *) uri
139 - (void) endPrefixMapping: (NSString *) prefix
144 - (void) _startSpecialTreatment: (NSString *) tagName
146 if ([tagName isEqualToString: @"br"]
147 || [tagName isEqualToString: @"p"])
148 [result appendString: @"\n"];
149 else if ([tagName isEqualToString: @"hr"])
150 [result appendString: @"______________________________________________________________________________\n"];
151 else if ([tagName isEqualToString: @"ul"])
153 [result appendString: @"\n"];
156 else if ([tagName isEqualToString: @"ol"])
158 [result appendString: @"\n"];
162 else if ([tagName isEqualToString: @"li"])
167 [result appendFormat: @" %d. ", listCount];
170 [result appendString: @" * "];
172 else if ([tagName isEqualToString: @"dd"])
173 [result appendString: @" "];
176 - (void) _endSpecialTreatment: (NSString *) tagName
178 if ([tagName isEqualToString: @"ul"])
180 [result appendString: @"\n"];
183 else if ([tagName isEqualToString: @"ol"])
185 [result appendString: @"\n"];
188 else if ([tagName isEqualToString: @"dt"])
190 [result appendString: @":\n"];
192 else if ([tagName isEqualToString: @"li"]
193 || [tagName isEqualToString: @"dd"])
194 [result appendString: @"\n"];
197 - (void) startElement: (NSString *) element
198 namespace: (NSString *) namespace
199 rawName: (NSString *) rawName
200 attributes: (id <SaxAttributes>) attributes
208 tagName = [rawName lowercaseString];
209 if ([ignoreContentTags containsObject: tagName])
211 else if ([specialTreatmentTags containsObject: tagName])
212 [self _startSpecialTreatment: tagName];
216 - (void) endElement: (NSString *) element
217 namespace: (NSString *) namespace
218 rawName: (NSString *) rawName
226 tagName = [rawName lowercaseString];
227 if ([ignoreContentTags containsObject: tagName])
229 else if ([specialTreatmentTags containsObject: tagName])
230 [self _endSpecialTreatment: tagName];
234 - (void) characters: (unichar *) characters
238 [result appendString: [NSString stringWithCharacters: characters
242 - (void) ignorableWhitespace: (unichar *) whitespaces
248 - (void) processingInstruction: (NSString *) pi
249 data: (NSString *) data
254 - (void) setDocumentLocator: (id <NSObject, SaxLocator>) locator
259 - (void) skippedEntity: (NSString *) entity
264 /* SaxLexicalHandler */
265 - (void) comment: (unichar *) chars
271 - (void) startDTD: (NSString *) name
272 publicId: (NSString *) pub
273 systemId: (NSString *) sys
283 - (void) startEntity: (NSString *) entity
288 - (void) endEntity: (NSString *) entity
305 // @interface NSDictionary (SOGoDebug)
311 // @implementation NSDictionary (SOGoDebug)
315 // NSEnumerator *keys;
317 // NSMutableString *dump;
319 // dump = [NSMutableString new];
320 // [dump appendFormat: @"\nNSDictionary dump (%@):\n", self];
321 // keys = [[self allKeys] objectEnumerator];
322 // key = [keys nextObject];
325 // [dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]];
326 // key = [keys nextObject];
328 // [dump appendFormat: @"--- end ---\n"];
336 @implementation NSString (SOGoExtension)
338 - (NSString *) htmlToText
340 id <NSObject, SaxXMLReader> parser;
341 _SOGoHTMLToTextContentHandler *handler;
343 parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
344 createXMLReaderForMimeType: @"text/html"];
345 handler = [_SOGoHTMLToTextContentHandler htmlToTextContentHandler];
346 [parser setContentHandler: handler];
347 [parser parseFromSource: self];
349 return [handler result];
352 - (NSString *) decodedSubject
354 NSString *decodedSubject;
356 if ([self hasPrefix: @"=?"] && [self hasSuffix: @"?="])
358 decodedSubject = [self stringByDecodingQuotedPrintable];
360 decodedSubject = self;
363 decodedSubject = self;
365 return decodedSubject;