2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2012 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.
25 #include "claws-features.h"
34 #include <glib/gi18n.h>
38 #include "exportldif.h"
44 #ifdef MKDIR_TAKES_ONE_ARG
46 #define mkdir(a,b) mkdir(a)
49 #define DFL_DIR_CLAWS_OUT "claws-mail-out"
50 #define DFL_FILE_CLAWS_OUT "addressbook.ldif"
52 #define FMT_BUFSIZE 2048
53 #define XML_BUFSIZE 2048
55 /* Settings - properties */
56 #define EXML_PROPFILE_NAME "exportldif.xml"
57 #define EXMLPROP_DIRECTORY "directory"
58 #define EXMLPROP_FILE "file"
59 #define EXMLPROP_SUFFIX "suffix"
60 #define EXMLPROP_RDN_INDEX "rdn"
61 #define EXMLPROP_USE_DN "use-dn"
62 #define EXMLPROP_EXCL_EMAIL "exclude-mail"
64 static gchar *_attrName_UID_ = "uid";
65 static gchar *_attrName_DName_ = "cn";
66 static gchar *_attrName_EMail_ = "mail";
69 * Create initialized LDIF export control object.
70 * \return Initialized export control data.
72 ExportLdifCtl *exportldif_create( void ) {
73 ExportLdifCtl *ctl = g_new0( ExportLdifCtl, 1 );
76 ctl->dirOutput = NULL;
79 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
81 ctl->excludeEMail = TRUE;
82 ctl->retVal = MGU_SUCCESS;
84 ctl->settingsFile = g_strconcat(
85 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
91 * Free up object by releasing internal memory.
92 * \return ctl Export control data.
94 void exportldif_free( ExportLdifCtl *ctl ) {
95 cm_return_if_fail( ctl != NULL );
98 g_free( ctl->fileLdif );
99 g_free( ctl->dirOutput );
100 g_free( ctl->suffix );
101 g_free( ctl->settingsFile );
105 ctl->dirOutput = NULL;
106 ctl->fileLdif = NULL;
108 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
110 ctl->excludeEMail = FALSE;
111 ctl->retVal = MGU_SUCCESS;
114 /* Now release object */
119 * Specify suffix to be used for creating DN entries.
120 * \param ctl Export control data.
121 * \param value Suffix.
123 void exportldif_set_suffix( ExportLdifCtl *ctl, const char *value ) {
124 cm_return_if_fail( ctl != NULL );
125 ctl->suffix = mgu_replace_string( ctl->suffix, value );
126 g_strstrip( ctl->suffix );
130 * Specify index of variable to be used for creating RDN entries.
131 * \param ctl Export control data.
132 * \param value Index to variable, as follows:
134 * <li><code>EXPORT_LDIF_ID_UID</code> - Use Claws Mail UID.</li>
135 * <li><code>EXPORT_LDIF_ID_DNAME</code> - Use Claws Mail display name.</li>
136 * <li><code>EXPORT_LDIF_ID_EMAIL</code> - Use first Email address.</li>
139 void exportldif_set_rdn( ExportLdifCtl *ctl, const gint value ) {
140 cm_return_if_fail( ctl != NULL );
141 ctl->rdnIndex = value;
145 * Specify that <code>DN</code> attribute, if present, should be used as the
147 * \param ctl Export control data.
148 * \param value <i>TRUE</i> if DN should be used.
150 void exportldif_set_use_dn( ExportLdifCtl *ctl, const gboolean value ) {
151 cm_return_if_fail( ctl != NULL );
156 * Specify that records without E-Mail addresses should be excluded.
157 * \param ctl Export control data.
158 * \param value <i>TRUE</i> if records without E-Mail should be excluded.
160 void exportldif_set_exclude_email( ExportLdifCtl *ctl, const gboolean value ) {
161 cm_return_if_fail( ctl != NULL );
162 ctl->excludeEMail = value;
166 * Format LDAP value name with no embedded commas.
167 * \param value Data value to format.
168 * \return Formatted string, should be freed after use.
170 static gchar *exportldif_fmt_value( gchar *value ) {
176 /* Duplicate incoming value */
177 dest = dupval = g_strdup( value );
179 /* Copy characters, ignoring commas */
194 * Build DN for entry.
195 * \param ctl Export control data.
196 * \param person Person to format.
197 * \return Formatted DN entry.
199 static gchar *exportldif_fmt_dn(
200 ExportLdifCtl *ctl, const ItemPerson *person )
202 gchar buf[ FMT_BUFSIZE + 1 ];
203 gchar *retVal = NULL;
206 gchar *dupval = NULL;
210 if( ctl->rdnIndex == EXPORT_LDIF_ID_UID ) {
211 attr = _attrName_UID_;
212 value = ADDRITEM_ID( person );
214 else if( ctl->rdnIndex == EXPORT_LDIF_ID_DNAME ) {
215 attr = _attrName_DName_;
216 value = ADDRITEM_NAME( person );
217 dupval = exportldif_fmt_value( value );
219 else if( ctl->rdnIndex == EXPORT_LDIF_ID_EMAIL ) {
222 node = person->listEMail;
224 ItemEMail *email = node->data;
226 attr = _attrName_EMail_;
227 value = email->address;
228 dupval = exportldif_fmt_value( value );
235 if( strlen( value ) > 0 ) {
236 strncat( buf, attr, FMT_BUFSIZE - strlen(buf) );
237 strncat( buf, "=", FMT_BUFSIZE - strlen(buf) );
239 /* Format and free duplicated value */
240 strncat( buf, dupval, FMT_BUFSIZE - strlen(buf) );
244 /* Use original value */
245 strncat( buf, value, FMT_BUFSIZE - strlen(buf) );
250 if( strlen( ctl->suffix ) > 0 ) {
251 strncat( buf, ",", FMT_BUFSIZE - strlen(buf) );
252 strncat( buf, ctl->suffix, FMT_BUFSIZE - strlen(buf) );
256 retVal = g_strdup( buf );
264 * Find DN by searching attribute list.
265 * \param ctl Export control data.
266 * \param person Person to format.
267 * \return Formatted DN entry, should be freed after use.
269 static gchar *exportldif_find_dn(
270 ExportLdifCtl *ctl, const ItemPerson *person )
272 gchar *retVal = NULL;
275 node = person->listAttrib;
277 UserAttribute *attrib = node->data;
279 node = g_list_next( node );
280 if( g_utf8_collate( attrib->name, LDIF_TAG_DN ) == 0 ) {
281 retVal = g_strdup( attrib->value );
289 * Format E-Mail entries for person.
290 * \param person Person to format.
291 * \param stream Output stream.
292 * \return <i>TRUE</i> if entry formatted.
294 static gboolean exportldif_fmt_email( const ItemPerson *person, FILE *stream ) {
295 gboolean retVal = FALSE;
298 node = person->listEMail;
300 ItemEMail *email = node->data;
302 node = g_list_next( node );
303 ldif_write_value( stream, LDIF_TAG_EMAIL, email->address );
310 * Test for E-Mail entries for person.
311 * \param person Person to test.
312 * \return <i>TRUE</i> if person has E-Mail address.
314 static gboolean exportldif_test_email( const ItemPerson *person )
316 gboolean retVal = FALSE;
319 node = person->listEMail;
321 ItemEMail *email = node->data;
323 node = g_list_next( node );
324 if( email->address ) {
325 if( strlen( email->address ) > 0 ) {
336 * Format other attributes for person.
337 * \param person ItemPerson.
338 * \param stream Output stream.
340 static void exportldif_fmt_other_attributes(ItemPerson* person, FILE* stream) {
342 GList* attrList = NULL;
347 debug_print("cn: %s\n-----------------------------\n", ADDRITEM_NAME(person));
348 attrList = person->listAttrib;
350 attr = (UserAttribute *) attrList->data;
352 /* Native address book which does not conform to
355 attrib = g_strdup_printf("# %s", attr->name);
358 attrib = g_strdup(attr->name);
360 debug_print("name: %s\nvalue: %s\n", attrib, attr->value);
361 ldif_write_value(stream, attrib, attr->value);
363 attrList = g_list_next(attrList);
365 debug_print("-------------------------------\n");
369 * Find persons displayName.
370 * \param person ItemPerson.
371 * \return displayName.
373 static gchar* exportldif_find_displayName(ItemPerson* person) {
379 if (person->nickName && strlen(person->nickName) > 0)
380 displayName = g_strdup(person->nickName);
382 displayName = g_strdup(ADDRITEM_NAME(person));
387 * Format persons in an address book folder.
388 * \param ctl Export control data.
389 * \param stream Output stream.
390 * \param folder Folder to format.
391 * \return <i>TRUE</i> if no persons were formatted.
393 static gboolean exportldif_fmt_person(
394 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
396 gboolean retVal = TRUE;
399 gchar* displayName = NULL;
401 if( folder->listPerson == NULL ) return retVal;
403 node = folder->listPerson;
405 AddrItemObject *aio = node->data;
406 node = g_list_next( node );
408 if( aio && aio->type == ITEMTYPE_PERSON ) {
409 ItemPerson *person = ( ItemPerson * ) aio;
410 gboolean classPerson = FALSE;
411 gboolean classInetP = FALSE;
414 /* Check for E-Mail */
415 if( exportldif_test_email( person ) ) {
419 /* Bail if no E-Mail address */
420 if( ctl->excludeEMail ) continue;
425 dn = exportldif_find_dn( ctl, person );
428 dn = exportldif_fmt_dn( ctl, person );
430 if( dn == NULL ) continue;
431 ldif_write_value( stream, LDIF_TAG_DN, dn );
435 * Test for schema requirements. This is a simple
436 * test and does not trap all LDAP schema errors.
437 * These can be detected when the LDIF file is
438 * loaded into an LDAP server.
440 if( person->lastName ) {
441 if( strlen( person->lastName ) > 0 ) {
448 ldif_write_value( stream,
449 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_PERSON );
452 ldif_write_value( stream,
453 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_INET_PERSON );
456 /* Format person attributes */
458 stream, LDIF_TAG_COMMONNAME, ADDRITEM_NAME( person ) );
459 sn = g_strdup(person->lastName);
460 if (classPerson || classInetP) {
461 if(! sn || strcmp("", sn) == 0 || strcmp(" ", sn) == 0) {
463 sn = g_strdup("Some SN");
467 stream, LDIF_TAG_LASTNAME, sn );
471 stream, LDIF_TAG_FIRSTNAME, person->firstName );
473 if (! person->externalID)
474 displayName = exportldif_find_displayName(person);
476 displayName = g_strdup(person->nickName);
477 ldif_write_value(stream, LDIF_TAG_NICKNAME, displayName);
482 exportldif_fmt_email( person, stream );
484 /* Handle other attributes */
485 exportldif_fmt_other_attributes(person, stream);
488 ldif_write_eor( stream );
498 * Format an address book folder.
499 * \param ctl Export control data.
500 * \param stream Output stream.
501 * \param folder Folder to format.
502 * \return <i>TRUE</i> if no persons were formatted.
504 static void exportldif_fmt_folder(
505 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
509 /* Export entries in this folder */
510 exportldif_fmt_person( ctl, stream, folder );
512 /* Export entries in sub-folders */
513 node = folder->listFolder;
515 AddrItemObject *aio = node->data;
517 node = g_list_next( node );
518 if( aio && aio->type == ITEMTYPE_FOLDER ) {
519 ItemFolder *subFolder = ( ItemFolder * ) aio;
520 exportldif_fmt_folder( ctl, stream, subFolder );
526 * Export address book to LDIF file.
527 * \param ctl Export control data.
528 * \param cache Address book/data source cache.
531 void exportldif_process( ExportLdifCtl *ctl, AddressCache *cache )
533 ItemFolder *rootFolder;
536 ldifFile = g_fopen( ctl->path, "wb" );
538 /* Cannot open file */
539 ctl->retVal = MGU_OPEN_FILE;
543 rootFolder = cache->rootFolder;
544 exportldif_fmt_folder( ctl, ldifFile, rootFolder );
545 safe_fclose( ldifFile );
546 ctl->retVal = MGU_SUCCESS;
550 * Build full export file specification.
551 * \param ctl Export control data.
553 static void exportldif_build_filespec( ExportLdifCtl *ctl ) {
556 fileSpec = g_strconcat(
557 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileLdif, NULL );
558 ctl->path = mgu_replace_string( ctl->path, fileSpec );
563 * Parse directory and filename from full export file specification.
564 * \param ctl Export control data.
565 * \param fileSpec File spec.
567 void exportldif_parse_filespec( ExportLdifCtl *ctl, gchar *fileSpec ) {
569 gchar *base = g_path_get_basename(fileSpec);
572 mgu_replace_string( ctl->fileLdif, base );
574 t = g_path_get_dirname( fileSpec );
575 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
577 ctl->path = mgu_replace_string( ctl->path, fileSpec );
581 * Create output directory.
582 * \param ctl Export control data.
583 * \return TRUE if directory created.
585 gboolean exportldif_create_dir( ExportLdifCtl *ctl ) {
586 gboolean retVal = FALSE;
589 if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
593 ctl->rcCreate = errno;
599 * Retrieve create directory error message.
600 * \param ctl Export control data.
603 gchar *exportldif_get_create_msg( ExportLdifCtl *ctl ) {
606 if( ctl->rcCreate == EEXIST ) {
607 msg = _( "Name already exists but is not a directory." );
609 else if( ctl->rcCreate == EACCES ) {
610 msg = _( "No permissions to create directory." );
612 else if( ctl->rcCreate == ENAMETOOLONG ) {
613 msg = _( "Name is too long." );
616 msg = _( "Not specified." );
622 * Set default values.
623 * \param ctl Export control data.
625 static void exportldif_default_values( ExportLdifCtl *ctl ) {
629 get_home_dir(), G_DIR_SEPARATOR_S,
630 DFL_DIR_CLAWS_OUT, NULL );
632 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
636 mgu_replace_string( ctl->fileLdif, DFL_FILE_CLAWS_OUT );
637 ctl->suffix = mgu_replace_string( ctl->suffix, "" );
639 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
641 ctl->retVal = MGU_SUCCESS;
645 * Load settings from XML properties file.
646 * \param ctl Export control data.
648 void exportldif_load_settings( ExportLdifCtl *ctl ) {
651 gchar buf[ XML_BUFSIZE ];
653 props = xmlprops_create();
654 xmlprops_set_path( props, ctl->settingsFile );
655 rc = xmlprops_load_file( props );
659 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
660 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
663 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
664 ctl->fileLdif = mgu_replace_string( ctl->fileLdif, buf );
667 xmlprops_get_property_s( props, EXMLPROP_SUFFIX, buf );
668 ctl->suffix = mgu_replace_string( ctl->suffix, buf );
671 xmlprops_get_property_i( props, EXMLPROP_RDN_INDEX );
673 xmlprops_get_property_b( props, EXMLPROP_USE_DN );
675 xmlprops_get_property_b( props, EXMLPROP_EXCL_EMAIL );
678 /* Set default values */
679 exportldif_default_values( ctl );
681 exportldif_build_filespec( ctl );
682 /* exportldif_print( ctl, stdout ); */
684 xmlprops_free( props );
688 * Save settings to XML properties file.
689 * \param ctl Export control data.
691 void exportldif_save_settings( ExportLdifCtl *ctl ) {
694 props = xmlprops_create();
695 xmlprops_set_path( props, ctl->settingsFile );
697 xmlprops_set_property( props, EXMLPROP_DIRECTORY, ctl->dirOutput );
698 xmlprops_set_property( props, EXMLPROP_FILE, ctl->fileLdif );
699 xmlprops_set_property( props, EXMLPROP_SUFFIX, ctl->suffix );
700 xmlprops_set_property_i( props, EXMLPROP_RDN_INDEX, ctl->rdnIndex );
701 xmlprops_set_property_b( props, EXMLPROP_USE_DN, ctl->useDN );
702 xmlprops_set_property_b( props, EXMLPROP_EXCL_EMAIL, ctl->excludeEMail );
703 if (xmlprops_save_file( props ) != MGU_SUCCESS)
704 g_warning("can't save settings");
705 xmlprops_free( props );
709 * ============================================================================
711 * ============================================================================