jabberd2  2.2.16
sm/mod_iq_vcard.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "sm.h"
00022 
00030 #define uri_VCARD    "vcard-temp"
00031 static int ns_VCARD = 0;
00032 
00033 #define VCARD_MAX_FIELD_SIZE    (16384)
00034 
00035 typedef struct _mod_iq_vcard_st {
00036     size_t vcard_max_field_size_default;
00037     size_t vcard_max_field_size_avatar;
00038 } *mod_iq_vcard_t;
00039 
00050 static const char *_iq_vcard_map[] = {
00051     "FN",           "fn",
00052     "N/FAMILY",     "n-family",
00053     "N/GIVEN",      "n-given",
00054     "N/MIDDLE",     "n-middle",
00055     "N/PREFIX",     "n-prefix",
00056     "N/SUFFIX",     "n-suffix",
00057     "NICKNAME",     "nickname",
00058     "PHOTO/TYPE",   "photo-type",
00059     "PHOTO/BINVAL", "photo-binval",
00060     "PHOTO/EXTVAL", "photo-extval",
00061     "BDAY",         "bday",
00062     "ADR/POBOX",    "adr-pobox",
00063     "ADR/EXTADD",   "adr-extadd",
00064     "ADR/STREET",   "adr-street",
00065     "ADR/LOCALITY", "adr-locality",
00066     "ADR/REGION",   "adr-region",
00067     "ADR/PCODE",    "adr-pcode",
00068     "ADR/CTRY",     "adr-country",
00069     "TEL/NUMBER",   "tel",
00070     "EMAIL/USERID", "email",
00071     "JABBERID",     "jabberid",
00072     "MAILER",       "mailer",
00073     "TZ",           "tz",
00074     "GEO/LAT",      "geo-lat",
00075     "GEO/LON",      "geo-lon",
00076     "TITLE",        "title",
00077     "ROLE",         "role",
00078     "LOGO/TYPE",    "logo-type",
00079     "LOGO/BINVAL",  "logo-binval",
00080     "LOGO/EXTVAL",  "logo-extval",
00081     "AGENT/EXTVAL", "agent-extval",
00082     "ORG/ORGNAME",  "org-orgname",
00083     "ORG/ORGUNIT",  "org-orgunit",
00084     "NOTE",         "note",
00085     "REV",          "rev",
00086     "SORT-STRING",  "sort-string",
00087     "SOUND/PHONETIC","sound-phonetic",
00088     "SOUND/BINVAL", "sound-binval",
00089     "SOUND/EXTVAL", "sound-extval",
00090     "UID",          "uid",
00091     "URL",          "url",
00092     "DESC",         "desc",
00093     "KEY/TYPE",     "key-type",
00094     "KEY/CRED",     "key-cred",
00095     NULL,           NULL
00096 };
00097 
00098 static os_t _iq_vcard_to_object(mod_instance_t mi, pkt_t pkt) {
00099     os_t os;
00100     os_object_t o;
00101     int i = 0, elem;
00102     char ekey[10], *cdata;
00103     const char *vkey, *dkey, *vskey;
00104     size_t fieldsize;
00105     mod_iq_vcard_t iq_vcard = (mod_iq_vcard_t) mi->mod->private;
00106 
00107     log_debug(ZONE, "building object from packet");
00108 
00109     os = os_new();
00110     o = os_object_new(os);
00111     
00112     while(_iq_vcard_map[i] != NULL) {
00113         vkey = _iq_vcard_map[i];
00114         dkey = _iq_vcard_map[i + 1];
00115 
00116         i += 2;
00117 
00118         if( !strcmp(vkey, "PHOTO/BINVAL") ) {
00119             fieldsize = iq_vcard->vcard_max_field_size_avatar;
00120         } else {
00121             fieldsize = iq_vcard->vcard_max_field_size_default;
00122         }
00123 
00124         vskey = strchr(vkey, '/');
00125         if(vskey == NULL) {
00126             vskey = vkey;
00127             elem = 2;
00128         } else {
00129             sprintf(ekey, "%.*s", (int) (vskey - vkey), vkey);
00130             elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
00131             if(elem < 0)
00132                 continue;
00133             vskey++;
00134         }
00135 
00136         elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, 2), vskey, 1);
00137         if(elem < 0 || NAD_CDATA_L(pkt->nad, elem) == 0)
00138             continue;
00139 
00140         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);
00141 
00142         cdata = malloc(fieldsize);
00143         if(cdata) {
00144             snprintf(cdata, fieldsize, "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
00145             cdata[fieldsize-1] = '\0';
00146             os_object_put(o, dkey, cdata, os_type_STRING);
00147             free(cdata);
00148         }
00149     }
00150 
00151     return os;
00152 }
00153 
00154 static pkt_t _iq_vcard_to_pkt(sm_t sm, os_t os) {
00155     pkt_t pkt;
00156     os_object_t o;
00157     int i = 0, elem;
00158     char ekey[10], *dval;
00159     const char *vkey, *dkey, *vskey;
00160     
00161     log_debug(ZONE, "building packet from object");
00162 
00163     pkt = pkt_create(sm, "iq", "result", NULL, NULL);
00164     nad_append_elem(pkt->nad, nad_add_namespace(pkt->nad, uri_VCARD, NULL), "vCard", 2);
00165 
00166     if(!os_iter_first(os))
00167         return pkt;
00168     o = os_iter_object(os);
00169 
00170     while(_iq_vcard_map[i] != NULL) {
00171         vkey = _iq_vcard_map[i];
00172         dkey = _iq_vcard_map[i + 1];
00173 
00174         i += 2;
00175 
00176         if(!os_object_get_str(os, o, dkey, &dval))
00177             continue;
00178 
00179         vskey = strchr(vkey, '/');
00180         if(vskey == NULL) {
00181             vskey = vkey;
00182             elem = 2;
00183         } else {
00184             sprintf(ekey, "%.*s", (int) (vskey - vkey), vkey);
00185             elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
00186             if(elem < 0)
00187                 elem = nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), ekey, 3);
00188             vskey++;
00189         }
00190 
00191         log_debug(ZONE, "extracted dbkey %s val '%s' for vcard key %s", dkey, dval, vkey);
00192 
00193         nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), vskey, pkt->nad->elems[elem].depth + 1);
00194         nad_append_cdata(pkt->nad, dval, strlen(dval), pkt->nad->elems[elem].depth + 2);
00195     }
00196 
00197     return pkt;
00198 }
00199 
00200 static mod_ret_t _iq_vcard_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
00201     os_t os;
00202     st_ret_t ret;
00203     pkt_t result;
00204 
00205     /* only handle vcard sets and gets that aren't to anyone */
00206     if(pkt->to != NULL || (pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
00207         return mod_PASS;
00208 
00209     /* get */
00210     if(pkt->type == pkt_IQ) {
00211         if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->jid)))
00212             return -stanza_err_RESOURCE_CONSTRAINT;
00213 
00214         ret = storage_get(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, &os);
00215         switch(ret) {
00216             case st_FAILED:
00217                 return -stanza_err_INTERNAL_SERVER_ERROR;
00218 
00219             case st_NOTIMPL:
00220                 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
00221 
00222             case st_NOTFOUND:
00223                 nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
00224                 nad_set_attr(pkt->nad, 1, -1, "to", NULL, 0);
00225                 nad_set_attr(pkt->nad, 1, -1, "from", NULL, 0);
00226 
00227                 pkt_sess(pkt, sess);
00228 
00229                 return mod_HANDLED;
00230 
00231             case st_SUCCESS:
00232                 result = _iq_vcard_to_pkt(sess->user->sm, os);
00233                 os_free(os);
00234 
00235                 nad_set_attr(result->nad, 1, -1, "type", "result", 6);
00236                 pkt_id(pkt, result);
00237 
00238                 pkt_sess(result, sess);
00239 
00240                 pkt_free(pkt);
00241 
00242                 return mod_HANDLED;
00243         }
00244 
00245         /* we never get here */
00246         pkt_free(pkt);
00247         return mod_HANDLED;
00248     }
00249 
00250     os = _iq_vcard_to_object(mi, pkt);
00251     
00252     if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->jid)))
00253         return -stanza_err_RESOURCE_CONSTRAINT;
00254 
00255     ret = storage_replace(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, os);
00256     os_free(os);
00257 
00258     switch(ret) {
00259         case st_FAILED:
00260             return -stanza_err_INTERNAL_SERVER_ERROR;
00261 
00262         case st_NOTIMPL:
00263             return -stanza_err_FEATURE_NOT_IMPLEMENTED;
00264 
00265         default:
00266             result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
00267 
00268             pkt_id(pkt, result);
00269 
00270             pkt_sess(result, sess);
00271             
00272             pkt_free(pkt);
00273 
00274             return mod_HANDLED;
00275     }
00276 
00277     /* we never get here */
00278     pkt_free(pkt);
00279     return mod_HANDLED;
00280 }
00281 
00282 /* for the special JID of your jabber server bare domain.
00283  * You can have one for every virtual host
00284  * you can populate it using your DBMS frontend
00285  */
00286 static mod_ret_t _iq_vcard_pkt_sm(mod_instance_t mi, pkt_t pkt) {
00287     os_t os;
00288     st_ret_t ret;
00289     pkt_t result;
00290 
00291     /* only handle vcard sets and gets */
00292     if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
00293         return mod_PASS;
00294 
00295     /* error them if they're trying to do a set */
00296     if(pkt->type == pkt_IQ_SET)
00297         return -stanza_err_FORBIDDEN;
00298 
00299     /* a vcard for the server */
00300     ret = storage_get(mi->sm->st, "vcard", pkt->to->domain, NULL, &os);
00301     switch(ret) {
00302         case st_FAILED:
00303             return -stanza_err_INTERNAL_SERVER_ERROR;
00304 
00305         case st_NOTIMPL:
00306             return -stanza_err_FEATURE_NOT_IMPLEMENTED;
00307 
00308         case st_NOTFOUND:
00309             return -stanza_err_ITEM_NOT_FOUND;
00310 
00311         case st_SUCCESS:
00312             result = _iq_vcard_to_pkt(mi->sm, os);
00313             os_free(os);
00314 
00315             result->to = jid_dup(pkt->from);
00316             result->from = jid_dup(pkt->to);
00317 
00318             nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
00319             nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
00320 
00321             pkt_id(pkt, result);
00322 
00323             pkt_router(result);
00324 
00325             pkt_free(pkt);
00326 
00327             return mod_HANDLED;
00328     }
00329 
00330     /* we never get here */
00331     pkt_free(pkt);
00332     return mod_HANDLED;
00333 }
00334 
00335 static mod_ret_t _iq_vcard_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
00336     os_t os;
00337     st_ret_t ret;
00338     pkt_t result;
00339 
00340     /* only handle vcard sets and gets, without resource */
00341     if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD || pkt->to->resource[0] !='\0')
00342         return mod_PASS;
00343 
00344     /* error them if they're trying to do a set */
00345     if(pkt->type == pkt_IQ_SET)
00346         return -stanza_err_FORBIDDEN;
00347 
00348     if (sm_storage_rate_limit(user->sm, pkt->from))
00349         return -stanza_err_RESOURCE_CONSTRAINT;
00350 
00351     ret = storage_get(user->sm->st, "vcard", jid_user(user->jid), NULL, &os);
00352     switch(ret) {
00353         case st_FAILED:
00354             return -stanza_err_INTERNAL_SERVER_ERROR;
00355 
00356         case st_NOTIMPL:
00357             return -stanza_err_FEATURE_NOT_IMPLEMENTED;
00358 
00359         case st_NOTFOUND:
00360             return -stanza_err_SERVICE_UNAVAILABLE;
00361 
00362         case st_SUCCESS:
00363             result = _iq_vcard_to_pkt(user->sm, os);
00364             os_free(os);
00365 
00366             result->to = jid_dup(pkt->from);
00367             result->from = jid_dup(pkt->to);
00368 
00369             nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
00370             nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
00371 
00372             pkt_id(pkt, result);
00373 
00374             pkt_router(result);
00375 
00376             pkt_free(pkt);
00377 
00378             return mod_HANDLED;
00379     }
00380 
00381     /* we never get here */
00382     pkt_free(pkt);
00383     return mod_HANDLED;
00384 }
00385 
00386 static void _iq_vcard_user_delete(mod_instance_t mi, jid_t jid) {
00387     log_debug(ZONE, "deleting vcard for %s", jid_user(jid));
00388 
00389     storage_delete(mi->sm->st, "vcard", jid_user(jid), NULL);
00390 }
00391 
00392 static void _iq_vcard_free(module_t mod) {
00393     sm_unregister_ns(mod->mm->sm, uri_VCARD);
00394     feature_unregister(mod->mm->sm, uri_VCARD);
00395     free(mod->private);
00396 }
00397 
00398 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
00399     module_t mod = mi->mod;
00400     mod_iq_vcard_t iq_vcard;
00401 
00402     if(mod->init) return 0;
00403 
00404     mod->pkt_sm = _iq_vcard_pkt_sm;
00405     mod->in_sess = _iq_vcard_in_sess;
00406     mod->pkt_user = _iq_vcard_pkt_user;
00407     mod->user_delete = _iq_vcard_user_delete;
00408     mod->free = _iq_vcard_free;
00409 
00410     ns_VCARD = sm_register_ns(mod->mm->sm, uri_VCARD);
00411     feature_register(mod->mm->sm, uri_VCARD);
00412 
00413     iq_vcard = (mod_iq_vcard_t) calloc(1, sizeof(struct _mod_iq_vcard_st));
00414     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);
00415     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);
00416     mod->private = iq_vcard;
00417 
00418     return 0;
00419 }