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