jabberd2  2.2.16
sm/mod_session.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002-2003 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 
00045 /* union for xhash_iter_get to comply with strict-alias rules for gcc3 */
00046 union xhashv
00047 {
00048   void **val;
00049   sess_t *sess_val;
00050 };
00051 
00052 static mod_ret_t _session_in_router(mod_instance_t mi, pkt_t pkt) {
00053     sm_t sm = mi->mod->mm->sm;
00054     int ns, iq, elem, attr;
00055     jid_t jid;
00056     sess_t sess = (sess_t) NULL;
00057     mod_ret_t ret;
00058 
00059     /* if we've got this namespace, its from a c2s */
00060     if(pkt->nad->ecur <= 1 || (ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL)) < 0)
00061         return mod_PASS;
00062 
00063     /* don't bother if its a failure */
00064     if(pkt->type & pkt_SESS_FAILED) {
00065         /* !!! check failed=1, handle */
00066         pkt_free(pkt);
00067         return mod_HANDLED;
00068     }
00069 
00070     /* session commands */
00071     if(pkt->type & pkt_SESS) {
00072 
00073         ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL);
00074 
00075         /* only end can get away without a target */
00076         attr = nad_find_attr(pkt->nad, 1, -1, "target", NULL);
00077         if(attr < 0 && pkt->type != pkt_SESS_END) {
00078             nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00079             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00080 
00081             pkt->nad = NULL;
00082             pkt_free(pkt);
00083 
00084             return mod_HANDLED;
00085         }
00086 
00087         /* session start */
00088         if(pkt->type == pkt_SESS) {
00089             jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00090 
00091             if(jid != NULL)
00092                 sess = sess_start(sm, jid);
00093 
00094             if(jid == NULL || sess == NULL) {
00095                 nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00096                 sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00097 
00098                 pkt->nad = NULL;
00099                 pkt_free(pkt);
00100                 if(jid != NULL)
00101                     jid_free(jid);
00102 
00103                 return mod_HANDLED;
00104             }
00105 
00106             /* c2s component that is handling this session */
00107             strcpy(sess->c2s, pkt->rfrom->domain);
00108 
00109             /* remember what c2s calls us */
00110             attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
00111             snprintf(sess->c2s_id, sizeof(sess->c2s_id), "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00112 
00113             /* mark PBX session as fake */
00114             if(!strncmp("PBX", sess->c2s_id, 3)) {
00115                 sess->fake = 1;
00116             }
00117 
00118             /* add our id */
00119             nad_set_attr(pkt->nad, 1, ns, "sm", sess->sm_id, 0);
00120 
00121             /* mark that it started OK */
00122             nad_set_attr(pkt->nad, 1, -1, "action", "started", 7);
00123 
00124             /* set this SM name */
00125             nad_set_attr(pkt->nad, 0, -1, "to", sm->id, 0);
00126 
00127             /* inform c2s */
00128             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00129 
00130             pkt->nad = NULL;
00131             pkt_free(pkt);
00132             jid_free(jid);
00133 
00134             return mod_HANDLED;
00135         }
00136 
00137         /* user create */
00138         if(pkt->type == pkt_SESS_CREATE) {
00139             jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00140 
00141             if(jid == NULL || user_create(sm, jid) != 0) {
00142                 nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00143                 sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00144 
00145                 pkt->nad = NULL;
00146                 pkt_free(pkt);
00147                 if(jid != NULL)
00148                     jid_free(jid);
00149 
00150                 return mod_HANDLED;
00151             }
00152 
00153             /* inform c2s */
00154             nad_set_attr(pkt->nad, 1, -1, "action", "created", 7);
00155             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00156 
00157             pkt->nad = NULL;
00158             pkt_free(pkt);
00159             jid_free(jid);
00160 
00161             return mod_HANDLED;
00162         }
00163 
00164         /* user delete */
00165         if(pkt->type == pkt_SESS_DELETE) {
00166             jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00167             if(jid == NULL) {
00168                 pkt_free(pkt);
00169                 return mod_HANDLED;
00170             }
00171 
00172             user_delete(sm, jid);
00173 
00174             /* inform c2s */
00175             nad_set_attr(pkt->nad, 1, -1, "action", "deleted", 7);
00176             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00177 
00178             pkt->nad = NULL;
00179             pkt_free(pkt);
00180             jid_free(jid);
00181 
00182             return mod_HANDLED;
00183         }
00184 
00185         /* get the session id */
00186         attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
00187         if(attr < 0) {
00188             log_debug(ZONE, "no session id, bouncing");
00189             nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00190             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00191 
00192             pkt->nad = NULL;
00193             pkt_free(pkt);
00194 
00195             return mod_HANDLED;
00196         }
00197 
00198         /* find the corresponding session */
00199         sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00200 
00201         /* active check */
00202         if(sess == NULL) {
00203             log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00204             nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00205             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00206 
00207             pkt->nad = NULL;
00208             pkt_free(pkt);
00209 
00210             return mod_HANDLED;
00211         }
00212 
00213         /* make sure its from them */
00214         attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
00215         if(attr >= 0 && (strlen(sess->c2s_id) != NAD_AVAL_L(pkt->nad, attr) || strncmp(sess->c2s_id, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)) != 0)) {
00216             log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", pkt->rfrom->domain, sess->sm_id, sess->c2s_id);
00217             pkt_free(pkt);
00218             return mod_HANDLED;
00219         }
00220 
00221         /* session end */
00222         if(pkt->type == pkt_SESS_END) {
00223             sm_c2s_action(sess, "ended", NULL);
00224             sess_end(sess);
00225 
00226             pkt_free(pkt);
00227             return mod_HANDLED;
00228         }
00229 
00230         log_debug(ZONE, "unknown session packet, dropping");
00231         pkt_free(pkt);
00232 
00233         return mod_HANDLED;
00234     }
00235 
00236     /* otherwise, its a normal packet for the session */
00237 
00238 /* #ifdef ENABLE_SUPERSEDED       // FIXME XXX TODO clients are not yet ready for this */
00239         /* check for RFC3920 session request *
00240          * with RFC3920bis it is unneeded *
00241          * session is activated by bind, so we just return back result */
00242         if((ns = nad_find_scoped_namespace(pkt->nad, uri_XSESSION, NULL)) >= 0 &&
00243            (iq = nad_find_elem(pkt->nad, 0, -1, "iq", 1)) >= 0 &&
00244            (elem = nad_find_elem(pkt->nad, iq, ns, "session", 1)) >= 0) {
00245             log_debug(ZONE, "session create request");
00246     
00247             /* build a result packet */
00248             nad_drop_elem(pkt->nad, elem);
00249             nad_set_attr(pkt->nad, iq, -1, "type", "result", 6);
00250     
00251             /* return the result */
00252             sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00253     
00254             pkt->nad = NULL;
00255             pkt_free(pkt);
00256     
00257             return mod_HANDLED;
00258         }
00259 /* #endif */
00260     /* get the session id */
00261     attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
00262     if(attr < 0) {
00263         log_debug(ZONE, "no session id, bouncing");
00264         nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00265         sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00266 
00267         pkt->nad = NULL;
00268         pkt_free(pkt);
00269 
00270         return mod_HANDLED;
00271     }
00272 
00273     /* find the corresponding session */
00274     sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00275 
00276     /* active check */
00277     if(sess == NULL) {
00278         log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00279         nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
00280         sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
00281 
00282         pkt->nad = NULL;
00283         pkt_free(pkt);
00284 
00285         return mod_HANDLED;
00286     }
00287 
00288     /* make sure its from them */
00289     attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
00290     if(attr >= 0 && (strlen(sess->c2s_id) != NAD_AVAL_L(pkt->nad, attr) || strncmp(sess->c2s_id, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)) != 0)) {
00291         log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", jid_full(pkt->rfrom), sess->sm_id, sess->c2s_id);
00292         pkt_free(pkt);
00293         return mod_HANDLED;
00294     }
00295 
00296     /* where it came from */
00297     pkt->source = sess;
00298 
00299     /* hand it to the modules */
00300     ret = mm_in_sess(pkt->sm->mm, sess, pkt);
00301     switch(ret) {
00302         case mod_HANDLED:
00303             break;
00304 
00305         case mod_PASS:
00306             /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */
00307             if(pkt->type == pkt_IQ_RESULT)
00308                break;
00309             else
00310                ret = -stanza_err_FEATURE_NOT_IMPLEMENTED;
00311 
00312         default:
00313             pkt_sess(pkt_error(pkt, -ret), sess);
00314 
00315             break;
00316     }
00317 
00318     return mod_HANDLED;
00319 }
00320 
00321 static mod_ret_t _session_pkt_router(mod_instance_t mi, pkt_t pkt) {
00322     sess_t sess;
00323     union xhashv xhv;
00324 
00325     /* we want unadvertisments */
00326     if(pkt->from == NULL || !(pkt->rtype & route_ADV) || pkt->rtype != route_ADV_UN)
00327         return mod_PASS;
00328 
00329     log_debug(ZONE, "component '%s' went offline, checking for sessions held there", jid_full(pkt->from));
00330 
00331     /* this is fairly inefficient, especially if we have a lot of sessions
00332      * online, but it shouldn't be called that often (components are usually
00333      * long-running) */
00334 
00335     xhv.sess_val = &sess;
00336     if(xhash_iter_first(mi->mod->mm->sm->sessions))
00337         do {
00338             xhash_iter_get(mi->mod->mm->sm->sessions, NULL, NULL, xhv.val);
00339             if(sess && strcmp(sess->c2s, pkt->from->domain) == 0)
00340                 sess_end(sess);
00341         } while (xhash_iter_next(mi->mod->mm->sm->sessions));
00342 
00343     return mod_PASS;
00344 }
00345 
00346 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
00347     if(mi->mod->init) return 0;
00348 
00349     mi->mod->in_router = _session_in_router;
00350     mi->mod->pkt_router = _session_pkt_router;
00351 
00352     return 0;
00353 }