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