jabberd2  2.3.3
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  default:
202  log_debug(ZONE, "pres_update got packet type 0x%X, this shouldn't happen", pkt->type);
203  pkt_free(pkt);
204  return;
205  }
206 
207  /* reset the top session */
208  _pres_top(sess->user);
209 }
210 
212 void pres_in(user_t user, pkt_t pkt) {
213  sess_t scan;
214 
215  log_debug(ZONE, "type 0x%X presence packet from %s", pkt->type, jid_full(pkt->from));
216 
217  /* handle probes */
218  if(pkt->type == pkt_PRESENCE_PROBE) {
219  /* unsubscribed for untrusted users */
220  if(!pres_trust(user, pkt->from)) {
221  log_debug(ZONE, "unsubscribed untrusted %s", jid_full(pkt->from));
222  pkt_router(pkt_create(user->sm, "presence", "unsubscribed", jid_full(pkt->from), jid_full(pkt->to)));
223 
224  pkt_free(pkt);
225  return;
226  }
227 
228  /* respond with last unavailable presence if no available session */
229  if(!user->available) {
230  os_t os;
231  os_object_t o;
232  nad_t nad;
233  pkt_t pres;
234 
235  /* get user last presence stanza */
236  if(storage_get(user->sm->st, "status", jid_user(user->jid), NULL, &os) == st_SUCCESS && os_iter_first(os)) {
237  o = os_iter_object(os);
238  os_object_get_nad(os, o, "xml", &nad);
239  if(nad != NULL) {
240  pres = pkt_new(pkt->sm, nad_copy(nad));
241  nad_set_attr(pres->nad, 1, -1, "type", "unavailable", 11);
242  pkt_router(pkt_dup(pres, jid_full(pkt->from), jid_user(user->jid)));
243  pkt_free(pres);
244  }
245  os_free(os);
246  }
247  pkt_free(pkt);
248  return;
249  }
250  }
251 
252  /* loop over each session */
253  for(scan = user->sessions; scan != NULL; scan = scan->next) {
254  /* don't deliver to unavailable sessions: B4(a) */
255  if(!scan->available || scan->fake)
256  continue;
257 
258  /* don't deliver to ourselves, lest we presence-bomb ourselves ;) */
259  if(jid_compare_full(pkt->from, scan->jid) == 0)
260  continue;
261 
262  /* handle probes */
263  if(pkt->type == pkt_PRESENCE_PROBE) {
264  log_debug(ZONE, "probe from %s for %s", jid_full(pkt->from), jid_full(scan->jid));
265 
266  /* B3: respond (already checked for T) */
267  if(pkt->to->resource[0] != '\0') {
268  /* this is a direct probe */
269  if(jid_compare_full(pkt->to, scan->jid) == 0) {
270  /* respond with simple stanza only */
271  log_debug(ZONE, "responding with simple presence");
272  pkt_router(pkt_create(user->sm, "presence", NULL, jid_full(pkt->from), jid_full(pkt->to)));
273  }
274  else
275  continue;
276  }
277  else {
278  log_debug(ZONE, "responding with last presence update");
279  pkt_router(pkt_dup(scan->pres, jid_full(pkt->from), jid_full(scan->jid)));
280  }
281 
282  /* remove from E */
283  scan->E = jid_zap(scan->E, pkt->from);
284 
285  continue;
286  }
287 
288  /* deliver to session: B4(b) */
289  log_debug(ZONE, "forwarding to %s", jid_full(scan->jid));
290  pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
291  }
292 
293  pkt_free(pkt);
294 }
295 
296 void pres_error(sess_t sess, jid_t jid) {
297  /* bounced updates: B5: add to E, remove from A */
298  log_debug(ZONE, "bounced presence from %s, adding to error list", jid_full(jid));
299  sess->E = jid_append(sess->E, jid);
300  sess->A = jid_zap(sess->A, jid);
301 }
302 
304 void pres_deliver(sess_t sess, pkt_t pkt) {
305 
306  if(jid_full(pkt->to) == NULL) {
307  log_debug(ZONE, "invalid jid in directed presence packet");
308  pkt_free(pkt);
309  return;
310  }
311 
312  if(pkt->type == pkt_PRESENCE) {
313  /* B6: forward, add to A (unless in T), remove from E */
314  log_debug(ZONE, "delivering directed available presence to %s", jid_full(pkt->to));
315  if(!pres_trust(sess->user, pkt->to))
316  sess->A = jid_append(sess->A, pkt->to);
317  sess->E = jid_zap(sess->E, pkt->to);
318  pkt_router(pkt);
319  return;
320  }
321 
322  if(pkt->type == pkt_PRESENCE_UN) {
323  /* B7: forward, remove from A and E */
324  log_debug(ZONE, "delivering directed unavailable presence to %s", jid_full(pkt->to));
325  sess->A = jid_zap(sess->A, pkt->to);
326  sess->E = jid_zap(sess->E, pkt->to);
327  pkt_router(pkt);
328  return;
329  }
330 
331  log_debug(ZONE, "don't know how to deliver presence type %d to %s, dropping", pkt->type, jid_full(pkt->to));
332 
333  pkt_free(pkt);
334 }
335 
337 int pres_trust(user_t user, jid_t jid) {
338  item_t item = NULL;
339 
340  /* get roster item with bare jid*/
341  item = xhash_get(user->roster, jid_user(jid));
342 
343  /* retry with full jid if not found */
344  if(item == NULL)
345  item = xhash_get(user->roster, jid_full(jid));
346 
347  /* trusted if they're in the roster and they can see us */
348  if(item != NULL && item->from)
349  return 1;
350 
351  /* always trust ourselves */
352  if(jid_compare_user(user->jid, jid) == 0)
353  return 1;
354 
355  return 0;
356 }
357 
359 void pres_roster(sess_t sess, item_t item) {
360  /* if we're not available, then forget it */
361  if(!sess->available)
362  return;
363 
364  /* if they were trusted previously, but aren't anymore, and we haven't
365  * explicitly sent them presence, then make them forget */
366  if(!item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
367  log_debug(ZONE, "forcing unavailable to %s after roster change", jid_full(item->jid));
368  pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(sess->jid)));
369  return;
370  }
371 
372  /* if they're now trusted and we haven't sent
373  * them directed presence, then they get to see us for the first time */
374  if(item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
375  log_debug(ZONE, "forcing available to %s after roster change", jid_full(item->jid));
376  pkt_router(pkt_dup(sess->pres, jid_full(item->jid), jid_full(sess->jid)));
377  }
378 }
379 
380 void pres_probe(user_t user) {
381  item_t item;
382 
383  log_debug(ZONE, "full roster probe for %s", jid_user(user->jid));
384 
385  /* loop the roster, looked for trusted */
386  if(xhash_iter_first(user->roster))
387  do {
388  xhash_iter_get(user->roster, NULL, NULL, (void *) &item);
389 
390  /* don't probe unless they trust us */
391  if(item->to) {
392  log_debug(ZONE, "probing %s", jid_full(item->jid));
393  pkt_router(pkt_create(user->sm, "presence", "probe", jid_full(item->jid), jid_user(user->jid)));
394  }
395  } while(xhash_iter_next(user->roster));
396 }
user_t user
user this session belongs to
Definition: sm.h:255
xht roster
roster for this user (key is full jid of item, value is item_t)
Definition: sm.h:240
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:257
Definition: nad.h:93
data structures and prototypes for the session manager
pkt_t pres
copy of the last presence packet we received
Definition: sm.h:264
int pri
current priority of this session
Definition: sm.h:267
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:339
int pri
presence priority
Definition: sm.h:144
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:347
jid_t A
list of jids that this session has sent directed presence to
Definition: sm.h:270
sm_t sm
sm context
Definition: sm.h:236
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:423
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:145
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:337
void nad_set_attr(nad_t nad, 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:375
sess_t next
next session (in a list of sessions)
Definition: sm.h:275
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:243
sess_t sessions
list of action sessions
Definition: sm.h:242
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:413
storage_t st
storage subsystem
Definition: sm.h:210
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:212
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:364
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:464
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:355
void pres_error(sess_t sess, jid_t jid)
Definition: pres.c:296
int xhash_iter_first(xht h)
iteration
Definition: xhash.c:311
int fake
true if session is fake (ie.
Definition: sm.h:268
int available
true if this session is available
Definition: sm.h:266
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:359
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:238
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:271
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:380
void pres_deliver(sess_t sess, pkt_t pkt)
outgoing directed presence
Definition: pres.c:304
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:244
data for a single user
Definition: sm.h:233