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