#include #include #include #include #include #include #include typedef struct XMPPSession { char *identifier; /* "Each session [...] SHOULD be valid only between one * requester/responder pair." */ /* The Parsee-managed entity managing the session. */ char *receiver; /* The command's issuer entity. */ char *issuer; /* The issued node */ char *node; /* Timestamp of the session's creation, from Parsee's * point-of-view */ uint64_t creation; } XMPPSession; struct XMPPCommandManager { pthread_mutex_t lock; /* A hashmap of XMPPCommands. */ HashMap *commands; HashMap *sessions; void *cookie; }; static void XMPPDestroySession(XMPPSession *session) { if (!session) { return; } Free(session->identifier); Free(session->receiver); Free(session->issuer); Free(session->node); Free(session); } static void InvalidateSession(XMPPCommandManager *m, char *session) { XMPPSession *s = HashMapDelete(m->sessions, session); XMPPDestroySession(s); } static char * NewSession(XMPPCommandManager *m, char *node) { char *session_id = NULL; do { int period = (UtilTsMillis() / (10 SECONDS)); char *rand = StrRandom(16); char *pstr = StrInt(period); session_id = StrConcat(5, node, "_", pstr, "_", rand); Free(rand); Free(pstr); } while (HashMapGet(m->sessions, session_id)); return session_id; } /* NOTE: The manager MUST be locked. */ static char * XMPPRegisterSession(XMPPCommandManager *m, char *p_jid, char *s_jid, char *node) { XMPPSession *session; char *session_ident; if (!m || !p_jid || !s_jid) { return NULL; } session = Malloc(sizeof(*session)); session->node = StrDuplicate(node); session->issuer = StrDuplicate(s_jid); session->receiver = StrDuplicate(p_jid); session->creation = UtilTsMillis(); session_ident = NewSession(m, node); session->identifier = session_ident; HashMapSet(m->sessions, session_ident, session); return session_ident; } XMPPCommandManager * XMPPCreateManager(void *cookie) { XMPPCommandManager *ret = Malloc(sizeof(*ret)); pthread_mutex_init(&ret->lock, NULL); ret->commands = HashMapCreate(); ret->sessions = HashMapCreate(); ret->cookie = cookie; return ret; } void XMPPRegisterCommand(XMPPCommandManager *m, XMPPCommand *cmd) { if (!m || !cmd) { return; } pthread_mutex_lock(&m->lock); HashMapSet(m->commands, XMPPGetCommandNode(cmd), cmd); pthread_mutex_unlock(&m->lock); } void XMPPFreeManager(XMPPCommandManager *manager) { char *node_name, *session_id; XMPPCommand *val; XMPPSession *session; if (!manager) { return; } while (HashMapIterate(manager->commands, &node_name, (void **) &val)) { XMPPFreeCommand(val); } while (HashMapIterate(manager->sessions, &session_id, (void **) &session)) { XMPPDestroySession(session); } pthread_mutex_destroy(&manager->lock); HashMapFree(manager->commands); HashMapFree(manager->sessions); Free(manager); } void XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p) { char *node_name; XMPPCommand *val; XMLElement *item; if (!m || !p || !jid) { return; } pthread_mutex_lock(&m->lock); while (HashMapIterate(m->commands, &node_name, (void **) &val)) { item = XMLCreateTag("item"); XMLAddAttr(item, "jid", jid); XMLAddAttr(item, "node", node_name); XMLAddAttr(item, "name", XMPPGetCommandDesc(val)); XMLAddChild(p, item); } pthread_mutex_unlock(&m->lock); } bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) { XMLElement *command; XMPPComponent *jabber; XMPPCommand *cmd; char *from, *to, *id; char *session_id = NULL; bool good = false; if (!m || !stanza || !data) { return false; } jabber = data->jabber; from = HashMapGet(stanza->attrs, "from"); to = HashMapGet(stanza->attrs, "to"); id = HashMapGet(stanza->attrs, "id"); pthread_mutex_lock(&m->lock); /* TODO */ #define CMD_NS "http://jabber.org/protocol/commands" if ((command = XMLookForTKV(stanza, "command", "xmlns", CMD_NS))) { char *action = HashMapGet(command->attrs, "action"); char *node = HashMapGet(command->attrs, "node"); if (!action || StrEquals(action, "execute")) { XMLElement *command_xml, *iq; /* This is an execution. */ cmd = HashMapGet(m->commands, node); if (!cmd) { Log(LOG_WARNING, "User %s asked to execute '%s', but it doesn't exist.", from, node ); goto end; } if (XMPPCommandRequiresForm(cmd)) { Log(LOG_WARNING, "User %s asked to execute '%s', but it requires a form, " "which is currently UNIMPLEMENTED", from, node ); goto end; } /* Doesn't need to be freed, as it lives with m. */ session_id = XMPPRegisterSession(m, to, from, node); /* No forms, we good. */ iq = XMLCreateTag("iq"); XMLAddAttr(iq, "type", "result"); XMLAddAttr(iq, "from", to); XMLAddAttr(iq, "to", from); XMLAddAttr(iq, "id", id); command_xml = XMLCreateTag("command"); XMLAddAttr(command_xml, "xmlns", CMD_NS); XMLAddAttr(command_xml, "node", node); XMLAddAttr(command_xml, "status", "completed"); XMLAddAttr(command_xml, "sessionid", session_id); XMPPExecuteCommand(m, cmd, from, command_xml, NULL); XMLAddChild(iq, command_xml); pthread_mutex_lock(&jabber->write_lock); XMLEncode(jabber->stream, iq); StreamFlush(jabber->stream); pthread_mutex_unlock(&jabber->write_lock); XMLFreeElement(iq); InvalidateSession(m, session_id); good = true; goto end; } } #undef CMD_NS end: pthread_mutex_unlock(&m->lock); return good; } void * XMPPGetManagerCookie(XMPPCommandManager *manager) { return manager ? manager->cookie : NULL; }