Sync Spanish manual
[claws.git] / src / exportldif.c
blob16006cf8be0cf18085cdad21ed391e84253be29b
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2024 the Claws Mail team and Match Grun
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.
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #include "claws-features.h"
26 #endif
28 #include <sys/stat.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <time.h>
32 #include <string.h>
33 #include <glib.h>
34 #include <glib/gi18n.h>
36 #include "mgutils.h"
37 #include "utils.h"
38 #include "exportldif.h"
39 #include "xmlprops.h"
40 #include "ldif.h"
41 #include "file-utils.h"
44 #define DFL_DIR_CLAWS_OUT "claws-mail-out"
45 #define DFL_FILE_CLAWS_OUT "addressbook.ldif"
47 #define FMT_BUFSIZE 2048
48 #define XML_BUFSIZE 2048
50 /* Settings - properties */
51 #define EXML_PROPFILE_NAME "exportldif.xml"
52 #define EXMLPROP_DIRECTORY "directory"
53 #define EXMLPROP_FILE "file"
54 #define EXMLPROP_SUFFIX "suffix"
55 #define EXMLPROP_RDN_INDEX "rdn"
56 #define EXMLPROP_USE_DN "use-dn"
57 #define EXMLPROP_EXCL_EMAIL "exclude-mail"
59 static gchar *_attrName_UID_ = "uid";
60 static gchar *_attrName_DName_ = "cn";
61 static gchar *_attrName_EMail_ = "mail";
63 /**
64 * Create initialized LDIF export control object.
65 * \return Initialized export control data.
67 ExportLdifCtl *exportldif_create( void ) {
68 ExportLdifCtl *ctl = g_new0( ExportLdifCtl, 1 );
70 ctl->path = NULL;
71 ctl->dirOutput = NULL;
72 ctl->fileLdif = NULL;
73 ctl->suffix = NULL;
74 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
75 ctl->useDN = FALSE;
76 ctl->excludeEMail = TRUE;
77 ctl->retVal = MGU_SUCCESS;
78 ctl->rcCreate = 0;
79 ctl->settingsFile = g_strconcat(
80 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
82 return ctl;
85 /**
86 * Free up object by releasing internal memory.
87 * \return ctl Export control data.
89 void exportldif_free( ExportLdifCtl *ctl ) {
90 cm_return_if_fail( ctl != NULL );
92 g_free( ctl->path );
93 g_free( ctl->fileLdif );
94 g_free( ctl->dirOutput );
95 g_free( ctl->suffix );
96 g_free( ctl->settingsFile );
98 /* Clear pointers */
99 ctl->path = NULL;
100 ctl->dirOutput = NULL;
101 ctl->fileLdif = NULL;
102 ctl->suffix = NULL;
103 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
104 ctl->useDN = FALSE;
105 ctl->excludeEMail = FALSE;
106 ctl->retVal = MGU_SUCCESS;
107 ctl->rcCreate = 0;
109 /* Now release object */
110 g_free( ctl );
114 * Specify suffix to be used for creating DN entries.
115 * \param ctl Export control data.
116 * \param value Suffix.
118 void exportldif_set_suffix( ExportLdifCtl *ctl, const char *value ) {
119 cm_return_if_fail( ctl != NULL );
120 ctl->suffix = mgu_replace_string( ctl->suffix, value );
121 g_strstrip( ctl->suffix );
125 * Specify index of variable to be used for creating RDN entries.
126 * \param ctl Export control data.
127 * \param value Index to variable, as follows:
128 * <ul>
129 * <li><code>EXPORT_LDIF_ID_UID</code> - Use Claws Mail UID.</li>
130 * <li><code>EXPORT_LDIF_ID_DNAME</code> - Use Claws Mail display name.</li>
131 * <li><code>EXPORT_LDIF_ID_EMAIL</code> - Use first Email address.</li>
132 * </ul>
134 void exportldif_set_rdn( ExportLdifCtl *ctl, const gint value ) {
135 cm_return_if_fail( ctl != NULL );
136 ctl->rdnIndex = value;
140 * Specify that <code>DN</code> attribute, if present, should be used as the
141 * DN for the entry.
142 * \param ctl Export control data.
143 * \param value <i>TRUE</i> if DN should be used.
145 void exportldif_set_use_dn( ExportLdifCtl *ctl, const gboolean value ) {
146 cm_return_if_fail( ctl != NULL );
147 ctl->useDN = value;
151 * Specify that records without E-Mail addresses should be excluded.
152 * \param ctl Export control data.
153 * \param value <i>TRUE</i> if records without E-Mail should be excluded.
155 void exportldif_set_exclude_email( ExportLdifCtl *ctl, const gboolean value ) {
156 cm_return_if_fail( ctl != NULL );
157 ctl->excludeEMail = value;
161 * Format LDAP value name with no embedded commas.
162 * \param value Data value to format.
163 * \return Formatted string, should be freed after use.
165 static gchar *exportldif_fmt_value( gchar *value ) {
166 gchar *dupval;
167 gchar *src;
168 gchar *dest;
169 gchar ch;
171 /* Duplicate incoming value */
172 dest = dupval = g_strdup( value );
174 /* Copy characters, ignoring commas */
175 src = value;
176 while( *src ) {
177 ch = *src;
178 if( ch != ',' ) {
179 *dest = ch;
180 dest++;
182 src++;
184 *dest = '\0';
185 return dupval;
189 * Build DN for entry.
190 * \param ctl Export control data.
191 * \param person Person to format.
192 * \return Formatted DN entry.
194 static gchar *exportldif_fmt_dn(
195 ExportLdifCtl *ctl, const ItemPerson *person )
197 gchar buf[ FMT_BUFSIZE + 1 ];
198 gchar *retVal = NULL;
199 gchar *attr = NULL;
200 gchar *value = NULL;
201 gchar *dupval = NULL;
203 /* Process RDN */
204 *buf = '\0';
205 if( ctl->rdnIndex == EXPORT_LDIF_ID_UID ) {
206 attr = _attrName_UID_;
207 value = ADDRITEM_ID( person );
209 else if( ctl->rdnIndex == EXPORT_LDIF_ID_DNAME ) {
210 attr = _attrName_DName_;
211 value = ADDRITEM_NAME( person );
212 dupval = exportldif_fmt_value( value );
214 else if( ctl->rdnIndex == EXPORT_LDIF_ID_EMAIL ) {
215 GList *node;
217 node = person->listEMail;
218 if( node ) {
219 ItemEMail *email = node->data;
221 attr = _attrName_EMail_;
222 value = email->address;
223 dupval = exportldif_fmt_value( value );
227 /* Format DN */
228 if( attr ) {
229 if( value ) {
230 if( strlen( value ) > 0 ) {
231 strncat( buf, attr, FMT_BUFSIZE - strlen(buf) );
232 strncat( buf, "=", FMT_BUFSIZE - strlen(buf) );
233 if( dupval ) {
234 /* Format and free duplicated value */
235 strncat( buf, dupval, FMT_BUFSIZE - strlen(buf) );
236 g_free( dupval );
238 else {
239 /* Use original value */
240 strncat( buf, value, FMT_BUFSIZE - strlen(buf) );
243 /* Append suffix */
244 if( ctl->suffix ) {
245 if( strlen( ctl->suffix ) > 0 ) {
246 strncat( buf, ",", FMT_BUFSIZE - strlen(buf) );
247 strncat( buf, ctl->suffix, FMT_BUFSIZE - strlen(buf) );
251 retVal = g_strdup( buf );
255 return retVal;
259 * Find DN by searching attribute list.
260 * \param ctl Export control data.
261 * \param person Person to format.
262 * \return Formatted DN entry, should be freed after use.
264 static gchar *exportldif_find_dn(
265 ExportLdifCtl *ctl, const ItemPerson *person )
267 gchar *retVal = NULL;
268 const GList *node;
270 node = person->listAttrib;
271 while( node ) {
272 UserAttribute *attrib = node->data;
274 node = g_list_next( node );
275 if( g_utf8_collate( attrib->name, LDIF_TAG_DN ) == 0 ) {
276 retVal = g_strdup( attrib->value );
277 break;
280 return retVal;
284 * Format E-Mail entries for person.
285 * \param person Person to format.
286 * \param stream Output stream.
287 * \return <i>TRUE</i> if entry formatted.
289 static gboolean exportldif_fmt_email( const ItemPerson *person, FILE *stream ) {
290 gboolean retVal = FALSE;
291 const GList *node;
293 node = person->listEMail;
294 while( node ) {
295 ItemEMail *email = node->data;
297 node = g_list_next( node );
298 ldif_write_value( stream, LDIF_TAG_EMAIL, email->address );
299 retVal = TRUE;
301 return retVal;
305 * Test for E-Mail entries for person.
306 * \param person Person to test.
307 * \return <i>TRUE</i> if person has E-Mail address.
309 static gboolean exportldif_test_email( const ItemPerson *person )
311 gboolean retVal = FALSE;
312 const GList *node;
314 node = person->listEMail;
315 while( node ) {
316 ItemEMail *email = node->data;
318 node = g_list_next( node );
319 if( email->address ) {
320 if( strlen( email->address ) > 0 ) {
321 retVal = TRUE;
322 break;
325 retVal = TRUE;
327 return retVal;
331 * Format other attributes for person.
332 * \param person ItemPerson.
333 * \param stream Output stream.
335 static void exportldif_fmt_other_attributes(ItemPerson* person, FILE* stream) {
336 UserAttribute* attr;
337 GList* attrList = NULL;
338 gchar* attrib;
340 if (! person)
341 return;
342 debug_print("cn: %s\n-----------------------------\n", ADDRITEM_NAME(person));
343 attrList = person->listAttrib;
344 while (attrList) {
345 attr = (UserAttribute *) attrList->data;
346 if (attr->uid) {
347 /* Native address book which does not conform to
348 * the LDAP schemas
350 attrib = g_strdup_printf("# %s", attr->name);
352 else {
353 attrib = g_strdup(attr->name);
355 debug_print("name: %s\nvalue: %s\n", attrib, attr->value);
356 ldif_write_value(stream, attrib, attr->value);
357 g_free(attrib);
358 attrList = g_list_next(attrList);
360 debug_print("-------------------------------\n");
364 * Find persons displayName.
365 * \param person ItemPerson.
366 * \return displayName.
368 static gchar* exportldif_find_displayName(ItemPerson* person) {
369 gchar* displayName;
371 if (! person)
372 return NULL;
374 if (person->nickName && strlen(person->nickName) > 0)
375 displayName = g_strdup(person->nickName);
376 else
377 displayName = g_strdup(ADDRITEM_NAME(person));
378 return displayName;
382 * Format persons in an address book folder.
383 * \param ctl Export control data.
384 * \param stream Output stream.
385 * \param folder Folder to format.
386 * \return <i>TRUE</i> if no persons were formatted.
388 static gboolean exportldif_fmt_person(
389 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
391 gboolean retVal = TRUE;
392 const GList *node;
393 gchar* sn = NULL;
394 gchar* displayName = NULL;
396 if( folder->listPerson == NULL ) return retVal;
398 node = folder->listPerson;
399 while( node ) {
400 AddrItemObject *aio = node->data;
401 node = g_list_next( node );
403 if( aio && aio->type == ITEMTYPE_PERSON ) {
404 ItemPerson *person = ( ItemPerson * ) aio;
405 gboolean classPerson = FALSE;
406 gboolean classInetP = FALSE;
407 gchar *dn = NULL;
409 /* Check for E-Mail */
410 if( exportldif_test_email( person ) ) {
411 classInetP = TRUE;
413 else {
414 /* Bail if no E-Mail address */
415 if( ctl->excludeEMail ) continue;
418 /* Format DN */
419 if( ctl->useDN ) {
420 dn = exportldif_find_dn( ctl, person );
422 if( dn == NULL ) {
423 dn = exportldif_fmt_dn( ctl, person );
425 if( dn == NULL ) continue;
426 ldif_write_value( stream, LDIF_TAG_DN, dn );
427 g_free( dn );
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.
435 if( person->lastName ) {
436 if( strlen( person->lastName ) > 0 ) {
437 classPerson = TRUE;
438 classInetP = TRUE;
442 if( classPerson ) {
443 ldif_write_value( stream,
444 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_PERSON );
446 if( classInetP ) {
447 ldif_write_value( stream,
448 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_INET_PERSON );
451 /* Format person attributes */
452 ldif_write_value(
453 stream, LDIF_TAG_COMMONNAME, ADDRITEM_NAME( person ) );
454 sn = g_strdup(person->lastName);
455 if (classPerson || classInetP) {
456 if(! sn || strcmp("", sn) == 0 || strcmp(" ", sn) == 0) {
457 g_free(sn);
458 sn = g_strdup("Some SN");
461 ldif_write_value(
462 stream, LDIF_TAG_LASTNAME, sn );
463 g_free(sn);
464 sn = NULL;
465 ldif_write_value(
466 stream, LDIF_TAG_FIRSTNAME, person->firstName );
468 if (! person->externalID)
469 displayName = exportldif_find_displayName(person);
470 else
471 displayName = g_strdup(person->nickName);
472 ldif_write_value(stream, LDIF_TAG_NICKNAME, displayName);
473 g_free(displayName);
474 displayName = NULL;
476 /* Format E-Mail */
477 exportldif_fmt_email( person, stream );
479 /* Handle other attributes */
480 exportldif_fmt_other_attributes(person, stream);
482 /* End record */
483 ldif_write_eor( stream );
485 retVal = FALSE;
489 return retVal;
493 * Format an address book folder.
494 * \param ctl Export control data.
495 * \param stream Output stream.
496 * \param folder Folder to format.
497 * \return <i>TRUE</i> if no persons were formatted.
499 static void exportldif_fmt_folder(
500 ExportLdifCtl *ctl, FILE *stream, const ItemFolder *folder )
502 const GList *node;
504 /* Export entries in this folder */
505 exportldif_fmt_person( ctl, stream, folder );
507 /* Export entries in subfolders */
508 node = folder->listFolder;
509 while( node ) {
510 AddrItemObject *aio = node->data;
512 node = g_list_next( node );
513 if( aio && aio->type == ITEMTYPE_FOLDER ) {
514 ItemFolder *subFolder = ( ItemFolder * ) aio;
515 exportldif_fmt_folder( ctl, stream, subFolder );
521 * Export address book to LDIF file.
522 * \param ctl Export control data.
523 * \param cache Address book/data source cache.
524 * \return Status.
526 void exportldif_process( ExportLdifCtl *ctl, AddressCache *cache )
528 ItemFolder *rootFolder;
529 FILE *ldifFile;
531 ldifFile = claws_fopen( ctl->path, "wb" );
532 if( ! ldifFile ) {
533 /* Cannot open file */
534 ctl->retVal = MGU_OPEN_FILE;
535 return;
538 rootFolder = cache->rootFolder;
539 exportldif_fmt_folder( ctl, ldifFile, rootFolder );
540 claws_safe_fclose( ldifFile );
541 ctl->retVal = MGU_SUCCESS;
545 * Build full export file specification.
546 * \param ctl Export control data.
548 static void exportldif_build_filespec( ExportLdifCtl *ctl ) {
549 gchar *fileSpec;
551 fileSpec = g_strconcat(
552 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileLdif, NULL );
553 ctl->path = mgu_replace_string( ctl->path, fileSpec );
554 g_free( fileSpec );
558 * Parse directory and filename from full export file specification.
559 * \param ctl Export control data.
560 * \param fileSpec File spec.
562 void exportldif_parse_filespec( ExportLdifCtl *ctl, gchar *fileSpec ) {
563 gchar *t;
564 gchar *base = g_path_get_basename(fileSpec);
566 ctl->fileLdif =
567 mgu_replace_string( ctl->fileLdif, base );
568 g_free(base);
569 t = g_path_get_dirname( fileSpec );
570 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
571 g_free( t );
572 ctl->path = mgu_replace_string( ctl->path, fileSpec );
576 * Create output directory.
577 * \param ctl Export control data.
578 * \return TRUE if directory created.
580 gboolean exportldif_create_dir( ExportLdifCtl *ctl ) {
581 gboolean retVal = FALSE;
583 ctl->rcCreate = 0;
584 if( g_mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
585 retVal = TRUE;
587 else {
588 ctl->rcCreate = errno;
590 return retVal;
594 * Retrieve create directory error message.
595 * \param ctl Export control data.
596 * \return Message.
598 gchar *exportldif_get_create_msg( ExportLdifCtl *ctl ) {
599 gchar *msg;
601 if( ctl->rcCreate == EEXIST ) {
602 msg = _( "Name already exists but is not a directory." );
604 else if( ctl->rcCreate == EACCES ) {
605 msg = _( "No permissions to create directory." );
607 else if( ctl->rcCreate == ENAMETOOLONG ) {
608 msg = _( "Name is too long." );
610 else {
611 msg = _( "Not specified." );
613 return msg;
617 * Set default values.
618 * \param ctl Export control data.
620 static void exportldif_default_values( ExportLdifCtl *ctl ) {
621 gchar *str;
623 str = g_strconcat(
624 get_home_dir(), G_DIR_SEPARATOR_S,
625 DFL_DIR_CLAWS_OUT, NULL );
627 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
628 g_free( str );
630 ctl->fileLdif =
631 mgu_replace_string( ctl->fileLdif, DFL_FILE_CLAWS_OUT );
632 ctl->suffix = mgu_replace_string( ctl->suffix, "" );
634 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
635 ctl->useDN = FALSE;
636 ctl->retVal = MGU_SUCCESS;
640 * Load settings from XML properties file.
641 * \param ctl Export control data.
643 void exportldif_load_settings( ExportLdifCtl *ctl ) {
644 XmlProperty *props;
645 gint rc;
646 gchar buf[ XML_BUFSIZE ];
648 props = xmlprops_create();
649 xmlprops_set_path( props, ctl->settingsFile );
650 rc = xmlprops_load_file( props );
651 if( rc == 0 ) {
652 /* Read settings */
653 *buf = '\0';
654 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
655 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
657 *buf = '\0';
658 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
659 ctl->fileLdif = mgu_replace_string( ctl->fileLdif, buf );
661 *buf = '\0';
662 xmlprops_get_property_s( props, EXMLPROP_SUFFIX, buf );
663 ctl->suffix = mgu_replace_string( ctl->suffix, buf );
665 ctl->rdnIndex =
666 xmlprops_get_property_i( props, EXMLPROP_RDN_INDEX );
667 ctl->useDN =
668 xmlprops_get_property_b( props, EXMLPROP_USE_DN );
669 ctl->excludeEMail =
670 xmlprops_get_property_b( props, EXMLPROP_EXCL_EMAIL );
672 else {
673 /* Set default values */
674 exportldif_default_values( ctl );
676 exportldif_build_filespec( ctl );
677 /* exportldif_print( ctl, stdout ); */
679 xmlprops_free( props );
683 * Save settings to XML properties file.
684 * \param ctl Export control data.
686 void exportldif_save_settings( ExportLdifCtl *ctl ) {
687 XmlProperty *props;
689 props = xmlprops_create();
690 xmlprops_set_path( props, ctl->settingsFile );
692 xmlprops_set_property( props, EXMLPROP_DIRECTORY, ctl->dirOutput );
693 xmlprops_set_property( props, EXMLPROP_FILE, ctl->fileLdif );
694 xmlprops_set_property( props, EXMLPROP_SUFFIX, ctl->suffix );
695 xmlprops_set_property_i( props, EXMLPROP_RDN_INDEX, ctl->rdnIndex );
696 xmlprops_set_property_b( props, EXMLPROP_USE_DN, ctl->useDN );
697 xmlprops_set_property_b( props, EXMLPROP_EXCL_EMAIL, ctl->excludeEMail );
698 if (xmlprops_save_file( props ) != MGU_SUCCESS)
699 g_warning("can't save settings");
700 xmlprops_free( props );
704 * ============================================================================
705 * End of Source.
706 * ============================================================================