Jabberd2 patch fixes. More samples of configuration.
[vcard2ldap.git] / src / v2l_conn.c
blob7bc6e7631bd6be09d4417f4d675fbb66fb107758
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <time.h>
20 #include <ldap.h>
22 #ifndef _V2L_JABBER2
23 #include <jabberd.h>
24 #else
25 #include <pthread.h>
26 #include "../util/util.h"
28 #define log_warn log_debug
29 #define log_error log_debug
30 typedef pool_t pool;
31 #endif
33 #include <v2l_conn.h>
34 #include <v2l_config.h>
36 #define V2L_CONN_LIFETIME 30 /* seconds */
37 #define V2L_POLL_INTERVAL 1 /* seconds */
39 #define LOG_ERROR_MEM log_error(ZONE, "Unable to allocate memory")
41 /* Global hashtable of all currently active LDAP connections */
42 static xht global_conn_list = NULL;
44 static v2l_LdapConn *_v2l_create_conn (char *host, int port, const char *binddn,
45 const char *user, const char *passwd, int add_in_list);
46 /* NOTE: Allocated memory must be freed by the caller */
47 static char *_v2l_ldap_get_passwd (v2l_Config * self, const char * user);
48 static void _v2l_free_conn (xht h, const char *user, void *val);
49 static int _v2l_count_attrs (v2l_LdapRequest * req);
50 static v2l_LdapRequest *_v2l_add_attr (v2l_LdapRequest *req, LDAPMod * attr);
51 static void _v2l_add_conn (v2l_LdapConn * ldap_conn);
52 static void _v2l_free_callback (xht h, const char *key, void *val,
53 void *arg);
55 /* Free connections when its time has expired (periodic thread) */
56 static void _v2l_purge_conn_callback (void *arg);
57 /* Free connections in a hashtable, walker function */
58 static void _v2l_free_expired (xht h, const char *key, void *val,
59 void *arg);
61 /* A thread for wait for LDAP results */
62 static int _v2l_ldap_wait_callback(void *arg);
64 #ifdef _V2L_JABBER2
65 static void *_v2l_ldap_wait_callback_g (void *arg);
66 static void *_v2l_purge_conn_callback_g (void *arg);
67 #endif
69 /* public api */
71 v2l_LdapConn *
72 v2l_get_conn (v2l_Config *self, const char *user)
74 v2l_LdapConn *user_conn;
76 user_conn = (v2l_LdapConn *) xhash_get (global_conn_list, user);
78 if(user_conn == NULL)
80 char *passwd, *binddn;
82 /* Get the user password for connecting him to LDAP server */
83 passwd = _v2l_ldap_get_passwd (self, user);
85 /* user exists? */
86 if (passwd == NULL)
88 log_error (ZONE, "User \"%s\" not found in the directory", user);
89 return NULL;
92 binddn = (char *) malloc (sizeof(char) * (strlen (self->suffix) +
93 strlen (self->uniqattr) + strlen (user) + 3));
95 if (binddn == NULL)
97 log_error (ZONE, "Unable to allocate memory");
98 free (passwd);
99 return NULL;
102 sprintf (binddn, "%s=%s,%s", self->uniqattr, user, self->suffix);
104 log_debug (ZONE, "Attempting to connect with DN: %s", binddn);
106 user_conn = _v2l_create_conn (self->host, self->port, binddn, user,
107 passwd, 1);
109 free (passwd);
110 free (binddn);
112 } /* user_conn == NULL */
114 return user_conn;
117 v2l_LdapConn *
118 v2l_get_master_conn (v2l_Config *self)
120 return _v2l_create_conn (self->host, self->port, self->binddn, V2L_ADMIN,
121 self->bindpw, 0);
124 void
125 v2l_free_allconn ()
127 xhash_walk (global_conn_list, _v2l_free_callback, NULL);
128 xhash_free (global_conn_list);
131 void
132 v2l_ldap_sync (v2l_LdapEvt *evt_res)
134 #ifndef _V2L_JABBER2
135 pth_event_t evt;
137 evt = pth_event (PTH_EVENT_FUNC, &_v2l_ldap_wait_callback,
138 (void *) evt_res, pth_time (V2L_POLL_INTERVAL, 0));
139 pth_wait (evt);
140 #else
141 pthread_t thr;
142 int rc;
144 rc = pthread_create(&thr, NULL, _v2l_ldap_wait_callback_g, (void *) evt_res);
146 if (rc != 0)
148 log_error (ZONE, "Thread create failed: %d", rc);
149 return;
152 pthread_join(thr, NULL);
153 #endif
157 v2l_ldap_search (char *dn, char *suffix, char **attrs, int subtree,
158 v2l_LdapEvt *evt_res)
160 int scope;
162 scope = subtree ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_ONELEVEL;
164 return ldap_search_ext (evt_res->ld, suffix, scope,
165 dn, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
166 &(evt_res->msgid));
170 v2l_ldap_modify (char *dn, LDAPMod **attrs, v2l_LdapEvt *evt_res)
172 return ldap_modify_ext (evt_res->ld, dn, attrs, NULL, NULL,
173 &(evt_res->msgid));
177 v2l_request_record (v2l_Config *self, v2l_LdapConn *curr_conn,
178 v2l_LdapRequest *req)
180 LDAPMod **attrs;
181 int i, nbmod, ret;
182 v2l_LdapRequest *cur_req, *cur_temp;
183 v2l_LdapEvt *evt_res;
185 if (req == NULL)
187 log_warn (ZONE, "LDAP request is NULL? I cannot record anything");
188 return 1;
191 nbmod = _v2l_count_attrs (req);
193 attrs = (LDAPMod **) malloc ((nbmod + 1) * sizeof (LDAPMod *));
195 if (attrs == NULL)
197 LOG_ERROR_MEM;
198 return 0;
201 /* to wait for the results */
202 evt_res = (v2l_LdapEvt *) malloc (sizeof (v2l_LdapEvt));
204 if (evt_res == NULL)
206 LOG_ERROR_MEM;
207 free (attrs);
208 return 0;
211 for (i = 0; i < nbmod; i++)
213 attrs[i] = (LDAPMod *) malloc (sizeof (LDAPMod));
215 if (attrs[i] == NULL)
217 while (--i >= 0)
219 free (attrs[i]);
222 LOG_ERROR_MEM;
223 free (attrs);
224 free (evt_res);
225 return 0;
229 for (cur_req = req, i = 0; i < nbmod; cur_req = cur_req->next, i++)
231 memcpy (attrs[i], cur_req->attr, sizeof (LDAPMod));
232 log_debug (ZONE, "Element \"%s\" (%d) in the LDAP request: %s",
233 attrs[i]->mod_type, i, attrs[i]->mod_values[0]);
236 attrs[nbmod] = NULL;
238 log_debug (ZONE, "LDAP attempting to modify \"%s\" with dn \"%s\"",
239 curr_conn->user, curr_conn->binddn);
241 evt_res->ld = curr_conn->ld;
242 evt_res->rc = v2l_ldap_modify (curr_conn->binddn, attrs, evt_res);
244 if (evt_res->rc != LDAP_SUCCESS)
246 log_error (ZONE, "LDAP error attempting to modify user info: %s",
247 ldap_err2string (evt_res->rc));
248 ret = 0;
250 else
252 v2l_ldap_sync (evt_res);
253 ret = 1;
256 ldap_msgfree (evt_res->result);
257 free (evt_res);
259 for (cur_req = req, i = 0; i < nbmod; i++)
261 cur_temp = cur_req;
262 cur_req = cur_req->next;
264 free (attrs[i]);
265 free (cur_temp->attr->mod_values[0]);
266 free (cur_temp->attr->mod_values);
267 free (cur_temp->attr);
268 free (cur_temp);
271 free (attrs);
273 return ret;
276 v2l_LdapRequest *
277 v2l_add_attr_str (v2l_LdapRequest *req, const char *attr, const char *str)
279 LDAPMod *mod;
281 mod = (LDAPMod *) malloc (sizeof (LDAPMod));
283 if (mod == NULL)
285 return NULL;
288 mod->mod_op = LDAP_MOD_REPLACE;
289 mod->mod_type = (char *) attr;
291 mod->mod_values = (char **) malloc (2 * sizeof (char *));
293 if (mod->mod_values == NULL)
295 free (mod);
296 return NULL;
299 mod->mod_values[0] = (char *) malloc ((strlen (str) + 1) * sizeof (char));
301 if (mod->mod_values[0] == NULL)
303 free (mod->mod_values);
304 free (mod);
305 return NULL;
308 memcpy (mod->mod_values[0], str, strlen (str) + 1);
309 mod->mod_values[1] = NULL;
311 return _v2l_add_attr (req, mod);
314 /************************/
315 /* public api ends here */
317 static v2l_LdapConn *
318 _v2l_create_conn (char *host, int port, const char *binddn,
319 const char *user, const char *passwd, int add_in_list)
321 LDAP *ld;
322 v2l_LdapConn *ldap_conn;
323 int version; /* LDAP protocol version */
324 int rc;
325 char *tmp_str, *ptr;
326 pool p;
328 if ((ld = ldap_init (host, port)) == NULL)
330 log_error (ZONE, "Unable to init LDAP");
331 return NULL;
334 /* XXX */
335 version = LDAP_VERSION3;
336 /***/
338 ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
340 if ((rc = ldap_simple_bind_s (ld, binddn, passwd)) != LDAP_SUCCESS)
342 log_error (ZONE,
343 "LDAP simple bind error : %s", ldap_err2string (rc));
344 return NULL;
347 tmp_str = (char *) malloc (sizeof (char) * (strlen (binddn) + 1));
349 if (tmp_str == NULL)
351 LOG_ERROR_MEM;
352 return NULL;
355 strcpy (tmp_str, binddn);
356 ptr = strtok (tmp_str, ", ");
358 p = pool_new ();
359 ldap_conn = pmalloc (p, sizeof (v2l_LdapConn));
360 ldap_conn->p = p;
361 ldap_conn->ld = ld;
363 ldap_conn->binddn =
364 pmalloc (ldap_conn->p, sizeof (char) * (strlen (binddn) + 1));
365 strcpy (ldap_conn->binddn, binddn);
367 ldap_conn->entry =
368 pmalloc (ldap_conn->p, sizeof (char) * (strlen (tmp_str) + 1));
369 strcpy (ldap_conn->entry, tmp_str);
371 ldap_conn->user =
372 pmalloc (ldap_conn->p, sizeof (char) * (strlen (user) + 1));
373 strcpy (ldap_conn->user, user);
375 ldap_conn->creation_time = time (NULL); /* timestamp */
376 ldap_conn->exists = 1;
378 if (add_in_list == 1) /* Add it to global_conn_list */
380 _v2l_add_conn (ldap_conn);
383 free (tmp_str);
384 return ldap_conn;
387 static char *
388 _v2l_ldap_get_passwd(v2l_Config *self, const char *user)
390 LDAPMessage *e;
391 v2l_LdapEvt *evt_res;
392 char *data, *filter, **vals, *attrs[2] = {"userPassword", NULL};
394 evt_res = (v2l_LdapEvt *) malloc(sizeof(v2l_LdapEvt));
396 if (evt_res == NULL)
398 LOG_ERROR_MEM;
399 return NULL;
402 filter = (char *) malloc (sizeof(char)*
403 (strlen(self->uniqattr) + strlen(user) + 2));
405 if (filter == NULL)
407 LOG_ERROR_MEM;
408 free (evt_res);
409 return NULL;
412 sprintf(filter, "%s=%s", self->uniqattr, user);
413 evt_res->ld = self->master_conn->ld;
415 evt_res->rc = v2l_ldap_search (filter, self->suffix, attrs, 0, evt_res);
416 free (filter);
418 if (evt_res->rc != LDAP_SUCCESS)
420 log_error(ZONE,
421 "LDAP error attempting to retrieve \"%s\"'s password: %s",
422 user, ldap_err2string (evt_res->rc));
423 free (evt_res);
424 return NULL;
427 v2l_ldap_sync (evt_res);
428 data = NULL;
430 if (ldap_count_entries(evt_res->ld, evt_res->result) == 1)
432 e = ldap_first_entry(evt_res->ld, evt_res->result);
433 vals = ldap_get_values(evt_res->ld, e, "userPassword");
435 if (vals == NULL)
437 log_debug(ZONE, "User has no password!");
438 data = (char *) malloc(sizeof(char));
440 if (data == NULL)
442 LOG_ERROR_MEM;
443 ldap_msgfree(evt_res->result);
444 free(evt_res);
445 return NULL;
448 data[0] = 0;
450 else
452 data = (char *) malloc(sizeof(char) * (strlen(vals[0]) + 1));
454 if (data != NULL)
456 strcpy(data, vals[0]);
459 ldap_value_free(vals);
462 ldap_msgfree(evt_res->result);
465 free(evt_res);
466 return data;
469 /* count the number of LDAPMod in the structure */
470 static int
471 _v2l_count_attrs (v2l_LdapRequest * req)
473 v2l_LdapRequest *ptr;
474 int nbmod;
476 for (nbmod = 0, ptr = req; ptr != NULL; ptr = ptr->next, nbmod++);
478 return nbmod;
481 static v2l_LdapRequest *
482 _v2l_add_attr (v2l_LdapRequest *req, LDAPMod * attr)
484 if (attr == NULL)
486 log_warn (ZONE, "LDAP attribute is NULL? I cannot add anything");
487 return NULL;
490 if (req == NULL)
492 req = (v2l_LdapRequest *) malloc (sizeof (v2l_LdapRequest));
494 if (req == NULL)
496 LOG_ERROR_MEM;
497 return NULL;
500 req->attr = attr;
501 req->next = NULL;
503 else
505 v2l_LdapRequest *ptr;
506 v2l_LdapRequest *new_req;
508 new_req = (v2l_LdapRequest *) malloc (sizeof (v2l_LdapRequest));
510 if (new_req == NULL)
512 LOG_ERROR_MEM;
513 return NULL;
516 new_req->attr = attr;
517 new_req->next = NULL;
519 for (ptr = req; ptr->next != NULL; ptr = ptr->next);
521 ptr->next = new_req;
524 return req;
527 static int
528 _v2l_ldap_wait_callback(void *arg)
530 v2l_LdapEvt *evt_res = (v2l_LdapEvt *) arg;
531 LDAPMessage *result;
532 int rc;
534 rc = ldap_result (evt_res->ld, evt_res->msgid, 1, NULL, &result);
536 if (rc == -1)
538 log_error(ZONE, "LDAP result error %s",
539 ldap_err2string(rc));
540 evt_res->result = NULL;
541 evt_res->rc = -1;
542 return 1;
545 if ((rc == LDAP_RES_ADD)
546 || (rc == LDAP_RES_MODIFY)
547 || (rc == LDAP_RES_SEARCH_RESULT)
548 || (rc == LDAP_RES_SEARCH_ENTRY)
549 || (rc == LDAP_RES_DELETE))
551 evt_res->result = result;
552 evt_res->rc = rc;
553 return 1;
556 return 0; /* still waiting */
559 #ifdef _V2L_JABBER2
560 static void *
561 _v2l_ldap_wait_callback_g (void *arg)
563 while (!_v2l_ldap_wait_callback (arg))
565 sleep (V2L_POLL_INTERVAL);
568 return NULL;
570 #endif
572 static void
573 _v2l_free_callback (xht h, const char *key, void *val, void *arg)
575 _v2l_free_conn (h, key, val);
578 static void
579 _v2l_free_conn (xht h, const char *user, void *val)
581 v2l_LdapConn *temp_conn = (v2l_LdapConn *) val;
583 log_debug (ZONE, "Freeing LDAP connection for user \"%s\"", user);
585 xhash_zap (h, user);
586 ldap_unbind (temp_conn->ld);
587 pool_free (temp_conn->p);
590 static void
591 _v2l_free_expired (xht h, const char *key, void *val, void *arg)
593 v2l_LdapConn *temp_conn = (v2l_LdapConn *) val;
595 /* kill connections older than V2L_CONN_LIFETIME */
596 if ((time (NULL) - temp_conn->creation_time) > (time_t) V2L_CONN_LIFETIME)
598 _v2l_free_conn (h, key, val);
602 static void
603 _v2l_purge_conn_callback (void *arg)
605 log_debug (ZONE, "Checking connections lifetime");
607 while (1)
609 /* global_conn_list has been freed? */
610 if (global_conn_list != NULL)
612 xhash_walk (global_conn_list, _v2l_free_expired, NULL);
614 #ifndef _V2L_JABBER2
615 pth_sleep (V2L_CONN_LIFETIME);
616 #else
617 sleep (V2L_CONN_LIFETIME);
618 #endif
623 #ifdef _V2L_JABBER2
624 static void *
625 _v2l_purge_conn_callback_g (void *arg)
627 _v2l_purge_conn_callback(arg);
628 return NULL;
630 #endif
632 static void
633 _v2l_add_conn (v2l_LdapConn * ldap_conn)
635 #ifndef _V2L_JABBER2
636 pth_attr_t attr;
637 #else
638 pthread_t thr;
639 pthread_attr_t attr;
640 int rc;
641 #endif
643 if (global_conn_list == NULL)
646 log_debug (ZONE, "global_conn_list hashtable is not initialized yet");
648 global_conn_list = xhash_new (509);
650 /* spawn the thread which deletes expired connections */
651 #ifndef _V2L_JABBER2
652 attr = pth_attr_new ();
653 pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
654 pth_spawn (attr, (void *) _v2l_purge_conn_callback, NULL);
655 pth_attr_destroy (attr);
656 #else
657 pthread_attr_init(&attr);
658 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
660 rc = pthread_create (&thr, &attr, _v2l_purge_conn_callback_g, NULL);
661 pthread_attr_destroy(&attr);
663 if (rc != 0)
665 log_error (ZONE, "Thread create failed: %d", rc);
667 #endif
670 xhash_put (global_conn_list, ldap_conn->user, (void *) ldap_conn);