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"
43 #ifdef MKDIR_TAKES_ONE_ARG
45 #define mkdir(a,b) mkdir(a)
48 #define DFL_DIR_CLAWS_OUT "claws-mail-out"
49 #define DFL_FILE_CLAWS_OUT "addressbook.ldif"
51 #define FMT_BUFSIZE 2048
52 #define XML_BUFSIZE 2048
54 /* Settings - properties */
55 #define EXML_PROPFILE_NAME "exportldif.xml"
56 #define EXMLPROP_DIRECTORY "directory"
57 #define EXMLPROP_FILE "file"
58 #define EXMLPROP_SUFFIX "suffix"
59 #define EXMLPROP_RDN_INDEX "rdn"
60 #define EXMLPROP_USE_DN "use-dn"
61 #define EXMLPROP_EXCL_EMAIL "exclude-mail"
63 static gchar
*_attrName_UID_
= "uid";
64 static gchar
*_attrName_DName_
= "cn";
65 static gchar
*_attrName_EMail_
= "mail";
68 * Create initialized LDIF export control object.
69 * \return Initialized export control data.
71 ExportLdifCtl
*exportldif_create( void ) {
72 ExportLdifCtl
*ctl
= g_new0( ExportLdifCtl
, 1 );
75 ctl
->dirOutput
= NULL
;
78 ctl
->rdnIndex
= EXPORT_LDIF_ID_UID
;
80 ctl
->excludeEMail
= TRUE
;
81 ctl
->retVal
= MGU_SUCCESS
;
83 ctl
->settingsFile
= g_strconcat(
84 get_rc_dir(), G_DIR_SEPARATOR_S
, EXML_PROPFILE_NAME
, NULL
);
90 * Free up object by releasing internal memory.
91 * \return ctl Export control data.
93 void exportldif_free( ExportLdifCtl
*ctl
) {
94 cm_return_if_fail( ctl
!= NULL
);
97 g_free( ctl
->fileLdif
);
98 g_free( ctl
->dirOutput
);
99 g_free( ctl
->suffix
);
100 g_free( ctl
->settingsFile
);
104 ctl
->dirOutput
= NULL
;
105 ctl
->fileLdif
= NULL
;
107 ctl
->rdnIndex
= EXPORT_LDIF_ID_UID
;
109 ctl
->excludeEMail
= FALSE
;
110 ctl
->retVal
= MGU_SUCCESS
;
113 /* Now release object */
118 * Specify suffix to be used for creating DN entries.
119 * \param ctl Export control data.
120 * \param value Suffix.
122 void exportldif_set_suffix( ExportLdifCtl
*ctl
, const char *value
) {
123 cm_return_if_fail( ctl
!= NULL
);
124 ctl
->suffix
= mgu_replace_string( ctl
->suffix
, value
);
125 g_strstrip( ctl
->suffix
);
129 * Specify index of variable to be used for creating RDN entries.
130 * \param ctl Export control data.
131 * \param value Index to variable, as follows:
133 * <li><code>EXPORT_LDIF_ID_UID</code> - Use Sylpheed UID.</li>
134 * <li><code>EXPORT_LDIF_ID_DNAME</code> - Use Sylpheed display name.</li>
135 * <li><code>EXPORT_LDIF_ID_EMAIL</code> - Use first Email address.</li>
138 void exportldif_set_rdn( ExportLdifCtl
*ctl
, const gint value
) {
139 cm_return_if_fail( ctl
!= NULL
);
140 ctl
->rdnIndex
= value
;
144 * Specify that <code>DN</code> attribute, if present, should be used as the
146 * \param ctl Export control data.
147 * \param value <i>TRUE</i> if DN should be used.
149 void exportldif_set_use_dn( ExportLdifCtl
*ctl
, const gboolean value
) {
150 cm_return_if_fail( ctl
!= NULL
);
155 * Specify that records without E-Mail addresses should be excluded.
156 * \param ctl Export control data.
157 * \param value <i>TRUE</i> if records without E-Mail should be excluded.
159 void exportldif_set_exclude_email( ExportLdifCtl
*ctl
, const gboolean value
) {
160 cm_return_if_fail( ctl
!= NULL
);
161 ctl
->excludeEMail
= value
;
165 * Format LDAP value name with no embedded commas.
166 * \param value Data value to format.
167 * \return Formatted string, should be freed after use.
169 static gchar
*exportldif_fmt_value( gchar
*value
) {
175 /* Duplicate incoming value */
176 dest
= dupval
= g_strdup( value
);
178 /* Copy characters, ignoring commas */
193 * Build DN for entry.
194 * \param ctl Export control data.
195 * \param person Person to format.
196 * \return Formatted DN entry.
198 static gchar
*exportldif_fmt_dn(
199 ExportLdifCtl
*ctl
, const ItemPerson
*person
)
201 gchar buf
[ FMT_BUFSIZE
+ 1 ];
202 gchar
*retVal
= NULL
;
205 gchar
*dupval
= NULL
;
209 if( ctl
->rdnIndex
== EXPORT_LDIF_ID_UID
) {
210 attr
= _attrName_UID_
;
211 value
= ADDRITEM_ID( person
);
213 else if( ctl
->rdnIndex
== EXPORT_LDIF_ID_DNAME
) {
214 attr
= _attrName_DName_
;
215 value
= ADDRITEM_NAME( person
);
216 dupval
= exportldif_fmt_value( value
);
218 else if( ctl
->rdnIndex
== EXPORT_LDIF_ID_EMAIL
) {
221 node
= person
->listEMail
;
223 ItemEMail
*email
= node
->data
;
225 attr
= _attrName_EMail_
;
226 value
= email
->address
;
227 dupval
= exportldif_fmt_value( value
);
234 if( strlen( value
) > 0 ) {
235 strncat( buf
, attr
, FMT_BUFSIZE
- strlen(buf
) );
236 strncat( buf
, "=", FMT_BUFSIZE
- strlen(buf
) );
238 /* Format and free duplicated value */
239 strncat( buf
, dupval
, FMT_BUFSIZE
- strlen(buf
) );
243 /* Use original value */
244 strncat( buf
, value
, FMT_BUFSIZE
- strlen(buf
) );
249 if( strlen( ctl
->suffix
) > 0 ) {
250 strncat( buf
, ",", FMT_BUFSIZE
- strlen(buf
) );
251 strncat( buf
, ctl
->suffix
, FMT_BUFSIZE
- strlen(buf
) );
255 retVal
= g_strdup( buf
);
263 * Find DN by searching attribute list.
264 * \param ctl Export control data.
265 * \param person Person to format.
266 * \return Formatted DN entry, should be freed after use.
268 static gchar
*exportldif_find_dn(
269 ExportLdifCtl
*ctl
, const ItemPerson
*person
)
271 gchar
*retVal
= NULL
;
274 node
= person
->listAttrib
;
276 UserAttribute
*attrib
= node
->data
;
278 node
= g_list_next( node
);
279 if( g_utf8_collate( attrib
->name
, LDIF_TAG_DN
) == 0 ) {
280 retVal
= g_strdup( attrib
->value
);
288 * Format E-Mail entries for person.
289 * \param person Person to format.
290 * \param stream Output stream.
291 * \return <i>TRUE</i> if entry formatted.
293 static gboolean
exportldif_fmt_email( const ItemPerson
*person
, FILE *stream
) {
294 gboolean retVal
= FALSE
;
297 node
= person
->listEMail
;
299 ItemEMail
*email
= node
->data
;
301 node
= g_list_next( node
);
302 ldif_write_value( stream
, LDIF_TAG_EMAIL
, email
->address
);
309 * Test for E-Mail entries for person.
310 * \param person Person to test.
311 * \return <i>TRUE</i> if person has E-Mail address.
313 static gboolean
exportldif_test_email( const ItemPerson
*person
)
315 gboolean retVal
= FALSE
;
318 node
= person
->listEMail
;
320 ItemEMail
*email
= node
->data
;
322 node
= g_list_next( node
);
323 if( email
->address
) {
324 if( strlen( email
->address
) > 0 ) {
335 * Format other attributes for person.
336 * \param person ItemPerson.
337 * \param stream Output stream.
339 static void exportldif_fmt_other_attributes(ItemPerson
* person
, FILE* stream
) {
341 GList
* attrList
= NULL
;
346 debug_print("cn: %s\n-----------------------------\n", ADDRITEM_NAME(person
));
347 attrList
= person
->listAttrib
;
349 attr
= (UserAttribute
*) attrList
->data
;
351 /* Native address book which does not conform to
354 attrib
= g_strdup_printf("# %s", attr
->name
);
357 attrib
= g_strdup(attr
->name
);
359 debug_print("name: %s\nvalue: %s\n", attrib
, attr
->value
);
360 ldif_write_value(stream
, attrib
, attr
->value
);
362 attrList
= g_list_next(attrList
);
364 debug_print("-------------------------------\n");
368 * Find persons displayName.
369 * \param person ItemPerson.
370 * \return displayName.
372 static gchar
* exportldif_find_displayName(ItemPerson
* person
) {
378 if (person
->nickName
&& strlen(person
->nickName
) > 0)
379 displayName
= g_strdup(person
->nickName
);
381 displayName
= g_strdup(ADDRITEM_NAME(person
));
386 * Format persons in an address book folder.
387 * \param ctl Export control data.
388 * \param stream Output stream.
389 * \param folder Folder to format.
390 * \return <i>TRUE</i> if no persons were formatted.
392 static gboolean
exportldif_fmt_person(
393 ExportLdifCtl
*ctl
, FILE *stream
, const ItemFolder
*folder
)
395 gboolean retVal
= TRUE
;
398 gchar
* displayName
= NULL
;
400 if( folder
->listPerson
== NULL
) return retVal
;
402 node
= folder
->listPerson
;
404 AddrItemObject
*aio
= node
->data
;
405 node
= g_list_next( node
);
407 if( aio
&& aio
->type
== ITEMTYPE_PERSON
) {
408 ItemPerson
*person
= ( ItemPerson
* ) aio
;
409 gboolean classPerson
= FALSE
;
410 gboolean classInetP
= FALSE
;
413 /* Check for E-Mail */
414 if( exportldif_test_email( person
) ) {
418 /* Bail if no E-Mail address */
419 if( ctl
->excludeEMail
) continue;
424 dn
= exportldif_find_dn( ctl
, person
);
427 dn
= exportldif_fmt_dn( ctl
, person
);
429 if( dn
== NULL
) continue;
430 ldif_write_value( stream
, LDIF_TAG_DN
, dn
);
434 * Test for schema requirements. This is a simple
435 * test and does not trap all LDAP schema errors.
436 * These can be detected when the LDIF file is
437 * loaded into an LDAP server.
439 if( person
->lastName
) {
440 if( strlen( person
->lastName
) > 0 ) {
447 ldif_write_value( stream
,
448 LDIF_TAG_OBJECTCLASS
, LDIF_CLASS_PERSON
);
451 ldif_write_value( stream
,
452 LDIF_TAG_OBJECTCLASS
, LDIF_CLASS_INET_PERSON
);
455 /* Format person attributes */
457 stream
, LDIF_TAG_COMMONNAME
, ADDRITEM_NAME( person
) );
458 sn
= g_strdup(person
->lastName
);
459 if (classPerson
|| classInetP
) {
460 if(! sn
|| strcmp("", sn
) == 0 || strcmp(" ", sn
) == 0) {
462 sn
= g_strdup("Some SN");
466 stream
, LDIF_TAG_LASTNAME
, sn
);
470 stream
, LDIF_TAG_FIRSTNAME
, person
->firstName
);
472 if (! person
->externalID
)
473 displayName
= exportldif_find_displayName(person
);
475 displayName
= g_strdup(person
->nickName
);
476 ldif_write_value(stream
, LDIF_TAG_NICKNAME
, displayName
);
481 exportldif_fmt_email( person
, stream
);
483 /* Handle other attributes */
484 exportldif_fmt_other_attributes(person
, stream
);
487 ldif_write_eor( stream
);
497 * Format an address book folder.
498 * \param ctl Export control data.
499 * \param stream Output stream.
500 * \param folder Folder to format.
501 * \return <i>TRUE</i> if no persons were formatted.
503 static void exportldif_fmt_folder(
504 ExportLdifCtl
*ctl
, FILE *stream
, const ItemFolder
*folder
)
508 /* Export entries in this folder */
509 exportldif_fmt_person( ctl
, stream
, folder
);
511 /* Export entries in sub-folders */
512 node
= folder
->listFolder
;
514 AddrItemObject
*aio
= node
->data
;
516 node
= g_list_next( node
);
517 if( aio
&& aio
->type
== ITEMTYPE_FOLDER
) {
518 ItemFolder
*subFolder
= ( ItemFolder
* ) aio
;
519 exportldif_fmt_folder( ctl
, stream
, subFolder
);
525 * Export address book to LDIF file.
526 * \param ctl Export control data.
527 * \param cache Address book/data source cache.
530 void exportldif_process( ExportLdifCtl
*ctl
, AddressCache
*cache
)
532 ItemFolder
*rootFolder
;
535 ldifFile
= g_fopen( ctl
->path
, "wb" );
537 /* Cannot open file */
538 ctl
->retVal
= MGU_OPEN_FILE
;
542 rootFolder
= cache
->rootFolder
;
543 exportldif_fmt_folder( ctl
, ldifFile
, rootFolder
);
545 ctl
->retVal
= MGU_SUCCESS
;
549 * Build full export file specification.
550 * \param ctl Export control data.
552 static void exportldif_build_filespec( ExportLdifCtl
*ctl
) {
555 fileSpec
= g_strconcat(
556 ctl
->dirOutput
, G_DIR_SEPARATOR_S
, ctl
->fileLdif
, NULL
);
557 ctl
->path
= mgu_replace_string( ctl
->path
, fileSpec
);
562 * Parse directory and filename from full export file specification.
563 * \param ctl Export control data.
564 * \param fileSpec File spec.
566 void exportldif_parse_filespec( ExportLdifCtl
*ctl
, gchar
*fileSpec
) {
568 gchar
*base
= g_path_get_basename(fileSpec
);
571 mgu_replace_string( ctl
->fileLdif
, base
);
573 t
= g_path_get_dirname( fileSpec
);
574 ctl
->dirOutput
= mgu_replace_string( ctl
->dirOutput
, t
);
576 ctl
->path
= mgu_replace_string( ctl
->path
, fileSpec
);
580 * Create output directory.
581 * \param ctl Export control data.
582 * \return TRUE if directory created.
584 gboolean
exportldif_create_dir( ExportLdifCtl
*ctl
) {
585 gboolean retVal
= FALSE
;
588 if( mkdir( ctl
->dirOutput
, S_IRWXU
) == 0 ) {
592 ctl
->rcCreate
= errno
;
598 * Retrieve create directory error message.
599 * \param ctl Export control data.
602 gchar
*exportldif_get_create_msg( ExportLdifCtl
*ctl
) {
605 if( ctl
->rcCreate
== EEXIST
) {
606 msg
= _( "Name already exists but is not a directory." );
608 else if( ctl
->rcCreate
== EACCES
) {
609 msg
= _( "No permissions to create directory." );
611 else if( ctl
->rcCreate
== ENAMETOOLONG
) {
612 msg
= _( "Name is too long." );
615 msg
= _( "Not specified." );
621 * Set default values.
622 * \param ctl Export control data.
624 static void exportldif_default_values( ExportLdifCtl
*ctl
) {
628 get_home_dir(), G_DIR_SEPARATOR_S
,
629 DFL_DIR_CLAWS_OUT
, NULL
);
631 ctl
->dirOutput
= mgu_replace_string( ctl
->dirOutput
, str
);
635 mgu_replace_string( ctl
->fileLdif
, DFL_FILE_CLAWS_OUT
);
636 ctl
->suffix
= mgu_replace_string( ctl
->suffix
, "" );
638 ctl
->rdnIndex
= EXPORT_LDIF_ID_UID
;
640 ctl
->retVal
= MGU_SUCCESS
;
644 * Load settings from XML properties file.
645 * \param ctl Export control data.
647 void exportldif_load_settings( ExportLdifCtl
*ctl
) {
650 gchar buf
[ XML_BUFSIZE
];
652 props
= xmlprops_create();
653 xmlprops_set_path( props
, ctl
->settingsFile
);
654 rc
= xmlprops_load_file( props
);
658 xmlprops_get_property_s( props
, EXMLPROP_DIRECTORY
, buf
);
659 ctl
->dirOutput
= mgu_replace_string( ctl
->dirOutput
, buf
);
662 xmlprops_get_property_s( props
, EXMLPROP_FILE
, buf
);
663 ctl
->fileLdif
= mgu_replace_string( ctl
->fileLdif
, buf
);
666 xmlprops_get_property_s( props
, EXMLPROP_SUFFIX
, buf
);
667 ctl
->suffix
= mgu_replace_string( ctl
->suffix
, buf
);
670 xmlprops_get_property_i( props
, EXMLPROP_RDN_INDEX
);
672 xmlprops_get_property_b( props
, EXMLPROP_USE_DN
);
674 xmlprops_get_property_b( props
, EXMLPROP_EXCL_EMAIL
);
677 /* Set default values */
678 exportldif_default_values( ctl
);
680 exportldif_build_filespec( ctl
);
681 /* exportldif_print( ctl, stdout ); */
683 xmlprops_free( props
);
687 * Save settings to XML properties file.
688 * \param ctl Export control data.
690 void exportldif_save_settings( ExportLdifCtl
*ctl
) {
693 props
= xmlprops_create();
694 xmlprops_set_path( props
, ctl
->settingsFile
);
696 xmlprops_set_property( props
, EXMLPROP_DIRECTORY
, ctl
->dirOutput
);
697 xmlprops_set_property( props
, EXMLPROP_FILE
, ctl
->fileLdif
);
698 xmlprops_set_property( props
, EXMLPROP_SUFFIX
, ctl
->suffix
);
699 xmlprops_set_property_i( props
, EXMLPROP_RDN_INDEX
, ctl
->rdnIndex
);
700 xmlprops_set_property_b( props
, EXMLPROP_USE_DN
, ctl
->useDN
);
701 xmlprops_set_property_b( props
, EXMLPROP_EXCL_EMAIL
, ctl
->excludeEMail
);
702 if (xmlprops_save_file( props
) != MGU_SUCCESS
)
703 g_warning("can't save settings");
704 xmlprops_free( props
);
708 * ============================================================================
710 * ============================================================================