/* 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 #include #include #include #include #include #include #include #include #include #include #ifdef JM_ENABLE_GNOME #include #else #include #endif #include #include #include #include #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 ( [optional])\n" " create_channel [channels_only]\n" " remove_channel \n" " open_channel \n" " add_file \n" " remove_file, rm \n" " add_directory [swallow]\n" " create_directory, mkdir \n" " remove_directory, rmdir \n" " list, ls [path]\n" " add_url \n" " remove_url \n" " create_chat \n" " remove_chat \n" " create_search \n" " remove_search \n" " include \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); }