Fix a possible use-after-free for ContactData pointers in new addressbook.
[claws.git] / src / exporthtml.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2002-2012 Match Grun and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 /*
21  * Export address book to HTML file.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #include "claws-features.h"
27 #endif
28
29 #ifdef USE_PTHREAD
30 #include <pthread.h>
31 #endif
32
33 #include <sys/stat.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <string.h>
38 #include <glib.h>
39 #include <glib/gi18n.h>
40
41 #ifdef G_OS_WIN32
42 #  include <w32lib.h>
43 #endif
44
45 #include "mgutils.h"
46 #include "utils.h"
47 #include "exporthtml.h"
48 #include "xmlprops.h"
49
50 #ifdef MKDIR_TAKES_ONE_ARG
51 #undef mkdir
52 #define mkdir(a,b) mkdir(a)
53 #endif
54
55 #define DFL_DIR_CLAWS_OUT  "claws-mail-out"
56 #define DFL_FILE_CLAWS_OUT "addressbook.html"
57
58 #define FMT_BUFSIZE         2048
59 #define SC_HTML_SPACE          "&nbsp;"
60 #define BORDER_SIZE         2
61 #define CELL_PADDING        2
62 #define CELL_SPACING        2
63 #define CHAR_ENCODING       "UTF-8"
64
65 /* Stylesheet names */
66 #define FILENAME_NONE       ""
67 #define FILENAME_DEFAULT    "claws-mail.css"
68 #define FILENAME_FULL       "full.css"
69 #define FILENAME_CUSTOM     "custom.css"
70 #define FILENAME_CUSTOM2    "custom2.css"
71 #define FILENAME_CUSTOM3    "custom3.css"
72 #define FILENAME_CUSTOM4    "custom4.css"
73
74 /* Settings - properties */
75 #define EXML_PROPFILE_NAME  "exporthtml.xml"
76 #define EXMLPROP_DIRECTORY  "directory"
77 #define EXMLPROP_FILE       "file"
78 #define EXMLPROP_STYLESHEET "stylesheet"
79 #define EXMLPROP_FMT_NAME   "format-full-name"
80 #define EXMLPROP_FMT_EMAIL  "format-email-links"
81 #define EXMLPROP_FMT_ATTRIB "format-attributes"
82 #define EXMLPROP_BANDING    "color-banding"
83 #define EXMLPROP_VALUE_YES  "y"
84 #define EXMLPROP_VALUE_NO   "n"
85
86 static gchar *_idTagRowEven_ = "tab-row0";
87 static gchar *_idTagRowOdd_  = "tab-row1";
88
89 /*
90  * Header entry.
91  */
92 typedef struct _StylesheetEntry StylesheetEntry;
93 struct _StylesheetEntry {
94         gchar    *fileName;
95         gint     id;
96         gboolean dflValue;
97 };
98
99 /*
100  * Build stylesheet entry.
101  * Enter: ctl   Export control data.
102  *        file  Filename.
103  *        id    File id.
104  *        dfl   Default flag.
105  */
106 static void exporthtml_build_entry(
107                 ExportHtmlCtl *ctl, const gchar *file, const gint id,
108                 const gboolean dfl )
109 {
110         StylesheetEntry *entry;
111
112         entry = g_new0( StylesheetEntry, 1 );
113         entry->fileName = g_strdup( file );
114         entry->id = id;
115         entry->dflValue = dfl;
116         ctl->listStyle = g_list_append( ctl->listStyle, entry );
117 }
118
119 /*
120  * Free up object by releasing internal memory.
121  * Enter: ctl Export control data.
122  */
123 ExportHtmlCtl *exporthtml_create( void ) {
124         ExportHtmlCtl *ctl = g_new0( ExportHtmlCtl, 1 );
125
126         ctl->path = NULL;
127         ctl->dirOutput = NULL;
128         ctl->fileHtml = NULL;
129         ctl->encoding = g_strconcat(CHAR_ENCODING, NULL);
130         ctl->stylesheet = EXPORT_HTML_ID_NONE;
131         ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
132         ctl->banding = FALSE;
133         ctl->linkEMail = FALSE;
134         ctl->showAttribs = FALSE;
135         ctl->retVal = MGU_SUCCESS;
136         ctl->listStyle = NULL;
137         ctl->rcCreate = 0;
138         ctl->settingsFile = g_strconcat(
139                 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
140
141         /* Build stylesheet list */
142         exporthtml_build_entry(
143                 ctl, FILENAME_NONE,    EXPORT_HTML_ID_NONE, FALSE );
144         exporthtml_build_entry(
145                 ctl, FILENAME_DEFAULT, EXPORT_HTML_ID_DEFAULT, TRUE );
146         exporthtml_build_entry(
147                 ctl, FILENAME_FULL,    EXPORT_HTML_ID_FULL, FALSE );
148         exporthtml_build_entry(
149                 ctl, FILENAME_CUSTOM,  EXPORT_HTML_ID_CUSTOM, FALSE );
150         exporthtml_build_entry(
151                 ctl, FILENAME_CUSTOM2, EXPORT_HTML_ID_CUSTOM2, FALSE );
152         exporthtml_build_entry(
153                 ctl, FILENAME_CUSTOM3, EXPORT_HTML_ID_CUSTOM3, FALSE );
154         exporthtml_build_entry(
155                 ctl, FILENAME_CUSTOM4, EXPORT_HTML_ID_CUSTOM4, FALSE );
156
157         return ctl;
158 }
159
160 /*
161  * Free up object by releasing internal memory.
162  * Enter: ctl Export control data.
163  */
164 void exporthtml_free( ExportHtmlCtl *ctl ) {
165         GList *node;
166         StylesheetEntry *entry;
167
168         cm_return_if_fail( ctl != NULL );
169
170         /* Free stylesheet list */
171         node = ctl->listStyle;
172         while( node ) {
173                 entry = ( StylesheetEntry * ) node->data;
174                 g_free( entry->fileName );
175                 entry->fileName = NULL;
176                 entry->id = 0;
177                 entry->dflValue = FALSE;
178                 g_free( entry );
179                 node->data = NULL;
180                 node = g_list_next( node );
181         }
182         g_list_free( ctl->listStyle );
183         ctl->listStyle = NULL;
184
185         g_free( ctl->path );
186         g_free( ctl->fileHtml );
187         g_free( ctl->encoding );
188         g_free( ctl->dirOutput );
189         g_free( ctl->settingsFile );
190
191         /* Clear pointers */
192         ctl->path = NULL;
193         ctl->dirOutput = NULL;
194         ctl->fileHtml = NULL;
195         ctl->encoding = NULL;
196         ctl->stylesheet = EXPORT_HTML_ID_NONE;
197         ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
198         ctl->banding = FALSE;
199         ctl->linkEMail = FALSE;
200         ctl->showAttribs = FALSE;
201         ctl->retVal = MGU_SUCCESS;
202         ctl->rcCreate = 0;
203
204         /* Now release object */
205         g_free( ctl );
206 }
207
208 /*
209  * Find style entry.
210  * Enter: ctl Export control data.
211  * Return: Stylesheet object, or NULL if nothing found. If a default entry is
212  * found in list, it will be returned.
213  */
214 static StylesheetEntry *exporthtml_find_stylesheet( ExportHtmlCtl *ctl ) {
215         StylesheetEntry *retVal = NULL;
216         StylesheetEntry *entry;
217         GList *node;
218
219         node = ctl->listStyle;
220         while( node ) {
221                 entry = ( StylesheetEntry * ) node->data;
222                 if( entry->id == ctl->stylesheet ) return entry;
223                 if( entry->dflValue ) retVal = entry;
224                 node = g_list_next( node );
225         }
226         return retVal;
227 }
228
229 void exporthtml_set_stylesheet( ExportHtmlCtl *ctl, const gint value ) {
230         cm_return_if_fail( ctl != NULL );
231         ctl->stylesheet = value;
232 }
233 void exporthtml_set_name_format( ExportHtmlCtl *ctl, const gint value ) {
234         cm_return_if_fail( ctl != NULL );
235         ctl->nameFormat = value;
236 }
237 void exporthtml_set_banding( ExportHtmlCtl *ctl, const gboolean value ) {
238         cm_return_if_fail( ctl != NULL );
239         ctl->banding = value;
240 }
241 void exporthtml_set_link_email( ExportHtmlCtl *ctl, const gboolean value ) {
242         cm_return_if_fail( ctl != NULL );
243         ctl->linkEMail = value;
244 }
245 void exporthtml_set_attributes( ExportHtmlCtl *ctl, const gboolean value ) {
246         cm_return_if_fail( ctl != NULL );
247         ctl->showAttribs = value;
248 }
249
250 /*
251  * Create default CSS file.
252  * Enter:  fileSpec File to create.
253  * Return: Status code.
254  */
255 static gint exporthtml_create_css_dfl( const gchar *fileSpec ) {
256         FILE *cssFile;
257
258         cssFile = g_fopen( fileSpec, "rb" );
259         if( cssFile ) {
260                 fclose( cssFile );
261                 return MGU_SUCCESS;
262         }
263         cssFile = g_fopen( fileSpec, "wb" );
264         if( ! cssFile ) {
265                 return MGU_OPEN_FILE;
266         }
267
268         fprintf( cssFile, "body {\n\tbackground: #ffffe0;\n" );
269         fprintf( cssFile, "\tfont-family: lucida, helvetica, sans-serif;\n" );
270         fprintf( cssFile, "\tfont-size: 10pt;\n" );
271         fprintf( cssFile, "}\n" );
272         fprintf( cssFile, "h1 {\n" );
273         fprintf( cssFile, "\tcolor: #000000;\n" );
274         fprintf( cssFile, "\ttext-align: center;\n" );
275         fprintf( cssFile, "}\n" );
276         fprintf( cssFile, "th {\n" );
277         fprintf( cssFile, "\tfont-size: 10pt;\n" );
278         fprintf( cssFile, "}\n" );
279         fprintf( cssFile, "td {\n" );
280         fprintf( cssFile, "\tfont-size: 10pt;\n" );
281         fprintf( cssFile, "}\n" );
282         fprintf( cssFile, ".fmt-folder {\n" );
283         fprintf( cssFile, "\tcolor: #0000ff;\n" );
284         fprintf( cssFile, "\tfont-size: 18pt;\n" );
285         fprintf( cssFile, "\tfont-weight: bold;\n" );
286         fprintf( cssFile, "}\n" );
287         fprintf( cssFile, ".tab-head {\n" );
288         fprintf( cssFile, "\tbackground: #80c0f0;\n" );
289         fprintf( cssFile, "}\n" );
290         fprintf( cssFile, ".tab-dn {\n" );
291         fprintf( cssFile, "}\n" );
292         fprintf( cssFile, ".tab-addr {\n" );
293         fprintf( cssFile, "\tfont-style: italic;\n" );
294         fprintf( cssFile, "}\n" );
295         fprintf( cssFile, ".tab-email {\n" );
296         fprintf( cssFile, "\tfont-weight: bold;\n" );
297         fprintf( cssFile, "\tfont-style: italic;\n" );
298         fprintf( cssFile, "}\n" );
299         fprintf( cssFile, ".tab-fn {\n" );
300         fprintf( cssFile, "}\n" );
301         fprintf( cssFile, ".tab-attr {\n" );
302         fprintf( cssFile, "}\n" );
303
304         fclose( cssFile );
305         return MGU_SUCCESS;
306 }
307
308 /*
309  * Create full CSS file.
310  * Enter:  fileSpec File to create.
311  * Return: Status code.
312  */
313 static gint exporthtml_create_css_full( const gchar *fileSpec ) {
314         FILE *cssFile;
315
316         cssFile = g_fopen( fileSpec, "rb" );
317         if( cssFile ) {
318                 fclose( cssFile );
319                 return MGU_SUCCESS;
320         }
321         cssFile = g_fopen( fileSpec, "wb" );
322         if( ! cssFile ) {
323                 return MGU_OPEN_FILE;
324         }
325
326         fprintf( cssFile, "body {\n\tbackground: #ffffe0;\n" );
327         fprintf( cssFile, "\tfont-family: lucida, helvetica, sans-serif;\n" );
328         fprintf( cssFile, "\tfont-size: 10pt;\n" );
329         fprintf( cssFile, "}\n" );
330         fprintf( cssFile, "h1 {\n" );
331         fprintf( cssFile, "\tcolor: #000000;\n" );
332         fprintf( cssFile, "\ttext-align: center;\n" );
333         fprintf( cssFile, "}\n" );
334         fprintf( cssFile, "th {\n" );
335         fprintf( cssFile, "\tfont-size: 10pt;\n" );
336         fprintf( cssFile, "}\n" );
337         fprintf( cssFile, "td {\n" );
338         fprintf( cssFile, "\tfont-size: 10pt;\n" );
339         fprintf( cssFile, "}\n" );
340         fprintf( cssFile, ".fmt-folder {\n" );
341         fprintf( cssFile, "\tcolor: #0000ff;\n" );
342         fprintf( cssFile, "\tfont-size: 18pt;\n" );
343         fprintf( cssFile, "\tfont-weight: bold;\n" );
344         fprintf( cssFile, "}\n" );
345         fprintf( cssFile, ".tab-head {\n" );
346         fprintf( cssFile, "\tbackground: #80c0f0;\n" );
347         fprintf( cssFile, "}\n" );
348         fprintf( cssFile, ".tab-row0 {\n" );
349         fprintf( cssFile, "\tbackground: #f0f0f0;\n" );
350         fprintf( cssFile, "}\n" );
351         fprintf( cssFile, ".tab-row1 {\n" );
352         fprintf( cssFile, "\tbackground: #d0d0d0;\n" );
353         fprintf( cssFile, "}\n" );
354         fprintf( cssFile, ".tab-dn {\n" );
355         fprintf( cssFile, "}\n" );
356         fprintf( cssFile, ".tab-addr {\n" );
357         fprintf( cssFile, "\tfont-style: italic;\n" );
358         fprintf( cssFile, "}\n" );
359         fprintf( cssFile, ".tab-email {\n" );
360         fprintf( cssFile, "\tfont-weight: bold;\n" );
361         fprintf( cssFile, "\tfont-style: italic;\n" );
362         fprintf( cssFile, "}\n" );
363         fprintf( cssFile, ".tab-fn {\n" );
364         fprintf( cssFile, "}\n" );
365         fprintf( cssFile, ".tab-attr {\n" );
366         fprintf( cssFile, "}\n" );
367
368         fclose( cssFile );
369         return MGU_SUCCESS;
370 }
371
372 /*
373  * Create stylesheet files.
374  * Enter:  ctl  Export control data.
375  */
376 static void exporthtml_create_css_files( ExportHtmlCtl *ctl ) {
377         gchar *fileSpec;
378         GList *node;
379
380         node = ctl->listStyle;
381         while( node ) {
382                 StylesheetEntry *entry = node->data;
383                 node = g_list_next( node );
384                 if( strlen( entry->fileName ) ) {
385                         fileSpec = g_strconcat(
386                                         ctl->dirOutput, G_DIR_SEPARATOR_S, 
387                                         entry->fileName, NULL );
388                         if( entry->id == EXPORT_HTML_ID_DEFAULT ) {
389                                 exporthtml_create_css_dfl( fileSpec );
390                         }
391                         else if( entry->id != EXPORT_HTML_ID_NONE ) {
392                                 exporthtml_create_css_full( fileSpec );
393                         }
394                         g_free( fileSpec );
395                 }
396         }
397 }
398
399 /*
400  * Comparison using linked list elements.
401  */
402 static gint exporthtml_compare_name(
403         gconstpointer ptr1, gconstpointer ptr2 )
404 {
405         const AddrItemObject *item1 = ptr1;
406         const AddrItemObject *item2 = ptr2;
407         const gchar *name1 = NULL, *name2 = NULL;
408         if( item1 ) name1 = ADDRITEM_NAME( item1 );
409         if( item2 ) name2 = ADDRITEM_NAME( item2 );
410         if( ! name1 ) return ( name2 != NULL );
411         if( ! name2 ) return -1;
412         return g_utf8_collate( name1, name2 );
413 }
414
415 /*
416  * Comparison using linked list elements.
417  */
418 static gint exporthtml_compare_email(
419         gconstpointer ptr1, gconstpointer ptr2 )
420 {
421         const ItemEMail *email1 = ptr1;
422         const ItemEMail *email2 = ptr2;
423         const gchar *name1 = NULL, *name2 = NULL;
424         if( email1 ) name1 = email1->address;
425         if( email2 ) name2 = email2->address;
426         if( ! name1 ) return ( name2 != NULL );
427         if( ! name2 ) return -1;
428         return g_utf8_collate( name1, name2 );
429 }
430
431 /*
432  * Comparison using linked list elements.
433  */
434 static gint exporthtml_compare_attrib(
435         gconstpointer ptr1, gconstpointer ptr2 )
436 {
437         const UserAttribute *attr1 = ptr1;
438         const UserAttribute *attr2 = ptr2;
439         const gchar *name1 = NULL, *name2 = NULL;
440         if( attr1 ) name1 = attr1->name;
441         if( attr2 ) name2 = attr2->name;
442         if( ! name1 ) return ( name2 != NULL );
443         if( ! name2 ) return -1;
444         return g_utf8_collate( name1, name2 );
445 }
446
447 /*
448  * Build sorted list of named items.
449  * Enter:  list  List of items to sorted.
450  * Return: Sorted list.
451  * Note: List should freed after use. Items referenced by list should not be
452  * freed since they are managed by the address cache.
453  */
454 static GList *exporthtml_sort_name( const GList *list ) {
455         const GList *node;
456         GList *sorted = NULL;
457
458         node = list;
459         while( node ) {
460                 sorted = g_list_insert_sorted(
461                                 sorted, node->data, exporthtml_compare_name );
462                 node = g_list_next( node );
463         }
464         return sorted;
465 }
466
467 /*
468  * Build sorted list of email items.
469  * Enter:  list  List of E-Mail items to sorted.
470  * Return: Sorted list.
471  * Note: List should freed after use. Items referenced by list should not be
472  * freed since they are managed by the address cache.
473  */
474 static GList *exporthtml_sort_email( const GList *list ) {
475         const GList *node;
476         GList *sorted = NULL;
477
478         node = list;
479         while( node ) {
480                 sorted = g_list_insert_sorted(
481                                 sorted, node->data, exporthtml_compare_email );
482                 node = g_list_next( node );
483         }
484         return sorted;
485 }
486
487 /*
488  * Build sorted list of attributes.
489  * Enter:  list  List of items to sorted.
490  * Return: Sorted list.
491  * Note: List should freed after use. Items referenced by list should not be
492  * freed since they are managed by the address cache.
493  */
494 static GList *exporthtml_sort_attrib( const GList *list ) {
495         const GList *node;
496         GList *sorted = NULL;
497
498         sorted = NULL;
499         node = list;
500         while( node ) {
501                 sorted = g_list_insert_sorted(
502                                 sorted, node->data, exporthtml_compare_attrib );
503                 node = g_list_next( node );
504         }
505         return sorted;
506 }
507
508 /*
509  * Format a list of E-Mail addresses.
510  * Enter: ctl       Export control data.
511  *        stream    Output stream.
512  *        listEMail List of addresses.
513  *        sortFlag  Set to TRUE if address list should be sorted.
514  */
515 static void exporthtml_fmt_email(
516                 ExportHtmlCtl *ctl, FILE *stream, const GList *listEMail,
517                 gboolean sortFlag )
518 {
519         const GList *node;
520         GList *list;
521         gchar *name;
522
523         if( listEMail == NULL ) {
524                 fprintf( stream, SC_HTML_SPACE );
525                 return;
526         }
527
528         list = NULL;
529         if( sortFlag ) {
530                 node = list = exporthtml_sort_email( listEMail );
531         }
532         else {
533                 node = listEMail;
534         }
535
536         while( node ) {
537                 ItemEMail *email = ( ItemEMail * ) node->data;
538                 node = g_list_next( node );
539
540                 name = ADDRITEM_NAME( email );
541                 if( name ) {
542                         fprintf( stream, "%s ", name );
543                 }
544                 if( ctl->linkEMail ) {
545                         fprintf( stream, "<a href=\"mailto:%s\">",
546                                 email->address );
547                 }
548                 fprintf( stream, "<span class=\"tab-email\">" );
549                 fprintf( stream, "%s", email->address );
550                 fprintf( stream, "</span>" );
551                 if( ctl->linkEMail ) {
552                         fprintf( stream, "</a>" );
553                 }
554                 if( email->remarks ) {
555                         if( strlen( email->remarks ) ) {
556                                 fprintf( stream, " (%s)", email->remarks );
557                         }
558                 }
559                 fprintf( stream, "<br>\n" );
560         }
561         g_list_free( list );
562 }
563
564 /*
565  * Format groups in an address book folder.
566  * Enter:  ctl      Export control data.
567  *         stream   Output stream.
568  *         folder   Folder.
569  *         prevFlag If FALSE, list of persons were output.
570  * Return: TRUE if no groups were formatted.
571  */
572 static gboolean exporthtml_fmt_group(
573                 ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder,
574                 gboolean prevFlag )
575 {
576         gboolean retVal, band;
577         GList *node, *list;
578         const gchar *tagName;
579
580         retVal = TRUE;
581         if( folder->listGroup == NULL ) return retVal;
582
583         /* Write separator */
584         if( ! prevFlag ) {
585                 fprintf( stream, "<br>\n" );
586         }
587
588         /* Write table headers */
589         fprintf( stream, "<table" );
590         fprintf( stream, " border=\"%d\"", BORDER_SIZE );
591         fprintf( stream, " cellpadding=\"%d\"", CELL_PADDING );
592         fprintf( stream, " cellspacing=\"%d\"", CELL_SPACING );
593         fprintf( stream, ">\n" );
594
595         fprintf( stream, "<tr class=\"tab-head\">\n" );
596         fprintf( stream, "  <th width=\"200\">" );
597         fprintf( stream, "%s", _( "Group Name" ) );
598         fprintf( stream, "</th>\n" );
599         fprintf( stream, "  <th width=\"300\">" );
600         fprintf( stream, "%s", _( "Email Address" ) );
601         fprintf( stream, "</th>\n" );
602         fprintf( stream, "</tr>\n" );
603         list = exporthtml_sort_name( folder->listGroup );
604
605         band = FALSE;
606         node = list;
607         while( node ) {
608                 AddrItemObject *aio = node->data;
609                 if( aio && aio->type == ITEMTYPE_GROUP ) {
610                         ItemGroup *group = ( ItemGroup * ) aio;
611
612                         fprintf( stream, "<tr valign=\"top\"" );
613                         if( ctl->banding ) {
614                                 if( band ) {
615                                         tagName = _idTagRowOdd_;
616                                 }
617                                 else {
618                                         tagName = _idTagRowEven_;
619                                 }
620                                 fprintf( stream, " class=\"%s\"", tagName );
621                                 band = ! band;
622                         }
623                         fprintf( stream, "\">\n" );
624
625                         fprintf( stream, "  <td class=\"tab-dn\">" );
626                         fprintf( stream, "%s", ADDRITEM_NAME( group ) );
627                         fprintf( stream, "</td>\n" );
628                         fprintf( stream, "  <td class=\"tab-addr\">" );
629                         exporthtml_fmt_email( ctl, stream, group->listEMail, TRUE );
630                         fprintf( stream, "</td>\n" );
631                         fprintf( stream, "</tr>\n" );
632                         retVal = FALSE;
633                 }
634                 node = g_list_next( node );
635         }
636
637         g_list_free( list );
638         fprintf( stream, "</table>\n" );
639         return retVal;
640 }
641
642 /*
643  * Format a list of E-Mail addresses.
644  * Enter: ctl       Export control data.
645  *        stream    Output stream.
646  *        listAttr  List of attributes.
647  */
648 static void exporthtml_fmt_attribs(
649                 ExportHtmlCtl *ctl, FILE *stream, const GList *listAttr )
650 {
651         const GList *node;
652         GList *list;
653
654         if( listAttr == NULL ) {
655                 fprintf( stream, SC_HTML_SPACE );
656                 return;
657         }
658
659         fprintf( stream, "<table border=\"0\">\n" );
660         node = list = exporthtml_sort_attrib( listAttr );
661         while( node ) {
662                 UserAttribute *attr = ( UserAttribute * ) node->data;
663                 node = g_list_next( node );
664                 fprintf( stream, "<tr valign=\"top\">" );
665                 fprintf( stream, "<td align=\"right\">%s:</td>", attr->name );
666                 fprintf( stream, "<td>%s</td>", attr->value );
667                 fprintf( stream, "</tr>\n" );
668         }
669
670         g_list_free( list );
671         fprintf( stream, "</table>" );
672 }
673
674 /*
675  * Format full name.
676  * Enter:  ctl     Export control data.
677  *         buf     Output buffer.
678  *         person  Person to format.
679  */
680 static void exporthtml_fmt_fullname(
681                 ExportHtmlCtl *ctl, gchar *buf, const ItemPerson *person )
682 {
683         gboolean flag;
684
685         if( ctl->nameFormat == EXPORT_HTML_LAST_FIRST ) {
686                 flag = FALSE;
687                 if( person->lastName ) {
688                         if( *person->lastName ) {
689                                 strcat( buf, " " );
690                                 strcat( buf, person->lastName );
691                                 flag = TRUE;
692                         }
693                 }
694                 if( person->firstName ) {
695                         if( *person->firstName ) {
696                                 if( flag ) {
697                                         strcat( buf, ", " );
698                                 }
699                                 strcat( buf, person->firstName );
700                         }
701                 }
702         }
703         else {
704                 if( person->firstName ) {
705                         if( *person->firstName ) {
706                                 strcat( buf, person->firstName );
707                         }
708                 }
709                 if( person->lastName ) {
710                         if( *person->lastName ) {
711                                 strcat( buf, " " );
712                                 strcat( buf, person->lastName );
713                         }
714                 }
715         }
716         g_strstrip( buf );
717
718         flag = FALSE;
719         if( *buf ) flag = TRUE;
720         if( person->nickName ) {
721                 if( strlen( person->nickName ) ) {
722                         if( flag ) {
723                                 strcat( buf, " (" );
724                         }
725                         strcat( buf, person->nickName );
726                         if( flag ) {
727                                 strcat( buf, ")" );
728                         }
729                 }
730         }
731         g_strstrip( buf );
732 }
733
734 /*
735  * Format persons in an address book folder.
736  * Enter:  ctl     Export control data.
737  *         stream  Output stream.
738  *         folder  Folder.
739  * Return: TRUE if no persons were formatted.
740  */
741 static gboolean exporthtml_fmt_person(
742                 ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder )
743 {
744         gboolean retVal, band;
745         GList *node, *list;
746         gchar buf[ FMT_BUFSIZE ];
747         const gchar *tagName;
748
749         retVal = TRUE;
750         if( folder->listPerson == NULL ) return retVal;
751
752         /* Write table headers */
753         fprintf( stream, "<table" );
754         fprintf( stream, " border=\"%d\"", BORDER_SIZE );
755         fprintf( stream, " cellpadding=\"%d\"", CELL_PADDING );
756         fprintf( stream, " cellspacing=\"%d\"", CELL_SPACING );
757         fprintf( stream, ">\n" );
758
759         fprintf( stream, "<tr class=\"tab-head\">\n" );
760         fprintf( stream, "  <th width=\"200\">" );
761         fprintf( stream, "%s", _( "Display Name" ) );
762         fprintf( stream, "</th>\n" );
763         fprintf( stream, "  <th width=\"300\">" );
764         fprintf( stream, "%s", _( "Email Address" ) );
765         fprintf( stream, "</th>\n" );
766         fprintf( stream, "  <th width=\"200\">" );
767         fprintf( stream, "%s", _( "Full Name" ) );
768         fprintf( stream, "</th>\n" );
769         if( ctl->showAttribs ) {
770                 fprintf( stream, "  <th width=\"250\">" );
771                 fprintf( stream, "%s", _( "Attributes" ) );
772                 fprintf( stream, "</th>\n" );
773         }
774         fprintf( stream, "</tr>\n" );
775
776         band = FALSE;
777         node = list = exporthtml_sort_name( folder->listPerson );
778         while( node ) {
779                 AddrItemObject *aio = node->data;
780                 if( aio && aio->type == ITEMTYPE_PERSON ) {
781                         ItemPerson *person = ( ItemPerson * ) aio;
782
783                         /* Format first/last/nick name */
784                         *buf = '\0';
785                         exporthtml_fmt_fullname( ctl, buf,person );
786
787                         fprintf( stream, "<tr valign=\"top\"" );
788                         if( ctl->banding ) {
789                                 if( band ) {
790                                         tagName = _idTagRowOdd_;
791                                 }
792                                 else {
793                                         tagName = _idTagRowEven_;
794                                 }
795                                 fprintf( stream, " class=\"%s\"", tagName );
796                                 band = ! band;
797                         }
798                         fprintf( stream, ">\n" );
799
800                         fprintf( stream, "  <td class=\"tab-dn\">" );
801                         fprintf( stream, "%s", ADDRITEM_NAME( person ) );
802                         fprintf( stream, "</td>\n" );
803
804                         fprintf( stream, "  <td class=\"tab-addr\">" );
805                         exporthtml_fmt_email( ctl, stream, person->listEMail, FALSE );
806                         fprintf( stream, "</td>\n" );
807
808                         fprintf( stream, "  <td class=\"tab-fn\">" );
809                         if( *buf ) {
810                                 fprintf( stream, "%s", buf );
811                         }
812                         else {
813                                 fprintf( stream, "%s", SC_HTML_SPACE );
814                         }
815                         fprintf( stream, "</td>\n" );
816
817                         if( ctl->showAttribs ) {
818                                 fprintf( stream, "  <td class=\"tab-attr\">" );
819                                 exporthtml_fmt_attribs(
820                                         ctl, stream, person->listAttrib );
821                                 fprintf( stream, "</td>\n" );
822                         }
823                         fprintf( stream, "</tr>\n" );
824
825                         retVal = FALSE;
826                 }
827                 node = g_list_next( node );
828         }
829
830         g_list_free( list );
831         fprintf( stream, "</table>\n" );
832         return retVal;
833 }
834
835 /*
836  * Format folder heirarchy.
837  * Enter: stream Output stream.
838  *        list   Heirarchy list.
839  */
840 static void exporthtml_fmt_folderhead( FILE *stream, const GList *list ) {
841         const GList *node;
842         gboolean flag;
843         gchar *name;
844
845         flag = FALSE;
846         node = list;
847         while( node ) {
848                 AddrItemObject *aio = node->data;
849                 if( aio && aio->type == ITEMTYPE_FOLDER ) {
850                         ItemFolder *folder = ( ItemFolder * ) aio;
851
852                         name = ADDRITEM_NAME( folder );
853                         if( name ) {
854                                 if( flag ) {
855                                         fprintf( stream, "&nbsp;&gt;&nbsp;" );
856                                 }
857                                 fprintf( stream, "%s", name );
858                                 flag = TRUE;
859                         }
860                 }
861                 node = g_list_next( node );
862         }
863 }
864
865 /*
866  * Format an address book folder.
867  * Enter: ctl    Export control data.
868  *        stream Output stream.
869  *        folder Folder.
870  */
871 static void exporthtml_fmt_folder(
872                 ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder )
873 {
874         const GList *node;
875         GList *listHeir, *list;
876         const gchar *name;
877         gboolean ret1;
878
879         name = ADDRITEM_NAME( folder );
880         if( name ) {
881                 listHeir = addritem_folder_path( folder, TRUE );
882                 if( listHeir ) {
883                         fprintf( stream, "<p class=\"fmt-folder\">" );
884                         fprintf( stream, "%s: ", _( "Folder" ) );
885                         exporthtml_fmt_folderhead( stream, listHeir );
886                         fprintf( stream, "</p>\n" );
887                         g_list_free( listHeir );
888                 }
889         }
890
891         ret1 = exporthtml_fmt_person( ctl, stream, folder );
892         exporthtml_fmt_group( ctl, stream, folder, ret1 );
893
894         node = list = exporthtml_sort_name( folder->listFolder );
895         while( node ) {
896                 AddrItemObject *aio = node->data;
897                 if( aio && aio->type == ITEMTYPE_FOLDER ) {
898                         ItemFolder *subFolder = ( ItemFolder * ) aio;
899                         exporthtml_fmt_folder( ctl, stream, subFolder );
900                 }
901                 node = g_list_next( node );
902         }
903         if( list ) {
904                 g_list_free( list );
905         }
906 }
907
908 /*
909  * Format header block.
910  * Enter:  ctl    Export control data.
911  *         stream Output stream.
912  *         title  Page title.
913  */
914 static void exporthtml_fmt_header(
915                 ExportHtmlCtl *ctl, FILE *stream, gchar *title )
916 {
917         StylesheetEntry *entry;
918
919         entry = exporthtml_find_stylesheet( ctl );
920
921         fprintf( stream,
922                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n" );
923         fprintf( stream,
924                 "\"http://www.w3.org/TR/html4/loose.dtd\">\n" );
925         fprintf( stream, "<html>\n" );
926         fprintf( stream, "<head>\n" );
927
928         if( ctl->encoding && strlen( ctl->encoding ) > 0 ) {
929                 fprintf( stream, "<meta " );
930                 fprintf( stream, "http-equiv=\"Content-Type\" " );
931                 fprintf( stream, "content=\"text/html; charset=%s\">\n",
932                         ctl->encoding );
933         }
934
935         fprintf( stream, "<title>%s</title>\n", title );
936
937         if( entry != NULL ) {
938                 if( entry->fileName && strlen( entry->fileName ) > 0 ) {
939                         fprintf( stream, "<link " );
940                         fprintf( stream, "rel=\"stylesheet\" " );
941                         fprintf( stream, "type=\"text/css\" " );
942                         fprintf( stream, "href=\"%s\" >\n", entry->fileName );
943                 }
944         }
945         fprintf( stream, "</head>\n" );
946 }
947
948 /*
949  * ============================================================================
950  * Export address book to HTML file.
951  * Enter:  ctl   Export control data.
952  *         cache Address book/data source cache.
953  * Return: Status.
954  * ============================================================================
955  */
956 void exporthtml_process(
957         ExportHtmlCtl *ctl, AddressCache *cache )
958 {
959         ItemFolder *rootFolder;
960         FILE *htmlFile;
961         time_t tt;
962         gchar *dsName;
963         static gchar *title;
964         gchar buf[512];
965
966         htmlFile = g_fopen( ctl->path, "wb" );
967         if( ! htmlFile ) {
968                 /* Cannot open file */
969                 g_print( "Cannot open file for write\n" );
970                 ctl->retVal = MGU_OPEN_FILE;
971                 return;
972         }
973
974         title = _( "Claws Mail Address Book" );
975         rootFolder = cache->rootFolder;
976         dsName = cache->name;
977
978         exporthtml_fmt_header( ctl, htmlFile, title );
979
980         fprintf( htmlFile, "<body>\n" );
981         fprintf( htmlFile, "<h1>%s</h1>\n", title );
982
983         fprintf( htmlFile, "<p class=\"fmt-folder\">" );
984         fprintf( htmlFile, "%s: ", _( "Address Book" ) );
985         fprintf( htmlFile, "%s", dsName );
986         fprintf( htmlFile, "</p>\n" );
987
988         exporthtml_fmt_folder( ctl, htmlFile, rootFolder );
989
990         tt = time( NULL );
991         fprintf( htmlFile, "<p>%s</p>\n", ctime_r( &tt, buf ) );
992         fprintf( htmlFile, "<hr width=\"100%%\">\n" );
993
994         fprintf( htmlFile, "</body>\n" );
995         fprintf( htmlFile, "</html>\n" );
996
997         fclose( htmlFile );
998         ctl->retVal = MGU_SUCCESS;
999
1000         /* Create stylesheet files */
1001         exporthtml_create_css_files( ctl );
1002
1003 }
1004
1005 /*
1006  * Build full export file specification.
1007  * Enter:  ctl  Export control data.
1008  */
1009 static void exporthtml_build_filespec( ExportHtmlCtl *ctl ) {
1010         gchar *fileSpec;
1011
1012         fileSpec = g_strconcat(
1013                 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileHtml, NULL );
1014         ctl->path = mgu_replace_string( ctl->path, fileSpec );
1015         g_free( fileSpec );
1016 }
1017
1018 /*
1019  * ============================================================================
1020  * Parse directory and filename from full export file specification.
1021  * Enter:  ctl      Export control data.
1022  *         fileSpec File spec.
1023  * ============================================================================
1024  */
1025 void exporthtml_parse_filespec( ExportHtmlCtl *ctl, gchar *fileSpec ) {
1026         gchar *t;
1027         gchar *base = g_path_get_basename(fileSpec);
1028
1029         ctl->fileHtml =
1030                 mgu_replace_string( ctl->fileHtml, base );
1031         g_free(base);
1032         t = g_path_get_dirname( fileSpec );
1033         ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
1034         g_free( t );
1035         ctl->path = mgu_replace_string( ctl->path, fileSpec );
1036 }
1037
1038 /*
1039  * ============================================================================
1040  * Create output directory.
1041  * Enter:  ctl  Export control data.
1042  * Return: TRUE if directory created.
1043  * ============================================================================
1044  */
1045 gboolean exporthtml_create_dir( ExportHtmlCtl *ctl ) {
1046         gboolean retVal = FALSE;
1047
1048         ctl->rcCreate = 0;
1049         if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
1050                 retVal = TRUE;
1051         }
1052         else {
1053                 ctl->rcCreate = errno;
1054         }
1055         return retVal;
1056 }
1057
1058 /*
1059  * ============================================================================
1060  * Retrieve create directory error message.
1061  * Enter:  ctl  Export control data.
1062  * Return: Message.
1063  * ============================================================================
1064  */
1065 gchar *exporthtml_get_create_msg( ExportHtmlCtl *ctl ) {
1066         gchar *msg;
1067
1068         if( ctl->rcCreate == EEXIST ) {
1069                 msg = _( "Name already exists but is not a directory." );
1070         }
1071         else if( ctl->rcCreate == EACCES ) {
1072                 msg = _( "No permissions to create directory." );
1073         }
1074         else if( ctl->rcCreate == ENAMETOOLONG ) {
1075                 msg = _( "Name is too long." );
1076         }
1077         else {
1078                 msg = _( "Not specified." );
1079         }
1080         return msg;
1081 }
1082
1083 /*
1084  * Set default values.
1085  * Enter: ctl Export control data.
1086  */
1087 static void exporthtml_default_values( ExportHtmlCtl *ctl ) {
1088         gchar *str;
1089
1090         str = g_strconcat(
1091                 get_home_dir(), G_DIR_SEPARATOR_S,
1092                 DFL_DIR_CLAWS_OUT, NULL );
1093
1094         ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
1095         g_free( str );
1096
1097         ctl->fileHtml =
1098                 mgu_replace_string( ctl->fileHtml, DFL_FILE_CLAWS_OUT );
1099         ctl->encoding = NULL;
1100         ctl->stylesheet = EXPORT_HTML_ID_DEFAULT;
1101         ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
1102         ctl->banding = TRUE;
1103         ctl->linkEMail = TRUE;
1104         ctl->showAttribs = TRUE;
1105         ctl->retVal = MGU_SUCCESS;
1106 }
1107
1108 /*
1109  * ============================================================================
1110  * Load settings from XML properties file.
1111  * Enter: ctl  Export control data.
1112  * ============================================================================
1113  */
1114 void exporthtml_load_settings( ExportHtmlCtl *ctl ) {
1115         XmlProperty *props;
1116         gint rc;
1117         gchar buf[256];
1118
1119         *buf = '\0';
1120         props = xmlprops_create();
1121         xmlprops_set_path( props, ctl->settingsFile );
1122         rc = xmlprops_load_file( props );
1123         if( rc == 0 ) {
1124                 /* Read settings */
1125                 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
1126                 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
1127
1128                 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
1129                 ctl->fileHtml = mgu_replace_string( ctl->fileHtml, buf );
1130
1131                 ctl->stylesheet =
1132                         xmlprops_get_property_i( props, EXMLPROP_STYLESHEET );
1133                 ctl->nameFormat =
1134                         xmlprops_get_property_i( props, EXMLPROP_FMT_NAME );
1135                 ctl->banding =
1136                         xmlprops_get_property_b( props, EXMLPROP_BANDING );
1137                 ctl->linkEMail =
1138                         xmlprops_get_property_b( props, EXMLPROP_FMT_EMAIL );
1139                 ctl->showAttribs =
1140                         xmlprops_get_property_b( props, EXMLPROP_FMT_ATTRIB );
1141         }
1142         else {
1143                 /* Set default values */
1144                 exporthtml_default_values( ctl );
1145         }
1146         exporthtml_build_filespec( ctl );
1147         /* exporthtml_print( ctl, stdout ); */
1148
1149         xmlprops_free( props );
1150 }
1151
1152 /*
1153  * ============================================================================
1154  * Save settings to XML properties file.
1155  * Enter: ctl  Export control data.
1156  * ============================================================================
1157  */
1158 void exporthtml_save_settings( ExportHtmlCtl *ctl ) {
1159         XmlProperty *props;
1160
1161         props = xmlprops_create();
1162         xmlprops_set_path( props, ctl->settingsFile );
1163
1164         xmlprops_set_property( props, EXMLPROP_DIRECTORY, ctl->dirOutput );
1165         xmlprops_set_property( props, EXMLPROP_FILE, ctl->fileHtml );
1166         xmlprops_set_property_i( props, EXMLPROP_STYLESHEET, ctl->stylesheet );
1167         xmlprops_set_property_i( props, EXMLPROP_FMT_NAME, ctl->nameFormat );
1168         xmlprops_set_property_b( props, EXMLPROP_BANDING, ctl->banding );
1169         xmlprops_set_property_b( props, EXMLPROP_FMT_EMAIL, ctl->linkEMail );
1170         xmlprops_set_property_b( props, EXMLPROP_FMT_ATTRIB, ctl->showAttribs );
1171         if (xmlprops_save_file( props ) != MGU_SUCCESS)
1172                 g_warning("can't save settings");
1173         xmlprops_free( props );
1174 }
1175
1176 /*
1177  * ============================================================================
1178  * End of Source.
1179  * ============================================================================
1180  */
1181
1182