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