jabberd2  2.5.0
pres.c
Go to the documentation of this file.
1 /* vim: set et ts=4 sw=4: */
2 /*
3  * jabberd - Jabber Open Source Server
4  * Copyright (c) 2002-2003 Jeremie Miller, Thomas Muldowney,
5  * Ryan Eatmon, Robert Norris
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
20  */
21 
22 #include "sm.h"
23 
31 /*
32  * there are four entry points
33  *
34  * pres_update(sess, pkt) - presence updates from a session (T1, T2, T3)
35  * pres_in(user, pkt) - presence updates from a remote jid (T4, T5)
36  * pres_error(sess, jid) - remote jid bounced an update (T6)
37  * pres_deliver(sess, pkt) - outgoing directed presence (T7, T8)
38  */
39 
41 static void _pres_top(user_t user) {
42  sess_t scan;
43 
44  user->top = NULL;
45  user->available = 0;
46 
47  /* loop the active sessions */
48  for(scan = user->sessions; scan != NULL; scan = scan->next) {
49  if(scan->available)
50  user->available = 1;
51 
52  /* non available and/or negative presence and/or fake can't become top session */
53  if(!scan->available || scan->pri < 0 || scan->fake) continue;
54 
55  /* if we don't have one, then this is it */
56  if(user->top == NULL)
57  user->top = scan;
58 
59  /* if we have higher priority than current top, we're the new top */
60  if(scan->pri >= user->top->pri)
61  user->top = scan;
62  }
63 
64  if(user->top == NULL) {
65  log_debug(ZONE, "no priority >= 0 sessions, so no top session");
66  } else {
67  log_debug(ZONE, "top session for %s is now %s (priority %d)", jid_user(user->jid), jid_full(user->top->jid), user->top->pri);
68  }
69 }
70 
72 void pres_update(sess_t sess, pkt_t pkt) {
73  item_t item;
74  int self;
75  jid_t scan, next;
76  sess_t sscan;
77 
78  switch(pkt->type) {
79  case pkt_PRESENCE:
80  log_debug(ZONE, "available presence for session %s", jid_full(sess->jid));
81 
82  /* cache packet for later */
83  if(sess->pres != NULL)
84  pkt_free(sess->pres);
85  sess->pres = pkt;
86 
87  /* B1: forward to all in T, unless in E */
88 
89  /* loop the roster, looking for trusted */
90  self = 0;
91  if(xhash_iter_first(sess->user->roster))
92  do {
93  xhash_iter_get(sess->user->roster, NULL, NULL, (void *) &item);
94 
95  /* if we're coming available, and we can see them, we need to probe them */
96  if(!sess->available && item->to) {
97  log_debug(ZONE, "probing %s", jid_full(item->jid));
98  pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_full(item->jid), jid_user(sess->jid)));
99 
100  /* flag if we probed ourselves */
101  if(strcmp(jid_user(sess->jid), jid_full(item->jid)) == 0)
102  self = 1;
103  }
104 
105  /* if they can see us, forward */
106  if(item->from && !jid_search(sess->E, item->jid)) {
107  log_debug(ZONE, "forwarding available to %s", jid_full(item->jid));
108  pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
109  }
110  } while(xhash_iter_next(sess->user->roster));
111 
112  /* probe ourselves if we need to and didn't already */
113  if(!self && !sess->available) {
114  log_debug(ZONE, "probing ourselves");
115  pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_user(sess->jid), jid_user(sess->jid)));
116  }
117 
118  /* forward to our active sessions */
119  for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
120  if(sscan != sess && sscan->available && !sscan->fake) {
121  log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
122  pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
123  }
124  }
125 
126  /* update vars */
127  sess->available = 1;
128 
129  /* new priority */
130  sess->pri = pkt->pri;
131 
132  /* stamp the saved presence so future probes know how old it is */
133  pkt_delay(pkt, time(NULL), jid_full(pkt->from));
134 
135  break;
136 
137  case pkt_PRESENCE_UN:
138  log_debug(ZONE, "unavailable presence for session %s", jid_full(sess->jid));
139 
140  /* free cached presence */
141  if(sess->pres != NULL) {
142  pkt_free(sess->pres);
143  sess->pres = NULL;
144  }
145 
146  /* B2: forward to all in T and A, unless in E */
147 
148  /* loop the roster, looking for trusted */
149  if(xhash_iter_first(sess->user->roster))
150  do {
151  xhash_iter_get(sess->user->roster, NULL, NULL, (void *) &item);
152 
153  /* forward if they're trusted and they're not E */
154  if(item->from && !jid_search(sess->E, item->jid)) {
155 
156  log_debug(ZONE, "forwarding unavailable to %s", jid_full(item->jid));
157  pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
158  }
159  } while(xhash_iter_next(sess->user->roster));
160 
161  /* walk A and forward to untrusted */
162  for(scan = sess->A; scan != NULL; scan = scan->next)
163  if(!pres_trust(sess->user, scan)) {
164  log_debug(ZONE, "forwarding unavailable to %s", jid_full(scan));
165  pkt_router(pkt_dup(pkt, jid_full(scan), jid_full(sess->jid)));
166  }
167 
168  /* forward to our active sessions */
169  for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
170  if(sscan != sess && sscan->available && !sscan->fake) {
171  log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
172  pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
173  }
174  }
175 
176  /* drop A, E */
177  scan = sess->A;
178  while(scan != NULL) {
179  next = scan->next;
180  jid_free(scan);
181  scan = next;
182  }
183  sess->A = NULL;
184 
185  scan = sess->E;
186  while(scan != NULL) {
187  next = scan->next;
188  jid_free(scan);
189  scan = next;
190  }
191  sess->E = NULL;
192 
193  /* update vars */
194  sess->available = 0;
195 
196  /* done */
197  pkt_free(pkt);
198 
199  break;
200 
201  case pkt_PRESENCE_PROBE:
202  log_debug(ZONE, "probe presence for session %s", jid_full(sess->jid));
203  jid_reset(pkt->from, jid_full(sess->jid), -1);
204  if (pkt->to) jid_free(pkt->to);
205  pkt->to = jid_new(jid_user(sess->jid), -1);
206  pres_in(sess->user, pkt);
207  return;
208 
209  default:
210  log_write(sess->user->sm->log, LOG_ERR, "pres_update got packet type 0x%X, this shouldn't happen - dropping it", pkt->type);
211  pkt_free(pkt);
212  return;
213  }
214 
215  /* reset the top session */
216  _pres_top(sess->user);
217 }
218 
220 void pres_in(user_t user, pkt_t pkt) {
221  sess_t scan;
222 
223  log_debug(ZONE, "type 0x%X presence packet from %s", pkt->type, jid_full(pkt->from));
224 
225  /* handle probes */
226  if(pkt->type == pkt_PRESENCE_PROBE) {
227  /* unsubscribed for untrusted users */
228  if(!pres_trust(user, pkt->from)) {
229  log_debug(ZONE, "unsubscribed untrusted %s", jid_full(pkt->from));
230  pkt_router(pkt_create(user->sm, "presence", "unsubscribed", jid_full(pkt->from), jid_full(pkt->to)));
231 
232  pkt_free(pkt);
233  return;
234  }
235 
236  /* respond with last unavailable presence if no available session */
237  if(!user->available) {
238  os_t os;
239  os_object_t o;
240  nad_t nad;
241  pkt_t pres;
242 
243  /* get user last presence stanza */
244  if(storage_get(user->sm->st, "status", jid_user(user->jid), NULL, &os) == st_SUCCESS && os_iter_first(os)) {
245  o = os_iter_object(os);
246  os_object_get_nad(os, o, "xml", &nad);
247  if(nad != NULL) {
248  pres = pkt_new(pkt->sm, nad_copy(nad));
249  nad_set_attr(pres->nad, 1, -1, "type", "unavailable", 11);
250  pkt_router(pkt_dup(pres, jid_full(pkt->from), jid_user(user->jid)));
251  pkt_free(pres);
252  }
253  else {
254  pkt_router(pkt_create(user->sm, "presence", "unavailable", jid_full(pkt->from), jid_full(pkt->to)));
255  }
256  os_free(os);
257  }
258  pkt_free(pkt);
259  return;
260  }
261  }
262 
263  /* loop over each session */
264  for(scan = user->sessions; scan != NULL; scan = scan->next) {
265  /* don't deliver to unavailable sessions: B4(a) */
266  if(!scan->available || scan->fake)
267  continue;
268 
269  /* don't deliver to ourselves, lest we presence-bomb ourselves ;) */
270  if(jid_compare_full(pkt->from, scan->jid) == 0)
271  continue;
272 
273  /* handle probes */
274  if(pkt->type == pkt_PRESENCE_PROBE) {
275  log_debug(ZONE, "probe from %s for %s", jid_full(pkt->from), jid_full(scan->jid));
276 
277  /* B3: respond (already checked for T) */
278  if(pkt->to->resource[0] != '\0') {
279  /* this is a direct probe */
280  if(jid_compare_full(pkt->to, scan->jid) == 0) {
281  /* respond with simple stanza only */
282  log_debug(ZONE, "responding with simple presence");
283  pkt_router(pkt_create(user->sm, "presence", NULL, jid_full(pkt->from), jid_full(pkt->to)));
284  }
285  else
286  continue;
287  }
288  else {
289  log_debug(ZONE, "responding with last presence update");
290  pkt_router(pkt_dup(scan->pres, jid_full(pkt->from), jid_full(scan->jid)));
291  }
292 
293  /* remove from E */
294  scan->E = jid_zap(scan->E, pkt->from);
295 
296  continue;
297  }
298 
299  /* deliver to session: B4(b) */
300  log_debug(ZONE, "forwarding to %s", jid_full(scan->jid));
301  pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
302  }
303 
304  pkt_free(pkt);
305 }
306 
307 void pres_error(sess_t sess, jid_t jid) {
308  /* bounced updates: B5: add to E, remove from A */
309  log_debug(ZONE, "bounced presence from %s, adding to error list", jid_full(jid));
310  sess->E = jid_append(sess->E, jid);
311  sess->A = jid_zap(sess->A, jid);
312 }
313 
315 void pres_deliver(sess_t sess, pkt_t pkt) {
316 
317  if(jid_full(pkt->to) == NULL) {
318  log_debug(ZONE, "invalid jid in directed presence packet");
319  pkt_free(pkt);
320  return;
321  }
322 
323  if(jid_compare_user(pkt->to, sess->jid) == 0) {
324  /* this is a presence for ourselves */
325  log_debug(ZONE, "delivering directed presence to self");
326  jid_reset(pkt->from, jid_full(sess->jid), -1);
327  pres_in(sess->user, pkt);
328  return;
329  }
330 
331  if(pkt->type == pkt_PRESENCE) {
332  /* B6: forward, add to A (unless in T), remove from E */
333  log_debug(ZONE, "delivering directed available presence to %s", jid_full(pkt->to));
334  if(!pres_trust(sess->user, pkt->to))
335  sess->A = jid_append(sess->A, pkt->to);
336  sess->E = jid_zap(sess->E, pkt->to);
337  pkt_router(pkt);
338  return;
339  }
340 
341  if(pkt->type == pkt_PRESENCE_UN) {
342  /* B7: forward, remove from A and E */
343  log_debug(ZONE, "delivering directed unavailable presence to %s", jid_full(pkt->to));
344  sess->A = jid_zap(sess->A, pkt->to);
345  sess->E = jid_zap(sess->E, pkt->to);
346  pkt_router(pkt);
347  return;
348  }
349 
350  log_debug(ZONE, "don't know how to deliver presence type %d to %s, dropping", pkt->type, jid_full(pkt->to));
351 
352  pkt_free(pkt);
353 }
354 
356 int pres_trust(user_t user, jid_t jid) {
357  item_t item = NULL;
358 
359  /* get roster item with bare jid*/
360  item = xhash_get(user->roster, jid_user(jid));
361 
362  /* retry with full jid if not found */
363  if(item == NULL)
364  item = xhash_get(user->roster, jid_full(jid));
365 
366  /* trusted if they're in the roster and they can see us */
367  if(item != NULL && item->from)
368  return 1;
369 
370  /* always trust ourselves */
371  if(jid_compare_user(user->jid, jid) == 0)
372  return 1;
373 
374  return 0;
375 }
376 
378 void pres_roster(sess_t sess, item_t item) {
379  /* if we're not available, then forget it */
380  if(!sess->available)
381  return;
382 
383  /* if they were trusted previously, but aren't anymore, and we haven't
384  * explicitly sent them presence, then make them forget */
385  if(!item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
386  log_debug(ZONE, "forcing unavailable to %s after roster change", jid_full(item->jid));
387  pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(sess->jid)));
388  return;
389  }
390 
391  /* if they're now trusted and we haven't sent
392  * them directed presence, then they get to see us for the first time */
393  if(item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
394  log_debug(ZONE, "forcing available to %s after roster change", jid_full(item->jid));
395  pkt_router(pkt_dup(sess->pres, jid_full(item->jid), jid_full(sess->jid)));
396  }
397 }
398 
399 void pres_probe(user_t user) {
400  item_t item;
401 
402  log_debug(ZONE, "full roster probe for %s", jid_user(user->jid));
403 
404  /* loop the roster, looked for trusted */
405  if(xhash_iter_first(user->roster))
406  do {
407  xhash_iter_get(user->roster, NULL, NULL, (void *) &item);
408 
409  /* don't probe unless they trust us */
410  if(item->to) {
411  log_debug(ZONE, "probing %s", jid_full(item->jid));
412  pkt_router(pkt_create(user->sm, "presence", "probe", jid_full(item->jid), jid_user(user->jid)));
413  }
414  } while(xhash_iter_next(user->roster));
415 }
user_t user
user this session belongs to
Definition: sm.h:256
xht roster
roster for this user (key is full jid of item, value is item_t)
Definition: sm.h:241
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:258
Definition: nad.h:93
jid_t jid_reset(jid_t jid, const char *id, int len)
build a jid from an id
Definition: jid.c:113
data structures and prototypes for the session manager
pkt_t pres
copy of the last presence packet we received
Definition: sm.h:265
int pri
current priority of this session
Definition: sm.h:268
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:338
int pri
presence priority
Definition: sm.h:144
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:346
jid_t jid_new(const char *id, int len)
make a new jid
Definition: jid.c:81
log_t log
log context
Definition: sm.h:200
void nad_set_attr(nad_t nad, unsigned int elem, int ns, const char *name, const char *val, int vallen)
create, update, or zap any matching attr on this elem
Definition: nad.c:407
void log_write(log_t log, int level, const char *msgfmt,...)
Definition: log.c:104
jid_t A
list of jids that this session has sent directed presence to
Definition: sm.h:271
sm_t sm
sm context
Definition: sm.h:237
pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from)
duplicate pkt, replacing addresses
Definition: pkt.c:84
jid_t jid_zap(jid_t list, jid_t jid)
remove a jid_t from a list, returning the new list
Definition: jid.c:422
sm_t sm
sm context
Definition: sm.h:130
char * resource
Definition: jid.h:46
nad_t nad_copy(nad_t nad)
copy a nad
Definition: nad.c:147
int xhash_iter_next(xht h)
Definition: xhash.c:320
int pres_trust(user_t user, jid_t jid)
see if the jid is trusted (ie in the roster with s10n="from" or "both")
Definition: pres.c:356
sess_t next
next session (in a list of sessions)
Definition: sm.h:276
pkt_t pkt_new(sm_t sm, nad_t nad)
Definition: pkt.c:113
presence (unavailable)
Definition: sm.h:100
sess_t top
top priority session
Definition: sm.h:244
sess_t sessions
list of action sessions
Definition: sm.h:243
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
packet summary data wrapper
Definition: sm.h:129
int jid_search(jid_t list, jid_t jid)
util to search through jids
Definition: jid.c:412
storage_t st
storage subsystem
Definition: sm.h:211
nad_t nad
nad of the entire packet
Definition: sm.h:146
void pres_in(user_t user, pkt_t pkt)
presence updates from a remote jid - RFC 3921bis 4.3.2.
Definition: pres.c:220
void jid_free(jid_t jid)
free a jid
Definition: jid.c:286
Definition: jid.h:42
int xhash_iter_get(xht h, const char **key, int *keylen, void **val)
Definition: xhash.c:374
int jid_compare_full(jid_t a, jid_t b)
compare two full jids
Definition: jid.c:363
void pkt_router(pkt_t pkt)
Definition: pkt.c:379
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
jid_t jid_append(jid_t list, jid_t jid)
make a copy of jid, link into list (avoiding dups)
Definition: jid.c:463
roster items
Definition: sm.h:150
static void _pres_top(user_t user)
select a new top session based on current session presence
Definition: pres.c:41
presence
Definition: sm.h:99
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
int jid_compare_user(jid_t a, jid_t b)
compare the user portion of two jids
Definition: jid.c:354
void pres_error(sess_t sess, jid_t jid)
Definition: pres.c:307
int xhash_iter_first(xht h)
iteration
Definition: xhash.c:311
int fake
true if session is fake (ie.
Definition: sm.h:269
int available
true if this session is available
Definition: sm.h:267
jid_t to
Definition: sm.h:140
int from
subscription to this item (they get presence FROM us, they send presence TO us)
Definition: sm.h:159
jid_t jid
id of this item
Definition: sm.h:151
int to
Definition: sm.h:159
void pres_roster(sess_t sess, item_t item)
send presence based on roster changes
Definition: pres.c:378
void pkt_delay(pkt_t pkt, time_t t, const char *from)
add an x:delay stamp
Definition: pkt.c:508
jid_t jid
user jid (user@host)
Definition: sm.h:239
void * xhash_get(xht h, const char *key)
Definition: xhash.c:184
#define ZONE
Definition: mio_impl.h:76
struct jid_st * next
Definition: jid.h:67
jid_t E
list of jids that bounced presence updates we sent them
Definition: sm.h:272
void pres_update(sess_t sess, pkt_t pkt)
presence updates from a session
Definition: pres.c:72
presence (probe)
Definition: sm.h:101
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
void pres_probe(user_t user)
Definition: pres.c:399
void pres_deliver(sess_t sess, pkt_t pkt)
outgoing directed presence
Definition: pres.c:315
void pkt_sess(pkt_t pkt, sess_t sess)
Definition: pkt.c:459
int available
true if this user has any available session
Definition: sm.h:245
data for a single user
Definition: sm.h:234