* src/widgets/modest-gtkhtml-msg-view.c:
[modest] / src / modest-stream-text-to-html.c
1 /* Copyright (c) 2007, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 /* modest-stream-text-to-html.c */
32
33 #include "modest-stream-text-to-html.h"
34 #include <tny-stream.h>
35 #include <string.h>
36 #include <modest-text-utils.h>
37
38 #define HTML_PREFIX "<html><head>" \
39         "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf8\">" \
40         "</head>" \
41         "<body>"
42 #define HTML_SUFFIX "</body></html>"
43
44
45 /* 'private'/'protected' functions */
46 static void  modest_stream_text_to_html_class_init   (ModestStreamTextToHtmlClass *klass);
47 static void  modest_stream_text_to_html_init         (ModestStreamTextToHtml *obj);
48 static void  modest_stream_text_to_html_finalize     (GObject *obj);
49
50 static void  modest_stream_text_to_html_iface_init   (gpointer g_iface, gpointer iface_data);
51 static gboolean write_line (TnyStream *self, const gchar *str, gboolean convert_to_html);
52
53
54 typedef struct _ModestStreamTextToHtmlPrivate ModestStreamTextToHtmlPrivate;
55 struct _ModestStreamTextToHtmlPrivate {
56         TnyStream *out_stream;
57         GString *line_buffer;
58         gboolean written_prefix;
59         gsize linkify_limit;
60         gsize full_limit;
61         gsize total_output;
62 };
63 #define MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
64                                                        MODEST_TYPE_STREAM_TEXT_TO_HTML, \
65                                                        ModestStreamTextToHtmlPrivate))
66 /* globals */
67 static GObjectClass *parent_class = NULL;
68
69 GType
70 modest_stream_text_to_html_get_type (void)
71 {
72         static GType my_type = 0;
73         if (!my_type) {
74                 static const GTypeInfo my_info = {
75                         sizeof(ModestStreamTextToHtmlClass),
76                         NULL,           /* base init */
77                         NULL,           /* base finalize */
78                         (GClassInitFunc) modest_stream_text_to_html_class_init,
79                         NULL,           /* class finalize */
80                         NULL,           /* class data */
81                         sizeof(ModestStreamTextToHtml),
82                         1,              /* n_preallocs */
83                         (GInstanceInitFunc) modest_stream_text_to_html_init,
84                         NULL
85                 };
86
87                 static const GInterfaceInfo iface_info = {
88                         (GInterfaceInitFunc) modest_stream_text_to_html_iface_init,
89                         NULL,         /* interface_finalize */
90                         NULL          /* interface_data */
91                 };
92
93                 my_type = g_type_register_static (G_TYPE_OBJECT,
94                                                   "ModestStreamTextToHtml",
95                                                   &my_info, 0);
96
97                 g_type_add_interface_static (my_type, TNY_TYPE_STREAM,
98                                              &iface_info);
99
100         }
101         return my_type;
102 }
103
104 static void
105 modest_stream_text_to_html_class_init (ModestStreamTextToHtmlClass *klass)
106 {
107         GObjectClass *gobject_class;
108         gobject_class = (GObjectClass*) klass;
109
110         parent_class            = g_type_class_peek_parent (klass);
111         gobject_class->finalize = modest_stream_text_to_html_finalize;
112
113         g_type_class_add_private (gobject_class, sizeof(ModestStreamTextToHtmlPrivate));
114 }
115
116 static void
117 modest_stream_text_to_html_init (ModestStreamTextToHtml *obj)
118 {
119         ModestStreamTextToHtmlPrivate *priv;
120         priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE(obj);
121
122         priv->out_stream  = NULL;
123         priv->written_prefix = FALSE;
124         priv->line_buffer = NULL;
125         priv->linkify_limit = 0;
126         priv->full_limit = 0;
127         priv->total_output = 0;
128 }
129
130 static void
131 modest_stream_text_to_html_finalize (GObject *obj)
132 {
133         ModestStreamTextToHtmlPrivate *priv;
134
135         priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE(obj);
136         if (priv->out_stream)
137                 g_object_unref (priv->out_stream);
138         priv->out_stream = NULL;
139         if (priv->line_buffer != NULL) {
140                 g_string_free (priv->line_buffer, TRUE);
141         }
142 }
143
144 GObject*
145 modest_stream_text_to_html_new (TnyStream *out_stream)
146 {
147         GObject *obj;
148         ModestStreamTextToHtmlPrivate *priv;
149         
150         obj  = G_OBJECT(g_object_new(MODEST_TYPE_STREAM_TEXT_TO_HTML, NULL));
151         priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE(obj);
152
153         g_return_val_if_fail (out_stream, NULL);
154         
155         priv->out_stream = g_object_ref (out_stream);
156
157         return obj;
158 }
159
160 void        
161 modest_stream_text_to_html_set_linkify_limit (ModestStreamTextToHtml *self, gssize limit)
162 {
163         ModestStreamTextToHtmlPrivate *priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE (self);
164         priv->linkify_limit = limit;
165 }
166
167 void        
168 modest_stream_text_to_html_set_full_limit (ModestStreamTextToHtml *self, gssize limit)
169 {
170         ModestStreamTextToHtmlPrivate *priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE (self);
171         priv->full_limit = limit;
172 }
173
174 /* the rest are interface functions */
175
176
177 static ssize_t
178 text_to_html_read (TnyStream *self, char *buffer, size_t n)
179 {
180         return -1; /* we cannot read */
181 }
182
183 static gboolean 
184 write_line (TnyStream *self, const gchar *str, gboolean convert_to_html)
185 {
186         ModestStreamTextToHtmlPrivate *priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE (self);
187         gchar *html_buffer;
188         gchar *offset;
189         gssize pending_bytes;
190         gboolean hyperlinkify = TRUE;
191
192         /* we only leave for full limit if we're converting to html, so that we
193            preserve the prefix and suffix */
194         if (convert_to_html && (priv->full_limit > 0) &&(priv->total_output > priv->full_limit))
195                 return TRUE;
196         if ((priv->linkify_limit > 0) && (priv->total_output > priv->linkify_limit))
197                 hyperlinkify = FALSE;
198         if (convert_to_html) {
199                 html_buffer = modest_text_utils_convert_to_html_body (str, -1, hyperlinkify);
200         } else {
201                 html_buffer = (gchar *) str;
202         }
203
204         pending_bytes = strlen (html_buffer);
205         priv->total_output += pending_bytes;
206         offset = html_buffer;
207
208         while (pending_bytes > 0) {
209                 gssize written_bytes = 0;
210                 written_bytes = tny_stream_write (priv->out_stream, offset, pending_bytes);
211                 if (written_bytes < 0) {
212                         if (convert_to_html)
213                                 g_free (html_buffer);
214                         return FALSE;
215                 }
216                 offset += written_bytes;
217                 pending_bytes -= written_bytes;
218         }
219         if (convert_to_html)
220                 g_free (html_buffer);
221
222         return TRUE;
223 }
224
225 static ssize_t
226 text_to_html_write (TnyStream *self, const char *buffer, size_t n)
227 {
228         
229         ModestStreamTextToHtmlPrivate *priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE (self);
230         gssize total = n;
231
232         modest_text_utils_hyperlinkify_begin ();
233         if ((!priv->written_prefix) && (n > 0)) {
234                 if (!write_line (self, HTML_PREFIX, FALSE)) {
235                         modest_text_utils_hyperlinkify_end ();
236                         return -1;
237                 }
238                 priv->written_prefix = TRUE;
239         }
240
241         while (n > 0) {
242                 gchar c = *buffer;
243                 if (priv->line_buffer == NULL)
244                         priv->line_buffer = g_string_new (NULL);
245
246                 priv->line_buffer = g_string_append_c (priv->line_buffer, c);
247                 if (c == '\n') {
248                         if (tny_stream_flush (self) == -1) {
249                                 modest_text_utils_hyperlinkify_end ();
250                                 return -1;
251                         }
252                 }
253                 buffer ++;
254                 n--;
255         }
256         modest_text_utils_hyperlinkify_end ();
257         return total;
258 }
259
260         
261 static gint
262 text_to_html_flush (TnyStream *self)
263 {
264         ModestStreamTextToHtmlPrivate *priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE (self);
265
266         if (priv->line_buffer != NULL) {
267                 if (!write_line (self, priv->line_buffer->str, TRUE))
268                         return -1;
269                 g_string_free (priv->line_buffer, TRUE);
270                 priv->line_buffer = NULL;
271         }
272         return 0;
273 }
274         
275
276 static gint
277 text_to_html_close (TnyStream *self)
278 {
279         ModestStreamTextToHtmlPrivate *priv;
280         g_return_val_if_fail (self, 0);
281         priv = MODEST_STREAM_TEXT_TO_HTML_GET_PRIVATE(self);
282
283         tny_stream_flush (self);
284         if (!write_line (self, HTML_SUFFIX, FALSE))
285                 return -1;
286         
287         tny_stream_close (priv->out_stream);
288         
289         priv->out_stream = NULL;
290
291         return 0;
292 }
293
294
295 static gboolean
296 text_to_html_is_eos (TnyStream *self)
297 {
298         return TRUE;
299 }
300
301
302         
303 static gint
304 text_to_html_reset (TnyStream *self)
305 {
306         return 0;
307 }
308
309         
310 static ssize_t
311 text_to_html_write_to_stream (TnyStream *self, TnyStream *output)
312 {
313         return 0;
314 }
315
316
317 static void
318 modest_stream_text_to_html_iface_init (gpointer g_iface, gpointer iface_data)
319 {
320         TnyStreamIface *klass;
321         
322         g_return_if_fail (g_iface);
323
324         klass = (TnyStreamIface*) g_iface;
325         
326         klass->read            = text_to_html_read;
327         klass->write           = text_to_html_write;
328         klass->flush           = text_to_html_flush;
329         klass->close           = text_to_html_close;
330         klass->is_eos          = text_to_html_is_eos;
331         klass->reset           = text_to_html_reset;
332         klass->write_to_stream = text_to_html_write_to_stream;
333 }