/* Jungle Monkey Lite
* Copyright (C) 1999, 2000 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 "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#ifdef JM_ENABLE_GNOME
#include <popt-gnome.h>
#else
#include <popt.h>
#endif
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <gnet/gnet.h>
#include "jmintl.h"
#include "jmutil.h"
#include "util/util.h"
#include "util/poll.h"
#include "btp/b_conn.h"
#include "btp/btp_debug.h"
#include "jmchannel/jmchannel.h"
#include "jmchannel/jmchannel_debug.h"
#include "jmchat/jmchat.h"
#include "jmchat/jmchat_debug.h"
#include "jmmsearch/jmmsearch.h"
#include "jmmsearch/jmmsp_debug.h"
#ifndef MAXLINELEN
#define MAXLINELEN 16000
#endif
#define MAX_TOKENS 10
#define MAX_INCLUDES 16
/* ******************** */
/* Globals */
static const gchar* script_name = NULL;
static int fail_on_error = 0;
static int clfl = 0; /* command line is first line */
static int no_prompt = 0;
static gboolean shutting_down = FALSE;
static GIOChannel* includes[MAX_INCLUDES] = {NULL, };
static guint include_level = 0;
static GNetIOChannelReadAsyncID readasync_id = NULL;
static gchar line_buffer[MAXLINELEN];
static GHashTable* name_to_channel = NULL;
static GHashTable* name_to_chat = NULL;
static GHashTable* name_to_search = NULL;
static GHashTable* url_to_obj = NULL;
#define HAVE_NAME(N) (g_hash_table_lookup(name_to_channel, (N)) || \
g_hash_table_lookup(name_to_chat, (N)) || \
g_hash_table_lookup(name_to_search, (N)))
static void start_backend (int argc, char* argv[]);
static gboolean readline_cb (GIOChannel* iochannel,
GNetIOChannelReadAsyncStatus status,
gchar* buffer, guint length,
gpointer user_data);
static void create_channel (const gchar* path, gboolean channels_only);
static void remove_channel (const gchar* path);
static void open_channel (const gchar* urlstr);
static void list (const gchar* path);
static void list_hfunc (gpointer key, gpointer value, gpointer user_data);
static void add_file (const gchar* path, const gchar* name);
static void remove_file (const gchar* path);
static void add_dir (const gchar* path, const gchar* name, gboolean swallow);
static void create_dir (const gchar* path);
static void remove_dir (const gchar* path);
static void add_url (const gchar* path, const gchar* urlstr);
static void remove_url (const gchar* path);
static void create_chat (const gchar* path);
static void remove_chat (const gchar* name);
static void create_search (const gchar* path);
static void remove_search (const gchar* name);
static void parse_path (const gchar* path, JMChannel** pchannel, JMAnn** pann);
static void channel_info (JMChannel* channel, JMChannelType type,
JMAnn* ann, gpointer user_data);
static void channel_error (JMChannel* jmchannel, gpointer user_data);
static void error (const gchar *format, ...);
static void sig_int_cb (int ignore);
static void sig_pipe_cb (int ignore);
static void try_exit (void);
static void delete_name_to_whatever_hfunc (gpointer key, gpointer value, gpointer user_data);
static void delete_url_to_obj_hfunc (gpointer key, gpointer value, gpointer user_data);
static void conn_zero_cb (void);
static gboolean exit_timeout (gpointer p);
int
main (int argc, char* argv[])
{
GMainLoop* main_loop;
/* Workaround strict poll */
# ifdef HAVE_STRICT_POLL
g_main_set_poll_func (gnu_poll);
# endif
/* Initialize the RNG */
srand(time(NULL));
/* Create name to channel map */
name_to_channel = g_hash_table_new (g_str_hash, g_str_equal);
name_to_chat = g_hash_table_new (g_str_hash, g_str_equal);
name_to_search = g_hash_table_new (g_str_hash, g_str_equal);
url_to_obj = g_hash_table_new (gnet_url_hash, gnet_url_equal);
/* If command line is the next line, then don't start the backend yet */
if (argc >= 2 && !strcmp(argv[1], "-c"))
{
/* Arg 2 may be the script name */
if (argc == 3)
script_name = argv[2];
clfl = TRUE;
}
/* Otherwise, parse CL and start backend now */
else
{
start_backend (argc, argv);
}
/* Load the script maybe */
if (script_name)
{
int fd;
no_prompt = TRUE;
fd = open (script_name, O_RDONLY);
if (fd == -1)
my_error (_("Could not open file %s\n"), script_name);
includes[0] = g_io_channel_unix_new (fd);
}
else
{
g_print (_("jmlite console mode\n"));
g_print (_("type ? for help\n"));
g_print (_("> "));
includes[0] = g_io_channel_unix_new (STDIN_FILENO);
}
g_assert (includes[0]);
readasync_id =
gnet_io_channel_readline_async (includes[0],
line_buffer, sizeof(line_buffer),
0, readline_cb, NULL);
signal (SIGINT, sig_int_cb);
signal (SIGPIPE, sig_pipe_cb);
main_loop = g_main_new(FALSE);
g_main_run(main_loop);
exit (EXIT_SUCCESS);
}
static void
start_backend (int argc, char* argv[])
{
JMUTIL_POPT_VARS
struct poptOption jm_options[] =
{
{"help", 'h', POPT_ARG_NONE, NULL, 1,
"Show this help message", NULL},
{"usage", '\0', POPT_ARG_NONE, NULL, 2,
"Display brief usage message", NULL},
{"version", 0, POPT_ARG_NONE, NULL, 3,
"Output verson information and exit", NULL},
JMUTIL_POPT_ARGS
{"exit-on-error", 'e', POPT_ARG_NONE, &fail_on_error, 0,
"Exit if an error occurs", NULL},
{"first-line-is-command-line", 'c', POPT_ARG_NONE, NULL, 0,
"First line read is command line (hack for scripts)", NULL},
{"no-prompt", 'n', POPT_ARG_NONE, &no_prompt, 0,
"Don't print the command prompt (on by default for scripts).", NULL},
{"noop", '\0', POPT_ARG_NONE, NULL, 0,
"No-operation. Does nothing.", NULL},
{NULL, '\0', 0, NULL, 0, NULL, NULL}
};
poptContext ctx;
int rc;
int exit_status = EXIT_SUCCESS;
/* btp_debug_flags = 0x8c0c; */
/* btp_debug_flags = 0xffff; */
/* jmchat_debug_flags = 0xffff; */
/* jmmsp_debug_flags = 0xffff; */
/* jmchannel_debug_flags = 0xffff; */
/* ******************** */
/* Initialize NLS */
#ifdef ENABLE_NLS
{
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
}
#endif
/* ******************** */
/* Parse command line */
ctx = poptGetContext(NULL, argc, (const char**) argv, jm_options, 0);
poptSetOtherOptionHelp(ctx, _("[OPTION]... [SCRIPT]"));
while ((rc = poptGetNextOpt(ctx)) > 0)
{
switch (rc)
{
case 1: goto help; break;
case 2: goto usage; break;
case 3: goto version; break;
}
}
/* Get the script name (if we aren't doing clfl - it's too late if
this is the first line). */
if (!clfl)
script_name = poptGetArg(ctx);
poptFreeContext(ctx);
/* Start backend */
if (JMUTIL_INIT)
my_error (_("Could not start backend\n"));
return;
usage:
poptPrintUsage (ctx, stdout, 0);
exit (exit_status);
help:
g_print (_("`jmlite' - Command-line interface version of Jungle Monkey, a distributed\n"
"file sharing program.\n"));
poptPrintHelp (ctx, stdout, 0);
exit (exit_status);
version:
g_print (_("jmlite %s\n"
"Written by David A. Helder\n"
"\n"
"Copyright (C) 2000 The Regents of the University of Michigan\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
VERSION);
exit(exit_status);
}
gboolean
readline_cb (GIOChannel* iochannel, GNetIOChannelReadAsyncStatus status,
gchar* buffer, guint length, gpointer user_data)
{
gchar* tokens[MAX_TOKENS] = {NULL, };
gint toknum;
gchar* p, *end;
g_assert (status == GNET_IOCHANNEL_READ_ASYNC_STATUS_OK);
if (!length)
{
readasync_id = NULL;
includes[include_level] = NULL;
if (include_level)
{
--include_level;
readasync_id =
gnet_io_channel_readline_async (includes[include_level],
line_buffer, sizeof(line_buffer),
0, readline_cb, NULL);
}
return FALSE;
}
/* If the command line is the first line, then just read the command
line */
if (clfl && buffer[0] != '#')
{
gchar* buffer2;
int argc = 0;
char** argv = NULL;
clfl = FALSE;
/* Convert line to argv format */
buffer2 = g_strconcat ("jmlite ", buffer, NULL);
poptParseArgvString (buffer2, &argc, (const char***) &argv);
g_free (buffer2);
/* Start the backend using this format */
start_backend (argc, argv);
free (argv);
return TRUE;
}
/* Tokenize */
toknum = 0;
p = buffer;
end = &buffer[length];
while (toknum < MAX_TOKENS)
{
gboolean quoted = FALSE;
/* Skip initial whitespace */
while (p < end && (isspace((int) *p) || !*p))
++p;
if (p == end)
break;
/* Skip if comment */
if (*p == '#')
break;
/* Skip initial quote */
if (*p == '\"')
{
quoted = TRUE;
++p;
if (p == end)
break;
}
/* Save token beginning */
tokens[toknum++] = p;
/* Read until next quote or white space */
if (quoted)
{
while (p < end && (*p != '\"'))
++p;
}
else
{
while (p < end && !(isspace((int) *p) || !*p))
++p;
}
*p = '\0';
if (p == end) /* assume end < buffer[sizeof(buffer)] */
break;
++p;
}
if (toknum == 0)
{
if (!no_prompt)
g_print ("> ");
return TRUE;
}
/* TODO: MOVE ALL THIS STUFF INTO FUNCTIONS! */
if (!g_strcasecmp ("help", tokens[0]) ||
!g_strcasecmp ("?", tokens[0]))
{
g_print (_("jmlite console help\n"
" COMMAND ARGUMENTS (<required> [optional])\n"
" create_channel <path> [channels_only]\n"
" remove_channel <name>\n"
" open_channel <url>\n"
" add_file <path> <filename>\n"
" remove_file, rm <path>\n"
" add_directory <parent path> <dirname> [swallow]\n"
" create_directory, mkdir <path>\n"
" remove_directory, rmdir <path>\n"
" list, ls [path]\n"
" add_url <path> <url>\n"
" remove_url <path>\n"
" create_chat <path>\n"
" remove_chat <name>\n"
" create_search <path>\n"
" remove_search <name>\n"
" include <path>\n"
" help\n"
" quit\n"
));
}
else if (!g_strcasecmp ("quit", tokens[0]) ||
!g_strcasecmp ("exit", tokens[0]))
{
try_exit ();
readasync_id = NULL;
return FALSE;
}
else if (!g_strcasecmp ("include", tokens[0]) && tokens[1])
{
int fd;
if ((include_level + 1) < MAX_INCLUDES)
{
fd = open (tokens[1], O_RDONLY);
if (fd != -1)
{
++include_level;
includes[include_level] = g_io_channel_unix_new (fd);
readasync_id =
gnet_io_channel_readline_async (includes[include_level],
line_buffer, sizeof(line_buffer),
0, readline_cb, NULL);
return FALSE;
}
else
error (_("Bad include filename: %s\n"), tokens[1]);
}
else
error (_("Maximum include depth of %d exceeded\n"), MAX_INCLUDES);
}
else if (!g_strcasecmp ("create_channel", tokens[0]) && tokens[1])
create_channel (tokens[1], tokens[2] != NULL);
else if (!g_strcasecmp ("remove_channel", tokens[0]) && tokens[1])
remove_channel (tokens[1]);
else if (!g_strcasecmp ("open_channel", tokens[0]) && tokens[1])
open_channel (tokens[1]);
else if ((!g_strcasecmp ("list", tokens[0]) ||
!g_strcasecmp ("ls", tokens[0])))
list (tokens[1]);
else if (!g_strcasecmp ("add_file", tokens[0]) && tokens[1] && tokens[2])
add_file (tokens[1], tokens[2]);
else if ((!g_strcasecmp ("remove_file", tokens[0]) ||
!g_strcasecmp ("rm", tokens[0])) && tokens[1])
remove_file (tokens[1]);
else if (!g_strcasecmp ("add_directory", tokens[0]) && tokens[1] && tokens[2])
add_dir (tokens[1], tokens[2], tokens[3] != NULL);
else if ((!g_strcasecmp ("create_directory", tokens[0]) ||
!g_strcasecmp ("mkdir", tokens[0])) && tokens[1])
create_dir (tokens[1]);
else if ((!g_strcasecmp ("remove_directory", tokens[0]) ||
!g_strcasecmp ("rmdir", tokens[0])) && tokens[1])
remove_dir (tokens[1]);
else if (!g_strcasecmp ("add_url", tokens[0]) && tokens[1] && tokens[2])
add_url (tokens[1], tokens[2]);
else if (!g_strcasecmp ("remove_url", tokens[0]) && tokens[1])
remove_url (tokens[1]);
else if (!g_strcasecmp ("create_chat", tokens[0]) && tokens[1])
create_chat (tokens[1]);
else if (!g_strcasecmp ("remove_chat", tokens[0]) && tokens[1])
remove_chat (tokens[1]);
else if (!g_strcasecmp ("create_search", tokens[0]) && tokens[1])
create_search (tokens[1]);
else if (!g_strcasecmp ("remove_search", tokens[0]) && tokens[1])
remove_search (tokens[1]);
else
{
error (_("Bad command: %s\n"), tokens[0]);
}
if (shutting_down)
return FALSE;
if (!no_prompt)
g_print ("> ");
return TRUE;
}
static void
add_url (const gchar* path, const gchar* urlstr)
{
gchar* basename;
gchar* dirname = NULL;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
GURL* url;
JMAnn* ann;
g_return_if_fail (path);
basename = g_basename (path);
dirname = g_dirname (path);
g_return_if_fail (basename);
g_return_if_fail (dirname);
/* Check URL */
url = gnet_url_new (urlstr);
if (!url)
{
error (_("Malformed URL: %s\n"), urlstr);
goto error;
}
/* Check path */
parse_path (dirname, &pchannel, &pann);
if (!pchannel || !pann)
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (pchannel && jmchannel_get (pchannel, basename))
{
error (_("%s already exists on channel %s\n"),
path, jmchannel_get_name(pchannel));
goto error;
}
g_print (_("URL %s add\n"), urlstr);
/* Add to hashtable */
g_hash_table_insert (url_to_obj, url, NULL);
/* Advertise on channel */
ann = jmchannel_add (pchannel, pann, basename, url);
if (!ann)
error (_("Could not advertise URL %s on channel %s\n"),
urlstr, jmchannel_get_name(pchannel));
error:
g_free (dirname);
}
static void
remove_url (const gchar* path)
{
JMChannel* pchannel;
JMAnn* ann;
GURL* url;
GURL* orig_url;
parse_path (path, &pchannel, &ann);
if (!pchannel || !ann)
{
error (_("URL %s does not exist\n"), path);
return;
}
if (!ann->is_local)
{
error (_("%s is not a local URL\n"), path);
return;
}
url = jmann_get_url (ann);
if (!url || g_hash_table_lookup (url_to_obj, url))
{
error (_("%s is not a URL\n"), path);
return;
}
g_assert (g_hash_table_lookup_extended (url_to_obj, url, (gpointer) &orig_url, NULL));
g_hash_table_remove (url_to_obj, url);
gnet_url_delete (orig_url);
jmchannel_remove (pchannel, ann);
}
static void
create_channel (const gchar* path, gboolean channels_only)
{
gchar* basename;
gchar* dirname = NULL;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
JMChannel* channel;
gchar* url_str;
g_return_if_fail (path);
basename = g_basename (path);
dirname = g_dirname (path);
g_return_if_fail (basename);
g_return_if_fail (dirname);
/* Make sure it doesn't exist */
if (g_hash_table_lookup (name_to_channel, basename))
{
error (_("Channel %s already exists\n"), basename);
goto error;
}
/* Check path */
parse_path (dirname, &pchannel, &pann);
if (strcmp(dirname, ".") && !(pchannel && pann))
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (pchannel && jmchannel_get (pchannel, basename))
{
error (_("%s already exists on channel %s\n"),
path, jmchannel_get_name(pchannel));
goto error;
}
/* Check if name exists */
if (HAVE_NAME(basename))
{
error (_("Something named %s already exists\n"), basename);
goto error;
}
/* Create channel */
channel = jmchannel_create (jm_btp_bpeer, basename);
if (!channel)
{
error (_("Could not create channel %s\n"), path);
goto error;
}
/* Initialize */
channel->info_func = channel_info;
channel->error_func = channel_error;
url_str = gnet_url_get_nice_string (channel->url);
g_print (_("Channel %s created (URL is %s)\n"),
jmchannel_get_name(channel), url_str);
g_free (url_str);
/* Add to hashtable */
g_hash_table_insert (name_to_channel, g_strdup(basename), channel);
g_hash_table_insert (url_to_obj, channel->url, channel);
/* Advertise channel */
if (pchannel && pann)
{
JMAnn* ann;
ann = jmchannel_add (pchannel, pann, basename, channel->url);
if (!ann)
error (_("Could not advertise channel %s on channel %s\n"),
jmchannel_get_name(channel), jmchannel_get_name(pchannel));
}
error:
g_free (dirname);
}
static void
remove_channel (const gchar* name)
{
gchar* orig_name;
JMChannel* channel;
/* TODO: Send removal announcements */
/* Lookup channel */
if (!g_hash_table_lookup_extended (name_to_channel, name,
(gpointer) &orig_name, (gpointer) &channel))
{
error (_("%s does not exist\n"), name);
return;
}
/* Delete it */
g_hash_table_remove (name_to_channel, name);
g_hash_table_remove (url_to_obj, channel->url);
g_free (orig_name);
jmchannel_delete (channel);
}
static void
open_channel (const gchar* urlstr)
{
GURL* url;
JMChannel* channel;
gchar* name;
url = gnet_url_new (urlstr);
if (!url || strcmp (url->protocol, "jm"))
{
error (_("Malformed URL: %s\n"), urlstr);
gnet_url_delete (url);
return;
}
channel = (JMChannel*) g_hash_table_lookup (url_to_obj, url);
if (channel)
{
error (_("Channel %s is already open\n"), urlstr);
gnet_url_delete (url);
return;
}
channel = jmchannel_join (jm_btp_bpeer, url);
if (!channel)
{
error (_("Could not open channels %s\n"), url->resource);
gnet_url_delete (url);
return;
}
/* Initialize */
channel->info_func = channel_info;
channel->error_func = channel_error;
/* Add to hashtable */
name = strrchr(url->resource, '/');
g_return_if_fail (name);
++name;
g_return_if_fail (*name);
g_hash_table_insert (name_to_channel, g_strdup(name), channel);
g_hash_table_insert (url_to_obj, channel->url, channel);
gnet_url_delete (url);
}
static void
list (const gchar* path)
{
if (path)
{
JMChannel* channel;
JMAnn* ann;
GList* i;
parse_path (path, &channel, &ann);
if (!channel || !ann)
{
error (_("Bad path: %s\n"), path);
return;
}
g_print (_("%s%s/:\n"), jmchannel_get_name(channel), ann->path);
for (i = ann->kids; i != NULL; i = i->next)
{
JMAnn* kid = (JMAnn*) i->data;
g_print (_("\t%s\n"), kid->name);
}
}
else
{
g_print (_("/:\n"));
g_hash_table_foreach (name_to_channel, list_hfunc, NULL);
}
}
static void
list_hfunc (gpointer key, gpointer value, gpointer user_data)
{
gchar* name = (gchar*) key;
g_print (_("\t%s\n"), name);
}
static void
add_file (const gchar* path, const gchar* name)
{
gchar* filename;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
GURL* url;
JMAnn* ann;
MtpLocalMirror* mirror;
gchar* resource;
gchar* urlstr;
g_return_if_fail (path);
g_return_if_fail (name);
/* Check if file exists */
filename = tilde_expand (name);
if (!file_exists (filename))
{
error (_("Bad path: %s\n"), name);
goto error;
}
/* Check path */
parse_path (path, &pchannel, &pann);
if (!pchannel || !pann)
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (jmann_get_child (pann, g_basename(name)))
{
error (_("%s already exists on channel %s\n"),
g_basename(name), jmchannel_get_name(pchannel));
goto error;
}
/* Create MTP URL */
url = gnet_url_clone (jm_mtp_rvous_url);
resource = g_strconcat(pann->path, "/", g_basename(name), NULL);
gnet_url_set_resource (url, resource);
g_free (resource);
g_return_if_fail (!g_hash_table_lookup (url_to_obj, url));
/* Create MTP client */
mirror = mtp_mirror_path (jm_mtp_server, url, filename);
if (!mirror)
{
error (_("MTP Error: %s\n"), path);
gnet_url_delete (url);
goto error;
}
urlstr = gnet_url_get_nice_string (url);
g_print (_("File name added (URL is %s\n"), urlstr);
g_free (urlstr);
/* Add to hashtable */
g_hash_table_insert (url_to_obj, url, mirror);
/* Advertise on channel */
ann = jmchannel_add (pchannel, pann, g_basename(name), url);
if (!ann)
error (_("Could not advertise file %s on channel %s\n"),
g_basename(name), jmchannel_get_name(pchannel));
error:
g_free (filename);
}
static void
remove_file (const gchar* path)
{
JMChannel* pchannel;
JMAnn* ann;
GURL* url;
GURL* orig_url;
MtpLocalMirror* mirror;
parse_path (path, &pchannel, &ann);
if (!pchannel || !ann)
{
error (_("File %s does not exist\n"), path);
return;
}
if (!ann->is_local)
{
error (_("%s is not a local file\n"), path);
return;
}
url = jmann_get_url (ann);
if (!url || strcmp(url->protocol, "mtp"))
{
error (_("%s is not a file\n"), path);
return;
}
g_assert (g_hash_table_lookup_extended (url_to_obj, url, (gpointer) &orig_url, (gpointer) &mirror));
g_hash_table_remove (url_to_obj, url);
gnet_url_delete (orig_url);
jmchannel_remove (pchannel, ann);
g_return_if_fail (mirror);
mtp_unmirror (mirror);
}
static void
add_dir (const gchar* path, const gchar* name, gboolean swallow)
{
error (_("Implement me.\n"));
}
static void
create_dir (const gchar* path)
{
gchar* basename;
gchar* dirname = NULL;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
JMAnn* ann;
g_return_if_fail (path);
basename = g_basename (path);
dirname = g_dirname (path);
/* Check path */
parse_path (dirname, &pchannel, &pann);
if (!pchannel || !pann)
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (jmann_get_child (pann, basename))
{
error (_("%s already exists on channel %s\n"),
basename, jmchannel_get_name(pchannel));
goto error;
}
/* Advertise on channel */
ann = jmchannel_add (pchannel, pann, basename, NULL);
if (!ann)
error (_("Could not advertise directory %s on channel %s\n"),
path, jmchannel_get_name(pchannel));
error:
g_free (dirname);
}
static void
remove_dir (const gchar* path)
{
JMChannel* pchannel;
JMAnn* ann;
parse_path (path, &pchannel, &ann);
if (!pchannel || !ann)
{
error (_("Directory %s does not exist\n"), path);
return;
}
if (!ann->is_local)
{
error (_("%s is not a local directory\n"), path);
return;
}
if (jmann_get_url (ann))
{
error (_("%s is not a directory\n"), path);
return;
}
jmchannel_remove (pchannel, ann);
}
static void
create_chat (const gchar* path)
{
gchar* basename;
gchar* dirname = NULL;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
JMChat* chat;
gchar* url_str;
g_return_if_fail (path);
basename = g_basename (path);
dirname = g_dirname (path);
g_return_if_fail (basename);
g_return_if_fail (dirname);
/* Check path */
parse_path (dirname, &pchannel, &pann);
if (strcmp(dirname, ".") && !(pchannel && pann))
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (pchannel && jmchannel_get (pchannel, basename))
{
error (_("%s already exists on channel %s\n"),
path, jmchannel_get_name(pchannel));
goto error;
}
/* Check if name exists */
if (HAVE_NAME(basename))
{
error (_("Something named %s already exists\n"), basename);
goto error;
}
/* Create chat */
chat = jmchat_create (jm_btp_bpeer, basename, NULL);
if (!chat)
{
error (_("Could not create chat %s\n"), path);
goto error;
}
/* TODO: Initialize */
url_str = gnet_url_get_nice_string (chat->url);
g_print (_("Chat %s created (URL is %s)\n"),
basename, url_str);
g_free (url_str);
/* Add to hashtable */
g_hash_table_insert (name_to_chat, g_strdup(basename), chat);
g_hash_table_insert (url_to_obj, chat->url, chat);
/* Advertise channel */
if (pchannel && pann)
{
JMAnn* ann;
ann = jmchannel_add (pchannel, pann, basename, chat->url);
if (!ann)
error (_("Could not advertise chat %s on channel %s\n"),
basename, jmchannel_get_name(pchannel));
}
error:
g_free (dirname);
}
static void
remove_chat (const gchar* name)
{
gchar* orig_name;
JMChat* chat;
/* TODO: Send removal announcements */
/* Lookup chat */
if (!g_hash_table_lookup_extended (name_to_chat, name,
(gpointer) &orig_name, (gpointer) &chat))
{
error (_("%s does not exist\n"), name);
return;
}
/* Delete it */
g_hash_table_remove (name_to_chat, name);
g_hash_table_remove (url_to_obj, chat->url);
g_free (orig_name);
jmchat_delete (chat);
}
static void
create_search (const gchar* path)
{
gchar* basename;
gchar* dirname = NULL;
JMChannel* pchannel = NULL;
JMAnn* pann = NULL;
JMMSearch* search;
gchar* url_str;
g_return_if_fail (path);
basename = g_basename (path);
dirname = g_dirname (path);
g_return_if_fail (basename);
g_return_if_fail (dirname);
/* Check path */
parse_path (dirname, &pchannel, &pann);
if (strcmp(dirname, ".") && !(pchannel && pann))
{
error (_("Bad path: %s\n"), path);
goto error;
}
/* Check if already exists */
if (pchannel && jmchannel_get (pchannel, basename))
{
error (_("%s already exists on channel %s\n"),
path, jmchannel_get_name(pchannel));
goto error;
}
/* Check if name exists */
if (HAVE_NAME(basename))
{
error (_("Something named %s already exists\n"), basename);
goto error;
}
/* Create search */
search = jmmsearch_create (jm_btp_bpeer, basename);
if (!search)
{
error (_("Could not create search %s\n"), path);
goto error;
}
/* TODO: Initialize */
url_str = gnet_url_get_nice_string (search->jmmsp->url);
g_print (_("Search %s created (URL is %s)\n"),
basename, url_str);
g_free (url_str);
/* Add to hashtable */
g_hash_table_insert (name_to_search, g_strdup(basename), search);
g_hash_table_insert (url_to_obj, search->jmmsp->url, search);
/* Advertise channel */
if (pchannel && pann)
{
JMAnn* ann;
ann = jmchannel_add (pchannel, pann, basename, search->jmmsp->url);
if (!ann)
error (_("Could not advertise search %s on channel %s\n"),
basename, jmchannel_get_name(pchannel));
}
error:
g_free (dirname);
}
static void
remove_search (const gchar* name)
{
gchar* orig_name;
JMMSearch* search;
/* TODO: Send removal announcements */
/* Lookup search */
if (!g_hash_table_lookup_extended (name_to_search, name,
(gpointer) &orig_name, (gpointer) &search))
{
error (_("%s does not exist\n"), name);
return;
}
/* Delete it */
g_hash_table_remove (name_to_search, name);
g_hash_table_remove (url_to_obj, search->jmmsp->url);
g_free (orig_name);
jmmsearch_delete (search);
}
/* ******************** */
static void
parse_path (const gchar* path, JMChannel** pchannel, JMAnn** pann)
{
const gchar* p;
const gchar* end;
gchar* name = NULL;
g_return_if_fail (path);
g_return_if_fail (pchannel);
g_return_if_fail (pann);
*pchannel = NULL;
*pann = NULL;
if (!strcmp(path, "."))
return;
/* Skip the initial /, if there is one */
p = path;
if (p[0] == '/')
p = &p[1];
/* Get the channel name */
end = strchr (p, '/');
if (end)
name = g_strndup (p, end - p);
else
name = g_strdup (p);
/* Get the channel */
(*pchannel) = (JMChannel*) g_hash_table_lookup (name_to_channel, name);
g_free (name);
if (!*pchannel)
return;
/* If there is no path, then it refers to the root */
if (!end)
{
*pann = (*pchannel)->root_ann;
return;
}
/* Get the announcement */
*pann = jmchannel_get (*pchannel, end);
}
/* **************************************** */
static void
channel_info (JMChannel* channel, JMChannelType type,
JMAnn* ann, gpointer user_data)
{
GURL* url = NULL;
gchar* url_str = NULL;
switch (type)
{
case JMCHANNEL_UPDATE:
{
if (url_str)
g_print (_("[Update %s:%s (%s)]\n"),
jmchannel_get_name(channel), ann->path, url_str);
else
g_print (_("[Update %s:%s]\n"),
jmchannel_get_name(channel), ann->path);
break;
}
case JMCHANNEL_REMOVE:
case JMCHANNEL_REMOVE_TIMEOUT:
{
g_print (_("[Remove %s:%s]\n"), jmchannel_get_name(channel), ann->path);
break;
}
default: g_assert_not_reached();
}
gnet_url_delete (url);
g_free (url_str);
}
static void
channel_error (JMChannel* jmchannel, gpointer user_data)
{
g_print ("JMChannel Error: %s\n", jmchannel_get_name(jmchannel));
}
/* **************************************** */
static void
error (const gchar *format, ...)
{
va_list args;
va_start (args, format);
g_print (_("Error: "));
vfprintf(stderr, format, args);
va_end (args);
if (fail_on_error)
{
g_return_if_fail (shutting_down == FALSE);
shutting_down = TRUE;
try_exit ();
}
}
static void
sig_int_cb (int ignore)
{
/* Reset signal handler */
signal (SIGINT, SIG_DFL);
/* Set shutting down flag */
g_return_if_fail (shutting_down == FALSE);
shutting_down = TRUE;
/* Read no more input */
if (readasync_id)
{
gnet_io_channel_read_async_cancel (readasync_id);
readasync_id = NULL;
}
try_exit ();
}
static void
sig_pipe_cb (int ignore)
{
/* Reset both signal handlers */
signal (SIGPIPE, SIG_DFL);
signal (SIGINT, SIG_DFL);
g_print (_("\nERROR: Caught SIGPIPE signal. This is a bug. Dumping core.\n"));
abort ();
}
static void
try_exit (void)
{
/* Delete objects */
g_hash_table_foreach (name_to_channel, delete_name_to_whatever_hfunc, NULL);
g_hash_table_destroy (name_to_channel);
name_to_channel = NULL;
g_hash_table_foreach (name_to_chat, delete_name_to_whatever_hfunc, NULL);
g_hash_table_destroy (name_to_chat);
name_to_chat = NULL;
g_hash_table_foreach (name_to_search, delete_name_to_whatever_hfunc, NULL);
g_hash_table_destroy (name_to_search);
name_to_search = NULL;
g_hash_table_foreach (url_to_obj, delete_url_to_obj_hfunc, NULL);
g_hash_table_destroy (url_to_obj);
url_to_obj = NULL;
/* Shutdown */
b_conn_num_conns_zero = conn_zero_cb;
if (!jm_running || jmutil_shutdown())
{
exit (EXIT_SUCCESS);
}
else
{
g_print (_("Exiting... (waiting for %d connections)..."), b_conn_num_conns);
g_timeout_add (5000, (GSourceFunc) exit_timeout, (void*) 0);
}
}
static void
delete_name_to_whatever_hfunc (gpointer key, gpointer value, gpointer user_data)
{
g_free ((gchar*) key);
}
static void
delete_url_to_obj_hfunc (gpointer key, gpointer value, gpointer user_data)
{
GURL* url;
url = (GURL*) key;
if (!strcmp(url->protocol, "jm"))
{
JMChannel* channel = (JMChannel*) value;
jmchannel_delete (channel);
}
else if (!strcmp(url->protocol, "jmchat"))
{
JMChat* chat = (JMChat*) value;
jmchat_delete (chat);
}
else if (!strcmp(url->protocol, "jmmsp"))
{
JMMSearch* search = (JMMSearch*) value;
jmmsearch_delete (search);
}
else if (!strcmp(url->protocol, "mtp"))
{
MtpLocalMirror* mirror = (MtpLocalMirror*) value;
mtp_unmirror (mirror);
}
else if (!value)
{
gnet_url_delete (url);
}
else
g_assert_not_reached();
}
static void
conn_zero_cb (void)
{
fprintf (stderr, " done\n");
exit (EXIT_SUCCESS);
}
static gboolean
exit_timeout (gpointer p)
{
exit (EXIT_SUCCESS);
}
syntax highlighted by Code2HTML, v. 0.9.1