More robust against LDAP faliures.
[vcard2ldap.git] / patch / mod_iq_vcard.c
blobdf34e2c83a1b0fd0b8d68cc76dac878b83546b91
1 /*
2 * jabberd - Jabber Open Source Server
3 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4 * Ryan Eatmon, Robert Norris
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
21 #include "sm.h"
23 /** @file sm/mod_iq_vcard.c
24 * @brief user profiles (vcard)
25 * @author Robert Norris
26 * $Date: 2005/08/17 07:48:28 $
27 * $Revision: 1.25 $
30 #ifdef _V2L_JABBER2
31 #include <xmlnode.h>
32 #include <v2l_config.h>
33 #include <v2l_conn.h>
34 #include <v2l_vcard.h>
36 static v2l_Config *v2l_get_config(config_t cfg) {
37 static v2l_Config ldap_config = { NULL, NULL, NULL, LDAP_PORT, NULL,
38 NULL, NULL, NULL };
40 if(!cfg)
41 return &ldap_config;
43 if(!ldap_config.host)
44 ldap_config.host = config_get_one(cfg, "v2l.host", 0);
45 if(!ldap_config.suffix)
46 ldap_config.suffix = config_get_one(cfg, "v2l.suffix", 0);
47 if(!ldap_config.uniqattr)
48 ldap_config.uniqattr = config_get_one(cfg, "v2l.uniqattr", 0);
49 if(!ldap_config.binddn)
50 ldap_config.binddn = config_get_one(cfg, "v2l.binddn", 0);
51 if(!ldap_config.bindpw)
52 ldap_config.bindpw = config_get_one(cfg, "v2l.bindpw", 0);
54 if(!ldap_config.master_conn)
55 ldap_config.master_conn = v2l_get_master_conn(&ldap_config);
57 return &ldap_config;
59 #endif
61 #define uri_VCARD "vcard-temp"
62 static int ns_VCARD = 0;
64 #define VCARD_MAX_FIELD_SIZE (16384)
66 /**
67 * these are the vcard attributes that gabber supports. they're also
68 * all strings, and thus easy to automate. there might be more in
69 * regular use, we need to check that out. one day, when we're all
70 * using real foaf profiles, we'll have bigger things to worry about :)
72 * darco(2005-09-15): Added quite a few more fields, including those
73 * necessary for vCard avatar support.
76 static const char *_iq_vcard_map[] = {
77 "FN", "fn",
78 "NICKNAME", "nickname",
79 "URL", "url",
80 "TEL/NUMBER", "tel",
81 "EMAIL/USERID", "email",
82 "TITLE", "title",
83 "ROLE", "role",
84 "BDAY", "bday",
85 "DESC", "desc",
86 "N/FAMILY", "n-family",
87 "N/GIVEN", "n-given",
88 "N/MIDDLE", "n-middle",
89 "N/PREFIX", "n-prefix",
90 "N/SUFFIX", "n-suffix",
91 "ADR/STREET", "adr-street",
92 "ADR/POBOX", "adr-pobox",
93 "ADR/EXTADD", "adr-extadd",
94 "ADR/LOCALITY", "adr-locality",
95 "ADR/REGION", "adr-region",
96 "ADR/PCODE", "adr-pcode",
97 "ADR/CTRY", "adr-country",
98 "ORG/ORGNAME", "org-orgname",
99 "ORG/ORGUNIT", "org-orgunit",
101 "TZ", "tz",
102 "GEO/LAT", "geo-lat",
103 "GEO/LON", "geo-lon",
104 "AGENT/EXTVAL", "agent-extval",
105 "NOTE", "note",
106 "REV", "rev",
107 "SORT-STRING", "sort-string",
109 "KEY/TYPE", "key-type",
110 "KEY/CRED", "key-cred",
112 "PHOTO/TYPE", "photo-type",
113 "PHOTO/BINVAL", "photo-binval",
114 "PHOTO/EXTVAL", "photo-extval",
116 "LOGO/TYPE", "logo-type",
117 "LOGO/BINVAL", "logo-binval",
118 "LOGO/EXTVAL", "logo-extval",
120 "SOUND/PHONETIC","sound-phonetic",
121 "SOUND/BINVAL", "sound-binval",
122 "SOUND/EXTVAL", "sound-extval",
124 NULL, NULL
127 static os_t _iq_vcard_to_object(pkt_t pkt) {
128 os_t os;
129 os_object_t o;
130 int i = 0, elem;
131 char ekey[10], cdata[VCARD_MAX_FIELD_SIZE];
132 const char *vkey, *dkey, *vskey;
134 #ifdef _V2L_JABBER2
135 v2l_LdapConn *ldap_conn = NULL;
136 v2l_Config *ldap_config;
138 xmlnode vcard, node = NULL;
139 char gtag[10], prev_gtag[10];
141 gtag[0] = 0;
142 prev_gtag[0] = 0;
143 #endif
145 log_debug(ZONE, "building object from packet");
147 #ifdef _V2L_JABBER2
148 vcard = NULL; /* Warning */
149 ldap_config = v2l_get_config(pkt->sm->config);
151 if(!ldap_config->master_conn)
152 log_debug(ZONE, "Cannot connect to LDAP/V2L not configured");
153 else {
154 ldap_conn = v2l_get_conn(ldap_config, pkt->source->user->jid->node);
156 if(ldap_conn)
157 vcard = xmlnode_new_tag ("vCard");
158 else
159 log_debug(ZONE, "Unable to create connection for \"%s\" user",
160 pkt->source->user->jid->node);
162 #endif
164 os = os_new();
165 o = os_object_new(os);
167 while(_iq_vcard_map[i] != NULL) {
168 vkey = _iq_vcard_map[i];
169 dkey = _iq_vcard_map[i + 1];
171 i += 2;
173 vskey = strchr(vkey, '/');
174 if(vskey == NULL) {
175 vskey = vkey;
176 elem = 2;
177 #ifdef _V2L_JABBER2
178 gtag[0] = 0;
179 #endif
180 } else {
181 sprintf(ekey, "%.*s", vskey - vkey, vkey);
182 elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
183 if(elem < 0)
184 continue;
185 vskey++;
187 #ifdef _V2L_JABBER2
188 if(vcard) {
189 strcpy(gtag, ekey);
191 if(strcmp(gtag, prev_gtag)) {
192 strcpy(prev_gtag, gtag);
193 xmlnode_insert_tag_node(vcard, node);
194 node = xmlnode_new_tag(gtag);
197 #endif
200 elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, 2), vskey, 1);
201 if(elem < 0 || NAD_CDATA_L(pkt->nad, elem) == 0)
202 continue;
204 log_debug(ZONE, "extracted vcard key %s val '%.*s' for db key %s", vkey, NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem), dkey);
206 snprintf(cdata, sizeof(cdata), "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
207 cdata[sizeof(cdata)-1] = '\0';
209 #ifdef _V2L_JABBER2
210 if(vcard) {
211 xmlnode tmp;
213 tmp = xmlnode_new_tag(vskey);
214 xmlnode_insert_cdata(tmp, cdata, strlen (cdata));
216 if(gtag[0])
217 xmlnode_insert_tag_node(node, tmp);
218 else
219 xmlnode_insert_tag_node(vcard, tmp);
221 #endif
222 os_object_put(o, dkey, cdata, os_type_STRING);
225 #ifdef _V2L_JABBER2
226 /* adding the last node if is the last group */
227 if(vcard) {
228 if (gtag[0])
229 xmlnode_insert_tag_node(vcard, node);
231 v2l_vcard_set(ldap_config, ldap_conn, vcard);
233 #endif
235 return os;
238 #ifndef _V2L_JABBER2
239 static pkt_t _iq_vcard_to_pkt(sm_t sm, os_t os) {
240 #else
241 static pkt_t _iq_vcard_to_pkt(sm_t sm, os_t os, const char *user) {
242 #endif
243 pkt_t pkt;
244 os_object_t o;
245 int i = 0, elem;
246 char ekey[10], *dval;
247 const char *vkey, *dkey, *vskey;
249 #ifdef _V2L_JABBER2
250 v2l_LdapConn *ldap_conn = NULL;
251 v2l_Config *ldap_config;
252 xmlnode vcard, node = NULL;
253 #endif
255 log_debug(ZONE, "building packet from object");
257 #ifdef _V2L_JABBER2
258 vcard = NULL; /* WARNING */
259 ldap_config = v2l_get_config(sm->config);
261 if(!ldap_config->master_conn)
262 log_debug(ZONE, "Cannot connect to LDAP/V2L not configured");
263 else {
264 ldap_conn = v2l_get_conn(ldap_config, pkt->source->user->jid->node);
266 if(ldap_conn)
267 vcard = v2l_vcard_get (ldap_config, ldap_conn);
268 else
269 log_debug(ZONE, "Unable to create connection for \"%s\" user",
270 pkt->source->user->jid->node);
272 #endif
274 pkt = pkt_create(sm, "iq", "result", NULL, NULL);
275 nad_append_elem(pkt->nad, nad_add_namespace(pkt->nad, uri_VCARD, NULL), "vCard", 2);
277 #ifdef _V2L_JABBER2
278 if(!vcard) {
279 #endif
280 if(!os_iter_first(os))
281 return pkt;
282 o = os_iter_object(os);
283 #ifdef _V2L_JABBER2
285 #endif
287 while(_iq_vcard_map[i] != NULL) {
288 vkey = _iq_vcard_map[i];
289 dkey = _iq_vcard_map[i + 1];
291 i += 2;
293 #ifdef _V2L_JABBER2
294 if(!vcard) {
295 #endif
296 if(!os_object_get_str(os, o, dkey, &dval))
297 continue;
298 #ifdef _V2L_JABBER2
299 } else
300 node = vcard;
301 #endif
303 vskey = strchr(vkey, '/');
304 if(vskey == NULL) {
305 vskey = vkey;
306 elem = 2;
307 } else {
308 sprintf(ekey, "%.*s", vskey - vkey, vkey);
309 elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
310 if(elem < 0)
311 elem = nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), ekey, 3);
312 vskey++;
313 #ifdef _V2L_JABBER2
314 if(vcard)
315 node = xmlnode_get_tag(node, ekey);
316 #endif
319 #ifdef _V2L_JABBER2
320 if (vcard) {
321 node = xmlnode_get_tag(node, vskey);
322 dval = node ? xmlnode_get_data(node) : "";
324 #endif
326 log_debug(ZONE, "extracted dbkey %s val '%s' for vcard key %s", dkey, dval, vkey);
328 nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), vskey, pkt->nad->elems[elem].depth + 1);
329 nad_append_cdata(pkt->nad, dval, strlen(dval), pkt->nad->elems[elem].depth + 2);
332 return pkt;
335 static mod_ret_t _iq_vcard_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
336 os_t os;
337 st_ret_t ret;
338 pkt_t result;
340 /* only handle vcard sets and gets that aren't to anyone */
341 if(pkt->to != NULL || (pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
342 return mod_PASS;
344 /* get */
345 if(pkt->type == pkt_IQ) {
346 ret = storage_get(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, &os);
347 switch(ret) {
348 case st_FAILED:
349 return -stanza_err_INTERNAL_SERVER_ERROR;
351 case st_NOTIMPL:
352 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
354 case st_NOTFOUND:
355 nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
356 nad_set_attr(pkt->nad, 1, -1, "to", NULL, 0);
357 nad_set_attr(pkt->nad, 1, -1, "from", NULL, 0);
359 pkt_sess(pkt, sess);
361 return mod_HANDLED;
363 case st_SUCCESS:
364 #ifndef _V2L_JABBER2
365 result = _iq_vcard_to_pkt(sess->user->sm, os);
366 #else
367 result = _iq_vcard_to_pkt(sess->user->sm, os, sess->jid->node);
368 #endif
369 os_free(os);
371 nad_set_attr(result->nad, 1, -1, "type", "result", 6);
372 pkt_id(pkt, result);
374 pkt_sess(result, sess);
376 pkt_free(pkt);
378 return mod_HANDLED;
381 /* we never get here */
382 pkt_free(pkt);
383 return mod_HANDLED;
386 os = _iq_vcard_to_object(pkt);
387 ret = storage_replace(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, os);
388 os_free(os);
390 switch(ret) {
391 case st_FAILED:
392 return -stanza_err_INTERNAL_SERVER_ERROR;
394 case st_NOTIMPL:
395 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
397 default:
398 result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
400 pkt_id(pkt, result);
402 pkt_sess(result, sess);
404 pkt_free(pkt);
406 return mod_HANDLED;
409 /* we never get here */
410 pkt_free(pkt);
411 return mod_HANDLED;
414 static mod_ret_t _iq_vcard_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
415 os_t os;
416 st_ret_t ret;
417 pkt_t result;
419 /* only handle vcard sets and gets */
420 if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
421 return mod_PASS;
423 /* error them if they're trying to do a set */
424 if(pkt->type == pkt_IQ_SET)
425 return -stanza_err_FORBIDDEN;
427 ret = storage_get(user->sm->st, "vcard", jid_user(user->jid), NULL, &os);
428 switch(ret) {
429 case st_FAILED:
430 return -stanza_err_INTERNAL_SERVER_ERROR;
432 case st_NOTIMPL:
433 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
435 case st_NOTFOUND:
436 return -stanza_err_SERVICE_UNAVAILABLE;
438 case st_SUCCESS:
439 #ifndef _V2L_JABBER2
440 result = _iq_vcard_to_pkt(user->sm, os);
441 #else
442 result = _iq_vcard_to_pkt(user->sm, os, user->jid->node);
443 #endif
444 os_free(os);
446 result->to = jid_dup(pkt->from);
447 result->from = jid_dup(pkt->to);
449 nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
450 nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
452 pkt_id(pkt, result);
454 pkt_router(result);
456 pkt_free(pkt);
458 return mod_HANDLED;
461 /* we never get here */
462 pkt_free(pkt);
463 return mod_HANDLED;
466 static void _iq_vcard_user_delete(mod_instance_t mi, jid_t jid) {
467 log_debug(ZONE, "deleting vcard for %s", jid_user(jid));
469 storage_delete(mi->sm->st, "vcard", jid_user(jid), NULL);
472 static void _iq_vcard_free(module_t mod) {
473 #ifdef _VL2
474 v2l_Config *ldap_config = v2l_get_config(NULL);
475 v2l_free_allconn();
477 /* free admin connection */
478 if(ldap_config->master_conn) {
479 ldap_unbind(ldap_config->master_conn->ld);
480 pool_free(ldap_config->master_conn->p);
482 #endif
484 sm_unregister_ns(mod->mm->sm, uri_VCARD);
485 feature_unregister(mod->mm->sm, uri_VCARD);
488 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
489 module_t mod = mi->mod;
491 if(mod->init) return 0;
493 mod->in_sess = _iq_vcard_in_sess;
494 mod->pkt_user = _iq_vcard_pkt_user;
495 mod->user_delete = _iq_vcard_user_delete;
496 mod->free = _iq_vcard_free;
498 ns_VCARD = sm_register_ns(mod->mm->sm, uri_VCARD);
499 feature_register(mod->mm->sm, uri_VCARD);
501 return 0;