2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2011 Match Grun and the Claws Mail team
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.
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.
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/>.
21 * Export address book to LDIF file.
33 #include <glib/gi18n.h>
37 #include "exportldif.h"
42 #ifdef MKDIR_TAKES_ONE_ARG
44 #define mkdir(a,b) mkdir(a)
47 #define DFL_DIR_CLAWS_OUT "claws-mail-out"
48 #define DFL_FILE_CLAWS_OUT "addressbook.ldif"
50 #define FMT_BUFSIZE 2048
51 #define XML_BUFSIZE 2048
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"
62 static gchar *_attrName_UID_ = "uid";
63 static gchar *_attrName_DName_ = "cn";
64 static gchar *_attrName_EMail_ = "mail";
67 * Create initialized LDIF export control object.
68 * \return Initialized export control data.
70 ExportLdifCtl *exportldif_create( void ) {
71 ExportLdifCtl *ctl = g_new0( ExportLdifCtl, 1 );
74 ctl->dirOutput = NULL;
77 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
79 ctl->excludeEMail = TRUE;
80 ctl->retVal = MGU_SUCCESS;
82 ctl->settingsFile = g_strconcat(
83 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
89 * Free up object by releasing internal memory.
90 * \return ctl Export control data.
92 void exportldif_free( ExportLdifCtl *ctl ) {
93 cm_return_if_fail( ctl != NULL );
96 g_free( ctl->fileLdif );
97 g_free( ctl->dirOutput );
98 g_free( ctl->suffix );
99 g_free( ctl->settingsFile );
103 ctl->dirOutput = NULL;
104 ctl->fileLdif = NULL;
106 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
108 ctl->excludeEMail = FALSE;
109 ctl->retVal = MGU_SUCCESS;
112 /* Now release object */
117 * Specify suffix to be used for creating DN entries.
118 * \param ctl Export control data.
119 * \param value Suffix.
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 );
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:
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>
137 void exportldif_set_rdn( ExportLdifCtl *ctl, const gint value ) {
138 cm_return_if_fail( ctl != NULL );
139 ctl->rdnIndex = value;
143 * Specify that <code>DN</code> attribute, if present, should be used as the
145 * \param ctl Export control data.
146 * \param value <i>TRUE</i> if DN should be used.
148 void exportldif_set_use_dn( ExportLdifCtl *ctl, const gboolean value ) {
149 cm_return_if_fail( ctl != NULL );
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.
158 void exportldif_set_exclude_email( ExportLdifCtl *ctl, const gboolean value ) {
159 cm_return_if_fail( ctl != NULL );
160 ctl->excludeEMail = value;
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.
168 static gchar *exportldif_fmt_value( gchar *value ) {
174 /* Duplicate incoming value */
175 dest = dupval = g_strdup( value );
177 /* Copy characters, ignoring commas */
192 * Build DN for entry.
193 * \param ctl Export control data.
194 * \param person Person to format.
195 * \return Formatted DN entry.
197 static gchar *exportldif_fmt_dn(
198 ExportLdifCtl *ctl, const ItemPerson *person )
200 gchar buf[ FMT_BUFSIZE ];
201 gchar *retVal = NULL;
204 gchar *dupval = NULL;
208 if( ctl->rdnIndex == EXPORT_LDIF_ID_UID ) {
209 attr = _attrName_UID_;
210 value = ADDRITEM_ID( person );
212 else if( ctl->rdnIndex == EXPORT_LDIF_ID_DNAME ) {
213 attr = _attrName_DName_;
214 value = ADDRITEM_NAME( person );
215 dupval = exportldif_fmt_value( value );
217 else if( ctl->rdnIndex == EXPORT_LDIF_ID_EMAIL ) {
220 node = person->listEMail;
222 ItemEMail *email = node->data;
224 attr = _attrName_EMail_;
225 value = email->address;
226 dupval = exportldif_fmt_value( value );
233 if( strlen( value ) > 0 ) {
234 strncat( buf, attr, FMT_BUFSIZE );
235 strncat( buf, "=", FMT_BUFSIZE );
237 /* Format and free duplicated value */
238 strncat( buf, dupval, FMT_BUFSIZE );
242 /* Use original value */
243 strncat( buf, value, FMT_BUFSIZE );
248 if( strlen( ctl->suffix ) > 0 ) {
249 strncat( buf, ",", FMT_BUFSIZE );
250 strncat( buf, ctl->suffix, FMT_BUFSIZE );
254 retVal = g_strdup( buf );
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.
267 static gchar *exportldif_find_dn(
268 ExportLdifCtl *ctl, const ItemPerson *person )
270 gchar *retVal = NULL;
273 node = person->listAttrib;
275 UserAttribute *attrib = node->data;
277 node = g_list_next( node );
278 if( g_utf8_collate( attrib->name, LDIF_TAG_DN ) == 0 ) {
279 retVal = g_strdup( attrib->value );
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.
292 static gboolean exportldif_fmt_email( const ItemPerson *person, FILE *stream ) {
293 gboolean retVal = FALSE;
296 node = person->listEMail;
298 ItemEMail *email = node->data;
300 node = g_list_next( node );
301 ldif_write_value( stream, LDIF_TAG_EMAIL, email->address );
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.
312 static gboolean exportldif_test_email( const ItemPerson *person )
314 gboolean retVal = FALSE;
317 node = person->listEMail;
319 ItemEMail *email = node->data;
321 node = g_list_next( node );
322 if( email->address ) {
323 if( strlen( email->address ) > 0 ) {
334 * Format other attributes for person.
335 * \param person ItemPerson.
336 * \param stream Output stream.
338 static void exportldif_fmt_other_attributes(ItemPerson* person, FILE* stream) {
340 GList* attrList = NULL;
345 debug_print("cn: %s\n-----------------------------\n", ADDRITEM_NAME(person));
346 attrList = person->listAttrib;
348 attr = (UserAttribute *) attrList->data;
350 /* Native address book which does not conform to
353 attrib = g_strdup_printf("# %s", attr->name);
356 attrib = g_strdup(attr->name);
358 debug_print("name: %s\nvalue: %s\n", attrib, attr->value);
359 ldif_write_value(stream, attrib, attr->value);
361 attrList = g_list_next(attrList);
363 debug_print("-------------------------------\n");
367 * Find persons displayName.
368 * \param person ItemPerson.
369 * \return displayName.
371 static gchar* exportldif_find_displayName(ItemPerson* person) {
377 if (person->nickName && strlen(person->nickName) > 0)
378 displayName = g_strdup(person->nickName);
380 displayName = g_strdup(ADDRITEM_NAME(person));
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.
391 static gboolean exportldif_fmt_person(
392 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
394 gboolean retVal = TRUE;
397 gchar* displayName = NULL;
399 if( folder->listPerson == NULL ) return retVal;
401 node = folder->listPerson;
403 AddrItemObject *aio = node->data;
404 node = g_list_next( node );
406 if( aio && aio->type == ITEMTYPE_PERSON ) {
407 ItemPerson *person = ( ItemPerson * ) aio;
408 gboolean classPerson = FALSE;
409 gboolean classInetP = FALSE;
412 /* Check for E-Mail */
413 if( exportldif_test_email( person ) ) {
417 /* Bail if no E-Mail address */
418 if( ctl->excludeEMail ) continue;
423 dn = exportldif_find_dn( ctl, person );
426 dn = exportldif_fmt_dn( ctl, person );
428 if( dn == NULL ) continue;
429 ldif_write_value( stream, LDIF_TAG_DN, dn );
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.
438 if( person->lastName ) {
439 if( strlen( person->lastName ) > 0 ) {
446 ldif_write_value( stream,
447 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_PERSON );
450 ldif_write_value( stream,
451 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_INET_PERSON );
454 /* Format person attributes */
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) {
461 sn = g_strdup("Some SN");
465 stream, LDIF_TAG_LASTNAME, sn );
469 stream, LDIF_TAG_FIRSTNAME, person->firstName );
471 if (! person->externalID)
472 displayName = exportldif_find_displayName(person);
474 displayName = g_strdup(person->nickName);
475 ldif_write_value(stream, LDIF_TAG_NICKNAME, displayName);
480 exportldif_fmt_email( person, stream );
482 /* Handle other attributes */
483 exportldif_fmt_other_attributes(person, stream);
486 ldif_write_eor( stream );
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.
502 static void exportldif_fmt_folder(
503 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
507 /* Export entries in this folder */
508 exportldif_fmt_person( ctl, stream, folder );
510 /* Export entries in sub-folders */
511 node = folder->listFolder;
513 AddrItemObject *aio = node->data;
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 );
524 * Export address book to LDIF file.
525 * \param ctl Export control data.
526 * \param cache Address book/data source cache.
529 void exportldif_process( ExportLdifCtl *ctl, AddressCache *cache )
531 ItemFolder *rootFolder;
534 ldifFile = g_fopen( ctl->path, "wb" );
536 /* Cannot open file */
537 ctl->retVal = MGU_OPEN_FILE;
541 rootFolder = cache->rootFolder;
542 exportldif_fmt_folder( ctl, ldifFile, rootFolder );
544 ctl->retVal = MGU_SUCCESS;
548 * Build full export file specification.
549 * \param ctl Export control data.
551 static void exportldif_build_filespec( ExportLdifCtl *ctl ) {
554 fileSpec = g_strconcat(
555 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileLdif, NULL );
556 ctl->path = mgu_replace_string( ctl->path, fileSpec );
561 * Parse directory and filename from full export file specification.
562 * \param ctl Export control data.
563 * \param fileSpec File spec.
565 void exportldif_parse_filespec( ExportLdifCtl *ctl, gchar *fileSpec ) {
567 gchar *base = g_path_get_basename(fileSpec);
570 mgu_replace_string( ctl->fileLdif, base );
572 t = g_path_get_dirname( fileSpec );
573 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
575 ctl->path = mgu_replace_string( ctl->path, fileSpec );
579 * Test whether output directory exists.
580 * \param ctl Export control data.
581 * \return TRUE if exists.
583 gboolean exportldif_test_dir( ExportLdifCtl *ctl ) {
588 if((dp = opendir( ctl->dirOutput )) != NULL) {
596 * Create output directory.
597 * \param ctl Export control data.
598 * \return TRUE if directory created.
600 gboolean exportldif_create_dir( ExportLdifCtl *ctl ) {
601 gboolean retVal = FALSE;
604 if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
608 ctl->rcCreate = errno;
614 * Retrieve create directory error message.
615 * \param ctl Export control data.
618 gchar *exportldif_get_create_msg( ExportLdifCtl *ctl ) {
621 if( ctl->rcCreate == EEXIST ) {
622 msg = _( "Name already exists but is not a directory." );
624 else if( ctl->rcCreate == EACCES ) {
625 msg = _( "No permissions to create directory." );
627 else if( ctl->rcCreate == ENAMETOOLONG ) {
628 msg = _( "Name is too long." );
631 msg = _( "Not specified." );
637 * Set default values.
638 * \param ctl Export control data.
640 static void exportldif_default_values( ExportLdifCtl *ctl ) {
644 get_home_dir(), G_DIR_SEPARATOR_S,
645 DFL_DIR_CLAWS_OUT, NULL );
647 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
651 mgu_replace_string( ctl->fileLdif, DFL_FILE_CLAWS_OUT );
652 ctl->suffix = mgu_replace_string( ctl->suffix, "" );
654 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
656 ctl->retVal = MGU_SUCCESS;
660 * Load settings from XML properties file.
661 * \param ctl Export control data.
663 void exportldif_load_settings( ExportLdifCtl *ctl ) {
666 gchar buf[ XML_BUFSIZE ];
668 props = xmlprops_create();
669 xmlprops_set_path( props, ctl->settingsFile );
670 rc = xmlprops_load_file( props );
674 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
675 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
678 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
679 ctl->fileLdif = mgu_replace_string( ctl->fileLdif, buf );
682 xmlprops_get_property_s( props, EXMLPROP_SUFFIX, buf );
683 ctl->suffix = mgu_replace_string( ctl->suffix, buf );
686 xmlprops_get_property_i( props, EXMLPROP_RDN_INDEX );
688 xmlprops_get_property_b( props, EXMLPROP_USE_DN );
690 xmlprops_get_property_b( props, EXMLPROP_EXCL_EMAIL );
693 /* Set default values */
694 exportldif_default_values( ctl );
696 exportldif_build_filespec( ctl );
697 /* exportldif_print( ctl, stdout ); */
699 xmlprops_free( props );
703 * Save settings to XML properties file.
704 * \param ctl Export control data.
706 void exportldif_save_settings( ExportLdifCtl *ctl ) {
709 props = xmlprops_create();
710 xmlprops_set_path( props, ctl->settingsFile );
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 );
724 * ============================================================================
726 * ============================================================================