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