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
24 /** @file sm/pres.c
25 * @brief presence tracker
26 * @author Robert Norris
27 * $Date: 2005/06/02 04:48:25 $
28 * $Revision: 1.41 $
29 */
30
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
40 /** select a new top session based on current session presence */
_pres_top(user_t user)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
71 /** presence updates from a session */
pres_update(sess_t sess,pkt_t pkt)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
219 /** presence updates from a remote jid - RFC 3921bis 4.3.2. */
pres_in(user_t user,pkt_t pkt)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
pres_error(sess_t sess,jid_t jid)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
314 /** outgoing directed presence */
pres_deliver(sess_t sess,pkt_t pkt)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
355 /** see if the jid is trusted (ie in the roster with s10n="from" or "both") */
pres_trust(user_t user,jid_t jid)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
377 /** send presence based on roster changes */
pres_roster(sess_t sess,item_t item)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
pres_probe(user_t user)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 }
416