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