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
23 /** @file sm/mod_iq_vcard.c
24 * @brief user profiles (vcard)
25 * @author Robert Norris
26 * $Date: 2005/08/17 07:48:28 $
32 #include <v2l_config.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
,
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
);
61 #define uri_VCARD "vcard-temp"
62 static int ns_VCARD
= 0;
64 #define VCARD_MAX_FIELD_SIZE (16384)
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
[] = {
78 "NICKNAME", "nickname",
81 "EMAIL/USERID", "email",
86 "N/FAMILY", "n-family",
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",
102 "GEO/LAT", "geo-lat",
103 "GEO/LON", "geo-lon",
104 "AGENT/EXTVAL", "agent-extval",
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",
127 static os_t
_iq_vcard_to_object(pkt_t pkt
) {
131 char ekey
[10], cdata
[VCARD_MAX_FIELD_SIZE
];
132 const char *vkey
, *dkey
, *vskey
;
135 v2l_LdapConn
*ldap_conn
= NULL
;
136 v2l_Config
*ldap_config
;
138 xmlnode vcard
, node
= NULL
;
139 char gtag
[10], prev_gtag
[10];
145 log_debug(ZONE
, "building object from packet");
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");
154 ldap_conn
= v2l_get_conn(ldap_config
, pkt
->source
->user
->jid
->node
);
157 vcard
= xmlnode_new_tag ("vCard");
159 log_debug(ZONE
, "Unable to create connection for \"%s\" user",
160 pkt
->source
->user
->jid
->node
);
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];
173 vskey
= strchr(vkey
, '/');
181 sprintf(ekey
, "%.*s", vskey
- vkey
, vkey
);
182 elem
= nad_find_elem(pkt
->nad
, 2, NAD_ENS(pkt
->nad
, 2), ekey
, 1);
191 if(strcmp(gtag
, prev_gtag
)) {
192 strcpy(prev_gtag
, gtag
);
193 xmlnode_insert_tag_node(vcard
, node
);
194 node
= xmlnode_new_tag(gtag
);
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)
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';
213 tmp
= xmlnode_new_tag(vskey
);
214 xmlnode_insert_cdata(tmp
, cdata
, strlen (cdata
));
217 xmlnode_insert_tag_node(node
, tmp
);
219 xmlnode_insert_tag_node(vcard
, tmp
);
222 os_object_put(o
, dkey
, cdata
, os_type_STRING
);
226 /* adding the last node if is the last group */
229 xmlnode_insert_tag_node(vcard
, node
);
231 v2l_vcard_set(ldap_config
, ldap_conn
, vcard
);
239 static pkt_t
_iq_vcard_to_pkt(sm_t sm
, os_t os
) {
241 static pkt_t
_iq_vcard_to_pkt(sm_t sm
, os_t os
, const char *user
) {
246 char ekey
[10], *dval
;
247 const char *vkey
, *dkey
, *vskey
;
250 v2l_LdapConn
*ldap_conn
= NULL
;
251 v2l_Config
*ldap_config
;
252 xmlnode vcard
, node
= NULL
;
255 log_debug(ZONE
, "building packet from object");
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");
264 ldap_conn
= v2l_get_conn(ldap_config
, pkt
->source
->user
->jid
->node
);
267 vcard
= v2l_vcard_get (ldap_config
, ldap_conn
);
269 log_debug(ZONE
, "Unable to create connection for \"%s\" user",
270 pkt
->source
->user
->jid
->node
);
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);
280 if(!os_iter_first(os
))
282 o
= os_iter_object(os
);
287 while(_iq_vcard_map
[i
] != NULL
) {
288 vkey
= _iq_vcard_map
[i
];
289 dkey
= _iq_vcard_map
[i
+ 1];
296 if(!os_object_get_str(os
, o
, dkey
, &dval
))
303 vskey
= strchr(vkey
, '/');
308 sprintf(ekey
, "%.*s", vskey
- vkey
, vkey
);
309 elem
= nad_find_elem(pkt
->nad
, 2, NAD_ENS(pkt
->nad
, 2), ekey
, 1);
311 elem
= nad_append_elem(pkt
->nad
, NAD_ENS(pkt
->nad
, 2), ekey
, 3);
315 node
= xmlnode_get_tag(node
, ekey
);
321 node
= xmlnode_get_tag(node
, vskey
);
322 dval
= node
? xmlnode_get_data(node
) : "";
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);
335 static mod_ret_t
_iq_vcard_in_sess(mod_instance_t mi
, sess_t sess
, pkt_t pkt
) {
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
)
345 if(pkt
->type
== pkt_IQ
) {
346 ret
= storage_get(sess
->user
->sm
->st
, "vcard", jid_user(sess
->jid
), NULL
, &os
);
349 return -stanza_err_INTERNAL_SERVER_ERROR
;
352 return -stanza_err_FEATURE_NOT_IMPLEMENTED
;
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);
365 result
= _iq_vcard_to_pkt(sess
->user
->sm
, os
);
367 result
= _iq_vcard_to_pkt(sess
->user
->sm
, os
, sess
->jid
->node
);
371 nad_set_attr(result
->nad
, 1, -1, "type", "result", 6);
374 pkt_sess(result
, sess
);
381 /* we never get here */
386 os
= _iq_vcard_to_object(pkt
);
387 ret
= storage_replace(sess
->user
->sm
->st
, "vcard", jid_user(sess
->jid
), NULL
, os
);
392 return -stanza_err_INTERNAL_SERVER_ERROR
;
395 return -stanza_err_FEATURE_NOT_IMPLEMENTED
;
398 result
= pkt_create(sess
->user
->sm
, "iq", "result", NULL
, NULL
);
402 pkt_sess(result
, sess
);
409 /* we never get here */
414 static mod_ret_t
_iq_vcard_pkt_user(mod_instance_t mi
, user_t user
, pkt_t pkt
) {
419 /* only handle vcard sets and gets */
420 if((pkt
->type
!= pkt_IQ
&& pkt
->type
!= pkt_IQ_SET
) || pkt
->ns
!= ns_VCARD
)
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
);
430 return -stanza_err_INTERNAL_SERVER_ERROR
;
433 return -stanza_err_FEATURE_NOT_IMPLEMENTED
;
436 return -stanza_err_SERVICE_UNAVAILABLE
;
440 result
= _iq_vcard_to_pkt(user
->sm
, os
);
442 result
= _iq_vcard_to_pkt(user
->sm
, os
, user
->jid
->node
);
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);
461 /* we never get here */
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
) {
474 v2l_Config
*ldap_config
= v2l_get_config(NULL
);
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
);
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
);