📄 mod_groups.c
字号:
/* update the jpacket with the data in jp->x */ jpacket_reset(jp); /* if we return the result of a specific group (not the list), let the user register the group */ if (gid) { xmlnode_insert_cdata(xmlnode_insert_tag(jp->iq,"ns"),NS_REGISTER,-1); xmlnode_free(info); } /* send the result to the user */ js_session_to(m->s,jp);}/** * handle roster requests by users * * Send the user all group members of users, that are in the same groups. * * @param mi the mod_groups_i structure containing the module internal data * @param m the mapi_struct containing the request for the roster */void mod_groups_roster(mod_groups_i mi, mapi m) { xmlnode groups, users, cur, roster; pool p; udata u = m->user; char *gid, *host = m->user->id->server; /* get group the user is a member of */ if ((groups = mod_groups_get_current(mi,u->id)) == NULL) return; p = xmlnode_pool(groups); roster = jutil_iqnew(JPACKET__SET,NS_ROSTER); /* push each group */ for (cur = xmlnode_get_firstchild(groups); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { if (xmlnode_get_type(cur) != NTYPE_TAG) continue; gid = xmlnode_get_attrib(cur,"id"); users = mod_groups_get_users(mi,p,host,gid); if (users != NULL) { xmlnode info; char *gn; info = mod_groups_get_info(mi,p,host,gid); gn = xmlnode_get_tag_data(info,"name"); mod_groups_roster_insert(u,roster,users,gn ? gn : gid,1); xmlnode_free(info); } else { log_debug2(ZONE, LOGT_DELIVER, "Failed to get users for group"); } } mod_groups_roster_push(m->s,roster,0); xmlnode_free(groups);}/** * handle iq stanzas sent to the server address * * Handled iq types are jabber:iq:roster requests, that are passed to mod_groups_roster(), * jabber:iq:browse to browse for groups, and jabber:iq:register to join groups. * * Browsing is delegated to mod_groups_browse_get() for get requests, * and mod_groups_browse_set() for set requests. * * Registeration is delegated to mod_groups_register_get() for get requests, * and mod_groups_register_set() for set requests. * * Requests in other namespaces to a resource that starts with "groups/" or is just * "groups" are rejected with an error. * * @param mi the mod_groups_i structure containing module internal data * @param m the mapi_struct containing the iq query * @return M_HANDLED if the packet has been finally handled, M_PASS if other modules should handle the packet */mreturn mod_groups_iq(mod_groups_i mi, mapi m) { char *ns, *res; int type; ns = xmlnode_get_attrib(m->packet->iq,"xmlns"); /* handle roster gets */ type = jpacket_subtype(m->packet); if (j_strcmp(ns,NS_ROSTER) == 0) { if (jpacket_subtype(m->packet) == JPACKET__GET) { log_debug2(ZONE, LOGT_DELIVER, "Roster request"); mod_groups_roster(mi,m); } return M_PASS; } /* handle iq's to groups */ res = m->packet->to ? m->packet->to->resource : NULL; if (res && strncmp(res,"groups",6) == 0 && (strlen(res) == 6 || res[6] == '/')) { if (j_strcmp(ns,NS_BROWSE) == 0) { log_debug2(ZONE, LOGT_DELIVER, "Browse request"); if (type == JPACKET__GET) mod_groups_browse_get(mi,m); else if (type == JPACKET__SET) mod_groups_browse_set(mi,m); else xmlnode_free(m->packet->x); } else if (j_strcmp(ns,NS_REGISTER) == 0) { log_debug2(ZONE, LOGT_DELIVER, "Register request"); if (type == JPACKET__GET) mod_groups_register_get(mi,m); else if (type == JPACKET__SET) mod_groups_register_set(mi,m); else xmlnode_free(m->packet->x); } else { js_bounce_xmpp(m->si,m->packet->x,XTERROR_NOTALLOWED); } return M_HANDLED; } return M_PASS;}/** * handle undirected presence stanzas sent by a user * * Check if the user is member of any groups, if yes get the group configuration. * Send presences to all members with "subscription" both and probe all members * of the group for their presence. * * @param mi the mod_groups_i module instance data * @param m the mapi_struct containing the presence stanza */void mod_groups_presence(mod_groups_i mi, mapi m) { grouptab gt; session s = m->s; udata u = m->user; xmlnode groups, cur; /* is the user member of any group? If not just ignore the presence */ if ((groups = mod_groups_get_current(mi,u->id)) == NULL) return; log_debug2(ZONE, LOGT_DELIVER, "Getting groups for %s",jid_full(u->id)); /* get each group */ for (cur = xmlnode_get_firstchild(groups); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { char *gid; if ((gid = xmlnode_get_attrib(cur,"id")) == NULL) continue; /* get the grouptab for this group or create a new one if it does not yet exist */ gt = GROUP_GET(mi,gid); if(j_strcmp(xmlnode_get_attrib(cur,"type"),"both") == 0) mod_groups_presence_from(s,gt,m->packet->x); /* if we are new or our old priority was less then -128 then "probe" the group members */ if (js_session_primary(m->user) || m->s->priority < -128) mod_groups_presence_to(s,gt); } xmlnode_free(groups);}/** * handle packets a user is sending * * If the user is sending an undirected presence stanza it is passed to mod_groups_presence() for processing * and always M_PASS is returned. * * If the user is sending an iq stanza, it is passed to mod_groups_iq() for processing and the * return value of this function is returned. * * Other stanzas than presence and iq are ignored and M_IGNORE is returned. * * @param m the mapi_struct containing the stanza * @param arg the mod_groups_i module instance data * @return M_IGNORE for other stanzas than presence and iq, M_PASS if other modules should process the packet, M_HANDLED if it is fully processed */mreturn mod_groups_out(mapi m, void *arg) { mod_groups_i mi = (mod_groups_i) arg; if (m->packet->type == JPACKET_PRESENCE) { if (m->packet->to == NULL) mod_groups_presence(mi,m); return M_PASS; } else if (m->packet->type == JPACKET_IQ) { return mod_groups_iq(mi,m); } return M_IGNORE;}/** * callback to be notified about ended sessions (users that are going offline) * * @param m the mapi_struct containing the offline event * @param arg mod_groups_i module instance data * @return always M_PASS */mreturn mod_groups_end(mapi m, void *arg) { mod_groups_i mi = (mod_groups_i) arg; xmlnode groups, cur; udata u = m->user; jid id = u->id; grouptab gt; if (js_session_primary(u) != NULL || (groups = mod_groups_get_current(mi,id)) == NULL) return M_PASS; log_debug2(ZONE, LOGT_DELIVER, "removing user from table"); for (cur = xmlnode_get_firstchild(groups); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { gt = (grouptab) xhash_get(mi->groups,xmlnode_get_attrib(cur,"id")); if (gt == NULL) continue; if(j_strcmp(xmlnode_get_attrib(cur,"type"),"both") == 0) xhash_zap(gt->from,jid_full(id)); xhash_zap(gt->to,jid_full(id)); } xmlnode_free(groups); return M_PASS;}/** * register session related callbacks if a new session is established * * @param m mapi_struct containing the session establishment request * @param arg pointer to the mod_groups_i structure containing module internal data * @return always M_PASS */mreturn mod_groups_session(mapi m, void *arg) { js_mapi_session(es_OUT,m->s,mod_groups_out,arg); js_mapi_session(es_END,m->s,mod_groups_end,arg); return M_PASS;}/** * xhash_walker() to walk the list of members of a group and send a message to them * * @param h the xht containing the members * @param key unused/ignored * @param val the udata_struct for a single recipient * @param arg xmlnode containing the message */void mod_groups_message_walk(xht h, const char *key, void *val, void *arg) { xmlnode m = (xmlnode) arg; udata u = (udata) val; m = xmlnode_dup(m); xmlnode_put_attrib(m,"to",jid_full(u->id)); js_deliver(u->si,jpacket_new(m));}/** * broadcast a message to all (online) members of a group * * @param mi the mod_groups_i module instance data * @param msg the message to broadcast * @param gid to which group the message should be broadcasted */void mod_groups_message_online(mod_groups_i mi, xmlnode msg, char *gid) { grouptab gt; log_debug2(ZONE, LOGT_DELIVER, "broadcast message to '%s'",gid); gt = (grouptab) xhash_get(mi->groups,gid); if (gt != NULL) { /* the group address becomes the sender of the message */ xmlnode_put_attrib(msg,"from",xmlnode_get_attrib(msg,"to")); /* existing recipient gets replaced, hide it */ xmlnode_hide_attrib(msg,"to"); /* send a copy to each member of the group */ xhash_walk(gt->from,mod_groups_message_walk,(void *) msg); } xmlnode_free(msg);}/** * handle messages sent to the server address * * only messages are handled if they are addressed to a resource of the server, * that starts with "groups/" * * @param m the mapi_struct containing the message * @param arg the mod_groups_i structure containing module configuration * @return M_IGNORE if not a message stanza, M_HANDLED if the packet has been handled, M_PASS else */mreturn mod_groups_message(mapi m, void *arg) { mod_groups_i mi = (mod_groups_i) arg; xmlnode info; jpacket jp = m->packet; char *gid; if(jp->type != JPACKET_MESSAGE) return M_IGNORE; if(jp->to == NULL || j_strncmp(jp->to->resource,"groups/",7) != 0) return M_PASS; /* circular safety: do not handle messages that contain an x element in the jabber:iq:delay namespace */ if(xmlnode_get_tag(jp->x,"x?xmlns=" NS_DELAY) != NULL) { xmlnode_free(jp->x); return M_HANDLED; } /* process the resource, it has the form "groups/" followed by the desired group id */ gid = strchr(jp->to->resource,'/'); if (gid == NULL || *++gid == '\0') { /* there is no group id: bounce! */ js_bounce_xmpp(m->si,jp->x,XTERROR_NOTACCEPTABLE); return M_HANDLED; } /* get the <info/> element for a group */ info = mod_groups_get_info(mi,jp->p,jp->to->server,gid); if (info == NULL) { /* there is no such group available */ js_bounce_xmpp(m->si,jp->x,XTERROR_NOTFOUND); return M_HANDLED; } /* check if this user has write access to the group */ if (xmlnode_get_tag(info,spools(jp->p,"write/user=",jid_full(jp->from),jp->p)) != NULL) mod_groups_message_online(mi,jp->x,gid); else js_bounce_xmpp(m->si,jp->x,XTERROR_NOTALLOWED); xmlnode_free(info); return M_HANDLED;}/** * xhash_walker() used to free the content of the to and from xhashes in all grouptabs * * @param h the xht containing all groups * @param key the group that should be freed * @param val the grouptab for this group * @param arg unused/ignored */void mod_groups_destroy(xht h, const char *key, void *val, void *arg) { grouptab gt = (grouptab) val; xhash_free(gt->to); xhash_free(gt->from);}/** * shutdown the module, free all allocated memory * * @param m the mapi_struct for the shutdown event * @param arg pointer to the mod_groups_i module internal data * @return always M_PASS */mreturn mod_groups_shutdown(mapi m, void *arg) { mod_groups_i mi = (mod_groups_i) arg; xhash_walk(mi->groups,mod_groups_destroy,NULL); xhash_free(mi->groups); xhash_free(mi->config); pool_free(mi->p); return M_PASS;}/** * init the module, register callbacks, parse configuration * * @param si the jsmi_struct containing the Jabber session manager instance-internal data */void mod_groups(jsmi si) { pool p; mod_groups_i mi; xmlnode cur, config; char *gid, *id = si->i->id; log_debug2(ZONE, LOGT_INIT, "initing"); /* generate our module configuration structure */ p = pool_new(); mi = pmalloco(p,sizeof(_mod_groups_i)); mi->p = p; mi->groups = xhash_new(67); mi->xc = si->xc; /* get the configuration xmlnode */ config = js_config(si,"groups"); mi->inst = xmlnode_get_tag_data(config,"instructions"); if (mi->inst == NULL) mi->inst = pstrdup(p,"This will add the group to your roster"); if (config != NULL) { mi->config = xhash_new(67); for (cur = xmlnode_get_firstchild(config); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { if (j_strcmp(xmlnode_get_name(cur),"group") != 0) continue; gid = xmlnode_get_attrib(cur,"id"); if (gid == NULL) { log_error(id,"mod_groups: Error loading, no id attribute on group"); pool_free(p); return; } else if (xhash_get(mi->config,gid) != NULL) { log_error(si->i->id,"mod_groups: Error loading, group '%s' configured twice",gid); pool_free(p); return; } if (xmlnode_get_tag(cur,"info") || xmlnode_get_tag(cur,"users")) xhash_put(mi->config,pstrdup(p,gid),cur); } } js_mapi_register(si,e_SERVER,mod_groups_message,(void *) mi); js_mapi_register(si,e_SESSION,mod_groups_session,(void *) mi); js_mapi_register(si,e_SHUTDOWN,mod_groups_shutdown,(void *) mi);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -