1 /*
2 * jabberd - Jabber Open Source Server
3 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4 * Ryan Eatmon, Robert Norris
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "sm.h"
22
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 $
28 */
29
30 #define uri_VCARD "vcard-temp"
31 static int ns_VCARD = 0;
32
33 #define VCARD_MAX_FIELD_SIZE (16384)
34
35 typedef struct _mod_iq_vcard_st {
36 size_t vcard_max_field_size_default;
37 size_t vcard_max_field_size_avatar;
38 } *mod_iq_vcard_t;
39
40 /**
41 * these are the vcard attributes that gabber supports. they're also
42 * all strings, and thus easy to automate. there might be more in
43 * regular use, we need to check that out. one day, when we're all
44 * using real foaf profiles, we'll have bigger things to worry about :)
45 *
46 * darco(2005-09-15): Added quite a few more fields, including those
47 * necessary for vCard avatar support.
48 */
49
50 static const char *_iq_vcard_map[] = {
51 "FN", "fn",
52 "N/FAMILY", "n-family",
53 "N/GIVEN", "n-given",
54 "N/MIDDLE", "n-middle",
55 "N/PREFIX", "n-prefix",
56 "N/SUFFIX", "n-suffix",
57 "NICKNAME", "nickname",
58 "PHOTO/TYPE", "photo-type",
59 "PHOTO/BINVAL", "photo-binval",
60 "PHOTO/EXTVAL", "photo-extval",
61 "BDAY", "bday",
62 "ADR/POBOX", "adr-pobox",
63 "ADR/EXTADD", "adr-extadd",
64 "ADR/STREET", "adr-street",
65 "ADR/LOCALITY", "adr-locality",
66 "ADR/REGION", "adr-region",
67 "ADR/PCODE", "adr-pcode",
68 "ADR/CTRY", "adr-country",
69 "TEL/NUMBER", "tel",
70 "EMAIL/USERID", "email",
71 "JABBERID", "jabberid",
72 "MAILER", "mailer",
73 "TZ", "tz",
74 "GEO/LAT", "geo-lat",
75 "GEO/LON", "geo-lon",
76 "TITLE", "title",
77 "ROLE", "role",
78 "LOGO/TYPE", "logo-type",
79 "LOGO/BINVAL", "logo-binval",
80 "LOGO/EXTVAL", "logo-extval",
81 "AGENT/EXTVAL", "agent-extval",
82 "ORG/ORGNAME", "org-orgname",
83 "ORG/ORGUNIT", "org-orgunit",
84 "NOTE", "note",
85 "REV", "rev",
86 "SORT-STRING", "sort-string",
87 "SOUND/PHONETIC","sound-phonetic",
88 "SOUND/BINVAL", "sound-binval",
89 "SOUND/EXTVAL", "sound-extval",
90 "UID", "uid",
91 "URL", "url",
92 "DESC", "desc",
93 "KEY/TYPE", "key-type",
94 "KEY/CRED", "key-cred",
95 NULL, NULL
96 };
97
_iq_vcard_to_object(mod_instance_t mi,pkt_t pkt)98 static os_t _iq_vcard_to_object(mod_instance_t mi, pkt_t pkt) {
99 os_t os;
100 os_object_t o;
101 int i = 0, elem;
102 char ekey[10], *cdata;
103 const char *vkey, *dkey, *vskey;
104 size_t fieldsize;
105 mod_iq_vcard_t iq_vcard = (mod_iq_vcard_t) mi->mod->private;
106
107 log_debug(ZONE, "building object from packet");
108
109 os = os_new();
110 o = os_object_new(os);
111
112 while(_iq_vcard_map[i] != NULL) {
113 vkey = _iq_vcard_map[i];
114 dkey = _iq_vcard_map[i + 1];
115
116 i += 2;
117
118 if( !strcmp(vkey, "PHOTO/BINVAL") ) {
119 fieldsize = iq_vcard->vcard_max_field_size_avatar;
120 } else {
121 fieldsize = iq_vcard->vcard_max_field_size_default;
122 }
123
124 vskey = strchr(vkey, '/');
125 if(vskey == NULL) {
126 vskey = vkey;
127 elem = 2;
128 } else {
129 sprintf(ekey, "%.*s", (int) (vskey - vkey), vkey);
130 elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
131 if(elem < 0)
132 continue;
133 vskey++;
134 }
135
136 elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, 2), vskey, 1);
137 if(elem < 0 || NAD_CDATA_L(pkt->nad, elem) == 0)
138 continue;
139
140 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);
141
142 cdata = malloc(fieldsize);
143 if(cdata) {
144 snprintf(cdata, fieldsize, "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
145 cdata[fieldsize-1] = '\0';
146 os_object_put(o, dkey, cdata, os_type_STRING);
147 free(cdata);
148 }
149 }
150
151 return os;
152 }
153
_iq_vcard_to_pkt(sm_t sm,os_t os)154 static pkt_t _iq_vcard_to_pkt(sm_t sm, os_t os) {
155 pkt_t pkt;
156 os_object_t o;
157 int i = 0, elem;
158 char ekey[10], *dval;
159 const char *vkey, *dkey, *vskey;
160
161 log_debug(ZONE, "building packet from object");
162
163 pkt = pkt_create(sm, "iq", "result", NULL, NULL);
164 nad_append_elem(pkt->nad, nad_add_namespace(pkt->nad, uri_VCARD, NULL), "vCard", 2);
165
166 if(!os_iter_first(os))
167 return pkt;
168 o = os_iter_object(os);
169
170 while(_iq_vcard_map[i] != NULL) {
171 vkey = _iq_vcard_map[i];
172 dkey = _iq_vcard_map[i + 1];
173
174 i += 2;
175
176 if(!os_object_get_str(os, o, dkey, &dval))
177 continue;
178
179 vskey = strchr(vkey, '/');
180 if(vskey == NULL) {
181 vskey = vkey;
182 elem = 2;
183 } else {
184 sprintf(ekey, "%.*s", (int) (vskey - vkey), vkey);
185 elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
186 if(elem < 0)
187 elem = nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), ekey, 3);
188 vskey++;
189 }
190
191 log_debug(ZONE, "extracted dbkey %s val '%s' for vcard key %s", dkey, dval, vkey);
192
193 if (!strcmp(dkey, "tel")) {
194 nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), "VOICE", pkt->nad->elems[elem].depth + 1);
195 }
196 nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), vskey, pkt->nad->elems[elem].depth + 1);
197 nad_append_cdata(pkt->nad, dval, strlen(dval), pkt->nad->elems[elem].depth + 2);
198 }
199
200 return pkt;
201 }
202
_iq_vcard_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)203 static mod_ret_t _iq_vcard_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
204 os_t os;
205 st_ret_t ret;
206 pkt_t result;
207
208 /* only handle vcard sets and gets that aren't to anyone */
209 if(pkt->to != NULL || (pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
210 return mod_PASS;
211
212 /* get */
213 if(pkt->type == pkt_IQ) {
214 if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->jid)))
215 return -stanza_err_RESOURCE_CONSTRAINT;
216
217 ret = storage_get(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, &os);
218 switch(ret) {
219 case st_FAILED:
220 return -stanza_err_INTERNAL_SERVER_ERROR;
221
222 case st_NOTIMPL:
223 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
224
225 case st_NOTFOUND:
226 nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
227 nad_set_attr(pkt->nad, 1, -1, "to", NULL, 0);
228 nad_set_attr(pkt->nad, 1, -1, "from", NULL, 0);
229
230 pkt_sess(pkt, sess);
231
232 return mod_HANDLED;
233
234 case st_SUCCESS:
235 result = _iq_vcard_to_pkt(sess->user->sm, os);
236 os_free(os);
237
238 nad_set_attr(result->nad, 1, -1, "type", "result", 6);
239 pkt_id(pkt, result);
240
241 pkt_sess(result, sess);
242
243 pkt_free(pkt);
244
245 return mod_HANDLED;
246 }
247
248 /* we never get here */
249 pkt_free(pkt);
250 return mod_HANDLED;
251 }
252
253 os = _iq_vcard_to_object(mi, pkt);
254
255 if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->jid)))
256 return -stanza_err_RESOURCE_CONSTRAINT;
257
258 ret = storage_replace(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, os);
259 os_free(os);
260
261 switch(ret) {
262 case st_FAILED:
263 return -stanza_err_INTERNAL_SERVER_ERROR;
264
265 case st_NOTIMPL:
266 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
267
268 default:
269 result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
270
271 pkt_id(pkt, result);
272
273 pkt_sess(result, sess);
274
275 pkt_free(pkt);
276
277 return mod_HANDLED;
278 }
279
280 /* we never get here */
281 pkt_free(pkt);
282 return mod_HANDLED;
283 }
284
285 /* for the special JID of your jabber server bare domain.
286 * You can have one for every virtual host
287 * you can populate it using your DBMS frontend
288 */
_iq_vcard_pkt_sm(mod_instance_t mi,pkt_t pkt)289 static mod_ret_t _iq_vcard_pkt_sm(mod_instance_t mi, pkt_t pkt) {
290 os_t os;
291 st_ret_t ret;
292 pkt_t result;
293
294 /* only handle vcard sets and gets */
295 if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
296 return mod_PASS;
297
298 /* error them if they're trying to do a set */
299 if(pkt->type == pkt_IQ_SET)
300 return -stanza_err_FORBIDDEN;
301
302 /* a vcard for the server */
303 ret = storage_get(mi->sm->st, "vcard", pkt->to->domain, NULL, &os);
304 switch(ret) {
305 case st_FAILED:
306 return -stanza_err_INTERNAL_SERVER_ERROR;
307
308 case st_NOTIMPL:
309 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
310
311 case st_NOTFOUND:
312 return -stanza_err_ITEM_NOT_FOUND;
313
314 case st_SUCCESS:
315 result = _iq_vcard_to_pkt(mi->sm, os);
316 os_free(os);
317
318 result->to = jid_dup(pkt->from);
319 result->from = jid_dup(pkt->to);
320
321 nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
322 nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
323
324 pkt_id(pkt, result);
325
326 pkt_router(result);
327
328 pkt_free(pkt);
329
330 return mod_HANDLED;
331 }
332
333 /* we never get here */
334 pkt_free(pkt);
335 return mod_HANDLED;
336 }
337
_iq_vcard_pkt_user(mod_instance_t mi,user_t user,pkt_t pkt)338 static mod_ret_t _iq_vcard_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
339 os_t os;
340 st_ret_t ret;
341 pkt_t result;
342
343 /* only handle vcard sets and gets, without resource */
344 if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD || pkt->to->resource[0] !='\0')
345 return mod_PASS;
346
347 /* error them if they're trying to do a set */
348 if(pkt->type == pkt_IQ_SET)
349 return -stanza_err_FORBIDDEN;
350
351 if (sm_storage_rate_limit(user->sm, jid_user(pkt->from)))
352 return -stanza_err_RESOURCE_CONSTRAINT;
353
354 ret = storage_get(user->sm->st, "vcard", jid_user(user->jid), NULL, &os);
355 switch(ret) {
356 case st_FAILED:
357 return -stanza_err_INTERNAL_SERVER_ERROR;
358
359 case st_NOTIMPL:
360 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
361
362 case st_NOTFOUND:
363 return -stanza_err_SERVICE_UNAVAILABLE;
364
365 case st_SUCCESS:
366 result = _iq_vcard_to_pkt(user->sm, os);
367 os_free(os);
368
369 result->to = jid_dup(pkt->from);
370 result->from = jid_dup(pkt->to);
371
372 nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
373 nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
374
375 pkt_id(pkt, result);
376
377 pkt_router(result);
378
379 pkt_free(pkt);
380
381 return mod_HANDLED;
382 }
383
384 /* we never get here */
385 pkt_free(pkt);
386 return mod_HANDLED;
387 }
388
_iq_vcard_user_delete(mod_instance_t mi,jid_t jid)389 static void _iq_vcard_user_delete(mod_instance_t mi, jid_t jid) {
390 log_debug(ZONE, "deleting vcard for %s", jid_user(jid));
391
392 storage_delete(mi->sm->st, "vcard", jid_user(jid), NULL);
393 }
394
_iq_vcard_free(module_t mod)395 static void _iq_vcard_free(module_t mod) {
396 sm_unregister_ns(mod->mm->sm, uri_VCARD);
397 feature_unregister(mod->mm->sm, uri_VCARD);
398 free(mod->private);
399 }
400
module_init(mod_instance_t mi,const char * arg)401 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
402 module_t mod = mi->mod;
403 mod_iq_vcard_t iq_vcard;
404
405 if(mod->init) return 0;
406
407 mod->pkt_sm = _iq_vcard_pkt_sm;
408 mod->in_sess = _iq_vcard_in_sess;
409 mod->pkt_user = _iq_vcard_pkt_user;
410 mod->user_delete = _iq_vcard_user_delete;
411 mod->free = _iq_vcard_free;
412
413 ns_VCARD = sm_register_ns(mod->mm->sm, uri_VCARD);
414 feature_register(mod->mm->sm, uri_VCARD);
415
416 iq_vcard = (mod_iq_vcard_t) calloc(1, sizeof(struct _mod_iq_vcard_st));
417 iq_vcard->vcard_max_field_size_default = j_atoi(config_get_one(mod->mm->sm->config, "user.vcard.max-field-size.default", 0), VCARD_MAX_FIELD_SIZE);
418 iq_vcard->vcard_max_field_size_avatar = j_atoi(config_get_one(mod->mm->sm->config, "user.vcard.max-field-size.avatar", 0), VCARD_MAX_FIELD_SIZE);
419 mod->private = iq_vcard;
420
421 return 0;
422 }
423