More Doxygen. API cleanup.
[vcard2ldap.git] / src / v2l_conn.c
blob6f415a811a18ce956f1360b610df16ba7d80d30c
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 /*! \file v2l_conn.c
18 \brief Handling the LDAP directory. Implementation
21 #include <stdlib.h>
23 #ifndef _V2L_JABBER2
24 #include <jabberd.h>
25 #else
26 #include <pthread.h>
27 #include "../util/util.h"
29 #define log_warn log_debug
30 #define log_error log_debug
31 typedef pool_t pool;
32 #endif
34 #include <v2l_conn.h>
36 /*! \brief Lifetime of an ephemeral connection, in seconds
37 The recall connections thread period.
39 #define V2L_CONN_LIFETIME 30
41 /*! How often thread checks for LDAP results, in seconds */
42 #define V2L_POLL_INTERVAL 1
44 #define LOG_ERROR_MEM log_error(ZONE, "Unable to allocate memory")
46 /*! Global hashtable of all currently active LDAP connections */
47 static xht V2L_CONN_LIST = NULL;
49 /*! \brief Creates new LDAP connection
50 \param host LDAP hostname.
51 \param port LDAP port.
52 \param binddn Bind DN.
53 \param user User name.
54 \param passwd User password.
55 \param expire Boolean, ephemeral connection?
56 \return LDAP connectior or NULL if error.
58 static v2l_LdapConn *_v2l_create_conn (char *host, int port, const char *binddn,
59 const char *user, const char *passwd, int expire);
61 /*! \brief Gets user password
62 Retrieves user password from LDAP directory using master connection.
63 \note Allocated memory must be freed by the caller.
64 \param self Module config.
65 \param user User name.
66 \pre self is valid, user is not NULL
67 \return user password string or NULL if doesn't exist or error.
69 static char *_v2l_ldap_get_passwd (v2l_Config *self, const char *user);
71 /*! \brief Closes and frees a connection.
72 Utility function, common code for two hash walker functions.
73 \param h Jabberd hash. Hash table "username"->connection.
74 \param user User name.
75 \param val deference pointer to connection.
76 \sa _v2l_free_walker
77 \sa _v2l_free_expired_walker
79 static void _v2l_free_conn (xht h, const char *user, void *val);
81 /*! \brief Gets the number or attributes in a LDAP request.
82 \param[in,out] req The SLL of LDAP requests.
83 \return number of attributes, 0 if req is NULL.
85 static int _v2l_count_attrs (v2l_LdapRequest *req);
87 /*! \brief Modifies an entry in the LDAP directory
88 Utility function.
89 \param dn Entry bind DN
90 \param attrs List of attributes changed.
91 \param[in,out] evt_res Control and info parameter.
92 \return LDAP error code.
94 static int _v2l_ldap_modify (char *dn, LDAPMod **attrs, v2l_LdapEvt *evt_res);
96 /*! \brief Searchs and retrieves an entry from the LDAP directory
97 Utility function.
98 \param dn Entry bind DN.
99 \param suffix LDAP suffix
100 \param[out] attrs Entry attributes.
101 \param subtree boolean. Search in a one level or in entire subtree?
102 \param[in,out] evt_res Control and info parameter.
103 \return LDAP error code.
105 static int _v2l_ldap_search (char *dn, char *suffix, char **attrs, int subtree,
106 v2l_LdapEvt *evt_res);
108 /*! \brief Waits for LDAP results
109 Operations on the directory are asynchronous. This is a sync wait function.
110 \param \param[in,out] evt_res Control and info parameter.
112 static void _v2l_ldap_sync (v2l_LdapEvt *evt_res);
114 /*! \brief Adds LDAP attr to request.
115 Utility function, hides OpenLDAP API to upper level.
116 \param[in,out] req The SLL of LDAP requests.
117 \param attr The attr, LDAPMod pointer.
118 \return The list of requests + the last added. The list of request if error.
120 static v2l_LdapRequest *_v2l_add_attr (v2l_LdapRequest *req, LDAPMod *attr);
122 /*! \brief Adds a conn to the global list.
123 If list is empty, initializes it and starts the thread who recalls expired
124 connections.
125 \param ldap_conn New connection.
127 static void _v2l_add_conn (v2l_LdapConn *ldap_conn);
129 /*! \brief Frees connection unconditionally.
130 Utility function. Hash walker function.
131 See jabberd API for details.
133 static void _v2l_free_walker (xht h, const char *key, void *val,
134 void *arg);
136 /*! \brief Frees connections when its slice time expired (periodic thread)
137 \dot
138 digraph G {
139 rankdir = LR
140 node [shape = ellipse, fontname = Helvetica fontsize = 12]
141 {node[style = filled, fontsize = 16, bgcolor = grey] sleep}
142 {node[shape = circle, label = delete] free}
143 {node[label = "expired?" ] is_expired}
144 {node[label = "conn left?" ] any_conn}
145 sleep -> sleep
146 sleep -> any_conn [label = " "]
147 any_conn -> sleep [label = no]
148 any_conn -> is_expired [label = yes]
149 is_expired -> free [label = yes]
150 is_expired -> any_conn [label = no]
151 free -> any_conn
153 \enddot
154 \param arg Unused.
156 static void *_v2l_purge_conn_callback (void *arg);
158 /*! \brief Frees expired connections.
159 Utility function. Hash walker function.
160 See jabberd API for details.
162 static void _v2l_free_expired_walker (xht h, const char *key, void *val,
163 void *arg);
165 /*! A thread for active wait for LDAP results */
166 static int _v2l_ldap_wait_callback(void *arg);
168 #ifdef _V2L_JABBER2
169 static void *_v2l_ldap_wait_callback_g (void *arg);
170 #endif
172 /* public api */
174 v2l_LdapConn *
175 v2l_get_conn (v2l_Config *self, const char *user)
177 v2l_LdapConn *user_conn;
179 user_conn = (v2l_LdapConn *) xhash_get (V2L_CONN_LIST, user);
181 if(user_conn == NULL)
183 char *passwd, *binddn;
185 /* Get the user password for connecting him to LDAP server */
186 passwd = _v2l_ldap_get_passwd (self, user);
188 /* user exists? */
189 if (passwd == NULL)
191 log_error (ZONE, "User \"%s\" not found in the directory", user);
192 return NULL;
195 binddn = (char *) malloc (sizeof(char) * (strlen (self->suffix) +
196 strlen (self->uniqattr) + strlen (user) + 3));
198 if (binddn == NULL)
200 LOG_ERROR_MEM;
201 free (passwd);
202 return NULL;
205 sprintf (binddn, "%s=%s,%s", self->uniqattr, user, self->suffix);
207 log_debug (ZONE, "Attempting to connect with DN: %s", binddn);
209 user_conn = _v2l_create_conn (self->host, self->port, binddn, user,
210 passwd, 1);
212 free (passwd);
213 free (binddn);
215 } /* user_conn == NULL */
217 return user_conn;
220 v2l_LdapConn *
221 v2l_get_master_conn (v2l_Config *self)
223 return _v2l_create_conn (self->host, self->port, self->binddn, V2L_ADMIN,
224 self->bindpw, 0);
227 void
228 v2l_free_allconn ()
230 xhash_walk (V2L_CONN_LIST, _v2l_free_walker, NULL);
231 xhash_free (V2L_CONN_LIST);
234 v2l_LdapEvt *
235 v2l_ldap_get_entry (v2l_Config *self, v2l_LdapConn *curr_conn)
237 v2l_LdapEvt *evt_res;
238 int rc;
240 evt_res = (v2l_LdapEvt *) malloc (sizeof (v2l_LdapEvt));
242 if (evt_res == NULL)
244 LOG_ERROR_MEM;
245 return NULL;
248 evt_res->ld = curr_conn->ld;
250 rc = _v2l_ldap_search (curr_conn->entry, self->suffix, NULL, 1, evt_res);
252 if (rc != LDAP_SUCCESS)
254 log_error (ZONE, "LDAP error attempting to retrieve user info: %s",
255 ldap_err2string (rc));
256 free (evt_res);
257 return NULL;
260 _v2l_ldap_sync(evt_res);
262 if (ldap_count_entries (evt_res->ld, evt_res->result) != 1)
264 log_warn (ZONE, "Multiple users with the same dn?");
265 free (evt_res);
266 return NULL;
269 return evt_res;
273 v2l_request_record (v2l_Config *self, v2l_LdapConn *curr_conn,
274 v2l_LdapRequest *req)
276 LDAPMod **attrs;
277 int i, nbmod, ret;
278 v2l_LdapRequest *cur_req, *cur_temp;
279 v2l_LdapEvt *evt_res;
281 if (req == NULL)
283 log_warn (ZONE, "LDAP request is NULL? I cannot record anything");
284 return 1;
287 nbmod = _v2l_count_attrs (req);
289 attrs = (LDAPMod **) malloc ((nbmod + 1) * sizeof (LDAPMod *));
291 if (attrs == NULL)
293 LOG_ERROR_MEM;
294 return 0;
297 /* to wait for the results */
298 evt_res = (v2l_LdapEvt *) malloc (sizeof (v2l_LdapEvt));
300 if (evt_res == NULL)
302 LOG_ERROR_MEM;
303 free (attrs);
304 return 0;
307 for (i = 0; i < nbmod; i++)
309 attrs[i] = (LDAPMod *) malloc (sizeof (LDAPMod));
311 if (attrs[i] == NULL)
313 while (--i >= 0)
315 free (attrs[i]);
318 LOG_ERROR_MEM;
319 free (attrs);
320 free (evt_res);
321 return 0;
325 for (cur_req = req, i = 0; i < nbmod; cur_req = cur_req->next, i++)
327 memcpy (attrs[i], cur_req->attr, sizeof (LDAPMod));
328 log_debug (ZONE, "Element \"%s\" (%d) in the LDAP request: %s",
329 attrs[i]->mod_type, i, attrs[i]->mod_values[0]);
332 attrs[nbmod] = NULL;
334 log_debug (ZONE, "LDAP attempting to modify \"%s\" with dn \"%s\"",
335 curr_conn->user, curr_conn->binddn);
337 evt_res->ld = curr_conn->ld;
338 evt_res->rc = _v2l_ldap_modify (curr_conn->binddn, attrs, evt_res);
339 ret = evt_res->rc == LDAP_SUCCESS;
341 if (!ret)
343 log_error (ZONE, "LDAP error attempting to modify user info: %s",
344 ldap_err2string (evt_res->rc));
346 else
348 _v2l_ldap_sync (evt_res);
351 ldap_msgfree (evt_res->result);
352 free (evt_res);
354 for (cur_req = req, i = 0; i < nbmod; i++)
356 cur_temp = cur_req;
357 cur_req = cur_req->next;
359 free (attrs[i]);
360 free (cur_temp->attr->mod_values[0]);
361 free (cur_temp->attr->mod_values);
362 free (cur_temp->attr);
363 free (cur_temp);
366 free (attrs);
368 return ret;
371 v2l_LdapRequest *
372 v2l_add_attr_str (v2l_LdapRequest *req, const char *attr, const char *str)
374 LDAPMod *mod;
376 mod = (LDAPMod *) malloc (sizeof (LDAPMod));
378 if (mod == NULL)
380 return NULL;
383 mod->mod_op = LDAP_MOD_REPLACE;
384 mod->mod_type = (char *) attr;
386 mod->mod_values = (char **) malloc (2 * sizeof (char *));
388 if (mod->mod_values == NULL)
390 free (mod);
391 return NULL;
394 mod->mod_values[0] = (char *) malloc (strlen (str) + 1);
396 if (mod->mod_values[0] == NULL)
398 free (mod->mod_values);
399 free (mod);
400 return NULL;
403 memcpy (mod->mod_values[0], str, strlen (str) + 1);
404 mod->mod_values[1] = NULL;
406 return _v2l_add_attr (req, mod);
409 /* public api ends here */
411 static v2l_LdapConn *
412 _v2l_create_conn (char *host, int port, const char *binddn, const char *user,
413 const char *passwd, int expire)
415 LDAP *ld;
416 v2l_LdapConn *ldap_conn;
417 int version; /* LDAP protocol version */
418 int rc;
419 pool poolref;
421 ld = ldap_init (host, port);
423 if (ld == NULL)
425 log_error (ZONE, "Unable to init LDAP");
426 return NULL;
429 /* XXX */
430 version = LDAP_VERSION3;
431 /***/
433 ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
434 rc = ldap_simple_bind_s (ld, binddn, passwd);
436 if (rc != LDAP_SUCCESS)
438 log_error (ZONE,
439 "LDAP simple bind error : %s", ldap_err2string (rc));
440 return NULL;
443 poolref = pool_new ();
445 if (poolref == NULL)
447 LOG_ERROR_MEM;
448 ldap_unbind_s (ld);
449 return NULL;
452 ldap_conn = (v2l_LdapConn *) pmalloc (poolref, sizeof (v2l_LdapConn));
454 if (ldap_conn == NULL)
456 LOG_ERROR_MEM;
457 ldap_unbind_s (ld);
458 pool_free(poolref);
459 return NULL;
462 ldap_conn->poolref = poolref;
463 ldap_conn->ld = ld;
465 ldap_conn->binddn = (char *) pmalloc (ldap_conn->poolref,
466 strlen (binddn) + 1);
468 if (ldap_conn->binddn == NULL)
470 LOG_ERROR_MEM;
471 ldap_unbind_s (ld);
472 pool_free(poolref);
473 return NULL;
476 strcpy (ldap_conn->binddn, binddn);
478 ldap_conn->entry = (char *) pmalloc (ldap_conn->poolref, strlen (binddn) + 1);
480 if (ldap_conn->entry == NULL)
482 LOG_ERROR_MEM;
483 ldap_unbind_s (ld);
484 pool_free(poolref);
485 return NULL;
488 strcpy (ldap_conn->entry, binddn);
489 strtok (ldap_conn->entry, ", ");
491 ldap_conn->user = (char *) pmalloc (ldap_conn->poolref, strlen (user) + 1);
493 if (ldap_conn->user == NULL)
495 LOG_ERROR_MEM;
496 ldap_unbind_s (ld);
497 pool_free(poolref);
498 return NULL;
501 strcpy (ldap_conn->user, user);
503 ldap_conn->creation_time = time (NULL); /* timestamp */
505 if (expire == 1) /* Add it to V2L_CONN_LIST */
507 _v2l_add_conn (ldap_conn);
510 return ldap_conn;
513 static char *
514 _v2l_ldap_get_passwd(v2l_Config *self, const char *user)
516 LDAPMessage *e;
517 v2l_LdapEvt *evt_res;
518 char *data, *filter, **vals, *attrs[2] = {"userPassword", NULL};
520 evt_res = (v2l_LdapEvt *) malloc(sizeof(v2l_LdapEvt));
522 if (evt_res == NULL)
524 LOG_ERROR_MEM;
525 return NULL;
528 filter = (char *) malloc (strlen (self->uniqattr) + strlen (user) + 2);
530 if (filter == NULL)
532 LOG_ERROR_MEM;
533 free (evt_res);
534 return NULL;
537 sprintf(filter, "%s=%s", self->uniqattr, user);
538 evt_res->ld = self->master_conn->ld;
540 evt_res->rc = _v2l_ldap_search (filter, self->suffix, attrs, 0, evt_res);
541 free (filter);
543 if (evt_res->rc != LDAP_SUCCESS)
545 log_error(ZONE,
546 "LDAP error attempting to retrieve \"%s\"'s password: %s",
547 user, ldap_err2string (evt_res->rc));
548 free (evt_res);
549 return NULL;
552 _v2l_ldap_sync (evt_res);
553 data = NULL;
555 if (ldap_count_entries(evt_res->ld, evt_res->result) == 1)
557 e = ldap_first_entry(evt_res->ld, evt_res->result);
558 vals = ldap_get_values(evt_res->ld, e, "userPassword");
560 if (vals == NULL)
562 log_debug(ZONE, "User has no password!");
563 data = (char *) malloc(sizeof(char));
565 if (data == NULL)
567 LOG_ERROR_MEM;
568 ldap_msgfree(evt_res->result);
569 free(evt_res);
570 return NULL;
573 data[0] = 0;
575 else
577 data = (char *) malloc(sizeof(char) * (strlen(vals[0]) + 1));
579 if (data != NULL)
581 strcpy(data, vals[0]);
584 ldap_value_free(vals);
587 ldap_msgfree(evt_res->result);
590 free(evt_res);
591 return data;
594 static int
595 _v2l_ldap_modify (char *dn, LDAPMod **attrs, v2l_LdapEvt *evt_res)
597 return ldap_modify_ext (evt_res->ld, dn, attrs, NULL, NULL,
598 &(evt_res->msgid));
601 static int
602 _v2l_ldap_search (char *dn, char *suffix, char **attrs, int subtree,
603 v2l_LdapEvt *evt_res)
605 int scope;
607 scope = subtree ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_ONELEVEL;
609 return ldap_search_ext (evt_res->ld, suffix, scope,
610 dn, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
611 &(evt_res->msgid));
614 static void
615 _v2l_ldap_sync (v2l_LdapEvt *evt_res)
617 #ifndef _V2L_JABBER2
618 pth_event_t evt;
620 evt = pth_event (PTH_EVENT_FUNC, &_v2l_ldap_wait_callback,
621 (void *) evt_res, pth_time (V2L_POLL_INTERVAL, 0));
622 pth_wait (evt);
623 #else
624 pthread_t thr;
625 int rc;
627 rc = pthread_create(&thr, NULL, _v2l_ldap_wait_callback_g, (void *) evt_res);
629 if (rc != 0)
631 log_error (ZONE, "Thread create failed: %d", rc);
632 return;
635 pthread_join(thr, NULL);
636 #endif
639 /*! Count the number of LDAPMod in the structure */
640 static int
641 _v2l_count_attrs (v2l_LdapRequest *req)
643 v2l_LdapRequest *ptr;
644 int nbmod;
646 for (nbmod = 0, ptr = req; ptr != NULL; ptr = ptr->next, nbmod++);
648 return nbmod;
651 static v2l_LdapRequest *
652 _v2l_add_attr (v2l_LdapRequest *req, LDAPMod *attr)
654 if (attr == NULL)
656 log_warn (ZONE, "LDAP attribute is NULL? I cannot add anything");
657 return NULL;
660 if (req == NULL)
662 req = (v2l_LdapRequest *) malloc (sizeof (v2l_LdapRequest));
664 if (req == NULL)
666 LOG_ERROR_MEM;
667 return NULL;
670 req->attr = attr;
671 req->next = NULL;
673 else
675 v2l_LdapRequest *ptr;
676 v2l_LdapRequest *new_req;
678 new_req = (v2l_LdapRequest *) malloc (sizeof (v2l_LdapRequest));
680 if (new_req == NULL)
682 LOG_ERROR_MEM;
683 return NULL;
686 new_req->attr = attr;
687 new_req->next = NULL;
689 for (ptr = req; ptr->next != NULL; ptr = ptr->next);
691 ptr->next = new_req;
694 return req;
697 static int
698 _v2l_ldap_wait_callback(void *arg)
700 v2l_LdapEvt *evt_res = (v2l_LdapEvt *) arg;
701 LDAPMessage *result;
702 int rc;
704 rc = ldap_result (evt_res->ld, evt_res->msgid, 1, NULL, &result);
706 if (rc == -1)
708 log_error(ZONE, "LDAP result error %s",
709 ldap_err2string(rc));
710 evt_res->result = NULL;
711 evt_res->rc = -1;
712 return 1;
715 if ((rc == LDAP_RES_ADD)
716 || (rc == LDAP_RES_MODIFY)
717 || (rc == LDAP_RES_SEARCH_RESULT)
718 || (rc == LDAP_RES_SEARCH_ENTRY)
719 || (rc == LDAP_RES_DELETE))
721 evt_res->result = result;
722 evt_res->rc = rc;
723 return 1;
726 return 0; /* still waiting */
729 #ifdef _V2L_JABBER2
730 static void *
731 _v2l_ldap_wait_callback_g (void *arg)
733 while (!_v2l_ldap_wait_callback (arg))
735 sleep (V2L_POLL_INTERVAL);
738 return NULL;
740 #endif
742 static void
743 _v2l_free_walker (xht h, const char *key, void *val, void *arg)
745 _v2l_free_conn (h, key, val);
748 static void
749 _v2l_free_conn (xht h, const char *user, void *val)
751 v2l_LdapConn *temp_conn = (v2l_LdapConn *) val;
753 log_debug (ZONE, "Freeing LDAP connection for user \"%s\"", user);
755 xhash_zap (h, user);
756 ldap_unbind_s (temp_conn->ld);
757 pool_free (temp_conn->poolref);
760 static void
761 _v2l_free_expired_walker (xht h, const char *key, void *val, void *arg)
763 v2l_LdapConn *temp_conn = (v2l_LdapConn *) val;
765 /* kill connections older than V2L_CONN_LIFETIME */
766 if ((time (NULL) - temp_conn->creation_time) > (time_t) V2L_CONN_LIFETIME)
768 _v2l_free_conn (h, key, val);
772 static void *
773 _v2l_purge_conn_callback (void *arg)
775 log_debug (ZONE, "Checking connections lifetime");
777 while (1)
779 /* V2L_CONN_LIST has been freed? */
780 if (V2L_CONN_LIST != NULL)
782 xhash_walk (V2L_CONN_LIST, _v2l_free_expired_walker, NULL);
784 #ifndef _V2L_JABBER2
785 pth_sleep (V2L_CONN_LIFETIME);
786 #else
787 sleep (V2L_CONN_LIFETIME);
788 #endif
791 return NULL;
794 static void
795 _v2l_add_conn (v2l_LdapConn *ldap_conn)
797 #ifndef _V2L_JABBER2
798 pth_attr_t attr;
799 #else
800 pthread_t thr;
801 pthread_attr_t attr;
802 int rc;
803 #endif
805 if (V2L_CONN_LIST == NULL)
808 log_debug (ZONE, "V2L_CONN_LIST hashtable is not initialized yet");
810 V2L_CONN_LIST = xhash_new (509);
812 /* spawn the thread which deletes expired connections */
813 #ifndef _V2L_JABBER2
814 attr = pth_attr_new ();
815 pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
816 pth_spawn (attr, _v2l_purge_conn_callback, NULL);
817 pth_attr_destroy (attr);
818 #else
819 pthread_attr_init(&attr);
820 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
822 rc = pthread_create (&thr, &attr, _v2l_purge_conn_callback, NULL);
823 pthread_attr_destroy(&attr);
825 if (rc != 0)
827 log_error (ZONE, "Thread create failed: %d", rc);
829 #endif
832 xhash_put (V2L_CONN_LIST, ldap_conn->user, (void *) ldap_conn);
835 void
836 v2l_ldap_for_all_attrs(v2l_AttrValueFunction value_func,
837 v2l_AttrMatchFunction match_func, void *pointer, v2l_LdapEvt *evt_res)
839 LDAPMessage *current_result;
840 BerElement *ber;
841 char *current_attr, **vals;
842 void *shrdata;
844 current_result = ldap_first_entry (evt_res->ld, evt_res->result);
845 current_attr = ldap_first_attribute (evt_res->ld, current_result, &ber);
847 /* step through each attribute in objectclass */
848 for (;
849 current_attr != NULL;
850 current_attr = ldap_next_attribute (evt_res->ld, current_result, ber))
853 if (match_func (current_attr, &shrdata))
855 vals = ldap_get_values (evt_res->ld, current_result, current_attr);
856 value_func (current_attr, (const char **) vals, pointer, shrdata);
858 if (vals != NULL)
860 ldap_value_free (vals);
864 ldap_memfree (current_attr);
865 } /* attributes loop */
867 if (ber != NULL)
869 ber_free (ber, 0);
872 /* don't forget to free the next attribute */
873 ldap_memfree (current_attr);
874 ldap_msgfree (evt_res->result);