/* 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 <string.h>
#include <stdlib.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1