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

Connection.cpp

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

#include "papaya/system.h"
#include "EntityHandler.h"
#include "papaya/Event.h"
#include "Socks.h"
#include "SocksFive.h"
#include "PluginHandler.h"
#include "BaseWindow.h"
#include "Connection.h"
#include "Prefs.h"
#include "SystemTriggerEntity.h"

#include "GTKTwoVT.h"

#include "HTTPSocket.h"
#include "SquidSocket.h"

extern PluginHandler * phandler;
extern int read_socket_timeout(gpointer data);
extern BaseWindow * mainWindow;
extern EntityHandler * entities;

// The global preferences data structure.
extern Prefs * globalPreferences;

// Function pointer for plugins to over-write as needed
void (*keepalive_send)(Connection *) = &Connection::keepalive_send;

MUD * Connection::lastMUD = NULL;


#define SOCKET_TIMEOUT 50

void Connection::init() {
  
  loginTrigger = NULL;
  passwordTrigger = NULL;
  
  name = NULL;
  connected = 0;
  copyover_event = TRUE;
  charMode = FALSE;
  deleted = FALSE;
  isclosed = false;
  
  next = (Connection *)NULL;
  
  vt = new GTKTwoVT(this);
  vt->initialise();

  if (queryPreferences()->getPreferenceBoolean("ProxyHTTPConnect")) {
    s = new SquidSocket(this);
  } else {
    if (queryPreferences()->getPreferenceBoolean("ProxyTurfHTTPd")) {
      s = new HTTPSocket(this);
    } else {
      if (queryPreferences()->getPreferenceBoolean("ProxySocks")) {
      if (queryPreferences()->getPreferenceBoolean("ProxySocks4")) {
        s = new Socks(this);
      } else {
        s = new SocksFive(this);
      }
      } else {
      s = new Socket(this);
      }
    }
  }
  
  timeout = 0;
  
  history = new History(queryPreferences()->getPreferenceBoolean("HistoryUseLimit")
                  ? queryPreferences()->getPreferenceInteger("HistoryLimit") : 0,
                  queryPreferences()->getPreferenceBoolean("HistoryIsCircular"));
}

Connection::Connection(MUD * m) {

  keepalive_timer = 0;

  mud = m;
  mud->incrRefCount(false);

  init();
  setName(m->getName());
}

Connection::Connection(int fd, MUD * m, int compress, int cookie) {
  mud = m;
  mud->incrRefCount(false);

  init();

  s->fd = fd;

  isclosed = false;
  copyover_event = FALSE;

  timeout = g_timeout_add(SOCKET_TIMEOUT, read_socket_timeout, (gpointer)this);

  // When doing copyover we're not too likely to call connect()
  keepalive_timer = g_timeout_add(60000, connection_keepalive, (gpointer)this);
  keepalive_pulses = 0;

  setName(m->getName());
}

void auto_reconnect_init(Connection *);

void Connection::closed() {
  isclosed = true;
  auto_reconnect_init(this);
}

bool Connection::isClosed() {
  return isclosed;
}

extern "C" G_MODULE_EXPORT gint on_failed_details_button_clicked(GtkWidget* button, gpointer data) {
  GladeXML * xml = (GladeXML *)data;
  Connection * conn = (Connection *)data;
  GtkWidget * vbox = glade_xml_get_widget(xml, "details_vbox");

  if (GTK_WIDGET_VISIBLE(vbox))
    gtk_widget_hide(vbox); // Hopefully this doesn't decrease ref count.
  else
    gtk_widget_show(vbox);

  return 1;
}


extern "C" G_MODULE_EXPORT gint on_failed_close_button_clicked(GtkWidget* button, gpointer data) {
  GladeXML * xml = (GladeXML *)data;

  GtkWidget * window = glade_xml_get_widget(xml, "connection_failed_window");
  gtk_widget_hide(window);
  gtk_widget_destroy(window);

  g_free(xml);
  return 1;
}
/*
extern "C" G_MODULE_EXPORT gint on_failed_connect_button_clicked(GtkWidget* button, gpointer data) {
  // @@ Try to connect.
  // @@ Don't currently have any extra data such as MUD *...
  on_failed_close_button_clicked(button, data);
  return 1;
}
*/
extern "C" G_MODULE_EXPORT gint on_connection_failed_window_delete_event(GtkWidget* window, GdkEvent * event, gpointer data) {
  return on_failed_close_button_clicked(NULL, data);
}

void Connection::failed(int error) {

  char buf[1024];
  snprintf(buf, 1024, "%s/share/papaya/connection_failed.glade", getPrefix());
  xml = glade_xml_new(buf, NULL, NULL);

  glade_xml_signal_connect_data(xml, "on_failed_details_button_clicked", GTK_SIGNAL_FUNC(on_failed_details_button_clicked), xml);
  //  glade_xml_signal_connect_data(xml, "on_failed_connect_button_clicked", GTK_SIGNAL_FUNC(on_failed_connect_button_clicked), xml);
  glade_xml_signal_connect_data(xml, "on_failed_close_button_clicked", GTK_SIGNAL_FUNC(on_failed_close_button_clicked), xml);
  glade_xml_signal_connect_data(xml, "on_connection_failed_window_delete_event", GTK_SIGNAL_FUNC(on_connection_failed_window_delete_event), xml);

  GtkWidget * error_label = glade_xml_get_widget(xml, "error_label");
  snprintf(buf, 1024, "<span weight=\"bold\" size=\"larger\">Connection to %s port %d failed</span>\n\nPapaya was unable to connect to the MUD specified.  This may be because of MUD server problems, problems with the Internet or problems with your computer's configuration.  If you are behind a firewall check Papaya's proxy settings to ensure that they are correct.", mud->getHostname(), mud->getPort());
  gtk_label_set_markup(GTK_LABEL(error_label), buf);

  error_label = glade_xml_get_widget(xml, "details_error_label");
  snprintf(buf, 1024, "<span weight=\"bold\">Errno:</span> %d", error);
  gtk_label_set_markup(GTK_LABEL(error_label), buf);

  error_label = glade_xml_get_widget(xml, "details_description_label");
  snprintf(buf, 1024, "<span weight=\"bold\">Description:</span> %s", strerror(errno));
  gtk_label_set_markup(GTK_LABEL(error_label), buf);

}

Connection::~Connection() {

  if (deleted)
    return;

  deleted = TRUE;

  close();

  mainWindow->removeConnection(this);

  if (timeout)
    g_source_remove(timeout);

  if (keepalive_timer) {
    g_source_remove(keepalive_timer);
    keepalive_timer = 0;
  }

  delete history;
  history = NULL;

  delete vt;
  vt = NULL;

  delete s;
  s = NULL;

  // Save the old last MUD so we can destroy it if needed
  MUD * old = lastMUD;

  // Assign lastMUD for the reconnection function
  // We don't change the reference count, as we are
  // holding over the reference from the inital Connection
  lastMUD = mud;

  // Decrement the reference count on the old MUD and destroy if needed
  if (old) {
    old->decrRefCount(false);
    if (old->getRefCount() == 0)
      delete old;
  }

  if (name)
    free(name);

}


int Connection::getPort() {
  return mud->getPort();
}

History * Connection::getHistory() {
  return history;
}

char * Connection::getName() {
  return name;
}

void Connection::setName(char * n) {
  if (name)
    free(name);

  name = strdup(n);
}

int login_trigger(regex_t * reg, Connection * c, char * in, char * stripped_in, void * data) {
  MUD * mud = c->getMUD();
  char buf2[1024];
  sprintf(buf2, "%s\n", mud->getLoginName());
  c->getSocket()->write(buf2, strlen(buf2));
  return 1;
}

int password_trigger(regex_t * reg, Connection * c, char * in, char * stripped_in, void * data) {
  MUD * mud = c->getMUD();
  char buf2[1024];
  sprintf(buf2, "%s\n", mud->getPassword());
  c->getSocket()->write(buf2, strlen(buf2));
  return 1;
}

int Connection::connect() {
  getVT()->append(_("Connecting..."));

  if (s->create(getHost(),getPort()) == -1) {
    connected = errno * -1;
    return -1;
  }

  isclosed = false;

  if (timeout)
    g_source_remove(timeout);

  timeout = g_timeout_add(SOCKET_TIMEOUT, read_socket_timeout, (gpointer)this);

  keepalive_timer = g_timeout_add(60000, connection_keepalive, (gpointer)this);
  keepalive_pulses = 0;

  if (strlen(mud->getLoginTrigger()) > 0 && strlen(mud->getLoginName()) > 0) {
    loginTrigger = new SystemTriggerEntity(mud->getLoginTrigger(), this, &login_trigger, NULL);
    entities->addEntity("Login Trigger", loginTrigger);
  }

  if (strlen(mud->getPasswordTrigger()) > 0 && strlen(mud->getPassword()) > 0) {
    passwordTrigger = new SystemTriggerEntity(mud->getPasswordTrigger(), this, &password_trigger, NULL);
    entities->addEntity("Login Trigger", passwordTrigger);
  }

  return 1;
}

int Connection::reconnect() {
  int res;

  // Reset VT session state settings
  getVT()->showCommands();
  getVT()->setInput("");

  // Disconnect the socket.
  close();

  // Delete the socket.
  delete s;

  if (queryPreferences()->getPreferenceBoolean("ProxyHTTPConnect")) {
    s = new SquidSocket(this);
  } else {
    if (queryPreferences()->getPreferenceBoolean("ProxyTurfHTTPd")) {
      s = new HTTPSocket(this);
    } else {
      if (queryPreferences()->getPreferenceBoolean("ProxySocks")) {
      if (queryPreferences()->getPreferenceBoolean("ProxySocks4")) {
        s = new Socks(this);
      } else {
        s = new SocksFive(this);
      }
      } else {
      s = new Socket(this);
      }
    }
  }

  // Reconnect
  res = connect();
  return res;
}

VT * Connection::getVT() {
  return vt;
}

Socket * Connection::getSocket() {
  return s;
}

char * Connection::getHost() {
  return mud->getHostname();
}

Connection * Connection::getNext() {
  return next;
}

void Connection::setNext(Connection * c) {
  next = c;
}

int Connection::isConnected() {

  if (connected)
    return 1;

  // No socket!
  if (!s)
    return -1;

  if (s->connected() == -1) {
    connected = errno * -1;
    return -1;
  }

  if (s->connected() == 1) {
    connected = 1;
    Event * e = new Event(EvConnect, NULL);
    phandler->fireEvent(e, this);
    delete e;
    if (copyover_event)
      getVT()->append(_("  Connected.\n"));
  } else 
    getVT()->append(".");

  return connected;
}

void Connection::close() {

  if (loginTrigger) {
    entities->removeEntity(loginTrigger);
    delete loginTrigger;
    loginTrigger = NULL;
  }

  if (passwordTrigger) {
    entities->removeEntity(passwordTrigger);
    delete passwordTrigger;
    passwordTrigger = NULL;
  }

  if (connected > 0) {
    Event * e = new Event(EvDisconnect, NULL);
    phandler->fireEvent(e, this);
    delete e;
  }

  if (keepalive_timer) {
    g_source_remove(keepalive_timer);
    keepalive_timer = 0;
  }

  if (timeout) {
    g_source_remove(timeout);
    timeout = 0;
  }

  if (s)
    s->dispose();

  connected = 0;
}

void Connection::setCharMode(bool b) {

  if (!queryPreferences()->getPreference("NegotiateCharmode"))
    return;

  if (b == charMode)
    return;

  charMode = b;

  if (charMode)
    getVT()->setMode(VTCharacterMode);
  else
    getVT()->setMode(VTLineMode);

}

bool Connection::getCharMode() {
  return charMode;
}

MUD * Connection::getMUD() {
  return mud;
}

/**
 * Returns the preferences object for this connection.  NULL if it is
 * using the default preferences.
 */

Prefs * Connection::getPreferences() {
  return mud->getPreferences();
}

Prefs * Connection::queryPreferences() {
  if (getPreferences())
    return getPreferences();
  
  return globalPreferences;
}

gint connection_keepalive(gpointer data) {
  Connection * c = (Connection *) data;

  // See if we should send data this minute - this goes above
  // the de-activation check so that it will work properly when
  // first set by the user during a run
  if (++c->keepalive_pulses < c->queryPreferences()->getPreferenceInteger("SendKeepaliveTimer"))
    return TRUE;

  // 0 or -1 both isable the feature
  if (c->queryPreferences()->getPreferenceBoolean("SendKeepalive") == FALSE
      || c->queryPreferences()->getPreferenceInteger("SendKeepaliveTimer") <= 0)
    return TRUE;

  // Send some bytes that most muds ignore for the most part
  ::keepalive_send(c);
  c->keepalive_pulses = 0;

  return TRUE;
}

void Connection::keepalive_send(Connection * c) {
  c->getSocket()->write(c->queryPreferences()->getPreference("SendKeepaliveText"));
  c->getSocket()->write("\r\n");
}

MUD * Connection::getLastMUD() {
  return lastMUD;
}

Generated by  Doxygen 1.6.0   Back to index