Logo Search packages:      
Sourcecode: papaya version File versions  Download package

PluginHandler.cpp

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <dlfcn.h>
#include <unistd.h>
#endif

#include <dirent.h>

#include <stdlib.h>
#include <string.h>

#include "papaya/system.h"
#include "mudclient.h"

#include "PapayaList.h"
#include "PluginData.h"
#include "PluginHandler.h"
#include "callbacks.h"

typedef int (*SystemTriggerCallbackFunction)(regex_t *, Connection *, char *, char *, void *);
#include "Win32PluginAPITable.h"
#include "papaya/Win32PluginAPIImplementation.h"

void plugin_selection(GtkTreeView * view, void * arg1, GtkTreeViewColumn * column, gpointer data);

extern PluginHandler * phandler;

void on_plugin_ok_clicked(GtkButton * button, gpointer data);
void on_plugin_destroyed(GtkWidget * widget, gpointer data);

PluginHandler::PluginHandler() {
  first_handler = NULL;
  availablePlugins = new PapayaList();
  pluginList = new PapayaList();
  module_message[0] = '\0';
}

PluginHandler::~PluginHandler() {
  saveConfigFile();
  unloadPlugins();

  PapayaListElement * tmp_next;
  for (PapayaListElement * tmp = availablePlugins->firstElement; tmp; tmp = tmp_next) {
    tmp_next = tmp->getNext();
    PluginData * pd = (PluginData *)tmp->getData();
    delete pd;
    tmp->setData(NULL);
    availablePlugins->deleteEntry(tmp);
  }
    
  delete pluginList;
  pluginList = NULL;
  delete availablePlugins;
  availablePlugins = NULL;
}

void PluginHandler::loadPlugins() {
  // Loads the data from the lib/papaya/plugins/ directory.
  loadPluginData();
  // Loads the user's plugin configuration file and actually loads the plugins.
  loadConfigFile();
  // See if there are any unknown plugins for the user to enable.
  checkPlugins();

  // Display any error messages?
  if (strlen(module_message) > 0) {
    new Message("Error", module_message, true);
    module_message[0] = '\0';
  }
}

void PluginHandler::unloadPlugins() {
  struct handler_entry * tmp, * tmp_next;

  // Unreference the plugins first, just so that they can't be called when
  // the shared object has been closed.

  for (tmp = first_handler; tmp; tmp = tmp_next) {
    tmp_next = tmp->next;
    // Call the plugin_cleanup function
    dl_call_func(tmp->handler, "plugin_cleanup");

    // Close the plugin - does this call global dtors?
#ifndef MALLOC
    g_module_close(tmp->handler);
#endif
    free(tmp);
  }

  // This should delete all the data structures in each Plugin item.
  //  pluginList->deleteAll();

}

/**
 * Goes through the new plugins in turn, asking the user if they
 * should be enabled.
 */

void PluginHandler::checkPlugins() {

  for (PapayaListElement * tmp = availablePlugins->firstElement; tmp; tmp = tmp->getNext()) {
    PluginData * pd = (PluginData *)tmp->getData();

    if (!pd->seen) {
      checkToLoad(pd, false);
        continue;
    }
  }
}

static void on_gtk2_turf_plugin_response(GtkDialog * dialog, gint response, gpointer data) {
  PluginData * pd = (PluginData *)data;

  gtk_widget_hide(GTK_WIDGET(dialog));
  gtk_widget_destroy(GTK_WIDGET(dialog));

  if (response == GTK_RESPONSE_YES)
    pd->enable(true);
  else
    pd->enable(false);
 
  pd->seen = true;

  // Loads the data from the lib/papaya/plugins/ directory.
  phandler->loadPluginData();
  // Loads the user's plugin configuration file and actually loads the plugins.
  phandler->loadConfigFile();
  // See if there are any unknown plugins for the user to enable.
  phandler->checkPlugins();
}

static void on_gtk2_plugin_response(GtkDialog * dialog, gint response, gpointer data) {
  PluginData * pd = (PluginData *)data;
  
  if (response == GTK_RESPONSE_YES)
    pd->enable(true);
  else
    pd->enable(false);

  pd->seen = true;

  //  gtk_widget_hide(GTK_WIDGET(dialog));
  //  gtk_widget_destroy(GTK_WIDGET(dialog));

}

void PluginHandler::checkToLoad(PluginData * pd, bool tp) {

  GtkWidget *new_plugin;

  char buf[1024];

  sprintf(buf, _("A new plugin '%s' (version %d.%d) is available.  Do you wish to load it?"), pd->getName(), pd->getMajor(), pd->getMinor());

  new_plugin = gtk_message_dialog_new(NULL,
                              GTK_DIALOG_DESTROY_WITH_PARENT,
                              (GtkMessageType)GTK_MESSAGE_QUESTION,
                              GTK_BUTTONS_YES_NO,
                              buf);

  if (tp)
    g_signal_connect_data(G_OBJECT(new_plugin), "response",
                    G_CALLBACK(on_gtk2_turf_plugin_response), 
                    (gpointer) pd,
                    NULL,
                    (GConnectFlags) 0);
  else
    g_signal_connect_data(G_OBJECT(new_plugin), "response",
                    G_CALLBACK(on_gtk2_plugin_response), 
                    (gpointer) pd,
                    NULL,
                    (GConnectFlags) 0);

  gtk_dialog_run(GTK_DIALOG(new_plugin));
  if (!tp)
    gtk_widget_destroy(new_plugin);

}

void PluginHandler::unloadFile(PluginData * pd) {

  // Look for this plugin's handler entry.
  GModule * handler = findPlugin(pd->getName());

  // Call the plugin's cleanup function.
  dl_call_func(handler, "plugin_cleanup");

  // The Plugin MUST remove itself from the plugin list, or we go bang.
  
  // Remove the handler entry.
  if (first_handler->handler == handler)
    first_handler = first_handler->next;
  else {
    struct handler_entry * tmp;
    struct handler_entry * tmp_next;
    for (tmp = first_handler; tmp; tmp = tmp_next) {
      tmp_next = tmp->next;
      
      if (handler == tmp_next->handler) {
      tmp->next = tmp_next->next;
      break;
      }
    }
  }

  // Finally close the link to the shared object.
#ifndef MALLOC
  g_module_close(handler);
#endif
}

#ifdef WIN32
static plugin_address_table_t pat;

int socket_write(Socket *, char *, int);
void vt_append(VT *, char *);

void init_pat() {
      pat.p_register_plugin = register_plugin;
      pat.p_unregister_plugin = unregister_plugin;
      
      pat.p_preference_add_editor = preference_add_editor;
      pat.p_preference_remove_editor = preference_remove_editor;

      pat.p_connection_get_socket = connection_get_socket;
      pat.p_connection_get_vt = connection_get_vt;
      pat.p_connection_get_name = connection_get_name;
      pat.p_connection_get_mud = connection_get_mud;
      pat.p_connection_get_next = connection_get_next;
      pat.p_connection_query_preferences = connection_query_preferences;

      pat.p_socket_write = socket_write;
      pat.p_socket_get_telnet_option = socket_get_telnet_option;
      
      pat.p_vt_append = vt_append;
      pat.p_vt_add_to_tray = vt_add_to_tray;
      pat.p_vt_remove_from_tray = vt_remove_from_tray;
      pat.p_vt_scroll = vt_scroll;

      pat.p_event_get_type = event_get_type;
      
      pat.p_preferences_set_preference = preferences_set_preference;
      pat.p_preferences_get_preference = preferences_get_preference;

      pat.p_get_prefix = get_prefix;
      pat.p_get_global_preferences = get_global_preferences;
      pat.p_get_command_interpreter = get_command_interpreter;
      pat.p_get_first_connection = get_first_connection;
      pat.p_get_main_window = get_main_window;
      pat.p_get_entity_handler = get_entity_handler;
      pat.p_get_plugin_handler = get_plugin_handler;
      pat.p_get_connection_by_name = get_connection_by_name;

      pat.p_main_window_get_current_connection = main_window_get_current_connection;
      pat.p_main_window_get_item_factory = main_window_get_item_factory;
      pat.p_main_window_add_page_for_app = main_window_add_page_for_app;

      pat.p_entity_handler_add_entity = entity_handler_add_entity;
      pat.p_entity_handler_remove_entity = entity_handler_remove_entity;

      pat.p_plugin_handler_client_message = plugin_handler_client_message;
      pat.p_plugin_handler_add_input_filter = plugin_handler_add_input_filter;
      pat.p_plugin_handler_add_output_filter = plugin_handler_add_output_filter;
      pat.p_plugin_handler_add_prompt_filter = plugin_handler_add_prompt_filter;
      pat.p_plugin_handler_add_telopt_filter = plugin_handler_add_telopt_filter;
      pat.p_plugin_handler_remove_input_filter = plugin_handler_remove_input_filter;
      pat.p_plugin_handler_remove_output_filter = plugin_handler_remove_output_filter;
      pat.p_plugin_handler_remove_prompt_filter = plugin_handler_remove_prompt_filter;
      pat.p_plugin_handler_remove_telopt_filter = plugin_handler_remove_telopt_filter;
      pat.p_plugin_handler_remove_telopt_filters = plugin_handler_remove_telopt_filters;

      pat.p_system_trigger_entity_new = system_trigger_entity_new;

      pat.p_command_interpreter_interpret = command_interpreter_interpret;

      pat.p_preferences_set_preference = preferences_set_preference;
      pat.p_preferences_get_preference = preferences_get_preference;
      pat.p_preferences_set_preference_boolean = preferences_set_preference_boolean;
      pat.p_preferences_get_preference_boolean = preferences_get_preference_boolean;
      pat.p_preferences_set_preference_integer = preferences_set_preference_integer;
      pat.p_preferences_get_preference_integer = preferences_get_preference_integer;


      pat.p_mud_get_preferences = mud_get_preferences;
      pat.p_mud_set_password = mud_set_password;
      pat.p_mud_get_password = mud_get_password;
      pat.p_mud_set_login_name = mud_set_login_name;
      pat.p_mud_get_login_name = mud_get_login_name;
      pat.p_mud_set_login_trigger = mud_set_login_trigger;
      pat.p_mud_get_login_trigger = mud_get_login_trigger;
      pat.p_mud_set_password_trigger = mud_set_password_trigger;
      pat.p_mud_get_password_trigger = mud_get_password_trigger;

      pat.p_message_new = message_new;

      pat.p_fade_get_shade = fade_get_shade;
      pat.p_fade_get_editor = fade_get_editor;
      pat.p_fade_on_prefs_cancel = fade_on_prefs_cancel;
      pat.p_fade_on_prefs_apply = fade_on_prefs_apply;
      pat.p_fade_string_max_colour = fade_string_max_colour;
      pat.p_fade_string_mid_colour = fade_string_mid_colour;
      pat.p_fade_string_min_colour = fade_string_min_colour;
      pat.p_fade_string_use_three = fade_string_use_three;
      pat.p_fade_new = fade_new;
      pat.p_fade_reset = fade_reset;
      pat.p_fade_delete = fade_delete;

      pat.p_turf_protocol_add_command = turf_protocol_add_command;
      pat.p_turf_protocol_is_supported = turf_protocol_is_supported;
}

#endif

void PluginHandler::loadFile(PluginData * pd) {
  char * name = pd->getFilename();

#ifdef WIN32
  init_pat();
#endif

  splash_update(_("Loading Plugin: %s"), pd->getName());

  GModule * handler = g_module_open(name, (GModuleFlags)0);
  if (!handler) {
    const gchar * msg = g_module_error();
    if (msg) {
      printf (_("Failed to open %s: %s."), name, msg);
      printf(CRLF);
      strcat(module_message, msg);
      strcat(module_message, "\n");
    }
    return;
  }

  void (*init_function)(void *);

  init_function = dl_get_init_function(handler);
#ifdef WIN32
  init_function(&pat);
#else
  init_function(NULL);
#endif
  addHandlerEntry(handler);
}

void PluginHandler::addHandlerEntry(GModule * handler) {

  struct handler_entry * entry = (struct handler_entry *)malloc(sizeof(struct handler_entry));

  entry->next = first_handler;
  entry->handler = handler;

  first_handler = entry;
}

void PluginHandler::unregisterPlugin(Plugin * pl) {

  // Remove this plugin from all filters.
  removeInputFilter(pl);
  removeOutputFilter(pl);
  removePromptFilter(pl);
  removeTeloptFilters(pl);

  // Remove this plugin from the plugin list.
  PapayaListElement * ele = pluginList->findEntry(pl);
  if (ele) {
    ele->setData(NULL);
    pluginList->deleteEntry(ele);
  }
  else {
    printf(_("CRITICAL - Could not remove plugin from plugin list.  Expect crash."));
    printf(CRLF);
  }
}

void PluginHandler::registerPlugin(Plugin * pl, char * version) {

  // This has been outdated by the plugin interface versioning, which
  // refuses to load plugins that will definitely crash.
#if 0
  // Check the 'mudclient' VERSION embedded in the Plugin to be installed.

  if (strcmp(VERSION, version)) {
    char buf[16384];
    sprintf(buf, _("WARNING: Plugin version (%s) doesn't match client version (%s)."), version, VERSION);
    new Message("Information", buf, true);
  }
#endif

  pluginList->newEntry(pl);

  // Generate a Connected event for every MUD window open at the moment.
  Event * e = new Event(EvConnect, NULL);
  for (Connection * tmp = first_connection; tmp; tmp = tmp->getNext()) {
    pl->onEvent(e, tmp);
  }
  delete e;

}

void PluginHandler::processOutputFilters(Connection * c, char * in) {

  for (PluginList::iterator i = outputFilters.begin();
       i != outputFilters.end(); i++) {
    (*i)->output(c, in);

    if (strlen(in) == 0)
      return;
  }

  /*
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (!p)
      continue;
          
    p->output(c, in);

    if (strlen(in) == 0)
      return;
  }
  */
}

void PluginHandler::processPromptFilters(Connection * c, char * in) {

  for (PluginList::iterator i = promptFilters.begin();
       i != promptFilters.end(); i++) {
    (*i)->prompt(c, in);

    if (strlen(in) == 0)
      return;
  }

  /*
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (!p)
      continue;
          
    p->prompt(c, in);

    if (strlen(in) == 0)
      return;
  }
  */
}

void PluginHandler::processInputFilters(Connection * c, char * in) {

  for (PluginList::iterator i = inputFilters.begin();
       i != inputFilters.end(); i++) {
    (*i)->input(c, in);

    if (strlen(in) == 0)
      return;
  }

  /*
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (!p)
      continue;
          
    p->input(c, in);

    if (strlen(in) == 0)
      return;

  }
  */

}

/*

void PluginHandler::processTeloptFilters(Connection *c, unsigned char option, unsigned char action) {

  for (PluginList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i++) {

    if ((*i)->option == option)
      (*i)->telopt(c, option, action);
  }

}

*/

void PluginHandler::teloptSubneg(Connection * c, Telnet::option_t option, char * data, int len) {

  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i++) {
    
    if ((*i)->option == option) {
      (*i)->plugin->teloptSubneg(c, (int) option, data, len);
      return; // Only the first plugin gets to handle this.
    }
  }
}

bool PluginHandler::teloptAllowLocal(Connection * c, Telnet::option_t option) {
  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i++) {
    if ((*i)->option == option)
      return (*i)->plugin->teloptAllowLocal(c, (int) option);
  }

  return false;
}

bool PluginHandler::teloptAllowRemote(Connection * c, Telnet::option_t option) {
  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i++) {
    if ((*i)->option == option)
      return (*i)->plugin->teloptAllowRemote(c, (int) option);
  }

  return false;
}

void PluginHandler::teloptHandleRemote(Connection * c, Telnet::option_t option, bool onoff) {
  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i++) {
    if ((*i)->option == option) {
      (*i)->plugin->teloptHandleRemote(c, (int) option, onoff);
      return;
      }
  }

  return;
}

void PluginHandler::clientMessage(Connection * c, char * in) {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (!p)
      continue;
          
    p->clientMessage(c, in);

    if (strlen(in) == 0)
      return;

  }
}

void PluginHandler::fireEvent(Event * e, Connection * c) {

  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (!p)
      continue;

    p->onEvent(e, c);
  }
}

void PluginHandler::pageSwitched() {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
    if (p)
      p->pageSwitched();
  }
}

/**
 * This function loads the PluginData config file from the user's home
 * directory, and enables/disables the appropriate plugins.
 */

void PluginHandler::loadConfigFile() {
  char plugin[1024];
  int status;
  int major = 0;
  int minor = 0;
  int personal = 0;

  FILE * fp = openFile("plugins.conf", NULL, "rb");
  if (!fp)
    return;

  while (!feof(fp)) {
    int res = fscanf(fp, "%s %d.%d %d %d\n", plugin, &major, &minor, &personal, &status);
    if (res != 5) {
      res = fscanf(fp, "%s %d.%d %d\n", plugin, &major, &minor, &status);
      if (res != 4)
      continue; // Possibly blank config file or malformed config file.
    }

    for (PapayaListElement * ele = availablePlugins->firstElement; ele; ele = ele->getNext()) {
      PluginData * pd = (PluginData *)ele->getData();

      // Right name?  Right location?
      if (!strcmp(pd->getName(), plugin)
          && ((personal && !strstr(pd->getFilename(), getPrefix()))
            || (!personal && strstr(pd->getFilename(), getPrefix()))
            )
          ) {

        if (pd->getMajor() > major || (pd->getMajor() == major && pd->getMinor() > minor)) {
          char buf[1024];
          snprintf(buf, 1024, _("Plugin %s is now at version %d.%d"), plugin, pd->getMajor(), pd->getMinor());
          /* The user has never used this version before. */
          new Message("Information", buf, false);
        }
        
        pd->enable((bool)status);
        pd->seen = TRUE;
        break;
      }

        }
  }

  fclose(fp);
}

/**
 * Saves the PluginData config file to the user's home directory.
 */

void PluginHandler::saveConfigFile() {

  FILE * fp = openFile("plugins.conf", NULL, "wb");
  if (!fp) {
    new Message("Error", _("Papaya was unable to save your personal plugin configuration file."), true);
    return;
  }

  for (PapayaListElement * tmp = availablePlugins->firstElement; tmp; tmp = tmp->getNext()) {
    PluginData * pd = (PluginData *)tmp->getData();
    int personal = 0;
    if (!strstr(pd->getFilename(), getPrefix())) // Is this a personal plugin?
      personal = 1;

    fprintf(fp, "%s %d.%d %d %d\n", pd->getName(), pd->getMajor(), pd->getMinor(), personal, pd->enabled());
  }

  fclose(fp);
}

static GtkListStore * plugin_list_store;

GtkWidget * PluginHandler::create_plugin_list() {

  GtkTreeViewColumn *column;
  GtkWidget * plugin_view;
  GtkCellRenderer * cell_renderer;

  GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Plugins");
  gtk_window_set_resizable(GTK_WINDOW(window), true);

  plugin_list_store = gtk_list_store_new(5, 
                               G_TYPE_STRING,
                               G_TYPE_STRING,
                               G_TYPE_STRING,
                               G_TYPE_STRING,
                               G_TYPE_STRING);
  populate_plugin_list();

  plugin_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(plugin_list_store));
  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(plugin_view), true);

  g_signal_connect_data(plugin_view, "row-activated",
                  GTK_SIGNAL_FUNC(plugin_selection), NULL,
                  NULL, (GConnectFlags)0);


  //  g_object_unref(plugin_list_store);

  cell_renderer = gtk_cell_renderer_text_new();

  // Now how to put the data in the tree_view
  column = gtk_tree_view_column_new_with_attributes(_("Name"),
                                        cell_renderer,
                                        "text", 0,
                                        NULL);


  gtk_tree_view_append_column(GTK_TREE_VIEW(plugin_view), column);

  column = gtk_tree_view_column_new_with_attributes(_("Version"),
                                        cell_renderer,
                                        "text", 1,
                                        NULL);


  gtk_tree_view_append_column(GTK_TREE_VIEW(plugin_view), column);

  column = gtk_tree_view_column_new_with_attributes(_("Location"),
                                        cell_renderer,
                                        "text", 2,
                                        NULL);


  gtk_tree_view_append_column(GTK_TREE_VIEW(plugin_view), column);

  column = gtk_tree_view_column_new_with_attributes(_("Description"),
                                        cell_renderer,
                                        "text", 3,
                                        NULL);


  gtk_tree_view_append_column(GTK_TREE_VIEW(plugin_view), column);

  column = gtk_tree_view_column_new_with_attributes(_("Enabled?"),
                                        cell_renderer,
                                        "text", 4,
                                        NULL);


  gtk_tree_view_append_column(GTK_TREE_VIEW(plugin_view), column);

  // Patch from Gareth Owen to fix plugin window not opening second time bug.
  g_signal_connect(G_OBJECT(window), "destroy",
               G_CALLBACK(on_plugin_destroyed), NULL);


  gtk_container_add(GTK_CONTAINER(window), plugin_view);
  gtk_widget_show(plugin_view);
  gtk_widget_show(window);
  return window;
}

void PluginHandler::populate_plugin_list() {

  GtkTreeIter iter;

  // Nuke the contents of the plugin list.
  gtk_list_store_clear(plugin_list_store);

  for (PapayaListElement * tmp = availablePlugins->firstElement; tmp; tmp = tmp->getNext()) {
    PluginData * p = (PluginData *)tmp->getData();

    
    gtk_list_store_append(plugin_list_store,
                    &iter);
    
    char version_buf[1024];
    snprintf(version_buf, 1024, "%d.%d", p->getMajor(), p->getMinor());
    
    gtk_list_store_set(plugin_list_store, &iter,
                   0, p->getName(),
                   1, version_buf,
                   2, strstr(p->getFilename(), getPrefix()) ? "global" : "Personal",
                   3, p->getDescription(),
                   4, p->enabled() ? "Enabled" : "Disabled",
                   -1);
  
  }
}

static GtkWidget * plugin_list = NULL;

void PluginHandler::displayAvailablePlugins() {

  if (plugin_list) {
    gtk_widget_show(plugin_list);
    gtk_window_present((GtkWindow *)plugin_list);
    return;
  }

  plugin_list = create_plugin_list();
  gtk_widget_show(plugin_list);
}

void on_plugin_destroyed(GtkWidget * widget, gpointer data) {
  phandler->saveConfigFile();

  plugin_list = NULL;
}

void on_plugin_ok_clicked(GtkButton * button, gpointer data) {
  gtk_widget_hide(plugin_list);
  gtk_widget_destroy(plugin_list);
}

void plugin_selection(GtkTreeView * view, void * arg1, GtkTreeViewColumn * column, gpointer data) {

  GtkTreeSelection * selection = gtk_tree_view_get_selection(view);
  GtkTreeIter iter;
  GtkTreeModel * model;

  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
    gchar * name;
    gtk_tree_model_get(model, &iter, 0, &name, -1);

    phandler->toggleEnabled((char *)name);
  }
}

void PluginHandler::toggleEnabled(char * name) {
  for (PapayaListElement * tmp = availablePlugins->firstElement; tmp; tmp = tmp->getNext()) {
    PluginData * p = (PluginData *)tmp->getData();
    if (!strcmp(p->getName(), name)) {
      p->enable(!p->enabled());
      phandler->populate_plugin_list();
      return;
    }
  }
}

/**
 * Yet more new plugin loading code.
 */

void PluginHandler::loadPluginData() {
  char dirname[1024];


#ifndef WIN32
  // Check all plugins in user's dir
  char * home = getenv("HOME");
  if (home) {
    // Don't just use sprintf, user might have $HOME set funny
    snprintf(dirname, 1024, "%s/.papaya/plugins", home);
    dirname[1023] = '\0';
    checkDirForPlugins(dirname);
  }
#endif

  // Check all plugins in systemwide dir
  snprintf(dirname, 1024, "%s/lib/papaya/plugins", getPrefix());
  checkDirForPlugins(dirname);

  return;
}

void PluginHandler::checkDirForPlugins(char * dirname) {
      DIR * dir;
  struct dirent * entry;

  dir = opendir(dirname);
  if (!dir)
    return;

  while ((entry = readdir(dir))) {
    if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
      continue;
    
    PluginData * pd = queryPlugin(entry->d_name, dirname);
    if (pd)
      availablePlugins->newEntry(pd);
  }

  closedir(dir);
}


extern "C" void dl_call_func_int(GModule * handle, char * name, int * retval);

PluginData * PluginHandler::queryPlugin(char * n, char * path) {
  char name[1024];
  char description[1024];
  char minor[1024];
  char major[1024];
  char fname[1024];
  int api_major, api_minor, api_bugfix, valid;

  struct stat file_stat;

  if (path) {
    snprintf(fname, 1024, "%s/%s", path, n);
  } else {
    snprintf(fname, 1024, "%s/lib/papaya/plugins/%s", getPrefix(), n);
  }

  /* Ensure that the requested file exists. */
  if (stat(fname, &file_stat) == -1)
    return NULL;

    // Gareth Owen's code
  if (!isNameValid(n))
    return NULL;

  GModule * handle = g_module_open(fname, (GModuleFlags)0);
  if (!handle) {
    const gchar * msg = g_module_error();
    if (msg) {
      printf (_("Failed to open %s: %s"), fname, msg);
      printf(CRLF);
      strcat(module_message, msg);
      strcat(module_message, "\n");
    }
    return NULL;
  }

  dl_call_func_ret(handle, "plugin_query_name", name);
  dl_call_func_ret(handle, "plugin_query_description", description);
  dl_call_func_ret(handle, "plugin_query_major", major);
  dl_call_func_ret(handle, "plugin_query_minor", minor);

  dl_call_func_int(handle, "plugin_query_api_minor", &api_minor);
  dl_call_func_int(handle, "plugin_query_api_major", &api_major);
  dl_call_func_int(handle, "plugin_query_api_bugfix", &api_bugfix);

  dl_call_func_int(handle, "plugin_is_valid", &valid);
#ifndef MALLOC  
  g_module_close(handle);
#endif

  // Verify that the plugin was compiled with -DPLUGIN
  if (valid == 0) {
    char buf[1024];
    snprintf(buf, 1024, "Plugin '%s' is not a valid plugin.  (Programmers: The plugin was compiled without the -DPLUGIN compiler option.  Recompile the plugin with this option enabled.", name);
    strcat(module_message, buf);
    return NULL;
  }

  // Check that the API version of the Plugin is compatible with this Papaya.

  // Major API change, won't work.
  if (api_major != PLUGIN_API_MAJOR)
    return NULL;

  // Plugin uses a newer API, won't work
  if (api_minor > PLUGIN_API_MINOR)
    return NULL;

  PluginData * pd = new PluginData(name);
  pd->setDescription(description);
  pd->setMajor(major);
  pd->setMinor(minor);
  pd->setFilename(fname);
  
  return pd;
}


void PluginHandler::onPrefsApply(MUD * mud) {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
  
    if (!p)
      continue;

    p->onPrefsApply(mud);
  }
}

void PluginHandler::onPrefsOk(MUD * mud) {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
  
    if (!p)
      continue;

    p->onPrefsOk(mud);
  }
}

void PluginHandler::savePrefs(FILE * fp, MUD * mud) {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
  
    if (!p)
      continue;

    p->savePrefs(fp, mud);
  }
}

void PluginHandler::onPrefsCancel(MUD * mud) {
  for (PapayaListElement * tmp = pluginList->firstElement; tmp; tmp = tmp->getNext()) {
    Plugin * p = (Plugin *)tmp->getData();
  
    if (!p)
      continue;

    p->onPrefsCancel(mud);
  }
}

GModule * PluginHandler::findPlugin(char * name) {
  char plugin_name[1024];
  struct handler_entry * tmp;

  for (tmp = first_handler; tmp; tmp = tmp->next) {
    dl_call_func_ret(tmp->handler, "plugin_query_name", plugin_name);
    if (!strcmp(name, plugin_name))
      return tmp->handler;
  }
  return NULL;
}

extern "C" GModule * plugin_handler_find_plugin(char * name) {
      return phandler->findPlugin(name);
}

void * PluginHandler::findPlugin(char * name, char * func) {
  char plugin_name[1024];
  struct handler_entry * tmp;

  for (tmp = first_handler; tmp; tmp = tmp->next) {
    dl_call_func_ret(tmp->handler, "plugin_query_name", plugin_name);
    if (!strcmp(name, plugin_name))
      return findFunction(tmp->handler, func);
  }
  return NULL;
}

// Code from Gareth Owen to circumvent libtool generously providing so many
// files/symlinks per plugin.

bool PluginHandler::isNameValid(char * name) {
  // pointer to the suffix
  char * pc = strchr(name, '.');

  if (!pc)
    return false;

  // The only valid suffixes are so, dll or none.
  if (!strcasecmp(pc, ".dll")
      || !strcasecmp(pc, ".so"))
    return true;

  return false;
}

static int PluginCmp(Plugin * p1, Plugin * p2) {
  return (p1 < p2);
}

static int TeloptFilterCmp(struct telopt_filter_entry * f1, struct telopt_filter_entry * f2) {

  if (!f1 || !f2)
    return (f1 < f2);

  if (f1->plugin < f2->plugin)
    return 1;

  if (f1->plugin > f2->plugin)
    return 0;

  return (f1 < f2);
}

void PluginHandler::addInputFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(inputFilters.begin(),
                                  inputFilters.end(),
                                  plugin,
                                  PluginCmp);
  inputFilters.insert(i, plugin);
}

void PluginHandler::addOutputFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(outputFilters.begin(),
                                  outputFilters.end(),
                                  plugin,
                                  PluginCmp);
  outputFilters.insert(i, plugin);
}

void PluginHandler::addPromptFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(promptFilters.begin(),
                                  promptFilters.end(),
                                  plugin,
                                  PluginCmp);
  promptFilters.insert(i, plugin);
}

void PluginHandler::removeInputFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(inputFilters.begin(),
                                  inputFilters.end(),
                                  plugin,
                                  PluginCmp);

  if (i == inputFilters.end() || (*i) != plugin)
    return;

  inputFilters.erase(i);
}

void PluginHandler::removeOutputFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(outputFilters.begin(),
                                  outputFilters.end(),
                                  plugin,
                                  PluginCmp);

  if (i == outputFilters.end() || (*i) != plugin)
    return;

  outputFilters.erase(i);
}

void PluginHandler::removePromptFilter(Plugin * plugin) {
  PluginList::iterator i = std::lower_bound(promptFilters.begin(),
                                  promptFilters.end(),
                                  plugin,
                                  PluginCmp);

  if (i == promptFilters.end() || (*i) != plugin)
    return;

  promptFilters.erase(i);
}

void PluginHandler::addTeloptFilter(Plugin * plugin, unsigned char option) {

  struct telopt_filter_entry * entry = (struct telopt_filter_entry *)malloc(sizeof(struct telopt_filter_entry));
  memset(entry, 0, sizeof(struct telopt_filter_entry));
  entry->plugin = plugin;
  entry->option = option;

  TeloptList::iterator i = std::lower_bound(teloptFilters.begin(),
                                  teloptFilters.end(),
                                  entry,
                                  TeloptFilterCmp);
  teloptFilters.insert(i, entry);
}

void PluginHandler::removeTeloptFilter(Plugin * plugin, unsigned char option) {
  TeloptList::iterator i_next;
  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i = i_next) {
    i_next = i;
    i_next++;

    struct telopt_filter_entry * entry = (struct telopt_filter_entry *)(*i);
    if (entry->plugin == plugin && entry->option == option)
      teloptFilters.erase(i);
  }
}

void PluginHandler::removeTeloptFilters(Plugin * plugin) {
  TeloptList::iterator i_next;
  for (TeloptList::iterator i = teloptFilters.begin();
       i != teloptFilters.end(); i = i_next) {
    i_next = i;
    i_next++;

    struct telopt_filter_entry * entry = (struct telopt_filter_entry *)(*i);
    if (entry->plugin == plugin)
      teloptFilters.erase(i);
  }
}

Generated by  Doxygen 1.6.0   Back to index