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