jabberd2  2.2.17
mod_session.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002-2003 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 
45 /* union for xhash_iter_get to comply with strict-alias rules for gcc3 */
46 union xhashv
47 {
48  void **val;
50 };
51 
53  sm_t sm = mi->mod->mm->sm;
54  int ns, iq, elem, attr;
55  jid_t jid;
56  sess_t sess = (sess_t) NULL;
57  mod_ret_t ret;
58 
59  /* if we've got this namespace, its from a c2s */
60  if(pkt->nad->ecur <= 1 || (ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL)) < 0)
61  return mod_PASS;
62 
63  /* don't bother if its a failure */
64  if(pkt->type & pkt_SESS_FAILED) {
65  /* !!! check failed=1, handle */
66  pkt_free(pkt);
67  return mod_HANDLED;
68  }
69 
70  /* session commands */
71  if(pkt->type & pkt_SESS) {
72 
73  ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL);
74 
75  /* only end can get away without a target */
76  attr = nad_find_attr(pkt->nad, 1, -1, "target", NULL);
77  if(attr < 0 && pkt->type != pkt_SESS_END) {
78  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
79  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
80 
81  pkt->nad = NULL;
82  pkt_free(pkt);
83 
84  return mod_HANDLED;
85  }
86 
87  /* session start */
88  if(pkt->type == pkt_SESS) {
89  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
90 
91  if(jid != NULL)
92  sess = sess_start(sm, jid);
93 
94  if(jid == NULL || sess == NULL) {
95  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
96  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
97 
98  pkt->nad = NULL;
99  pkt_free(pkt);
100  if(jid != NULL)
101  jid_free(jid);
102 
103  return mod_HANDLED;
104  }
105 
106  /* c2s component that is handling this session */
107  strcpy(sess->c2s, pkt->rfrom->domain);
108 
109  /* remember what c2s calls us */
110  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
111  snprintf(sess->c2s_id, sizeof(sess->c2s_id), "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
112 
113  /* mark PBX session as fake */
114  if(!strncmp("PBX", sess->c2s_id, 3)) {
115  sess->fake = 1;
116  }
117 
118  /* add our id */
119  nad_set_attr(pkt->nad, 1, ns, "sm", sess->sm_id, 0);
120 
121  /* mark that it started OK */
122  nad_set_attr(pkt->nad, 1, -1, "action", "started", 7);
123 
124  /* set this SM name */
125  nad_set_attr(pkt->nad, 0, -1, "to", sm->id, 0);
126 
127  /* inform c2s */
128  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
129 
130  pkt->nad = NULL;
131  pkt_free(pkt);
132  jid_free(jid);
133 
134  return mod_HANDLED;
135  }
136 
137  /* user create */
138  if(pkt->type == pkt_SESS_CREATE) {
139  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
140 
141  if(jid == NULL || user_create(sm, jid) != 0) {
142  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
143  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
144 
145  pkt->nad = NULL;
146  pkt_free(pkt);
147  if(jid != NULL)
148  jid_free(jid);
149 
150  return mod_HANDLED;
151  }
152 
153  /* inform c2s */
154  nad_set_attr(pkt->nad, 1, -1, "action", "created", 7);
155  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
156 
157  pkt->nad = NULL;
158  pkt_free(pkt);
159  jid_free(jid);
160 
161  return mod_HANDLED;
162  }
163 
164  /* user delete */
165  if(pkt->type == pkt_SESS_DELETE) {
166  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
167  if(jid == NULL) {
168  pkt_free(pkt);
169  return mod_HANDLED;
170  }
171 
172  user_delete(sm, jid);
173 
174  /* inform c2s */
175  nad_set_attr(pkt->nad, 1, -1, "action", "deleted", 7);
176  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
177 
178  pkt->nad = NULL;
179  pkt_free(pkt);
180  jid_free(jid);
181 
182  return mod_HANDLED;
183  }
184 
185  /* get the session id */
186  attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
187  if(attr < 0) {
188  log_debug(ZONE, "no session id, bouncing");
189  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
190  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
191 
192  pkt->nad = NULL;
193  pkt_free(pkt);
194 
195  return mod_HANDLED;
196  }
197 
198  /* find the corresponding session */
199  sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
200 
201  /* active check */
202  if(sess == NULL) {
203  log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
204  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
205  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
206 
207  pkt->nad = NULL;
208  pkt_free(pkt);
209 
210  return mod_HANDLED;
211  }
212 
213  /* make sure its from them */
214  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
215  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)) {
216  log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", pkt->rfrom->domain, sess->sm_id, sess->c2s_id);
217  pkt_free(pkt);
218  return mod_HANDLED;
219  }
220 
221  /* session end */
222  if(pkt->type == pkt_SESS_END) {
223  sm_c2s_action(sess, "ended", NULL);
224  sess_end(sess);
225 
226  pkt_free(pkt);
227  return mod_HANDLED;
228  }
229 
230  log_debug(ZONE, "unknown session packet, dropping");
231  pkt_free(pkt);
232 
233  return mod_HANDLED;
234  }
235 
236  /* otherwise, its a normal packet for the session */
237 
238 /* #ifdef ENABLE_SUPERSEDED // FIXME XXX TODO clients are not yet ready for this */
239  /* check for RFC3920 session request *
240  * with RFC3920bis it is unneeded *
241  * session is activated by bind, so we just return back result */
242  if((ns = nad_find_scoped_namespace(pkt->nad, uri_XSESSION, NULL)) >= 0 &&
243  (iq = nad_find_elem(pkt->nad, 0, -1, "iq", 1)) >= 0 &&
244  (elem = nad_find_elem(pkt->nad, iq, ns, "session", 1)) >= 0) {
245  log_debug(ZONE, "session create request");
246 
247  /* build a result packet */
248  nad_drop_elem(pkt->nad, elem);
249  nad_set_attr(pkt->nad, iq, -1, "type", "result", 6);
250 
251  /* return the result */
252  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
253 
254  pkt->nad = NULL;
255  pkt_free(pkt);
256 
257  return mod_HANDLED;
258  }
259 /* #endif */
260  /* get the session id */
261  attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
262  if(attr < 0) {
263  log_debug(ZONE, "no session id, bouncing");
264  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
265  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
266 
267  pkt->nad = NULL;
268  pkt_free(pkt);
269 
270  return mod_HANDLED;
271  }
272 
273  /* find the corresponding session */
274  sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
275 
276  /* active check */
277  if(sess == NULL) {
278  log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
279  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
280  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
281 
282  pkt->nad = NULL;
283  pkt_free(pkt);
284 
285  return mod_HANDLED;
286  }
287 
288  /* make sure its from them */
289  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
290  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)) {
291  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);
292  pkt_free(pkt);
293  return mod_HANDLED;
294  }
295 
296  /* where it came from */
297  pkt->source = sess;
298 
299  /* hand it to the modules */
300  ret = mm_in_sess(pkt->sm->mm, sess, pkt);
301  switch(ret) {
302  case mod_HANDLED:
303  break;
304 
305  case mod_PASS:
306  /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */
307  if(pkt->type == pkt_IQ_RESULT)
308  break;
309  else
311 
312  default:
313  pkt_sess(pkt_error(pkt, -ret), sess);
314 
315  break;
316  }
317 
318  return mod_HANDLED;
319 }
320 
322  sess_t sess;
323  union xhashv xhv;
324 
325  /* we want unadvertisments */
326  if(pkt->from == NULL || !(pkt->rtype & route_ADV) || pkt->rtype != route_ADV_UN)
327  return mod_PASS;
328 
329  log_debug(ZONE, "component '%s' went offline, checking for sessions held there", jid_full(pkt->from));
330 
331  /* this is fairly inefficient, especially if we have a lot of sessions
332  * online, but it shouldn't be called that often (components are usually
333  * long-running) */
334 
335  xhv.sess_val = &sess;
336  if(xhash_iter_first(mi->mod->mm->sm->sessions))
337  do {
338  xhash_iter_get(mi->mod->mm->sm->sessions, NULL, NULL, xhv.val);
339  if(sess && strcmp(sess->c2s, pkt->from->domain) == 0)
340  sess_end(sess);
341  } while (xhash_iter_next(mi->mod->mm->sm->sessions));
342 
343  return mod_PASS;
344 }
345 
347  if(mi->mod->init) return 0;
348 
351 
352  return 0;
353 }