]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Mailer/NSString+Mail.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1233 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Mailer / NSString+Mail.m
1 /* NSString+Mail.m - this file is part of SOGo
2  *
3  * Copyright (C) 2007 Inverse groupe conseil
4  *
5  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
6  *
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)
10  * any later version.
11  *
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.
16  *
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.
21  */
22
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>
34
35 #import "NSString+Mail.h"
36
37 #if 1
38 #define showWhoWeAre() \
39   [self logWithFormat: @"invoked '%@'", NSStringFromSelector (_cmd)]
40 #else
41 #define showWhoWeAre() {}
42 #endif
43
44 @interface _SOGoHTMLToTextContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
45 {
46   NSArray *ignoreContentTags;
47   NSArray *specialTreatmentTags;
48
49   BOOL ignoreContent;
50   BOOL orderedList;
51   BOOL unorderedList;
52   unsigned int listCount;
53
54   NSMutableString *result;
55 }
56
57 + (id) htmlToTextContentHandler;
58
59 - (NSString *) result;
60
61 @end
62
63 @implementation _SOGoHTMLToTextContentHandler
64
65 + (id) htmlToTextContentHandler
66 {
67   static id htmlToTextContentHandler;
68
69   if (!htmlToTextContentHandler)
70     htmlToTextContentHandler = [self new];
71
72   return htmlToTextContentHandler;
73 }
74
75 - (id) init
76 {
77   if ((self = [super init]))
78     {
79       ignoreContentTags = [NSArray arrayWithObjects: @"head", @"script",
80                                    @"style", nil];
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];
86
87       ignoreContent = NO;
88       result = nil;
89
90       orderedList = NO;
91       unorderedList = NO;
92       listCount = 0;
93     }
94
95   return self;
96 }
97
98 - (void) dealloc
99 {
100   [ignoreContentTags release];
101   [specialTreatmentTags release];
102   [result release];
103   [super dealloc];
104 }
105
106 - (NSString *) result
107 {
108   NSString *newResult;
109
110   newResult = [NSString stringWithString: result];
111   [result release];
112   result = nil;
113
114   return newResult;
115 }
116
117 /* SaxContentHandler */
118 - (void) startDocument
119 {
120   showWhoWeAre();
121
122   [result release];
123   result = [NSMutableString new];
124 }
125
126 - (void) endDocument
127 {
128   showWhoWeAre();
129
130   ignoreContent = NO;
131 }
132
133 - (void) startPrefixMapping: (NSString *) prefix
134                         uri: (NSString *) uri
135 {
136   showWhoWeAre();
137 }
138
139 - (void) endPrefixMapping: (NSString *) prefix
140 {
141   showWhoWeAre();
142 }
143
144 - (void) _startSpecialTreatment: (NSString *) tagName
145 {
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"])
152     {
153       [result appendString: @"\n"];
154       unorderedList = YES;
155     }
156   else if ([tagName isEqualToString: @"ol"])
157     {
158       [result appendString: @"\n"];
159       orderedList = YES;
160       listCount = 0;
161     }
162   else if ([tagName isEqualToString: @"li"])
163     {
164       if (orderedList)
165         {
166           listCount++;
167           [result appendFormat: @" %d. ", listCount];
168         }
169       else
170         [result appendString: @" * "];
171     }
172   else if ([tagName isEqualToString: @"dd"])
173     [result appendString: @"  "];
174 }
175
176 - (void) _endSpecialTreatment: (NSString *) tagName
177 {
178   if ([tagName isEqualToString: @"ul"])
179     {
180       [result appendString: @"\n"];
181       unorderedList = NO;
182     }
183   else if ([tagName isEqualToString: @"ol"])
184     {
185       [result appendString: @"\n"];
186       orderedList = NO;
187     }
188   else if ([tagName isEqualToString: @"dt"])
189     {
190       [result appendString: @":\n"];
191     }
192   else if ([tagName isEqualToString: @"li"]
193            || [tagName isEqualToString: @"dd"])
194     [result appendString: @"\n"];
195 }
196
197 - (void) startElement: (NSString *) element
198             namespace: (NSString *) namespace
199               rawName: (NSString *) rawName
200            attributes: (id <SaxAttributes>) attributes
201 {
202   NSString *tagName;
203
204   showWhoWeAre();
205
206   if (!ignoreContent)
207     {
208       tagName = [rawName lowercaseString];
209       if ([ignoreContentTags containsObject: tagName])
210         ignoreContent = YES;
211       else if ([specialTreatmentTags containsObject: tagName])
212         [self _startSpecialTreatment: tagName];
213     }
214 }
215
216 - (void) endElement: (NSString *) element
217           namespace: (NSString *) namespace
218             rawName: (NSString *) rawName
219 {
220   NSString *tagName;
221
222   showWhoWeAre();
223
224   if (ignoreContent)
225     {
226       tagName = [rawName lowercaseString];
227       if ([ignoreContentTags containsObject: tagName])
228         ignoreContent = NO;
229       else if ([specialTreatmentTags containsObject: tagName])
230         [self _endSpecialTreatment: tagName];
231     }
232 }
233
234 - (void) characters: (unichar *) characters
235              length: (int) length
236 {
237   if (!ignoreContent)
238     [result appendString: [NSString stringWithCharacters: characters
239                                     length: length]];
240 }
241
242 - (void) ignorableWhitespace: (unichar *) whitespaces
243                       length: (int) length
244 {
245   showWhoWeAre();
246 }
247
248 - (void) processingInstruction: (NSString *) pi
249                           data: (NSString *) data
250 {
251   showWhoWeAre();
252 }
253
254 - (void) setDocumentLocator: (id <NSObject, SaxLocator>) locator
255 {
256   showWhoWeAre();
257 }
258
259 - (void) skippedEntity: (NSString *) entity
260 {
261   showWhoWeAre();
262 }
263
264 /* SaxLexicalHandler */
265 - (void) comment: (unichar *) chars
266           length: (int) len
267 {
268   showWhoWeAre();
269 }
270
271 - (void) startDTD: (NSString *) name
272          publicId: (NSString *) pub
273          systemId: (NSString *) sys
274 {
275   showWhoWeAre();
276 }
277
278 - (void) endDTD
279 {
280   showWhoWeAre();
281 }
282
283 - (void) startEntity: (NSString *) entity
284 {
285   showWhoWeAre();
286 }
287
288 - (void) endEntity: (NSString *) entity
289 {
290   showWhoWeAre();
291 }
292
293 - (void) startCDATA
294 {
295   showWhoWeAre();
296 }
297
298 - (void) endCDATA
299 {
300   showWhoWeAre();
301 }
302
303 @end
304
305 // @interface NSDictionary (SOGoDebug)
306
307 // - (void) dump;
308
309 // @end
310
311 // @implementation NSDictionary (SOGoDebug)
312
313 // - (void) dump
314 // {
315 //   NSEnumerator *keys;
316 //   NSString *key;
317 //   NSMutableString *dump;
318
319 //   dump = [NSMutableString new];
320 //   [dump appendFormat: @"\nNSDictionary dump (%@):\n", self];
321 //   keys = [[self allKeys] objectEnumerator];
322 //   key = [keys nextObject];
323 //   while (key)
324 //     {
325 //       [dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]];
326 //       key = [keys nextObject];
327 //     }
328 //   [dump appendFormat: @"--- end ---\n"];
329
330 //   NSLog (dump);
331 //   [dump release];
332 // }
333
334 // @end
335
336 @implementation NSString (SOGoExtension)
337
338 - (NSString *) htmlToText
339 {
340   id <NSObject, SaxXMLReader> parser;
341   _SOGoHTMLToTextContentHandler *handler;
342
343   parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
344              createXMLReaderForMimeType: @"text/html"];
345   handler = [_SOGoHTMLToTextContentHandler htmlToTextContentHandler];
346   [parser setContentHandler: handler];
347   [parser parseFromSource: self];
348
349   return [handler result];
350 }
351
352 - (NSString *) decodedSubject
353 {
354   NSString *decodedSubject;
355
356   if ([self hasPrefix: @"=?"] && [self hasSuffix: @"?="])
357     {
358       decodedSubject = [self stringByDecodingQuotedPrintable];
359       if (!decodedSubject)
360         decodedSubject = self;
361     }
362   else
363     decodedSubject = self;
364
365   return decodedSubject;
366 }
367
368 @end