jabberd2
2.2.16
|
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 }