Improve some sieve-related translations
[claws.git] / src / exportldif.c
blobf3273203a548351e74244703b55074bd065ed2d6
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2022 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.
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 #ifdef MKDIR_TAKES_ONE_ARG
45 #undef mkdir
46 #define mkdir(a,b) mkdir(a)
47 #endif
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";
68 /**
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 );
75 ctl->path = NULL;
76 ctl->dirOutput = NULL;
77 ctl->fileLdif = NULL;
78 ctl->suffix = NULL;
79 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
80 ctl->useDN = FALSE;
81 ctl->excludeEMail = TRUE;
82 ctl->retVal = MGU_SUCCESS;
83 ctl->rcCreate = 0;
84 ctl->settingsFile = g_strconcat(
85 get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
87 return ctl;
90 /**
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 );
97 g_free( ctl->path );
98 g_free( ctl->fileLdif );
99 g_free( ctl->dirOutput );
100 g_free( ctl->suffix );
101 g_free( ctl->settingsFile );
103 /* Clear pointers */
104 ctl->path = NULL;
105 ctl->dirOutput = NULL;
106 ctl->fileLdif = NULL;
107 ctl->suffix = NULL;
108 ctl->rdnIndex = EXPORT_LDIF_ID_UID;
109 ctl->useDN = FALSE;
110 ctl->excludeEMail = FALSE;
111 ctl->retVal = MGU_SUCCESS;
112 ctl->rcCreate = 0;
114 /* Now release object */
115 g_free( ctl );
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:
133 * <ul>
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>
137 * </ul>
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
146 * DN for the entry.
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 );
152 ctl->useDN = value;
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 ) {
171 gchar *dupval;
172 gchar *src;
173 gchar *dest;
174 gchar ch;
176 /* Duplicate incoming value */
177 dest = dupval = g_strdup( value );
179 /* Copy characters, ignoring commas */
180 src = value;
181 while( *src ) {
182 ch = *src;
183 if( ch != ',' ) {
184 *dest = ch;
185 dest++;
187 src++;
189 *dest = '\0';
190 return dupval;
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;
204 gchar *attr = NULL;
205 gchar *value = NULL;
206 gchar *dupval = NULL;
208 /* Process RDN */
209 *buf = '\0';
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 ) {
220 GList *node;
222 node = person->listEMail;
223 if( node ) {
224 ItemEMail *email = node->data;
226 attr = _attrName_EMail_;
227 value = email->address;
228 dupval = exportldif_fmt_value( value );
232 /* Format DN */
233 if( attr ) {
234 if( value ) {
235 if( strlen( value ) > 0 ) {
236 strncat( buf, attr, FMT_BUFSIZE - strlen(buf) );
237 strncat( buf, "=", FMT_BUFSIZE - strlen(buf) );
238 if( dupval ) {
239 /* Format and free duplicated value */
240 strncat( buf, dupval, FMT_BUFSIZE - strlen(buf) );
241 g_free( dupval );
243 else {
244 /* Use original value */
245 strncat( buf, value, FMT_BUFSIZE - strlen(buf) );
248 /* Append suffix */
249 if( ctl->suffix ) {
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 );
260 return retVal;
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;
273 const GList *node;
275 node = person->listAttrib;
276 while( node ) {
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 );
282 break;
285 return retVal;
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;
296 const GList *node;
298 node = person->listEMail;
299 while( node ) {
300 ItemEMail *email = node->data;
302 node = g_list_next( node );
303 ldif_write_value( stream, LDIF_TAG_EMAIL, email->address );
304 retVal = TRUE;
306 return retVal;
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;
317 const GList *node;
319 node = person->listEMail;
320 while( node ) {
321 ItemEMail *email = node->data;
323 node = g_list_next( node );
324 if( email->address ) {
325 if( strlen( email->address ) > 0 ) {
326 retVal = TRUE;
327 break;
330 retVal = TRUE;
332 return retVal;
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) {
341 UserAttribute* attr;
342 GList* attrList = NULL;
343 gchar* attrib;
345 if (! person)
346 return;
347 debug_print("cn: %s\n-----------------------------\n", ADDRITEM_NAME(person));
348 attrList = person->listAttrib;
349 while (attrList) {
350 attr = (UserAttribute *) attrList->data;
351 if (attr->uid) {
352 /* Native address book which does not conform to
353 * the LDAP schemas
355 attrib = g_strdup_printf("# %s", attr->name);
357 else {
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);
362 g_free(attrib);
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) {
374 gchar* displayName;
376 if (! person)
377 return NULL;
379 if (person->nickName && strlen(person->nickName) > 0)
380 displayName = g_strdup(person->nickName);
381 else
382 displayName = g_strdup(ADDRITEM_NAME(person));
383 return displayName;
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;
397 const GList *node;
398 gchar* sn = NULL;
399 gchar* displayName = NULL;
401 if( folder->listPerson == NULL ) return retVal;
403 node = folder->listPerson;
404 while( node ) {
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;
412 gchar *dn = NULL;
414 /* Check for E-Mail */
415 if( exportldif_test_email( person ) ) {
416 classInetP = TRUE;
418 else {
419 /* Bail if no E-Mail address */
420 if( ctl->excludeEMail ) continue;
423 /* Format DN */
424 if( ctl->useDN ) {
425 dn = exportldif_find_dn( ctl, person );
427 if( dn == NULL ) {
428 dn = exportldif_fmt_dn( ctl, person );
430 if( dn == NULL ) continue;
431 ldif_write_value( stream, LDIF_TAG_DN, dn );
432 g_free( 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 ) {
442 classPerson = TRUE;
443 classInetP = TRUE;
447 if( classPerson ) {
448 ldif_write_value( stream,
449 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_PERSON );
451 if( classInetP ) {
452 ldif_write_value( stream,
453 LDIF_TAG_OBJECTCLASS, LDIF_CLASS_INET_PERSON );
456 /* Format person attributes */
457 ldif_write_value(
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) {
462 g_free(sn);
463 sn = g_strdup("Some SN");
466 ldif_write_value(
467 stream, LDIF_TAG_LASTNAME, sn );
468 g_free(sn);
469 sn = NULL;
470 ldif_write_value(
471 stream, LDIF_TAG_FIRSTNAME, person->firstName );
473 if (! person->externalID)
474 displayName = exportldif_find_displayName(person);
475 else
476 displayName = g_strdup(person->nickName);
477 ldif_write_value(stream, LDIF_TAG_NICKNAME, displayName);
478 g_free(displayName);
479 displayName = NULL;
481 /* Format E-Mail */
482 exportldif_fmt_email( person, stream );
484 /* Handle other attributes */
485 exportldif_fmt_other_attributes(person, stream);
487 /* End record */
488 ldif_write_eor( stream );
490 retVal = FALSE;
494 return retVal;
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 )
507 const GList *node;
509 /* Export entries in this folder */
510 exportldif_fmt_person( ctl, stream, folder );
512 /* Export entries in subfolders */
513 node = folder->listFolder;
514 while( node ) {
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.
529 * \return Status.
531 void exportldif_process( ExportLdifCtl *ctl, AddressCache *cache )
533 ItemFolder *rootFolder;
534 FILE *ldifFile;
536 ldifFile = claws_fopen( ctl->path, "wb" );
537 if( ! ldifFile ) {
538 /* Cannot open file */
539 ctl->retVal = MGU_OPEN_FILE;
540 return;
543 rootFolder = cache->rootFolder;
544 exportldif_fmt_folder( ctl, ldifFile, rootFolder );
545 claws_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 ) {
554 gchar *fileSpec;
556 fileSpec = g_strconcat(
557 ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileLdif, NULL );
558 ctl->path = mgu_replace_string( ctl->path, fileSpec );
559 g_free( 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 ) {
568 gchar *t;
569 gchar *base = g_path_get_basename(fileSpec);
571 ctl->fileLdif =
572 mgu_replace_string( ctl->fileLdif, base );
573 g_free(base);
574 t = g_path_get_dirname( fileSpec );
575 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
576 g_free( 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;
588 ctl->rcCreate = 0;
589 if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
590 retVal = TRUE;
592 else {
593 ctl->rcCreate = errno;
595 return retVal;
599 * Retrieve create directory error message.
600 * \param ctl Export control data.
601 * \return Message.
603 gchar *exportldif_get_create_msg( ExportLdifCtl *ctl ) {
604 gchar *msg;
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." );
615 else {
616 msg = _( "Not specified." );
618 return msg;
622 * Set default values.
623 * \param ctl Export control data.
625 static void exportldif_default_values( ExportLdifCtl *ctl ) {
626 gchar *str;
628 str = g_strconcat(
629 get_home_dir(), G_DIR_SEPARATOR_S,
630 DFL_DIR_CLAWS_OUT, NULL );
632 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
633 g_free( str );
635 ctl->fileLdif =
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;
640 ctl->useDN = FALSE;
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 ) {
649 XmlProperty *props;
650 gint rc;
651 gchar buf[ XML_BUFSIZE ];
653 props = xmlprops_create();
654 xmlprops_set_path( props, ctl->settingsFile );
655 rc = xmlprops_load_file( props );
656 if( rc == 0 ) {
657 /* Read settings */
658 *buf = '\0';
659 xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
660 ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
662 *buf = '\0';
663 xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
664 ctl->fileLdif = mgu_replace_string( ctl->fileLdif, buf );
666 *buf = '\0';
667 xmlprops_get_property_s( props, EXMLPROP_SUFFIX, buf );
668 ctl->suffix = mgu_replace_string( ctl->suffix, buf );
670 ctl->rdnIndex =
671 xmlprops_get_property_i( props, EXMLPROP_RDN_INDEX );
672 ctl->useDN =
673 xmlprops_get_property_b( props, EXMLPROP_USE_DN );
674 ctl->excludeEMail =
675 xmlprops_get_property_b( props, EXMLPROP_EXCL_EMAIL );
677 else {
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 ) {
692 XmlProperty *props;
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 * ============================================================================
710 * End of Source.
711 * ============================================================================