#include "jsm.h"
xmlnode mod_roster_get(udata u)
{
xmlnode ret;
log_debug("mod_roster","getting %s's roster",u->user);
ret = xdb_get(u->si->xc, u->id, NS_ROSTER);
if(ret == NULL)
{
log_debug("mod_roster","creating");
ret = xmlnode_new_tag("query");
xmlnode_put_attrib(ret,"xmlns",NS_ROSTER);
}
return ret;
}
xmlnode mod_roster_get_item(xmlnode roster, jid id, char *name, int *newflag)
{
xmlnode ret;
log_debug("mod_roster","getting item %s",jid_full(id));
ret = jid_nodescan(id,roster);
if(ret == NULL)
{
log_debug("mod_roster","creating");
ret = xmlnode_insert_tag(roster,"item");
xmlnode_put_attrib(ret,"jid",jid_full(id));
if(name != NULL)
xmlnode_put_attrib(ret,"name",name);
xmlnode_put_attrib(ret,"subscription","none");
*newflag = 1;
}
return ret;
}
void mod_roster_push(udata user, xmlnode item)
{
session cur;
xmlnode packet, query;
log_debug("mod_roster","pushing %s",xmlnode2str(item));
if(xmlnode_get_attrib(item,"hidden") != NULL) return;
packet = xmlnode_new_tag("iq");
xmlnode_put_attrib(packet, "type", "set");
query = xmlnode_insert_tag(packet, "query");
xmlnode_put_attrib(query,"xmlns",NS_ROSTER);
xmlnode_insert_tag_node(query,item);
xmlnode_hide_attrib(xmlnode_get_firstchild(query),"subscribe");
for(cur = user->sessions; cur != NULL; cur = cur->next)
if(cur->roster)
js_session_to(cur, jpacket_new(xmlnode_dup(packet)));
xmlnode_free(packet);
}
#define S10N_ADD_FROM 1
#define S10N_ADD_TO 2
#define S10N_REM_FROM 3
#define S10N_REM_TO 4
void mod_roster_set_s10n(int set, xmlnode item)
{
switch(set)
{
case S10N_ADD_FROM:
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"to") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0)
xmlnode_put_attrib(item,"subscription","both");
else
xmlnode_put_attrib(item,"subscription","from");
break;
case S10N_ADD_TO:
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"from") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0)
xmlnode_put_attrib(item,"subscription","both");
else
xmlnode_put_attrib(item,"subscription","to");
break;
case S10N_REM_FROM:
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"to") == 0)
xmlnode_put_attrib(item,"subscription","to");
else
xmlnode_put_attrib(item,"subscription","none");
break;
case S10N_REM_TO:
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"from") == 0)
xmlnode_put_attrib(item,"subscription","from");
else
xmlnode_put_attrib(item,"subscription","none");
break;
}
}
void mod_roster_pforce(udata u, jid to, int uflag)
{
session s;
xmlnode x;
log_debug(ZONE,"brute forcing presence updates");
for(s = u->sessions; s != NULL; s = s->next)
{
if(uflag)
x = jutil_presnew(JPACKET__UNAVAILABLE,NULL,NULL);
else
x = xmlnode_dup(s->presence);
xmlnode_put_attrib(x,"to",jid_full(to));
js_session_from(s,jpacket_new(x));
}
}
mreturn mod_roster_out_s10n(mapi m)
{
xmlnode roster, item;
int newflag, to, from;
jid curr;
if(m->packet->to == NULL) return M_PASS;
if(jid_cmpx(jid_user(m->s->id),m->packet->to,JID_USER|JID_SERVER) == 0) return M_PASS;
log_debug("mod_roster","handling outgoing s10n");
newflag = to = from = 0;
roster = mod_roster_get(m->user);
item = mod_roster_get_item(roster,m->packet->to,NULL,&newflag);
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"to") == 0)
to = 1;
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"from") == 0)
from = 1;
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0)
to = from = 1;
switch(jpacket_subtype(m->packet))
{
case JPACKET__SUBSCRIBE:
if(!to)
{
xmlnode_put_attrib(item,"ask","subscribe");
mod_roster_push(m->user, item);
}
break;
case JPACKET__SUBSCRIBED:
mod_roster_set_s10n(S10N_ADD_FROM,item);
jid_append(js_trustees(m->user),m->packet->to);
xmlnode_hide_attrib(item,"subscribe");
xmlnode_hide_attrib(item,"hidden");
mod_roster_pforce(m->user, m->packet->to, 0);
mod_roster_push(m->user, item);
break;
case JPACKET__UNSUBSCRIBE:
if(to)
{
xmlnode_put_attrib(item,"ask","unsubscribe");
mod_roster_push(m->user, item);
}else if(newflag){
xmlnode_hide(item);
}
break;
case JPACKET__UNSUBSCRIBED:
if(from)
{
mod_roster_set_s10n(S10N_REM_FROM,item);
for(curr = js_trustees(m->user);curr != NULL && jid_cmp(curr->next,m->packet->to) != 0;curr = curr->next);
if(curr != NULL && curr->next != NULL)
curr->next = curr->next->next;
mod_roster_pforce(m->user, m->packet->to, 1);
mod_roster_push(m->user, item);
}else if(newflag){
xmlnode_hide(item);
}else{
if(xmlnode_get_attrib(item,"hidden") != NULL)
xmlnode_hide(item);
else
xmlnode_hide_attrib(item,"subscribe");
}
break;
}
xdb_set(m->si->xc, m->user->id, NS_ROSTER, roster);
xmlnode_put_attrib(m->packet->x,"from",jid_full(jid_user(m->s->id)));
jpacket_reset(m->packet);
xmlnode_free(roster);
return M_PASS;
}
mreturn mod_roster_out_iq(mapi m)
{
xmlnode roster, cur, pres, item;
int newflag;
jid id;
if(!NSCHECK(m->packet->iq,NS_ROSTER)) return M_PASS;
roster = mod_roster_get(m->user);
switch(jpacket_subtype(m->packet))
{
case JPACKET__GET:
log_debug("mod_roster","handling get request");
xmlnode_put_attrib(m->packet->x,"type","result");
m->s->roster = 1;
xmlnode_hide(m->packet->iq);
xmlnode_insert_tag_node(m->packet->x, roster);
jpacket_reset(m->packet);
for(cur = xmlnode_get_firstchild(m->packet->iq); cur != NULL; cur = xmlnode_get_nextsibling(cur))
{
if(xmlnode_get_attrib(cur,"subscribe") != NULL)
xmlnode_hide_attrib(cur,"subscribe");
if(xmlnode_get_attrib(cur,"hidden") != NULL)
xmlnode_hide(cur);
}
js_session_to(m->s,m->packet);
for(cur = xmlnode_get_firstchild(roster); cur != NULL; cur = xmlnode_get_nextsibling(cur))
if(xmlnode_get_attrib(cur,"subscribe") != NULL)
{
pres = xmlnode_new_tag("presence");
xmlnode_put_attrib(pres,"type","subscribe");
xmlnode_put_attrib(pres,"from",xmlnode_get_attrib(cur,"jid"));
if(strlen(xmlnode_get_attrib(cur,"subscribe")) > 0)
xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),xmlnode_get_attrib(cur,"subscribe"),-1);
js_session_to(m->s,jpacket_new(pres));
}
break;
case JPACKET__SET:
log_debug("mod_roster","handling set request");
for(cur = xmlnode_get_firstchild(m->packet->iq); cur != NULL; cur = xmlnode_get_nextsibling(cur))
{
if(xmlnode_get_type(cur) != NTYPE_TAG || xmlnode_get_attrib(cur,"jid") == NULL)
continue;
id = jid_new(m->packet->p,xmlnode_get_attrib(cur,"jid"));
if(id == NULL || jid_cmpx(jid_user(m->s->id),id,JID_USER|JID_SERVER) == 0) continue;
item = mod_roster_get_item(roster, id, NULL, &newflag);
xmlnode_hide(item);
if(j_strcmp(xmlnode_get_attrib(cur,"subscription"),"remove") == 0)
{
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"to") == 0 || j_strcmp(xmlnode_get_attrib(item,"ask"),"subscribe") == 0)
js_session_from(m->s,jpacket_new(jutil_presnew(JPACKET__UNSUBSCRIBE,xmlnode_get_attrib(cur,"jid"),NULL)));
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0 || j_strcmp(xmlnode_get_attrib(item,"subscription"),"from") == 0)
js_session_from(m->s,jpacket_new(jutil_presnew(JPACKET__UNSUBSCRIBED,xmlnode_get_attrib(cur,"jid"),NULL)));
mod_roster_push(m->user,cur);
continue;
}
xmlnode_put_attrib(cur,"subscription",xmlnode_get_attrib(item,"subscription"));
xmlnode_put_attrib(cur,"ask",xmlnode_get_attrib(item,"ask"));
xmlnode_insert_tag_node(roster,cur);
mod_roster_push(m->user,cur);
}
jutil_iqresult(m->packet->x);
jpacket_reset(m->packet);
js_session_to(m->s,m->packet);
log_debug(ZONE,"SROSTER: %s",xmlnode2str(roster));
xdb_set(m->si->xc, m->user->id, NS_ROSTER, roster);
break;
default:
xmlnode_free(m->packet->x);
break;
}
xmlnode_free(roster);
return M_HANDLED;
}
mreturn mod_roster_out(mapi m, void *arg)
{
if(m->packet->type == JPACKET_IQ) return mod_roster_out_iq(m);
if(m->packet->type == JPACKET_S10N) return mod_roster_out_s10n(m);
return M_IGNORE;
}
mreturn mod_roster_session(mapi m, void *arg)
{
js_mapi_session(es_OUT,m->s,mod_roster_out,NULL);
return M_PASS;
}
mreturn mod_roster_s10n(mapi m, void *arg)
{
xmlnode roster, item, reply, reply2;
char *status;
session top;
int newflag, drop, to, from, push;
push = newflag = drop = to = from = 0;
if(m->packet->type != JPACKET_S10N) return M_IGNORE;
if(m->user == NULL) return M_PASS;
if(jid_cmpx(m->packet->from,m->packet->to,JID_USER|JID_SERVER) == 0) return M_PASS;
roster = mod_roster_get(m->user);
item = mod_roster_get_item(roster,m->packet->from,xmlnode_get_attrib(m->packet->x,"name"),&newflag);
reply2 = reply = NULL;
jid_set(m->packet->to,NULL,JID_RESOURCE);
log_debug("mod_roster","s10n %s request from %s with existing item %s",xmlnode_get_attrib(m->packet->x,"type"),jid_full(m->packet->from),xmlnode2str(item));
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"to") == 0)
to = 1;
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"from") == 0)
from = 1;
if(j_strcmp(xmlnode_get_attrib(item,"subscription"),"both") == 0)
to = from = 1;
switch(jpacket_subtype(m->packet))
{
case JPACKET__SUBSCRIBE:
if(from)
{
reply = jutil_presnew(JPACKET__SUBSCRIBED,jid_full(m->packet->from),"Already Subscribed");
jid_set(m->packet->to,NULL,JID_RESOURCE);
xmlnode_put_attrib(reply,"from",jid_full(m->packet->to));
drop = 1;
reply2 = jutil_presnew(JPACKET__PROBE,jid_full(m->packet->to),NULL);
xmlnode_put_attrib(reply2,"from",jid_full(m->packet->from));
}else{
status = xmlnode_get_tag_data(m->packet->x,"status");
if(status == NULL)
xmlnode_put_attrib(item,"subscribe","");
else
xmlnode_put_attrib(item,"subscribe",status);
if(newflag)
xmlnode_put_attrib(item,"hidden","");
}
break;
case JPACKET__SUBSCRIBED:
if(to)
{
drop = 1;
}else{
xmlnode_hide_attrib(item,"ask");
mod_roster_set_s10n(S10N_ADD_TO,item);
push = 1;
}
break;
case JPACKET__UNSUBSCRIBE:
if(from)
{
xmlnode_hide_attrib(item,"subscribe");
mod_roster_set_s10n(S10N_REM_FROM,item);
if(xmlnode_get_attrib(item,"hidden") != NULL)
xmlnode_hide(item);
else
push = 1;
}else{
if(newflag)
xmlnode_hide(item);
drop = 1;
}
reply = jutil_presnew(JPACKET__UNSUBSCRIBED,jid_full(m->packet->from),"Autoreply");
jid_set(m->packet->to,NULL,JID_RESOURCE);
xmlnode_put_attrib(reply,"from",jid_full(m->packet->to));
break;
case JPACKET__UNSUBSCRIBED:
if(to || xmlnode_get_attrib(item,"ask") != NULL)
{
xmlnode_hide_attrib(item,"ask");
mod_roster_set_s10n(S10N_REM_TO,item);
push = 1;
}else{
if(newflag)
xmlnode_hide(item);
drop = 1;
}
}
xdb_set(m->si->xc, m->user->id, NS_ROSTER, roster);
if(reply != NULL)
js_deliver(m->si,jpacket_new(reply));
if(reply2 != NULL)
js_deliver(m->si,jpacket_new(reply2));
top = js_session_primary(m->user);
if(!drop && top != NULL && top->roster)
js_session_to(top,m->packet);
else
xmlnode_free(m->packet->x);
if(push)
mod_roster_push(m->user,item);
xmlnode_free(roster);
return M_HANDLED;
}
void mod_roster(jsmi si)
{
js_mapi_register(si,e_SESSION,mod_roster_session,NULL);
js_mapi_register(si,e_DELIVER,mod_roster_s10n,NULL);
}