jabberd2  2.2.16
sm/pres.c
Go to the documentation of this file.
00001 /* vim: set et ts=4 sw=4: */
00002 /*
00003  * jabberd - Jabber Open Source Server
00004  * Copyright (c) 2002-2003 Jeremie Miller, Thomas Muldowney,
00005  *                         Ryan Eatmon, Robert Norris
00006  *
00007  * This program is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00020  */
00021 
00022 #include "sm.h"
00023 
00031 /*
00032  * there are four entry points
00033  *
00034  * pres_update(sess, pkt)  - presence updates from a session    (T1, T2, T3)
00035  * pres_in(user, pkt)      - presence updates from a remote jid (T4, T5)
00036  * pres_error(sess, jid)   - remote jid bounced an update       (T6)
00037  * pres_deliver(sess, pkt) - outgoing directed presence         (T7, T8)
00038  */
00039 
00041 static void _pres_top(user_t user) {
00042     sess_t scan;
00043 
00044     user->top = NULL;
00045     user->available = 0;
00046 
00047     /* loop the active sessions */
00048     for(scan = user->sessions; scan != NULL; scan = scan->next) {
00049         if(scan->available)
00050             user->available = 1;
00051 
00052         /* non available and/or negative presence and/or fake can't become top session */
00053         if(!scan->available || scan->pri < 0 || scan->fake) continue;
00054 
00055         /* if we don't have one, then this is it */
00056         if(user->top == NULL)
00057             user->top = scan;
00058 
00059         /* if we have higher priority than current top, we're the new top */
00060         if(scan->pri >= user->top->pri)
00061             user->top = scan;
00062     }
00063 
00064     if(user->top == NULL) {
00065         log_debug(ZONE, "no priority >= 0 sessions, so no top session");
00066     } else {
00067         log_debug(ZONE, "top session for %s is now %s (priority %d)", jid_user(user->jid), jid_full(user->top->jid), user->top->pri);
00068     }
00069 }
00070 
00072 void pres_update(sess_t sess, pkt_t pkt) {
00073     item_t item;
00074     int self;
00075     jid_t scan, next;
00076     sess_t sscan;
00077 
00078     switch(pkt->type) {
00079         case pkt_PRESENCE:
00080             log_debug(ZONE, "available presence for session %s", jid_full(sess->jid));
00081 
00082             /* cache packet for later */
00083             if(sess->pres != NULL)
00084                 pkt_free(sess->pres);
00085             sess->pres = pkt;
00086 
00087             /* B1: forward to all in T, unless in E */
00088 
00089             /* loop the roster, looking for trusted */
00090             self = 0;
00091             if(xhash_iter_first(sess->user->roster))
00092             do {
00093                 xhash_iter_get(sess->user->roster, NULL, NULL, (void *) &item);
00094 
00095                 /* if we're coming available, and we can see them, we need to probe them */
00096                 if(!sess->available && item->to) {
00097                     log_debug(ZONE, "probing %s", jid_full(item->jid));
00098                     pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_full(item->jid), jid_user(sess->jid)));
00099 
00100                     /* flag if we probed ourselves */
00101                     if(strcmp(jid_user(sess->jid), jid_full(item->jid)) == 0)
00102                         self = 1;
00103                 }
00104 
00105                 /* if they can see us, forward */
00106                 if(item->from && !jid_search(sess->E, item->jid)) {
00107                     log_debug(ZONE, "forwarding available to %s", jid_full(item->jid));
00108                     pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
00109                 }
00110             } while(xhash_iter_next(sess->user->roster));
00111 
00112             /* probe ourselves if we need to and didn't already */
00113             if(!self && !sess->available) {
00114                 log_debug(ZONE, "probing ourselves");
00115                 pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_user(sess->jid), jid_user(sess->jid)));
00116             }
00117 
00118             /* forward to our active sessions */
00119             for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
00120                 if(sscan != sess && sscan->available && !sscan->fake) {
00121                     log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
00122                     pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
00123                 }
00124             }
00125 
00126             /* update vars */
00127             sess->available = 1;
00128 
00129             /* new priority */
00130             sess->pri = pkt->pri;
00131 
00132             /* stamp the saved presence so future probes know how old it is */
00133             pkt_delay(pkt, time(NULL), jid_full(pkt->from));
00134 
00135             break;
00136 
00137         case pkt_PRESENCE_UN:
00138             log_debug(ZONE, "unavailable presence for session %s", jid_full(sess->jid));
00139 
00140             /* free cached presence */
00141             if(sess->pres != NULL) {
00142                 pkt_free(sess->pres);
00143                 sess->pres = NULL;
00144             }
00145 
00146             /* B2: forward to all in T and A, unless in E */
00147 
00148             /* loop the roster, looking for trusted */
00149             if(xhash_iter_first(sess->user->roster))
00150             do {
00151                 xhash_iter_get(sess->user->roster, NULL, NULL, (void *) &item);
00152 
00153                 /* forward if they're trusted and they're not E */
00154                 if(item->from && !jid_search(sess->E, item->jid)) {
00155 
00156                     log_debug(ZONE, "forwarding unavailable to %s", jid_full(item->jid));
00157                     pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
00158                 }
00159             } while(xhash_iter_next(sess->user->roster));
00160 
00161             /* walk A and forward to untrusted */
00162             for(scan = sess->A; scan != NULL; scan = scan->next)
00163                 if(!pres_trust(sess->user, scan)) {
00164                     log_debug(ZONE, "forwarding unavailable to %s", jid_full(scan));
00165                     pkt_router(pkt_dup(pkt, jid_full(scan), jid_full(sess->jid)));
00166                 }
00167 
00168             /* forward to our active sessions */
00169             for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
00170                 if(sscan != sess && sscan->available && !sscan->fake) {
00171                     log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
00172                     pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
00173                 }
00174             }
00175 
00176             /* drop A, E */
00177             scan = sess->A;
00178             while(scan != NULL) {
00179                 next = scan->next;
00180                 jid_free(scan);
00181                 scan = next;
00182             }
00183             sess->A = NULL;
00184 
00185             scan = sess->E;
00186             while(scan != NULL) {
00187                 next = scan->next;
00188                 jid_free(scan);
00189                 scan = next;
00190             }
00191             sess->E = NULL;
00192 
00193             /* update vars */
00194             sess->available = 0;
00195 
00196             /* done */
00197             pkt_free(pkt);
00198 
00199             break;
00200 
00201         default:
00202             log_debug(ZONE, "pres_update got packet type 0x%X, this shouldn't happen", pkt->type);
00203             pkt_free(pkt);
00204             return;
00205     }
00206 
00207     /* reset the top session */
00208     _pres_top(sess->user);
00209 }
00210 
00212 void pres_in(user_t user, pkt_t pkt) {
00213     sess_t scan;
00214 
00215     log_debug(ZONE, "type 0x%X presence packet from %s", pkt->type, jid_full(pkt->from));
00216 
00217     /* handle probes */
00218     if(pkt->type == pkt_PRESENCE_PROBE) {
00219         /* unsubscribed for untrusted users */
00220         if(!pres_trust(user, pkt->from)) {
00221             log_debug(ZONE, "unsubscribed untrusted %s", jid_full(pkt->from));
00222             pkt_router(pkt_create(user->sm, "presence", "unsubscribed", jid_full(pkt->from), jid_full(pkt->to)));
00223 
00224             pkt_free(pkt);
00225             return;
00226         }
00227 
00228         /* respond with last unavailable presence if no available session */
00229         if(!user->available) {
00230             os_t os;
00231             os_object_t o;
00232             nad_t nad;
00233             pkt_t pres;
00234 
00235             /* get user last presence stanza */
00236             if(storage_get(user->sm->st, "status", jid_user(user->jid), NULL, &os) == st_SUCCESS && os_iter_first(os)) {
00237                 o = os_iter_object(os);
00238                 os_object_get_nad(os, o, "xml", &nad);
00239                 if(nad != NULL) {
00240                     pres = pkt_new(pkt->sm, nad_copy(nad));
00241                     nad_set_attr(pres->nad, 1, -1, "type", "unavailable", 11);
00242                     pkt_router(pkt_dup(pres, jid_full(pkt->from), jid_user(user->jid)));
00243                     pkt_free(pres);
00244                 }
00245                 os_free(os);
00246             }
00247             pkt_free(pkt);
00248             return;
00249         }
00250     }
00251 
00252     /* loop over each session */
00253     for(scan = user->sessions; scan != NULL; scan = scan->next) {
00254         /* don't deliver to unavailable sessions: B4(a) */
00255         if(!scan->available || scan->fake)
00256             continue;
00257 
00258         /* don't deliver to ourselves, lest we presence-bomb ourselves ;) */
00259         if(jid_compare_full(pkt->from, scan->jid) == 0)
00260             continue;
00261 
00262         /* handle probes */
00263         if(pkt->type == pkt_PRESENCE_PROBE) {
00264             log_debug(ZONE, "probe from %s for %s", jid_full(pkt->from), jid_full(scan->jid));
00265 
00266             /* B3: respond (already checked for T) */
00267             if(pkt->to->resource[0] != '\0') {
00268                 /* this is a direct probe */
00269                 if(jid_compare_full(pkt->to, scan->jid) == 0) {
00270                     /* respond with simple stanza only */
00271                     log_debug(ZONE, "responding with simple presence");
00272                     pkt_router(pkt_create(user->sm, "presence", NULL, jid_full(pkt->from), jid_full(pkt->to)));
00273                 }
00274                 else
00275                     continue;
00276             }
00277             else {
00278                 log_debug(ZONE, "responding with last presence update");
00279                 pkt_router(pkt_dup(scan->pres, jid_full(pkt->from), jid_full(scan->jid)));
00280             }
00281 
00282             /* remove from E */
00283             scan->E = jid_zap(scan->E, pkt->from);
00284 
00285             continue;
00286         }
00287 
00288         /* deliver to session: B4(b) */
00289         log_debug(ZONE, "forwarding to %s", jid_full(scan->jid));
00290         pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
00291     }
00292 
00293     pkt_free(pkt);
00294 }
00295 
00296 void pres_error(sess_t sess, jid_t jid) {
00297     /* bounced updates: B5: add to E, remove from A  */
00298     log_debug(ZONE, "bounced presence from %s, adding to error list", jid_full(jid));
00299     sess->E = jid_append(sess->E, jid);
00300     sess->A = jid_zap(sess->A, jid);
00301 }
00302 
00304 void pres_deliver(sess_t sess, pkt_t pkt) {
00305 
00306     if(jid_full(pkt->to) == NULL) {
00307         log_debug(ZONE, "invalid jid in directed presence packet");
00308         pkt_free(pkt);
00309         return;
00310     }
00311 
00312     if(pkt->type == pkt_PRESENCE) {
00313         /* B6: forward, add to A (unless in T), remove from E */
00314         log_debug(ZONE, "delivering directed available presence to %s", jid_full(pkt->to));
00315         if(!pres_trust(sess->user, pkt->to))
00316             sess->A = jid_append(sess->A, pkt->to);
00317         sess->E = jid_zap(sess->E, pkt->to);
00318         pkt_router(pkt);
00319         return;
00320     }
00321 
00322     if(pkt->type == pkt_PRESENCE_UN) {
00323         /* B7: forward, remove from A and E */
00324         log_debug(ZONE, "delivering directed unavailable presence to %s", jid_full(pkt->to));
00325         sess->A = jid_zap(sess->A, pkt->to);
00326         sess->E = jid_zap(sess->E, pkt->to);
00327         pkt_router(pkt);
00328         return;
00329     }
00330 
00331     log_debug(ZONE, "don't know how to deliver presence type %d to %s, dropping", pkt->type, jid_full(pkt->to));
00332 
00333     pkt_free(pkt);
00334 }
00335 
00337 int pres_trust(user_t user, jid_t jid) {
00338     item_t item = NULL;
00339 
00340     /* get roster item with bare jid*/
00341     item = xhash_get(user->roster, jid_user(jid));
00342 
00343     /* retry with full jid if not found */
00344     if(item == NULL)
00345         item = xhash_get(user->roster, jid_full(jid));
00346 
00347     /* trusted if they're in the roster and they can see us */
00348     if(item != NULL && item->from)
00349         return 1;
00350 
00351     /* always trust ourselves */
00352     if(jid_compare_user(user->jid, jid) == 0)
00353         return 1;
00354 
00355     return 0;
00356 }
00357 
00359 void pres_roster(sess_t sess, item_t item) {
00360     /* if we're not available, then forget it */
00361     if(!sess->available)
00362         return;
00363 
00364     /* if they were trusted previously, but aren't anymore, and we haven't
00365      * explicitly sent them presence, then make them forget */
00366     if(!item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
00367         log_debug(ZONE, "forcing unavailable to %s after roster change", jid_full(item->jid));
00368         pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(sess->jid)));
00369         return;
00370     }
00371 
00372     /* if they're now trusted and we haven't sent
00373      * them directed presence, then they get to see us for the first time */
00374     if(item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
00375         log_debug(ZONE, "forcing available to %s after roster change", jid_full(item->jid));
00376         pkt_router(pkt_dup(sess->pres, jid_full(item->jid), jid_full(sess->jid)));
00377     }
00378 }
00379 
00380 void pres_probe(user_t user) {
00381     item_t item;
00382 
00383     log_debug(ZONE, "full roster probe for %s", jid_user(user->jid));
00384 
00385     /* loop the roster, looked for trusted */
00386     if(xhash_iter_first(user->roster))
00387     do {
00388         xhash_iter_get(user->roster, NULL, NULL, (void *) &item);
00389 
00390         /* don't probe unless they trust us */
00391         if(item->to) {
00392             log_debug(ZONE, "probing %s", jid_full(item->jid));
00393             pkt_router(pkt_create(user->sm, "presence", "probe", jid_full(item->jid), jid_user(user->jid)));
00394         }
00395     } while(xhash_iter_next(user->roster));
00396 }