* src/alertpanel.h
[claws.git] / src / exportldif.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003 Match Grun
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Export address book to LDIF file.
22  */
23
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <string.h>
29 #include <glib.h>
30
31 #include "intl.h"
32 #include "mgutils.h"
33 #include "utils.h"
34 #include "exportldif.h"
35 #include "xmlprops.h"
36 #include "ldif.h"
37
38 #define DFL_DIR_SYLPHEED_OUT  "sylpheed-out"
39 #define DFL_FILE_SYLPHEED_OUT "addressbook.ldif"
40
41 #define FMT_BUFSIZE           2048
42 #define XML_BUFSIZE           2048
43
44 /* Settings - properties */
45 #define EXML_PROPFILE_NAME    "exportldif.xml"
46 #define EXMLPROP_DIRECTORY    "directory"
47 #define EXMLPROP_FILE         "file"
48 #define EXMLPROP_SUFFIX       "suffix"
49 #define EXMLPROP_RDN_INDEX    "rdn"
50 #define EXMLPROP_USE_DN       "use-dn"
51 #define EXMLPROP_EXCL_EMAIL   "exclude-mail"
52
53 static gchar *_attrName_UID_   = "uid";
54 static gchar *_attrName_DName_ = "cn";
55 static gchar *_attrName_EMail_ = "mail";
56
57 /**
58  * Create initialized LDIF export control object.
59  * \return Initialized export control data.
60  */
61 ExportLdifCtl *exportldif_create( void ) {
62         ExportLdifCtl *ctl = g_new0( ExportLdifCtl, 1 );
63
64         ctl->path = NULL;
65         ctl->dirOutput = NULL;
66         ctl->fileLdif = NULL;
67         ctl->suffix = NULL;
68         ctl->rdnIndex = EXPORT_LDIF_ID_UID;
69         ctl->useDN = FALSE;
70         ctl->excludeEMail = TRUE;
71         ctl->retVal = MGU_SUCCESS;
72         ctl->rcCreate = 0;
73         ctl->settingsFile = g_strconcat(
74                 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
75
76         return ctl;
77 }
78
79 /**
80  * Free up object by releasing internal memory.
81  * \return ctl Export control data.
82  */
83 void exportldif_free( ExportLdifCtl *ctl ) {
84         GList *node;
85
86         g_return_if_fail( ctl != NULL );
87
88         g_free( ctl->path );
89         g_free( ctl->fileLdif );
90         g_free( ctl->dirOutput );
91         g_free( ctl->suffix );
92         g_free( ctl->settingsFile );
93
94         /* Clear pointers */
95         ctl->path = NULL;
96         ctl->dirOutput = NULL;
97         ctl->fileLdif = NULL;
98         ctl->suffix = NULL;
99         ctl->rdnIndex = EXPORT_LDIF_ID_UID;
100         ctl->useDN = FALSE;
101         ctl->excludeEMail = FALSE;
102         ctl->retVal = MGU_SUCCESS;
103         ctl->rcCreate = 0;
104
105         /* Now release object */
106         g_free( ctl );
107 }
108
109 /**
110  * Print control object.
111  * \param ctl    Export control data.
112  * \param stream Output stream.
113  */
114 void exportldif_print( ExportLdifCtl *ctl, FILE *stream ) {
115         fprintf( stream, "ExportLdifCtl:\n" );
116         fprintf( stream, "     path: %s\n", ctl->path );
117         fprintf( stream, "directory: %s\n", ctl->dirOutput );
118         fprintf( stream, "     file: %s\n", ctl->fileLdif );
119         fprintf( stream, "   suffix: %s\n", ctl->suffix );
120         fprintf( stream, "      rdn: %d\n", ctl->rdnIndex );
121         fprintf( stream, "   use DN: %s\n", ctl->useDN ? "y" : "n" );
122         fprintf( stream, " ex EMail: %s\n", ctl->excludeEMail ? "y" : "n" );
123         fprintf( stream, " settings: %s\n", ctl->settingsFile );
124 }
125
126 /**
127  * Specify directory where LDIF files are created.
128  * \param ctl   Export control data.
129  * \param value Full directory path.
130  */
131 void exportldif_set_output_dir( ExportLdifCtl *ctl, const gchar *value ) {
132         g_return_if_fail( ctl != NULL );
133         ctl->dirOutput = mgu_replace_string( ctl->dirOutput, value );
134         g_strstrip( ctl->dirOutput );
135 }
136
137 /**
138  * Specify full file specification of LDIF file.
139  * \param ctl   Export control data.
140  * \param value Full file specification.
141  */
142 void exportldif_set_path( ExportLdifCtl *ctl, const gchar *value ) {
143         g_return_if_fail( ctl != NULL );
144         ctl->path = mgu_replace_string( ctl->path, value );
145         g_strstrip( ctl->path );
146 }
147
148 /**
149  * Specify file name of LDIF file.
150  * \param ctl   Export control data.
151  * \param value File name.
152  */
153 void exportldif_set_file_ldif( ExportLdifCtl *ctl, const gchar *value ) {
154         g_return_if_fail( ctl != NULL );
155         ctl->fileLdif = mgu_replace_string( ctl->fileLdif, value );
156         g_strstrip( ctl->fileLdif );
157 }
158
159 /**
160  * Specify suffix to be used for creating DN entries.
161  * \param ctl   Export control data.
162  * \param value Suffix.
163  */
164 void exportldif_set_suffix( ExportLdifCtl *ctl, const char *value ) {
165         g_return_if_fail( ctl != NULL );
166         ctl->suffix = mgu_replace_string( ctl->suffix, value );
167         g_strstrip( ctl->suffix );
168 }
169
170 /**
171  * Specify index of variable to be used for creating RDN entries.
172  * \param ctl   Export control data.
173  * \param value Index to variable, as follows:
174  * <ul>
175  * <li><code>EXPORT_LDIF_ID_UID</code> - Use Sylpheed UID.</li>
176  * <li><code>EXPORT_LDIF_ID_DNAME</code> - Use Sylpheed display name.</li>
177  * <li><code>EXPORT_LDIF_ID_EMAIL</code> - Use first E-Mail address.</li>
178  * </ul>
179  */
180 void exportldif_set_rdn( ExportLdifCtl *ctl, const gint value ) {
181         g_return_if_fail( ctl != NULL );
182         ctl->rdnIndex = value;
183 }
184
185 /**
186  * Specify that <code>DN</code> attribute, if present, should be used as the
187  * DN for the entry.
188  * \param ctl   Export control data.
189  * \param value <i>TRUE</i> if DN should be used.
190  */
191 void exportldif_set_use_dn( ExportLdifCtl *ctl, const gboolean value ) {
192         g_return_if_fail( ctl != NULL );
193         ctl->useDN = value;
194 }
195
196 /**
197  * Specify that records without E-Mail addresses should be excluded.
198  * \param ctl   Export control data.
199  * \param value <i>TRUE</i> if records without E-Mail should be excluded.
200  */
201 void exportldif_set_exclude_email( ExportLdifCtl *ctl, const gboolean value ) {
202         g_return_if_fail( ctl != NULL );
203         ctl->excludeEMail = value;
204 }
205
206 /**
207  * Format LDAP value name with no embedded commas.
208  * \param  value Data value to format.
209  * \return Formatted string, should be freed after use.
210  */
211 static gchar *exportldif_fmt_value( gchar *value ) {
212         gchar *dupval;
213         gchar *src;
214         gchar *dest;
215         gchar ch;
216
217         /* Duplicate incoming value */
218         dest = dupval = g_strdup( value );
219
220         /* Copy characters, ignoring commas */
221         src = value;
222         while( *src ) {
223                 ch = *src;
224                 if( ch != ',' ) {
225                         *dest = ch;
226                         dest++;
227                 }
228                 src++;
229         }
230         *dest = '\0';
231         return dupval;
232 }
233
234 /**
235  * Build DN for entry.
236  * \param  ctl    Export control data.
237  * \param  person Person to format.
238  * \return Formatted DN entry.
239  */
240 static gchar *exportldif_fmt_dn(
241                 ExportLdifCtl *ctl, const ItemPerson *person )
242 {
243         gchar buf[ FMT_BUFSIZE ];
244         gchar *retVal = NULL;
245         gchar *attr = NULL;
246         gchar *value = NULL;
247         gchar *dupval = NULL;
248
249         /* Process RDN */
250         *buf = '\0';
251         if( ctl->rdnIndex == EXPORT_LDIF_ID_UID ) {
252                 attr = _attrName_UID_;
253                 value = ADDRITEM_ID( person );
254         }
255         else if( ctl->rdnIndex == EXPORT_LDIF_ID_DNAME ) {
256                 attr = _attrName_DName_;
257                 value = ADDRITEM_NAME( person );
258                 dupval = exportldif_fmt_value( value );
259         }
260         else if( ctl->rdnIndex == EXPORT_LDIF_ID_EMAIL ) {
261                 GList *node;
262
263                 node = person->listEMail;
264                 if( node ) {
265                         ItemEMail *email = node->data;
266
267                         attr = _attrName_EMail_;
268                         value = email->address;
269                         dupval = exportldif_fmt_value( value );
270                 }
271         }
272
273         /* Format DN */
274         if( attr ) {
275                 if( value ) {
276                         if( strlen( value ) > 0 ) {
277                                 strcat( buf, attr );
278                                 strcat( buf, "=" );
279                                 if( dupval ) {
280                                         /* Format and free duplicated value */
281                                         strcat( buf, dupval );
282                                         g_free( dupval );
283                                 }
284                                 else {
285                                         /* Use original value */
286                                         strcat( buf, value );
287                                 }
288
289                                 /* Append suffix */
290                                 if( ctl->suffix ) {
291                                         if( strlen( ctl->suffix ) > 0 ) {
292                                                 strcat( buf, "," );
293                                                 strcat( buf, ctl->suffix );
294                                         }
295                                 }
296
297                                 retVal = g_strdup( buf );
298                         }
299                 }
300         }
301         return retVal;
302 }
303
304 /**
305  * Find DN by searching attribute list.
306  * \param  ctl    Export control data.
307  * \param  person Person to format.
308  * \return Formatted DN entry, should be freed after use.
309  */
310 static gchar *exportldif_find_dn(
311                         ExportLdifCtl *ctl, const ItemPerson *person )
312 {
313         gchar *retVal = NULL;
314         const GList *node;
315
316         node = person->listAttrib;
317         while( node ) {
318                 UserAttribute *attrib = node->data;
319
320                 node = g_list_next( node );
321                 if( g_strcasecmp( attrib->name, LDIF_TAG_DN ) == 0 ) {
322                         retVal = g_strdup( attrib->value );
323                         break;
324                 }
325         }
326         return retVal;
327 }
328
329 /**
330  * Format E-Mail entries for person.
331  * \param  person Person to format.
332  * \param  stream Output stream.
333  * \return <i>TRUE</i> if entry formatted.
334  */
335 static gboolean exportldif_fmt_email( const ItemPerson *person, FILE *stream ) {
336         gboolean retVal = FALSE;
337         const GList *node;
338
339         node = person->listEMail;
340         while( node ) {
341                 ItemEMail *email = node->data;
342
343                 node = g_list_next( node );
344                 ldif_write_value( stream, LDIF_TAG_EMAIL, email->address );
345                 retVal = TRUE;
346         }
347         return retVal;
348 }
349
350 /**
351  * Test for E-Mail entries for person.
352  * \param  person Person to test.
353  * \return <i>TRUE</i> if person has E-Mail address.
354  */
355 static gboolean exportldif_test_email( const ItemPerson *person )
356 {
357         gboolean retVal = FALSE;
358         const GList *node;
359
360         node = person->listEMail;
361         while( node ) {
362                 ItemEMail *email = node->data;
363
364                 node = g_list_next( node );
365                 if( email->address ) {
366                         if( strlen( email->address ) > 0 ) {
367                                 retVal = TRUE;
368                                 break;
369                         }
370                 }
371                 retVal = TRUE;
372         }
373         return retVal;
374 }
375
376 /**
377  * Format persons in an address book folder.
378  * \param  ctl    Export control data.
379  * \param  stream Output stream.
380  * \param  folder Folder to format.
381  * \return <i>TRUE</i> if no persons were formatted.
382  */
383 static gboolean exportldif_fmt_person(
384                 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
385 {
386         gboolean retVal = TRUE;
387         const GList *node;
388
389         if( folder->listPerson == NULL ) return retVal;
390
391         node = folder->listPerson;
392         while( node ) {
393                 AddrItemObject *aio = node->data;
394                 node = g_list_next( node );
395
396                 if( aio && aio->type == ITEMTYPE_PERSON ) {
397                         ItemPerson *person = ( ItemPerson * ) aio;
398                         gboolean classPerson = FALSE;
399                         gboolean classInetP = FALSE;
400                         gchar *dn = NULL;
401
402                         /* Check for E-Mail */
403                         if( exportldif_test_email( person ) ) {
404                                 classInetP = TRUE;
405                         }
406                         else {
407                                 /* Bail if no E-Mail address */
408                                 if( ctl->excludeEMail ) continue;
409                         }
410
411                         /* Format DN */
412                         if( ctl->useDN ) {
413                                 dn = exportldif_find_dn( ctl, person );
414                         }
415                         if( dn == NULL ) {
416                                 dn = exportldif_fmt_dn( ctl, person );
417                         }
418                         if( dn == NULL ) continue;
419                         ldif_write_value( stream, LDIF_TAG_DN, dn );
420                         g_free( dn );
421
422                         /*
423                          * Test for schema requirements. This is a simple
424                          * test and does not trap all LDAP schema errors.
425                          * These can be detected when the LDIF file is
426                          * loaded into an LDAP server.
427                          */
428                         if( person->lastName ) {
429                                 if( strlen( person->lastName ) > 0 ) {
430                                         classPerson = TRUE;
431                                         classInetP = TRUE;
432                                 }
433                         }
434
435                         if( classPerson ) {
436                                 ldif_write_value( stream,
437                                         LDIF_TAG_OBJECTCLASS, LDIF_CLASS_PERSON );
438                         }
439                         if( classInetP ) {
440                                 ldif_write_value( stream,
441                                         LDIF_TAG_OBJECTCLASS, LDIF_CLASS_INET_PERSON );
442                         }
443
444                         /* Format person attributes */
445                         ldif_write_value(
446                                 stream, LDIF_TAG_COMMONNAME, ADDRITEM_NAME( person ) );
447                         ldif_write_value(
448                                 stream, LDIF_TAG_LASTNAME, person->lastName );
449                         ldif_write_value(
450                                 stream, LDIF_TAG_FIRSTNAME, person->firstName );
451                         ldif_write_value(
452                                 stream, LDIF_TAG_NICKNAME, person->nickName );
453
454                         /* Format E-Mail */
455                         exportldif_fmt_email( person, stream );
456
457                         /* End record */
458                         ldif_write_eor( stream );
459
460                         retVal = FALSE;
461                 }
462         }
463
464         return retVal;
465 }
466
467 /**
468  * Format an address book folder.
469  * \param  ctl    Export control data.
470  * \param  stream Output stream.
471  * \param  folder Folder to format.
472  * \return <i>TRUE</i> if no persons were formatted.
473  */
474 static void exportldif_fmt_folder(
475                 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
476 {
477         const GList *node;
478
479         /* Export entries in this folder */
480         exportldif_fmt_person( ctl, stream, folder );
481
482         /* Export entries in sub-folders */
483         node = folder->listFolder;
484         while( node ) {
485                 AddrItemObject *aio = node->data;
486
487                 node = g_list_next( node );
488                 if( aio && aio->type == ITEMTYPE_FOLDER ) {
489                         ItemFolder *subFolder = ( ItemFolder * ) aio;
490                         exportldif_fmt_folder( ctl, stream, subFolder );
491                 }
492         }
493 }
494
495 /**
496  * Export address book to LDIF file.
497  * \param  ctl   Export control data.
498  * \param  cache Address book/data source cache.
499  * \return Status.
500  */
501 void exportldif_process( ExportLdifCtl *ctl, AddressCache *cache )
502 {
503         ItemFolder *rootFolder;
504         FILE *ldifFile;
505
506         ldifFile = fopen( ctl->path, "wb" );
507         if( ! ldifFile ) {
508                 /* Cannot open file */
509                 ctl->retVal = MGU_OPEN_FILE;
510                 return;
511         }
512
513         rootFolder = cache->rootFolder;
514         exportldif_fmt_folder( ctl, ldifFile, rootFolder );
515         fclose( ldifFile );
516         ctl->retVal = MGU_SUCCESS;
517 }
518
519 /**
520  * Build full export file specification.
521  * \param ctl  Export control data.
522  */
523 static void exportldif_build_filespec( ExportLdifCtl *ctl ) {
524         gchar *fileSpec;
525
526         fileSpec = g_strconcat(
527                 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileLdif, NULL );
528         ctl->path = mgu_replace_string( ctl->path, fileSpec );
529         g_free( fileSpec );
530 }
531
532 /**
533  * Parse directory and filename from full export file specification.
534  * \param ctl      Export control data.
535  * \param fileSpec File spec.
536  */
537 void exportldif_parse_filespec( ExportLdifCtl *ctl, gchar *fileSpec ) {
538         gchar *t;
539
540         ctl->fileLdif =
541                 mgu_replace_string( ctl->fileLdif, g_basename( fileSpec ) );
542         t = g_dirname( fileSpec );
543         ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
544         g_free( t );
545         ctl->path = mgu_replace_string( ctl->path, fileSpec );
546 }
547
548 /**
549  * Test whether output directory exists.
550  * \param  ctl Export control data.
551  * \return TRUE if exists.
552  */
553 gboolean exportldif_test_dir( ExportLdifCtl *ctl ) {
554         gboolean retVal;
555         DIR *dp;
556
557         retVal = FALSE;
558         if((dp = opendir( ctl->dirOutput )) != NULL) {
559                 retVal = TRUE;
560                 closedir( dp );
561         }
562         return retVal;
563 }
564
565 /**
566  * Create output directory.
567  * \param  ctl Export control data.
568  * \return TRUE if directory created.
569  */
570 gboolean exportldif_create_dir( ExportLdifCtl *ctl ) {
571         gboolean retVal = FALSE;
572
573         ctl->rcCreate = 0;
574         if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
575                 retVal = TRUE;
576         }
577         else {
578                 ctl->rcCreate = errno;
579         }
580         return retVal;
581 }
582
583 /**
584  * Retrieve create directory error message.
585  * \param  ctl Export control data.
586  * \return Message.
587  */
588 gchar *exportldif_get_create_msg( ExportLdifCtl *ctl ) {
589         gchar *msg;
590
591         if( ctl->rcCreate == EEXIST ) {
592                 msg = _( "Name already exists but is not a directory." );
593         }
594         else if( ctl->rcCreate == EACCES ) {
595                 msg = _( "No permissions to create directory." );
596         }
597         else if( ctl->rcCreate == ENAMETOOLONG ) {
598                 msg = _( "Name is too long." );
599         }
600         else {
601                 msg = _( "Not specified." );
602         }
603         return msg;
604 }
605
606 /**
607  * Set default values.
608  * \param  ctl Export control data.
609  */
610 static void exportldif_default_values( ExportLdifCtl *ctl ) {
611         gchar *str;
612
613         str = g_strconcat(
614                 g_get_home_dir(), G_DIR_SEPARATOR_S,
615                 DFL_DIR_SYLPHEED_OUT, NULL );
616
617         ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
618         g_free( str );
619
620         ctl->fileLdif =
621                 mgu_replace_string( ctl->fileLdif, DFL_FILE_SYLPHEED_OUT );
622         ctl->suffix = mgu_replace_string( ctl->suffix, "" );
623
624         ctl->rdnIndex = EXPORT_LDIF_ID_UID;
625         ctl->useDN = FALSE;
626         ctl->retVal = MGU_SUCCESS;
627 }
628
629 /**
630  * Load settings from XML properties file.
631  * \param  ctl Export control data.
632  */
633 void exportldif_load_settings( ExportLdifCtl *ctl ) {
634         XmlProperty *props;
635         gint rc;
636         gchar buf[ XML_BUFSIZE ];
637
638         props = xmlprops_create();
639         xmlprops_set_path( props, ctl->settingsFile );
640         rc = xmlprops_load_file( props );
641         if( rc == 0 ) {
642                 /* Read settings */
643                 *buf = '\0';
644                 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
645                 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
646
647                 *buf = '\0';
648                 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
649                 ctl->fileLdif = mgu_replace_string( ctl->fileLdif, buf );
650
651                 *buf = '\0';
652                 xmlprops_get_property_s( props, EXMLPROP_SUFFIX, buf );
653                 ctl->suffix = mgu_replace_string( ctl->suffix, buf );
654
655                 ctl->rdnIndex =
656                         xmlprops_get_property_i( props, EXMLPROP_RDN_INDEX );
657                 ctl->useDN =
658                         xmlprops_get_property_b( props, EXMLPROP_USE_DN );
659                 ctl->excludeEMail =
660                         xmlprops_get_property_b( props, EXMLPROP_EXCL_EMAIL );
661         }
662         else {
663                 /* Set default values */
664                 exportldif_default_values( ctl );
665         }
666         exportldif_build_filespec( ctl );
667         /* exportldif_print( ctl, stdout ); */
668
669         xmlprops_free( props );
670 }
671
672 /**
673  * Save settings to XML properties file.
674  * \param  ctl Export control data.
675  */
676 void exportldif_save_settings( ExportLdifCtl *ctl ) {
677         XmlProperty *props;
678
679         props = xmlprops_create();
680         xmlprops_set_path( props, ctl->settingsFile );
681
682         xmlprops_set_property( props, EXMLPROP_DIRECTORY, ctl->dirOutput );
683         xmlprops_set_property( props, EXMLPROP_FILE, ctl->fileLdif );
684         xmlprops_set_property( props, EXMLPROP_SUFFIX, ctl->suffix );
685         xmlprops_set_property_i( props, EXMLPROP_RDN_INDEX, ctl->rdnIndex );
686         xmlprops_set_property_b( props, EXMLPROP_USE_DN, ctl->useDN );
687         xmlprops_set_property_b( props, EXMLPROP_EXCL_EMAIL, ctl->excludeEMail );
688         xmlprops_save_file( props );
689         xmlprops_free( props );
690 }
691
692 /*
693  * ============================================================================
694  * End of Source.
695  * ============================================================================
696  */
697
698