📄 mod_presence.c
字号:
/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/#include "jsm.h"/** * @file mod_presence.c * @brief handles presences: send to subscribers, send offline on session end, probe for subscribed presences * * This module is responsible for sending presences to all contacts that subscribed to the user's presence. * It will send an unavailable presence to everybudy that got an available presence. It will send * probes to the contacts to which we have subscribed presence. * * This module is NOT responsible for handling subscriptions, they are handled in mod_roster.c. * * We have three sets of jids for each user: * - T: trusted (roster s10ns) * - A: availables (who knows were available) - stored in modpres_struct * - I: invisibles (who were invisible to) - stored in modpres_struct * * action points: * - broadcasting available presence: intersection of T and A * (don't broadcast updates to them if they don't think we're * available any more, either we told them that or their jidd i * returned a presence error) * - broadcasting unavailable presence: union of A and I * (even invisible jids need to be notified when going unavail, * since invisible is still technically an available presence * and may be used as such by a transport or other remote service) * - allowed to return presence to a probe when available: compliment * of I in T (all trusted jids, except the ones were invisible to, * may poll our presence any time) * - allowed to return presence to a probe when invisible: * intersection of T and A (of the trusted jids, only the ones * we've sent availability to can poll, and we return a generic * available presence) * - individual avail presence: forward, add to A, remove from I * - individual unavail presence: forward and remove from A, remove from I * - individual invisible presence: add to I, remove from A * - first avail: populate A with T and broadcast *//** * @brief hold configuration data for this module instance * * This structure holds all information about the configuration of this module. */typedef struct modpres_conf_struct { jid bcc; /**< who gets a blind carbon copy of our presences */ int pres_to_xdb; /**< if the (primary) presence of a user should be stored in xdb */} *modpres_conf, _modpres_conf;/** * @brief hold all data belonging to this module and a single (online) user * * This structure holds the A and I (see description of mod_presence.c) list for a user, * a flag if a user is invisible, and a list of JIDs we have to send a blind carbon copy * of each presence */typedef struct modpres_struct{ int invisible; /**< flags that the user is invisible */ jid A; /**< who knows the user is available */ jid I; /**< who knows the user is invisible */ modpres_conf conf; /**< configuration of this module's instance */} *modpres, _modpres;/** * util to check if someone knows about us * * checks if the JID id is contained in the JID list ids * * @param id the JabberID that should be checked * @param ids the list of JabberIDs * @return 1 if it is contained, 0 else */int _mod_presence_search(jid id, jid ids){ jid cur; for(cur = ids; cur != NULL; cur = cur->next) if(jid_cmp(cur,id) == 0) return 1; return 0;}/** * remove a jid from a list, returning the new list * * @param id the JabberID that should be removed * @param ids the list of JabberIDs * @return the new list */jid _mod_presence_whack(jid id, jid ids){ jid curr; if(id == NULL || ids == NULL) return NULL; /* check first */ if(jid_cmp(id,ids) == 0) return ids->next; /* check through the list, stopping at the previous list entry to a matching one */ for(curr = ids;curr != NULL && jid_cmp(curr->next,id) != 0;curr = curr->next); /* clip it out if found */ if(curr != NULL) curr->next = curr->next->next; return ids;}/** * broadcast a presence stanza to a list of JabberIDs * * this function broadcasts the stanza given as x to all users that are in the notify list of JabberIDs * as well as in the intersect list of JabberIDs. If intersect is a NULL pointer the presences are * broadcasted to all JabberIDs in the notify list. * * @param s the session of the user owning the presence * @param notify list of JabberIDs that should be notified * @param x the presence that should be broadcasted * @param intersect if non-NULL only send presence to the intersection of notify and intersect */void _mod_presence_broadcast(session s, jid notify, xmlnode x, jid intersect){ jid cur; xmlnode pres; for(cur = notify; cur != NULL; cur = cur->next) { if(intersect != NULL && !_mod_presence_search(cur,intersect)) continue; /* perform insersection search, must be in both */ s->c_out++; pres = xmlnode_dup(x); xmlnode_put_attrib(pres, "to",jid_full(cur)); js_deliver(s->si,jpacket_new(pres)); }}/** * filter the incoming presence to this session * * incoming presence probes get handled and replied if the sender is allowed to see the user's presence and there is a session (presence) * * filters presences which are sent by the user itself * * removes JabberIDs from the list of entites that know a user is online if a presence bounced * * converts incoming invisible presences to unavailable presences as users should not get invisible presences at all * * @param m the mapi structure * @param arg the modpres structure containing the module data belonging to the user's session * @return M_IGNORE if stanza is no presence, M_HANDLED if a presence should not be delivered or has been completely handled, M_PASS else */mreturn mod_presence_in(mapi m, void *arg){ modpres mp = (modpres)arg; xmlnode pres; if(m->packet->type != JPACKET_PRESENCE) return M_IGNORE; log_debug2(ZONE, LOGT_DELIVER, "incoming filter for %s",jid_full(m->s->id)); if(jpacket_subtype(m->packet) == JPACKET__PROBE) { /* reply with our presence */ if(m->s->presence == NULL) { log_debug2(ZONE, LOGT_DELIVER, "probe from %s and no presence to return",jid_full(m->packet->from)); }else if(!mp->invisible && js_trust(m->user,m->packet->from) && !_mod_presence_search(m->packet->from,mp->I)){ /* compliment of I in T */ log_debug2(ZONE, LOGT_DELIVER, "got a probe, responding to %s",jid_full(m->packet->from)); pres = xmlnode_dup(m->s->presence); xmlnode_put_attrib(pres,"to",jid_full(m->packet->from)); js_session_from(m->s, jpacket_new(pres)); }else if(mp->invisible && js_trust(m->user,m->packet->from) && _mod_presence_search(m->packet->from,mp->A)){ /* when invisible, intersection of A and T */ log_debug2(ZONE, LOGT_DELIVER, "got a probe when invisible, responding to %s",jid_full(m->packet->from)); pres = jutil_presnew(JPACKET__AVAILABLE,jid_full(m->packet->from),NULL); js_session_from(m->s, jpacket_new(pres)); }else{ log_debug2(ZONE, LOGT_DELIVER, "%s attempted to probe by someone not qualified",jid_full(m->packet->from)); } xmlnode_free(m->packet->x); return M_HANDLED; } if(m->packet->from == NULL || jid_cmp(m->packet->from,m->s->id) == 0) { /* this is our presence, don't send to ourselves */ xmlnode_free(m->packet->x); return M_HANDLED; } /* if a presence packet bounced, remove from the A list */ if(jpacket_subtype(m->packet) == JPACKET__ERROR) mp->A = _mod_presence_whack(m->packet->from, mp->A); /* doh! this is a user, they should see invisibles as unavailables */ if(jpacket_subtype(m->packet) == JPACKET__INVISIBLE) xmlnode_put_attrib(m->packet->x,"type","unavailable"); return M_PASS;}/** * process the roster to probe outgoing s10ns, and populate a list of the jids that should be notified * * this function requests the roster from xdb and does for each contact: * - if the user is subscribed to the contacts presence: send a presence probe * - if the user has a subscription from the contact: adds the contacts JabberID to the existing list given as parameter \a notify * (if this parameter is NULL, than this function does not care about other users that have subscribed to us) * * @note the argument given as \a notify is the list A, this list is initialized to contain the user itself, therefore * there is always already a first element in the list and we can just append new items. Still I don't like that we do * not pass back a pointer to the resulting list, that would allow use to handle an empty initial list as well. * * @param m the mapi structure * @param notify list where contacts that are subscribed to the users presences should be added, if this is NULL we don't add anything */void mod_presence_roster(mapi m, jid notify){ xmlnode roster, cur, pnew; jid id; int to, from; /* do our roster setup stuff */ roster = xdb_get(m->si->xc, m->user->id, NS_ROSTER); for(cur = xmlnode_get_firstchild(roster); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { id = jid_new(m->packet->p,xmlnode_get_attrib(cur,"jid")); if(id == NULL) continue; log_debug2(ZONE, LOGT_DELIVER, "roster item %s s10n=%s",jid_full(id),xmlnode_get_attrib(cur,"subscription")); /* vars */ to = from = 0; if(j_strcmp(xmlnode_get_attrib(cur,"subscription"),"to") == 0) to = 1; if(j_strcmp(xmlnode_get_attrib(cur,"subscription"),"from") == 0) from = 1; if(j_strcmp(xmlnode_get_attrib(cur,"subscription"),"both") == 0) to = from = 1; /* curiosity phase */ if(to) { log_debug2(ZONE, LOGT_DELIVER, "we're new here, probe them"); pnew = jutil_presnew(JPACKET__PROBE,jid_full(id),NULL); xmlnode_put_attrib(pnew,"from",jid_full(jid_user(m->s->id))); js_session_from(m->s, jpacket_new(pnew)); } /* notify phase, only if it's global presence */ if(from && notify != NULL) { log_debug2(ZONE, LOGT_DELIVER, "we need to notify them"); jid_append(notify, id); } } xmlnode_free(roster);}/** * store the top presence information to xdb * * @param m the mapi struct */void mod_presence_store(mapi m) { /* get the top session */ session top = js_session_primary(m->user); /* store to xdb */ xdb_set(m->si->xc, m->user->id, NS_JABBERD_STOREDPRESENCE, top ? top->presence : NULL);}/** * handles undirected outgoing presences (presences with no to attribute) * * checks that the presence's priority is in the valid range * * if the outgoing presence is an invisible presence and we are available, * we inject an unavailable presence first and reinject the unavailable * presence afterwards again (we are then not available anymore and therefore * will not do this twice for the same presence) * * If the outgoing presence is not an invisible presence, it is stored in the structure * of this session in the session manager and the presence is stamped with the * current timestamp. * * Unavailable presences are broadcasted to everyone that thinks we are online, * available presence are broadcasted to everyone that has subscribed to our presence. * * If we are already online with other resources, our existing presences are sent to * our new resource. * * Presence probes are sent out to our contacts we are subscribed to. * * @note this is our second callback for outgoing presences, mod_presence_avails() should have handled the presence first * * @todo think about if we shouldn't check the presence's priority earlier, maybe in mod_presence_avails()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -