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