/* Jungle Monkey * Copyright (C) 1999-2001 The Regents of the University of Michigan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "jmmsp.h" #include "jmmsp_debug.h" #include "util/util.h" #include "util/elf.h" static void jmmsp_reset (JMMSP* jmmsp); static void jmmsp_packet_cb (Btp* btp, const void* buffer, guint length, gpointer user_data); static void jmmsp_error_cb (Btp* btp, gpointer user_data); JMMSP* jmmsp_create (BPeer* bpeer, const gchar* name) { JMMSP* jmmsp; Btp* btp; GURL* url; g_return_val_if_fail (name, NULL); /* Create a new Btp session */ btp = btp_create (bpeer, name); if (btp == NULL) return NULL; /* Create the URL */ url = gnet_url_clone (btp_get_url (btp)); gnet_url_set_protocol (url, "jmmsp"); /* Create a new JMMSP */ jmmsp = g_new0 (JMMSP, 1); jmmsp->peer = bpeer; jmmsp->url = url; jmmsp->btp = btp; jmmsp->is_local = TRUE; /* Initialize BTP */ btp->packet_func = jmmsp_packet_cb; btp->error_func = jmmsp_error_cb; btp->packet_user_data = jmmsp; btp->error_user_data = jmmsp; return jmmsp; } JMMSP* jmmsp_join (BPeer* bpeer, const GURL* url) { JMMSP* jmmsp; Btp* btp; g_return_val_if_fail (url, NULL); /* Join the BTP */ btp = btp_join (bpeer, url); if (!btp) return NULL; /* Create a new JMMSP */ jmmsp = g_new0 (JMMSP, 1); jmmsp->peer = bpeer; jmmsp->url = gnet_url_clone (url); jmmsp->btp = btp; /* Initialize BTP */ btp->packet_func = jmmsp_packet_cb; btp->error_func = jmmsp_error_cb; btp->packet_user_data = jmmsp; btp->error_user_data = jmmsp; return jmmsp; } JMMSP* jmmsp_setup (BPeer* bpeer, const GURL* url) { JMMSP* jmmsp; g_return_val_if_fail (url, NULL); /* Create a new JMMSP */ jmmsp = g_new0 (JMMSP, 1); jmmsp->peer = bpeer; jmmsp->url = gnet_url_clone (url); return jmmsp; } gboolean jmmsp_rejoin (JMMSP* jmmsp) { Btp* btp; g_return_val_if_fail (jmmsp, TRUE); g_return_val_if_fail (!jmmsp->btp, TRUE); /* Join the BTP */ btp = btp_join (jmmsp->peer, jmmsp->url); if (!btp) return TRUE; jmmsp->btp = btp; /* Initialize BTP */ btp->packet_func = jmmsp_packet_cb; btp->error_func = jmmsp_error_cb; btp->packet_user_data = jmmsp; btp->error_user_data = jmmsp; /* We aren't down any more */ jmmsp->is_down = FALSE; return FALSE; } void jmmsp_leave (JMMSP* jmmsp) { g_return_if_fail (jmmsp); /* Close the BTP */ if (jmmsp->btp) { jmmsp_reset (jmmsp); } } void jmmsp_delete (JMMSP* jmmsp) { GSList* i; if (!jmmsp) return; gnet_url_delete (jmmsp->url); jmmsp_reset (jmmsp); /* Call destroy on each handler, then delete handler */ for (i = jmmsp->handlers; i != NULL; i = i->next) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPDestroyFunc destroy; destroy = handler->funcs->destroy; if (destroy) (*destroy)(handler, jmmsp); g_free (handler); } g_slist_free (jmmsp->handlers); memset (jmmsp, 0, sizeof(*jmmsp)); g_free (jmmsp); } static void jmmsp_reset (JMMSP* jmmsp) { g_return_if_fail (jmmsp); /* Delete the BTP */ if (jmmsp->btp) { btp_leave (jmmsp->btp); jmmsp->btp = NULL; } } /* **************************************** */ static void jmmsp_packet_cb (Btp* btp, const void* buffer, guint length, gpointer user_data) { JMMSP* jmmsp = (JMMSP*) user_data; GList* nodes; GList* i; /* JMMSPP (0x01, "jmmsp_packet_cb\n"); */ /* fwrite (buffer, length, 1, stderr); */ nodes = elf_read_list (buffer, length); for (i = nodes; i != NULL; i = i->next) { ElfNode* node = (ElfNode*) i->data; if (!strcasecmp(node->name, "query")) { gchar* keyword; GSList* i; /* Parse */ keyword = elf_get_attribute (node, "keyword"); if (!keyword) continue; JMMSPP (0x01, "jmmsp_packet_cb: QUERY %s\n", keyword); /* Dispatch */ for (i = jmmsp->handlers; i != NULL; ) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPQueryFunc queryf; i = i->next; queryf = handler->funcs->query; if (queryf && handler->is_enabled) (queryf)(handler, jmmsp, keyword); } } else if (!strcmp(node->name, "reply")) { JMMSPReply reply; gchar* keyword; gchar* url; gchar* length; GSList* i; /* Parse */ keyword = elf_get_attribute (node, "keyword"); reply.name = elf_get_attribute (node, "name"); url = elf_get_attribute (node, "url"); if (!keyword || !reply.name || !url) continue; reply.url = gnet_url_new (url); if (!reply.url) continue; length = elf_get_attribute (node, "length"); if (length) reply.length = atoi(length); else reply.length = -1; JMMSPP (0x01, "jmmsp_packet_cb: REPLY %s\n", reply.name); /* Dispatch */ for (i = jmmsp->handlers; i != NULL; ) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPReplyFunc replyf; i = i->next; replyf = handler->funcs->reply; if (replyf && handler->is_enabled) (replyf)(handler, jmmsp, keyword, &reply); } gnet_url_delete (reply.url); } } elf_delete_list (nodes); } static void jmmsp_error_cb (Btp* btp, gpointer user_data) { JMMSP* jmmsp = (JMMSP*) user_data; GSList* i; JMMSPP (1, "jmmsp_error_cb: DOWN\n"); jmmsp_reset (jmmsp); jmmsp->is_down = TRUE; /* Dispatch */ for (i = jmmsp->handlers; i != NULL; ) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPErrorFunc error; i = i->next; error = handler->funcs->error; if (error && handler->is_enabled) (error)(handler, jmmsp); } if (jmmsp->error_func) (jmmsp->error_func)(jmmsp, jmmsp->user_data); } /* **************************************** */ void jmmsp_send_query (JMMSP* jmmsp, const gchar* keyword, gint ttl) { gchar* buffer; guint length; GSList* i; g_return_if_fail (jmmsp); g_return_if_fail (keyword); if (!jmmsp->btp) return; /* Create a query */ elf_write_format (&buffer, &length, "query", "s", "keyword", keyword); g_return_if_fail (buffer); /* Send the query */ btp_send (jmmsp->btp, buffer, length); g_free (buffer); /* Dispatch */ for (i = jmmsp->handlers; i != NULL; ) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPQueryFunc queryf; i = i->next; queryf = handler->funcs->query; if (queryf && handler->is_enabled && handler->loopback) (queryf)(handler, jmmsp, keyword); } } void jmmsp_send_reply (JMMSP* jmmsp, const gchar* keyword, JMMSPReply* reply, gint ttl) { gchar* buffer; guint length; gchar* url_str; GSList* i; g_return_if_fail (jmmsp); g_return_if_fail (keyword); g_return_if_fail (reply); if (!jmmsp->btp) return; url_str = gnet_url_get_nice_string (reply->url); g_return_if_fail (url_str); /* Create a reply */ if (reply->length >= 0) elf_write_format (&buffer, &length, "reply", "sssd", "keyword", keyword, "name", reply->name, "url", url_str, "length", reply->length); else elf_write_format (&buffer, &length, "reply", "sss", "keyword", keyword, "name", reply->name, "url", url_str); g_free (url_str); g_return_if_fail (buffer); /* Send the reply */ btp_send (jmmsp->btp, buffer, length); g_free (buffer); /* Dispatch */ for (i = jmmsp->handlers; i != NULL; ) { JMMSPHandler* handler = (JMMSPHandler*) i->data; JMMSPReplyFunc replyf; i = i->next; replyf = handler->funcs->reply; if (replyf && handler->is_enabled && handler->loopback) (replyf)(handler, jmmsp, keyword, reply); } } /* **************************************** */ void jmmsp_add_handler (JMMSP* jmmsp, JMMSPHandler* handler) { g_return_if_fail (jmmsp); g_return_if_fail (handler); if (g_slist_find (jmmsp->handlers, handler) || g_slist_find (handler->jmmsps, jmmsp)) return; jmmsp->handlers = g_slist_prepend (jmmsp->handlers, handler); handler->jmmsps = g_slist_prepend (handler->jmmsps, jmmsp); } void jmmsp_remove_handler (JMMSP* jmmsp, JMMSPHandler* handler) { g_return_if_fail (jmmsp); g_return_if_fail (handler); jmmsp->handlers = g_slist_remove (jmmsp->handlers, handler); handler->jmmsps = g_slist_remove (handler->jmmsps, jmmsp); } /* **************************************** */ void jmmsp_handler_init (JMMSPHandler* handler, JMMSPHandlerFuncs* funcs) { g_return_if_fail (handler); g_return_if_fail (funcs); handler->is_enabled = TRUE; handler->funcs = funcs; handler->loopback = FALSE; } void jmmsp_handler_uninit (JMMSPHandler* handler) { GSList* i; g_return_if_fail (handler); for (i = handler->jmmsps; i != NULL; i = i->next) { JMMSP* jmmsp = (JMMSP*) i->data; jmmsp->handlers = g_slist_remove (jmmsp->handlers, handler); } g_slist_free (handler->jmmsps); handler->jmmsps = NULL; } void jmmsp_handler_set_enabled (JMMSPHandler* handler, gboolean is_enabled) { handler->is_enabled = is_enabled; } gboolean jmmsp_handler_is_enabled (JMMSPHandler* handler) { return handler->is_enabled; } /* **************************************** */ void jmmsp_reply_delete (JMMSPReply* reply) { if (!reply) return; g_free (reply->name); gnet_url_delete (reply->url); g_free (reply); } JMMSPReply* jmmsp_reply_clone (const JMMSPReply* reply) { JMMSPReply* new_reply; g_return_val_if_fail (reply, NULL); new_reply = g_new0 (JMMSPReply, 1); new_reply->name = g_strdup(reply->name); new_reply->url = gnet_url_clone(reply->url); new_reply->length = reply->length; return new_reply; } gint jmmsp_reply_equal (gconstpointer a, gconstpointer b) { const JMMSPReply* ma = (const JMMSPReply*) a; const JMMSPReply* mb = (const JMMSPReply*) b; if (!SAFESTRCMP(ma->name, mb->name) && gnet_url_equal(ma->url, mb->url) && ma->length == mb->length) return 1; return 1; } guint jmmsp_reply_hash (gconstpointer p) { const JMMSPReply* r = (const JMMSPReply*) p; guint h = 0; if (r->name) h ^= g_str_hash (r->name); if (r->url) h ^= gnet_url_hash (r->url); h ^= r->length; return h; } #define SAFESTRLEN(A) ((A)? (strlen(A)) : (0)) guint jmmsp_reply_size (const JMMSPReply* reply) { g_return_val_if_fail (reply, 0); return SAFESTRLEN(reply->name) + SAFESTRLEN(reply->url->protocol) + SAFESTRLEN(reply->url->hostname) + SAFESTRLEN(reply->url->resource); } gboolean jmmsp_reply_matches (const JMMSPReply* reply, const gchar* query) { g_return_val_if_fail (reply, FALSE); g_return_val_if_fail (query, FALSE); /* Ignore NULL queries */ if (!*query) return FALSE; if (reply->name && strcasestr (reply->name, query)) return TRUE; if (reply->url && reply->url->resource && strcasestr (reply->url->resource, query)) return TRUE; return FALSE; }