#include "jsm.h"
typedef struct modpres_struct
{
int invisible;
jid A;
jid I;
jid bcc;
} *modpres, _modpres;
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;
}
jid _mod_presence_whack(jid id, jid ids)
{
jid curr;
if(id == NULL || ids == NULL) return NULL;
if(jid_cmp(id,ids) == 0) return ids->next;
for(curr = ids;curr != NULL && jid_cmp(curr->next,id) != 0;curr = curr->next);
if(curr != NULL)
curr->next = curr->next->next;
return ids;
}
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;
s->c_out++;
pres = xmlnode_dup(x);
xmlnode_put_attrib(pres, "to",jid_full(cur));
js_deliver(s->si,jpacket_new(pres));
}
}
mreturn mod_presence_in(mapi m, void *arg)
{
modpres mp = (modpres)arg;
xmlnode pres;
if(m->packet->type != JPACKET_PRESENCE) return M_IGNORE;
log_debug("mod_presence","incoming filter for %s",jid_full(m->s->id));
if(jpacket_subtype(m->packet) == JPACKET__PROBE)
{
if(m->s->presence == NULL)
{
log_debug("mod_presence","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)){
log_debug("mod_presence","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)){
log_debug("mod_presence","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_debug("mod_presence","%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)
{
xmlnode_free(m->packet->x);
return M_HANDLED;
}
if(jpacket_subtype(m->packet) == JPACKET__ERROR)
mp->A = _mod_presence_whack(m->packet->from, mp->A);
if(jpacket_subtype(m->packet) == JPACKET__INVISIBLE)
xmlnode_put_attrib(m->packet->x,"type","unavailable");
return M_PASS;
}
void mod_presence_roster(mapi m, jid notify)
{
xmlnode roster, cur, pnew;
jid id;
int to, from;
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_debug(ZONE,"roster item %s s10n=%s",jid_full(id),xmlnode_get_attrib(cur,"subscription"));
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;
if(to)
{
log_debug(ZONE,"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));
}
if(from && notify != NULL)
{
log_debug(ZONE,"we need to notify them");
jid_append(notify, id);
}
}
xmlnode_free(roster);
}
mreturn mod_presence_out(mapi m, void *arg)
{
xmlnode pnew, delay;
modpres mp = (modpres)arg;
session top;
int oldpri;
if(m->packet->type != JPACKET_PRESENCE) return M_IGNORE;
if(m->packet->to != NULL || jpacket_subtype(m->packet) == JPACKET__PROBE || jpacket_subtype(m->packet) == JPACKET__ERROR) return M_PASS;
log_debug("mod_presence","new presence from %s of %s",jid_full(m->s->id),xmlnode2str(m->packet->x));
top = js_session_primary(m->user);
oldpri = m->s->priority;
if(jpacket_subtype(m->packet) == JPACKET__INVISIBLE)
{
log_debug(ZONE,"handling invisible mode request");
if(oldpri >= 0)
{
js_session_from(m->s, jpacket_new(jutil_presnew(JPACKET__UNAVAILABLE,NULL,NULL)));
js_session_from(m->s, m->packet);
return M_HANDLED;
}
mp->invisible = 1;
mod_presence_roster(m, NULL);
m->s->priority = j_atoi(xmlnode_get_tag_data(m->packet->x,"priority"),0);
xmlnode_free(m->packet->x);
return M_HANDLED;
}
xmlnode_free(m->s->presence);
m->s->presence = xmlnode_dup(m->packet->x);
m->s->priority = jutil_priority(m->packet->x);
delay = xmlnode_insert_tag(m->s->presence,"x");
xmlnode_put_attrib(delay,"xmlns",NS_DELAY);
xmlnode_put_attrib(delay,"from",jid_full(m->s->id));
xmlnode_put_attrib(delay,"stamp",jutil_timestamp());
log_debug(ZONE,"presence oldp %d newp %d top %X",oldpri,m->s->priority,top);
if(m->s->priority < 0)
{
if(!mp->invisible)
_mod_presence_broadcast(m->s,mp->bcc,m->packet->x,NULL);
_mod_presence_broadcast(m->s,mp->A,m->packet->x,NULL);
_mod_presence_broadcast(m->s,mp->I,m->packet->x,NULL);
mp->invisible = 0;
if(mp->A != NULL)
mp->A->next = NULL;
mp->I = NULL;
xmlnode_free(m->packet->x);
return M_HANDLED;
}
if(oldpri >= 0 && !mp->invisible)
{
_mod_presence_broadcast(m->s,mp->A,m->packet->x,js_trustees(m->user));
xmlnode_free(m->packet->x);
return M_HANDLED;
}
mp->invisible = 0;
pnew = jutil_presnew(JPACKET__PROBE,jid_full(jid_user(m->s->id)),NULL);
xmlnode_put_attrib(pnew,"from",jid_full(jid_user(m->s->id)));
js_session_from(m->s, jpacket_new(pnew));
mod_presence_roster(m,mp->A);
_mod_presence_broadcast(m->s,mp->bcc,m->packet->x,NULL);
_mod_presence_broadcast(m->s,mp->A,m->packet->x,NULL);
xmlnode_free(m->packet->x);
return M_HANDLED;
}
mreturn mod_presence_avails(mapi m, void *arg)
{
modpres mp = (modpres)arg;
if(m->packet->type != JPACKET_PRESENCE) return M_IGNORE;
if(m->packet->to == NULL) return M_PASS;
log_debug(ZONE,"track presence sent to jids");
if(jpacket_subtype(m->packet) == JPACKET__INVISIBLE)
{
if(mp->I == NULL)
mp->I = jid_new(m->s->p,jid_full(m->packet->to));
else
jid_append(mp->I, m->packet->to);
mp->A = _mod_presence_whack(m->packet->to,mp->A);
return M_PASS;
}
mp->I = _mod_presence_whack(m->packet->to,mp->I);
if(jpacket_subtype(m->packet) == JPACKET__AVAILABLE)
jid_append(mp->A, m->packet->to);
if(jpacket_subtype(m->packet) == JPACKET__UNAVAILABLE)
mp->A = _mod_presence_whack(m->packet->to,mp->A);
return M_PASS;
}
mreturn mod_presence_avails_end(mapi m, void *arg)
{
modpres mp = (modpres)arg;
log_debug("mod_presence","avail tracker guarantee checker");
xmlnode_put_attrib(m->s->presence, "from",jid_full(m->s->id));
_mod_presence_broadcast(m->s, mp->bcc, m->s->presence, NULL);
_mod_presence_broadcast(m->s, mp->A, m->s->presence, NULL);
_mod_presence_broadcast(m->s, mp->I, m->s->presence, NULL);
return M_PASS;
}
mreturn mod_presence_session(mapi m, void *arg)
{
jid bcc = (jid)arg;
modpres mp;
mp = pmalloco(m->s->p, sizeof(_modpres));
mp->A = jid_user(m->s->id);
mp->bcc = bcc;
js_mapi_session(es_IN, m->s, mod_presence_in, mp);
js_mapi_session(es_OUT, m->s, mod_presence_avails, mp);
js_mapi_session(es_OUT, m->s, mod_presence_out, mp);
js_mapi_session(es_END, m->s, mod_presence_avails_end, mp);
return M_PASS;
}
mreturn mod_presence_deliver(mapi m, void *arg)
{
session cur;
if(m->packet->type != JPACKET_PRESENCE) return M_IGNORE;
log_debug("mod_presence","deliver phase");
if(m->user != NULL && m->packet->to->resource == NULL && js_session_primary(m->user) != NULL)
{
log_debug("mod_presence","broadcasting to %s",m->user->user);
for(cur = m->user->sessions; cur != NULL; cur = cur->next)
{
if(cur->priority < 0) continue;
js_session_to(cur, jpacket_new(xmlnode_dup(m->packet->x)));
}
if(jpacket_subtype(m->packet) != JPACKET__PROBE)
{
xmlnode_free(m->packet->x);
return M_HANDLED;
}
}
return M_PASS;
}
void mod_presence(jsmi si)
{
xmlnode cfg = js_config(si, "presence");
jid bcc = NULL;
log_debug("mod_presence","init");
for(cfg = xmlnode_get_firstchild(cfg); cfg != NULL; cfg = xmlnode_get_nextsibling(cfg))
{
if(xmlnode_get_type(cfg) != NTYPE_TAG || j_strcmp(xmlnode_get_name(cfg),"bcc") != 0) continue;
if(bcc == NULL)
bcc = jid_new(si->p,xmlnode_get_data(cfg));
else
jid_append(bcc,jid_new(si->p,xmlnode_get_data(cfg)));
}
js_mapi_register(si,e_DELIVER, mod_presence_deliver, NULL);
js_mapi_register(si,e_SESSION, mod_presence_session, (void*)bcc);
}