ca81ae5d9c6c640deb5cce55fcec7aaf160e5657
[citadel.git] / citadel / serv_ldap.c
1 /*
2  * $Id$
3  *
4  * A module which implements the LDAP connector for Citadel.
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include <sys/wait.h>
30 #include <string.h>
31 #include <limits.h>
32 #include "citadel.h"
33 #include "server.h"
34 #include "sysdep_decls.h"
35 #include "citserver.h"
36 #include "support.h"
37 #include "config.h"
38 #include "serv_extensions.h"
39 #include "room_ops.h"
40 #include "policy.h"
41 #include "database.h"
42 #include "msgbase.h"
43 #include "serv_ldap.h"
44 #include "vcard.h"
45 #include "tools.h"
46
47 #ifdef HAVE_LDAP
48
49 #include <ldap.h>
50
51 LDAP *dirserver = NULL;
52
53 /*
54  * LDAP connector cleanup function
55  */
56 void serv_ldap_cleanup(void)
57 {
58         if (!dirserver) return;
59
60         lprintf(CTDL_INFO, "Unbinding from directory server\n");
61         ldap_unbind(dirserver);
62         dirserver = NULL;
63 }
64
65
66
67 /*
68  * Create the root node.  If it's already there, so what?
69  */
70 void CtdlCreateLdapRoot(void) {
71         char *dc_values[2];
72         char *objectClass_values[3];
73         LDAPMod dc, objectClass;
74         LDAPMod *mods[3];
75         char topdc[SIZ];
76         int i;
77
78         /* We just want the top-level dc, not the whole hierarchy */
79         strcpy(topdc, config.c_ldap_base_dn);
80         for (i=0; i<strlen(topdc); ++i) {
81                 if (topdc[i] == ',') topdc[i] = 0;
82         }
83         for (i=0; i<strlen(topdc); ++i) {
84                 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
85         }
86
87         /* Set up the transaction */
88         dc.mod_op               = LDAP_MOD_ADD;
89         dc.mod_type             = "dc";
90         dc_values[0]            = topdc;
91         dc_values[1]            = NULL;
92         dc.mod_values           = dc_values;
93         objectClass.mod_op      = LDAP_MOD_ADD;
94         objectClass.mod_type    = "objectClass";
95         objectClass_values[0]   = "top";
96         objectClass_values[1]   = "domain";
97         objectClass_values[2]   = NULL;
98         objectClass.mod_values  = objectClass_values;
99         mods[0] = &dc;
100         mods[1] = &objectClass;
101         mods[2] = NULL;
102
103         /* Perform the transaction */
104         lprintf(CTDL_DEBUG, "Setting up Base DN node...\n");
105         begin_critical_section(S_LDAP);
106         i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
107         end_critical_section(S_LDAP);
108
109         if (i != LDAP_SUCCESS) {
110                 lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
111                         ldap_err2string(i), i);
112         }
113 }
114
115
116 /*
117  * Create an OU node representing a Citadel host.
118  */
119 void CtdlCreateHostOU(char *host) {
120         char *dc_values[2];
121         char *objectClass_values[3];
122         LDAPMod dc, objectClass;
123         LDAPMod *mods[3];
124         int i;
125         char dn[SIZ];
126
127         /* The DN is this OU plus the base. */
128         snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
129
130         /* Set up the transaction */
131         dc.mod_op               = LDAP_MOD_ADD;
132         dc.mod_type             = "ou";
133         dc_values[0]            = host;
134         dc_values[1]            = NULL;
135         dc.mod_values           = dc_values;
136         objectClass.mod_op      = LDAP_MOD_ADD;
137         objectClass.mod_type    = "objectClass";
138         objectClass_values[0]   = "top";
139         objectClass_values[1]   = "organizationalUnit";
140         objectClass_values[2]   = NULL;
141         objectClass.mod_values  = objectClass_values;
142         mods[0] = &dc;
143         mods[1] = &objectClass;
144         mods[2] = NULL;
145
146         /* Perform the transaction */
147         lprintf(CTDL_DEBUG, "Setting up Host OU node...\n");
148         begin_critical_section(S_LDAP);
149         i = ldap_add_s(dirserver, dn, mods);
150         end_critical_section(S_LDAP);
151
152         /* ignore the error -- it's ok if it already exists
153         if (i != LDAP_SUCCESS) {
154                 lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
155                         ldap_err2string(i), i);
156         }
157         */
158 }
159
160
161
162
163
164
165
166
167 void CtdlConnectToLdap(void) {
168         int i;
169         int ldap_version = 3;
170
171         lprintf(CTDL_INFO, "Connecting to LDAP server %s:%d...\n",
172                 config.c_ldap_host, config.c_ldap_port);
173
174         dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
175         if (dirserver == NULL) {
176                 lprintf(CTDL_CRIT, "Could not connect to %s:%d : %s\n",
177                         config.c_ldap_host,
178                         config.c_ldap_port,
179                         strerror(errno));
180                 return;
181         }
182
183         ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
184
185         lprintf(CTDL_INFO, "Binding to %s\n", config.c_ldap_bind_dn);
186
187         i = ldap_simple_bind_s(dirserver,
188                                 config.c_ldap_bind_dn,
189                                 config.c_ldap_bind_pw
190         );
191         if (i != LDAP_SUCCESS) {
192                 lprintf(CTDL_CRIT, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
193                 dirserver = NULL;       /* FIXME disconnect from ldap */
194                 return;
195         }
196
197         CtdlCreateLdapRoot();
198 }
199
200
201 /* 
202  * vCard-to-LDAP conversions.
203  *
204  * If 'op' is set to V2L_WRITE, then write
205  * (add, or change if already exists) a directory entry to the
206  * LDAP server, based on the information supplied in a vCard.
207  *
208  * If 'op' is set to V2L_DELETE, then delete the entry from LDAP.
209  */
210 void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) {
211         struct vCard *v = NULL;
212         int i, j;
213         char this_dn[SIZ];
214         LDAPMod **attrs = NULL;
215         int num_attrs = 0;
216         int num_emails = 0;
217         int alias_attr = (-1);
218         int num_phones = 0;
219         int phone_attr = (-1);
220         int have_addr = 0;
221         int have_cn = 0;
222
223         char givenname[SIZ];
224         char sn[SIZ];
225         char uid[SIZ];
226         char street[SIZ];
227         char city[SIZ];
228         char state[SIZ];
229         char zipcode[SIZ];
230         char calFBURL[SIZ];
231
232         if (dirserver == NULL) return;
233         if (msg == NULL) return;
234         if (msg->cm_fields['M'] == NULL) return;
235         if (msg->cm_fields['A'] == NULL) return;
236         if (msg->cm_fields['N'] == NULL) return;
237
238         /* Initialize variables */
239         strcpy(givenname, "");
240         strcpy(sn, "");
241         strcpy(calFBURL, "");
242
243         sprintf(this_dn, "cn=%s,ou=%s,%s",
244                 msg->cm_fields['A'],
245                 msg->cm_fields['N'],
246                 config.c_ldap_base_dn
247         );
248                 
249         sprintf(uid, "%s@%s",
250                 msg->cm_fields['A'],
251                 msg->cm_fields['N']
252         );
253
254         /* Are we just deleting?  If so, it's simple... */
255         if (op == V2L_DELETE) {
256                 lprintf(CTDL_DEBUG, "Calling ldap_delete_s()\n");
257                 begin_critical_section(S_LDAP);
258                 i = ldap_delete_s(dirserver, this_dn);
259                 end_critical_section(S_LDAP);
260                 if (i != LDAP_SUCCESS) {
261                         lprintf(CTDL_ERR, "ldap_delete_s() failed: %s (%d)\n",
262                                 ldap_err2string(i), i);
263                 }
264                 return;
265         }
266
267         /*
268          * If we get to this point then it must be a V2L_WRITE operation.
269          */
270
271         /* First make sure the OU for the user's home Citadel host is created */
272         CtdlCreateHostOU(msg->cm_fields['N']);
273
274         /* The first LDAP attribute will be an 'objectclass' list.  Citadel
275          * doesn't do anything with this.  It's just there for compatibility
276          * with Kolab.
277          */
278         num_attrs = 1;
279         attrs = malloc( (sizeof(LDAPMod *) * num_attrs) );
280         attrs[0] = malloc(sizeof(LDAPMod));
281         memset(attrs[0], 0, sizeof(LDAPMod));
282         attrs[0]->mod_op        = LDAP_MOD_ADD;
283         attrs[0]->mod_type      = "objectclass";
284         attrs[0]->mod_values    = malloc(3 * sizeof(char *));
285         attrs[0]->mod_values[0] = strdup("inetOrgPerson");
286         attrs[0]->mod_values[1] = NULL;
287
288         /* Convert the vCard fields to LDAP properties */
289         v = vcard_load(msg->cm_fields['M']);
290         if (v->numprops) for (i=0; i<(v->numprops); ++i) if (striplt(v->prop[i].value), strlen(v->prop[i].value) > 0) {
291
292                 if (!strcasecmp(v->prop[i].name, "n")) {
293                         extract_token(sn,               v->prop[i].value, 0, ';');
294                         extract_token(givenname,        v->prop[i].value, 1, ';');
295                 }
296
297                 if (!strcasecmp(v->prop[i].name, "fn")) {
298                         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
299                         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
300                         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
301                         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
302                         attrs[num_attrs-1]->mod_type            = "cn";
303                         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
304                         attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
305                         attrs[num_attrs-1]->mod_values[1]       = NULL;
306                         have_cn = 1;
307                 }
308
309                 if (!strcasecmp(v->prop[i].name, "title")) {
310                         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
311                         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
312                         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
313                         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
314                         attrs[num_attrs-1]->mod_type            = "title";
315                         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
316                         attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
317                         attrs[num_attrs-1]->mod_values[1]       = NULL;
318                 }
319
320                 if (!strcasecmp(v->prop[i].name, "org")) {
321                         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
322                         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
323                         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
324                         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
325                         attrs[num_attrs-1]->mod_type            = "o";
326                         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
327                         attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
328                         attrs[num_attrs-1]->mod_values[1]       = NULL;
329                 }
330
331                 if ( (!strcasecmp(v->prop[i].name, "adr"))
332                    ||(!strncasecmp(v->prop[i].name, "adr;", 4)) ) {
333                         /* Unfortunately, we can only do a single address */
334                         if (!have_addr) {
335                                 have_addr = 1;
336                                 strcpy(street, "");
337                                 extract_token(&street[strlen(street)],
338                                         v->prop[i].value, 0, ';'); /* po box */
339                                 strcat(street, " ");
340                                 extract_token(&street[strlen(street)],
341                                         v->prop[i].value, 1, ';'); /* extend addr */
342                                 strcat(street, " ");
343                                 extract_token(&street[strlen(street)],
344                                         v->prop[i].value, 2, ';'); /* street */
345                                 striplt(street);
346                                 extract_token(city, v->prop[i].value, 3, ';');
347                                 extract_token(state, v->prop[i].value, 4, ';');
348                                 extract_token(zipcode, v->prop[i].value, 5, ';');
349
350                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
351                                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
352                                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
353                                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
354                                 attrs[num_attrs-1]->mod_type            = "street";
355                                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
356                                 attrs[num_attrs-1]->mod_values[0]       = strdup(street);
357                                 attrs[num_attrs-1]->mod_values[1]       = NULL;
358
359                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
360                                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
361                                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
362                                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
363                                 attrs[num_attrs-1]->mod_type            = "l";
364                                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
365                                 attrs[num_attrs-1]->mod_values[0]       = strdup(city);
366                                 attrs[num_attrs-1]->mod_values[1]       = NULL;
367
368                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
369                                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
370                                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
371                                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
372                                 attrs[num_attrs-1]->mod_type            = "st";
373                                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
374                                 attrs[num_attrs-1]->mod_values[0]       = strdup(state);
375                                 attrs[num_attrs-1]->mod_values[1]       = NULL;
376
377                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
378                                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
379                                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
380                                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
381                                 attrs[num_attrs-1]->mod_type            = "postalcode";
382                                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
383                                 attrs[num_attrs-1]->mod_values[0]       = strdup(zipcode);
384                                 attrs[num_attrs-1]->mod_values[1]       = NULL;
385                         }
386                 }
387
388                 if ( (!strcasecmp(v->prop[i].name, "tel"))
389                    ||(!strncasecmp(v->prop[i].name, "tel;", 4)) ) {
390                         ++num_phones;
391                         /* The first 'tel' property creates the 'telephoneNumber' attribute */
392                         if (num_phones == 1) {
393                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
394                                 phone_attr = num_attrs-1;
395                                 attrs[phone_attr] = malloc(sizeof(LDAPMod));
396                                 memset(attrs[phone_attr], 0, sizeof(LDAPMod));
397                                 attrs[phone_attr]->mod_op               = LDAP_MOD_ADD;
398                                 attrs[phone_attr]->mod_type             = "telephoneNumber";
399                                 attrs[phone_attr]->mod_values           = malloc(2 * sizeof(char *));
400                                 attrs[phone_attr]->mod_values[0]        = strdup(v->prop[i].value);
401                                 attrs[phone_attr]->mod_values[1]        = NULL;
402                         }
403                         /* Subsequent 'tel' properties *add to* the 'telephoneNumber' attribute */
404                         else {
405                                 attrs[phone_attr]->mod_values = realloc(attrs[phone_attr]->mod_values,
406                                                                      num_phones * sizeof(char *));
407                                 attrs[phone_attr]->mod_values[num_phones-1]
408                                                                         = strdup(v->prop[i].value);
409                                 attrs[phone_attr]->mod_values[num_phones]
410                                                                         = NULL;
411                         }
412                 }
413
414
415                 if ( (!strcasecmp(v->prop[i].name, "email"))
416                    ||(!strcasecmp(v->prop[i].name, "email;internet")) ) {
417         
418                         ++num_emails;
419                         lprintf(CTDL_DEBUG, "email addr %d\n", num_emails);
420
421                         /* The first email address creates the 'mail' attribute */
422                         if (num_emails == 1) {
423                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
424                                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
425                                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
426                                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
427                                 attrs[num_attrs-1]->mod_type            = "mail";
428                                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
429                                 attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
430                                 attrs[num_attrs-1]->mod_values[1]       = NULL;
431                         }
432                         /* The second email address creates the 'alias' attribute */
433                         else if (num_emails == 2) {
434                                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
435                                 alias_attr = num_attrs-1;
436                                 attrs[alias_attr] = malloc(sizeof(LDAPMod));
437                                 memset(attrs[alias_attr], 0, sizeof(LDAPMod));
438                                 attrs[alias_attr]->mod_op               = LDAP_MOD_ADD;
439                                 attrs[alias_attr]->mod_type             = "alias";
440                                 attrs[alias_attr]->mod_values           = malloc(2 * sizeof(char *));
441                                 attrs[alias_attr]->mod_values[0]        = strdup(v->prop[i].value);
442                                 attrs[alias_attr]->mod_values[1]        = NULL;
443                         }
444                         /* Subsequent email addresses *add to* the 'alias' attribute */
445                         else if (num_emails > 2) {
446                                 attrs[alias_attr]->mod_values = realloc(attrs[alias_attr]->mod_values,
447                                                                      num_emails * sizeof(char *));
448                                 attrs[alias_attr]->mod_values[num_emails-2]
449                                                                         = strdup(v->prop[i].value);
450                                 attrs[alias_attr]->mod_values[num_emails-1]
451                                                                         = NULL;
452                         }
453
454
455                 }
456
457                 /* Calendar free/busy URL (take the first one we find, but if a subsequent
458                  * one contains the "pref" designation then we go with that instead.)
459                  */
460                 if ( (!strcasecmp(v->prop[i].name, "fburl"))
461                    ||(!strncasecmp(v->prop[i].name, "fburl;", 6)) ) {
462                         if ( (strlen(calFBURL) == 0)
463                            || (!strncasecmp(v->prop[i].name, "fburl;pref", 10)) ) {
464                                 safestrncpy(calFBURL, v->prop[i].value, sizeof calFBURL);
465                         }
466                 }
467
468         }
469         vcard_free(v);  /* Don't need this anymore. */
470
471         /* "sn" (surname) based on info in vCard */
472         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
473         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
474         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
475         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
476         attrs[num_attrs-1]->mod_type            = "sn";
477         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
478         attrs[num_attrs-1]->mod_values[0]       = strdup(sn);
479         attrs[num_attrs-1]->mod_values[1]       = NULL;
480
481         /* "givenname" (first name) based on info in vCard */
482         if (strlen(givenname) == 0) strcpy(givenname, "_");
483         if (strlen(sn) == 0) strcpy(sn, "_");
484         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
485         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
486         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
487         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
488         attrs[num_attrs-1]->mod_type            = "givenname";
489         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
490         attrs[num_attrs-1]->mod_values[0]       = strdup(givenname);
491         attrs[num_attrs-1]->mod_values[1]       = NULL;
492
493         /* "uid" is a Kolab compatibility thing.  We just do cituser@citnode */
494         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
495         attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
496         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
497         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
498         attrs[num_attrs-1]->mod_type            = "uid";
499         attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
500         attrs[num_attrs-1]->mod_values[0]       = strdup(uid);
501         attrs[num_attrs-1]->mod_values[1]       = NULL;
502
503         /* Add a "cn" (Common Name) attribute based on the user's screen name,
504          * but only there was no 'fn' (full name) property in the vCard 
505          */
506         if (!have_cn) {
507                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
508                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
509                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
510                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
511                 attrs[num_attrs-1]->mod_type            = "cn";
512                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
513                 attrs[num_attrs-1]->mod_values[0]       = strdup(msg->cm_fields['A']);
514                 attrs[num_attrs-1]->mod_values[1]       = NULL;
515         }
516
517         /* Add a "calFBURL" attribute if a calendar free/busy URL exists */
518         if (strlen(calFBURL) > 0) {
519                 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
520                 attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
521                 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
522                 attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
523                 attrs[num_attrs-1]->mod_type            = "calFBURL";
524                 attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
525                 attrs[num_attrs-1]->mod_values[0]       = strdup(calFBURL);
526                 attrs[num_attrs-1]->mod_values[1]       = NULL;
527         }
528         
529         /* The last attribute must be a NULL one. */
530         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
531         attrs[num_attrs - 1] = NULL;
532         
533         lprintf(CTDL_DEBUG, "Calling ldap_add_s()\n");
534         begin_critical_section(S_LDAP);
535         i = ldap_add_s(dirserver, this_dn, attrs);
536         end_critical_section(S_LDAP);
537
538         /* If the entry already exists, repopulate it instead */
539         if (i == LDAP_ALREADY_EXISTS) {
540                 for (j=0; j<(num_attrs-1); ++j) {
541                         attrs[j]->mod_op = LDAP_MOD_REPLACE;
542                 }
543                 lprintf(CTDL_DEBUG, "Calling ldap_modify_s()\n");
544                 begin_critical_section(S_LDAP);
545                 i = ldap_modify_s(dirserver, this_dn, attrs);
546                 end_critical_section(S_LDAP);
547         }
548
549         if (i != LDAP_SUCCESS) {
550                 lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
551                         ldap_err2string(i), i);
552         }
553
554         lprintf(CTDL_DEBUG, "Freeing attributes\n");
555         /* Free the attributes */
556         for (i=0; i<num_attrs; ++i) {
557                 if (attrs[i] != NULL) {
558
559                         /* First, free the value strings */
560                         if (attrs[i]->mod_values != NULL) {
561                                 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
562                                         free(attrs[i]->mod_values[j]);
563                                 }
564                         }
565
566                         /* Free the value strings pointer list */       
567                         if (attrs[i]->mod_values != NULL) {
568                                 free(attrs[i]->mod_values);
569                         }
570
571                         /* Now free the LDAPMod struct itself. */
572                         free(attrs[i]);
573                 }
574         }
575         free(attrs);
576         lprintf(CTDL_DEBUG, "LDAP write operation complete.\n");
577 }
578
579
580 #endif                          /* HAVE_LDAP */
581
582
583 /*
584  * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
585  */
586 char *serv_ldap_init(void)
587 {
588 #ifdef HAVE_LDAP
589         CtdlRegisterCleanupHook(serv_ldap_cleanup);
590
591         if (strlen(config.c_ldap_host) > 0) {
592                 CtdlConnectToLdap();
593         }
594
595 #endif                          /* HAVE_LDAP */
596         return "$Id$";
597 }