36bdeb7c25b81fcd5c679355f3d698add65127ce
[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
46 #ifdef HAVE_LDAP
47
48 #include <ldap.h>
49
50 LDAP *dirserver = NULL;
51
52 /*
53  * LDAP connector cleanup function
54  */
55 void serv_ldap_cleanup(void)
56 {
57         if (!dirserver) return;
58
59         lprintf(7, "Unbinding from directory server\n");
60         ldap_unbind(dirserver);
61         dirserver = NULL;
62 }
63
64 #endif                          /* HAVE_LDAP */
65
66
67 void CtdlConnectToLdap(void) {
68         int i;
69         int ldap_version = 3;
70
71         lprintf(7, "Connecting to LDAP server %s:%d...\n",
72                 config.c_ldap_host, config.c_ldap_port);
73
74         dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
75         if (dirserver == NULL) {
76                 lprintf(3, "Could not connect to %s:%d : %s\n",
77                         config.c_ldap_host,
78                         config.c_ldap_port,
79                         strerror(errno));
80                 return;
81         }
82
83         ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
84
85         lprintf(7, "Binding to %s\n", config.c_ldap_bind_dn);
86
87         i = ldap_simple_bind_s(dirserver,
88                                 config.c_ldap_bind_dn,
89                                 config.c_ldap_bind_pw
90         );
91         if (i != LDAP_SUCCESS) {
92                 lprintf(3, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
93                 dirserver = NULL;       /* FIXME disconnect from ldap */
94         }
95 }
96
97
98
99 /*
100  * Write (add, or change if already exists) a directory entry to the
101  * LDAP server, based on the information supplied in a vCard.
102  */
103 void ctdl_vcard_to_ldap(struct CtdlMessage *msg) {
104         struct vCard *v = NULL;
105         int i, j;
106         char this_dn[SIZ];
107         LDAPMod **attrs = NULL;
108         int num_attrs = 0;
109
110         if (dirserver == NULL) return;
111         if (msg == NULL) return;
112         if (msg->cm_fields['M'] == NULL) return;
113         if (msg->cm_fields['A'] == NULL) return;
114         if (msg->cm_fields['N'] == NULL) return;
115
116         sprintf(this_dn, "cn=%s,ou=%s,%s",
117                 msg->cm_fields['A'],
118                 msg->cm_fields['N'],
119                 config.c_ldap_base_dn
120         );
121
122         /* The first LDAP attribute will be an 'objectclass' list.  Citadel
123          * doesn't do anything with this.  It's just there for compatibility
124          * with Kolab.
125          */
126         num_attrs = 1;
127         attrs = mallok( (sizeof(LDAPMod *) * num_attrs) );
128         attrs[0] = mallok(sizeof(LDAPMod));
129         memset(attrs[0], 0, sizeof(LDAPMod));
130         attrs[0]->mod_op        = LDAP_MOD_ADD;
131         attrs[0]->mod_type      = "objectclass";
132         attrs[0]->mod_values    = mallok(5 * sizeof(char *));
133         attrs[0]->mod_values[0] = strdoop("inetOrgPerson");
134         attrs[0]->mod_values[1] = strdoop("organizationalPerson");
135         attrs[0]->mod_values[2] = strdoop("person");
136         attrs[0]->mod_values[3] = strdoop("Top");
137         attrs[0]->mod_values[4] = NULL;
138         
139         /* Convert the vCard fields to LDAP properties */
140         v = vcard_load(msg->cm_fields['M']);
141         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
142 /*
143                 v->prop[i].name
144                 v->prop[i].value
145  */
146         }
147         vcard_free(v);  /* Don't need this anymore. */
148
149         /* The last attribute must be a NULL one. */
150         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
151         attrs[num_attrs - 1] = NULL;
152         
153         lprintf(9, "this_dn: <%s>\n", this_dn);
154
155         begin_critical_section(S_LDAP);
156         i = ldap_add_s(dirserver, this_dn, attrs);
157         end_critical_section(S_LDAP);
158         if (i != LDAP_SUCCESS) {
159                 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
160                         ldap_err2string(i), i);
161         }
162
163         /* Free the attributes */
164         for (i=0; i<num_attrs; ++i) {
165                 if (attrs[i] != NULL) {
166
167                         /* First, free the value strings */
168                         if (attrs[i]->mod_values != NULL) {
169                                 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
170                                         phree(attrs[i]->mod_values[j]);
171                                 }
172                         }
173
174                         /* Free the value strings pointer list */       
175                         if (attrs[i]->mod_values != NULL) {
176                                 phree(attrs[i]->mod_values);
177                         }
178
179                         /* Now free the LDAPMod struct itself. */
180                         phree(attrs[i]);
181                 }
182         }
183         phree(attrs);
184 }
185
186
187
188
189 /*
190  * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
191  */
192 char *serv_ldap_init(void)
193 {
194 #ifdef HAVE_LDAP
195         CtdlRegisterCleanupHook(serv_ldap_cleanup);
196
197         if (strlen(config.c_ldap_host) > 0) {
198                 CtdlConnectToLdap();
199         }
200
201 #endif                          /* HAVE_LDAP */
202         return "$Id$";
203 }