#include "sm.h"
void _pres_top(user_t user) {
sess_t scan;
user->top = NULL;
for(scan = user->sessions; scan != NULL; scan = scan->next) {
if(!scan->available || scan->pri < 0) continue;
if(user->top == NULL)
user->top = scan;
if(scan->pri >= user->top->pri)
user->top = scan;
}
if(user->top == NULL) {
log_debug(ZONE, "no priority >= 0 sessions, so no top session");
} else {
log_debug(ZONE, "top session for %s is now %s (priority %d)", jid_user(user->jid), jid_full(user->top->jid), user->top->pri);
}
}
void pres_update(sess_t sess, pkt_t pkt) {
item_t item;
int self;
jid_t scan, next;
sess_t sscan;
user_t user;
int user_is_local;
int user_connected = 0;
switch(pkt->type) {
case pkt_PRESENCE:
log_debug(ZONE, "available presence for session %s", jid_full(sess->jid));
if(sess->pres != NULL)
pkt_free(sess->pres);
sess->pres = pkt;
self = 0;
if(xhash_iter_first(sess->user->roster))
do {
xhash_iter_get(sess->user->roster, NULL, (void *) &item);
user_is_local = (strcmp(pkt->sm->id, item->jid->domain)==0);
if (user_is_local) {
user = xhash_get(pkt->sm->users, jid_user(item->jid));
user_connected = ((user!=NULL) && (user->sessions != NULL));
}
if(!sess->available && item->to) {
if ((!user_is_local) || (user_is_local && user_connected)) {
log_debug(ZONE, "probing %s", jid_full(item->jid));
pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_full(item->jid), jid_user(sess->jid)));
} else
log_debug(ZONE, "skipping probe to local user %s - not connected", jid_full(item->jid));
if(strcmp(jid_user(sess->jid), jid_full(item->jid)) == 0)
self = 1;
}
if(item->from && !jid_search(sess->E, item->jid)) {
if ((!user_is_local) || (user_is_local && user_connected)) {
log_debug(ZONE, "forwarding available to %s", jid_full(item->jid));
pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
} else
log_debug(ZONE, "skipping forwarding available to %s - not connected", jid_full(item->jid));
}
} while(xhash_iter_next(sess->user->roster));
if(!self && !sess->available) {
log_debug(ZONE, "probing ourselves");
pkt_router(pkt_create(sess->user->sm, "presence", "probe", jid_user(sess->jid), jid_user(sess->jid)));
}
for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
if(sscan != sess && sscan->available) {
log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
}
}
sess->available = 1;
sess->invisible = 0;
sess->pri = pkt->pri;
pkt_delay(pkt, time(NULL), jid_full(pkt->from));
break;
case pkt_PRESENCE_UN:
log_debug(ZONE, "unavailable presence for session %s", jid_full(sess->jid));
if(sess->pres != NULL) {
pkt_free(sess->pres);
sess->pres = NULL;
}
if(xhash_iter_first(sess->user->roster))
do {
xhash_iter_get(sess->user->roster, NULL, (void *) &item);
user_is_local = (strcmp(pkt->sm->id, item->jid->domain)==0);
if (user_is_local) {
user = xhash_get(pkt->sm->users, jid_user(item->jid));
user_connected = ((user!=NULL) && (user->sessions != NULL));
}
if(item->from && !jid_search(sess->E, item->jid)) {
if (!user_is_local || (user_is_local && user_connected)) {
log_debug(ZONE, "forwarding unavailable to %s", jid_full(item->jid));
pkt_router(pkt_dup(pkt, jid_full(item->jid), jid_full(sess->jid)));
} else
log_debug(ZONE, "skipping forwarding unavailable to %s - not connected", jid_full(item->jid));
}
} while(xhash_iter_next(sess->user->roster));
for(scan = sess->A; scan != NULL; scan = scan->next)
if(!pres_trust(sess->user, scan)) {
log_debug(ZONE, "forwarding unavailable to %s", jid_full(scan));
pkt_router(pkt_dup(pkt, jid_full(scan), jid_full(sess->jid)));
}
for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
if(sscan != sess && sscan->available) {
log_debug(ZONE, "forwarding available to our session %s", jid_full(sscan->jid));
pkt_router(pkt_dup(pkt, jid_full(sscan->jid), jid_full(sess->jid)));
}
}
scan = sess->A;
while(scan != NULL) {
next = scan->next;
jid_free(scan);
scan = next;
}
sess->A = NULL;
scan = sess->E;
while(scan != NULL) {
next = scan->next;
jid_free(scan);
scan = next;
}
sess->E = NULL;
sess->available = 0;
sess->invisible = 0;
pkt_free(pkt);
break;
case pkt_PRESENCE_INVIS:
log_debug(ZONE, "invisible presence for session %s", jid_full(sess->jid));
if(!sess->invisible) {
if(xhash_iter_first(sess->user->roster))
do {
xhash_iter_get(sess->user->roster, NULL, (void *) &item);
if(item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
log_debug(ZONE, "sending unavailable (invisible) to %s", jid_full(item->jid));
pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(sess->jid)));
}
} while(xhash_iter_next(sess->user->roster));
for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
if(sscan != sess && sscan->available) {
log_debug(ZONE, "sending unavailable (invisible) to our session %s", jid_full(sscan->jid));
pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(sscan->jid), jid_full(sess->jid)));
}
}
}
sess->invisible = 1;
pkt_free(pkt);
break;
default:
log_debug(ZONE, "pres_update got packet type %d, this shouldn't happen", pkt->type);
pkt_free(pkt);
return;
}
_pres_top(sess->user);
}
void pres_in(user_t user, pkt_t pkt) {
sess_t scan;
log_debug(ZONE, "type %d presence packet from %s", pkt->type, jid_full(pkt->from));
for(scan = user->sessions; scan != NULL; scan = scan->next) {
if(!scan->available)
continue;
if(jid_compare_full(pkt->from, scan->jid) == 0)
continue;
if(pkt->type == pkt_PRESENCE_PROBE) {
log_debug(ZONE, "probe from %s for %s", jid_full(pkt->from), jid_full(scan->jid));
if(!scan->invisible && pres_trust(user, pkt->from)) {
log_debug(ZONE, "responding with last presence update");
pkt_router(pkt_dup(scan->pres, jid_full(pkt->from), jid_full(scan->jid)));
}
else if(scan->invisible && pres_trust(user, pkt->from) && jid_search(scan->A, pkt->from)) {
log_debug(ZONE, "we're invisible, responding with raw available");
pkt_router(pkt_create(user->sm, "presence", NULL, jid_full(pkt->from), jid_full(scan->jid)));
}
else {
log_debug(ZONE, "probe not authorised, ignoring");
}
scan->E = jid_zap(scan->E, pkt->from);
continue;
}
log_debug(ZONE, "forwarding to %s", jid_full(scan->jid));
pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
}
pkt_free(pkt);
}
void pres_error(sess_t sess, jid_t jid) {
log_debug(ZONE, "bounced presence from %s, adding to error list", jid_full(jid));
sess->E = jid_append(sess->E, jid);
sess->A = jid_zap(sess->A, jid);
}
void pres_deliver(sess_t sess, pkt_t pkt) {
if(jid_full(pkt->to) == NULL) {
log_debug(ZONE, "invalid jid in directed presence packet");
pkt_free(pkt);
return;
}
if(pkt->type == pkt_PRESENCE) {
log_debug(ZONE, "delivering directed available presence to %s", jid_full(pkt->to));
if(!pres_trust(sess->user, pkt->to))
sess->A = jid_append(sess->A, pkt->to);
sess->E = jid_zap(sess->E, pkt->to);
pkt_router(pkt);
return;
}
if(pkt->type == pkt_PRESENCE_UN) {
log_debug(ZONE, "delivering directed unavailable presence to %s", jid_full(pkt->to));
sess->A = jid_zap(sess->A, pkt->to);
sess->E = jid_zap(sess->E, pkt->to);
pkt_router(pkt);
return;
}
log_debug(ZONE, "don't know how to deliver presence type %d to %s, dropping", pkt->type, jid_full(pkt->to));
pkt_free(pkt);
}
int pres_trust(user_t user, jid_t jid) {
item_t item;
item = xhash_get(user->roster, jid_user(jid));
if(item != NULL && item->from)
return 1;
if(jid_compare_user(user->jid, jid) == 0)
return 1;
return 0;
}
void pres_roster(sess_t sess, item_t item) {
if(!sess->available)
return;
if(!item->from && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
log_debug(ZONE, "forcing unavailable to %s after roster change", jid_full(item->jid));
pkt_router(pkt_create(sess->user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(sess->jid)));
return;
}
if(item->from && !sess->invisible && !jid_search(sess->A, item->jid) && !jid_search(sess->E, item->jid)) {
log_debug(ZONE, "forcing available to %s after roster change", jid_full(item->jid));
pkt_router(pkt_dup(sess->pres, jid_full(item->jid), jid_full(sess->jid)));
}
}
void pres_probe(user_t user) {
item_t item;
log_debug(ZONE, "full roster probe for %s", jid_user(user->jid));
if(xhash_iter_first(user->roster))
do {
xhash_iter_get(user->roster, NULL, (void *) &item);
if(item->to) {
log_debug(ZONE, "probing %s", jid_full(item->jid));
pkt_router(pkt_create(user->sm, "presence", "probe", jid_full(item->jid), jid_user(user->jid)));
}
} while(xhash_iter_next(user->roster));
}