721 lines
17 KiB
C
721 lines
17 KiB
C
#include <glib.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
|
|
#include "gm-net.h"
|
|
#include "gm-marshal.h"
|
|
#include "gm-debug.h"
|
|
#include "gm-support.h"
|
|
|
|
#define GM_NET_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_NET, GmNetPrivate))
|
|
|
|
typedef struct _threadinfo {
|
|
gchar *host;
|
|
gchar *port;
|
|
struct addrinfo *addr;
|
|
struct addrinfo *current;
|
|
struct addrinfo *tmp;
|
|
|
|
pthread_mutex_t *mutex_idle;
|
|
guint idle_id;
|
|
gint ret;
|
|
gchar *message;
|
|
GmNet *net;
|
|
} threadinfo;
|
|
|
|
struct _GmNetPrivate {
|
|
int socket; /**< the connection socket, is -1 if not connected */
|
|
GIOChannel *channel; /**< the channel which is used to 'listen' on the socket via the glib main loop */
|
|
guint source; /**< the id of the socket watch */
|
|
guint connect_timeout_id; /**< the connect timeout id */
|
|
guint connect_check_id; /**< the connect timeout id */
|
|
struct timeval last_connected; /**< contains when the last connect happened */
|
|
int tn_last; /**< used for telnet */
|
|
int tn_subneg; /**< used for telnet */
|
|
|
|
pthread_t thread;
|
|
threadinfo *thread_info;
|
|
pthread_mutex_t mutex_idle;
|
|
|
|
struct addrinfo *addr;
|
|
struct addrinfo *current;
|
|
|
|
GmNetState state; /**< state of the connection */
|
|
gchar *current_host;
|
|
gchar *current_port;
|
|
};
|
|
|
|
/* Signals */
|
|
|
|
enum {
|
|
STATE_CHANGING,
|
|
STATE_CHANGED,
|
|
NET_ERROR,
|
|
BYTES_RECV,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint net_signals[NUM_SIGNALS] = {0};
|
|
|
|
G_DEFINE_TYPE(GmNet, gm_net, G_TYPE_OBJECT)
|
|
|
|
/* These values taken from RFC 854 and RFC 857. */
|
|
#define TN_WILL 251
|
|
#define TN_WONT 252
|
|
#define TN_DO 253
|
|
#define TN_DONT 254
|
|
#define TN_IAC 255 /* Interpret As Command */
|
|
#define TN_SB 250 /* start of subnegotiation */
|
|
#define TN_SE 240 /* end of subnegotiaton */
|
|
|
|
void gm_net_connect_next(GmNet *net);
|
|
|
|
gboolean on_gm_net_input_recv(GIOChannel * source, GIOCondition condition,
|
|
GmNet *net);
|
|
gboolean on_gm_net_connect_check(GIOChannel * source, GIOCondition condition,
|
|
GmNet *net);
|
|
gboolean on_gm_net_connect_timeout(GmNet *net);
|
|
|
|
static void
|
|
gm_net_free_thread_info(GmNet *net) {
|
|
g_free(net->priv->thread_info->message);
|
|
g_free(net->priv->thread_info->host);
|
|
g_free(net->priv->thread_info->port);
|
|
g_free(net->priv->thread_info);
|
|
|
|
net->priv->thread_info = NULL;
|
|
}
|
|
|
|
static void
|
|
gm_net_close_thread(GmNet *net) {
|
|
if (net->priv->thread != 0) {
|
|
pthread_kill(net->priv->thread, SIGKILL);
|
|
|
|
if (net->priv->thread_info->idle_id) {
|
|
g_source_remove(net->priv->thread_info->idle_id);
|
|
}
|
|
|
|
gm_net_free_thread_info(net);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gm_net_finalize(GObject *object) {
|
|
GmNet *net = GM_NET(object);
|
|
|
|
gm_net_close_thread(net);
|
|
|
|
g_free(net->priv->current_host);
|
|
g_free(net->priv->current_port);
|
|
|
|
G_OBJECT_CLASS(gm_net_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
gm_net_class_init(GmNetClass *klass) {
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
|
|
object_class->finalize = gm_net_finalize;
|
|
|
|
net_signals[STATE_CHANGING] =
|
|
g_signal_new("state_changing",
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(GmNetClass, state_changing),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__UINT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
G_TYPE_UINT);
|
|
|
|
net_signals[STATE_CHANGED] =
|
|
g_signal_new("state_changed",
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(GmNetClass, state_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__UINT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
G_TYPE_UINT);
|
|
|
|
net_signals[NET_ERROR] =
|
|
g_signal_new("net_error",
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(GmNetClass, net_error),
|
|
NULL, NULL,
|
|
gm_marshal_VOID__STRING_INT,
|
|
G_TYPE_NONE,
|
|
2,
|
|
G_TYPE_STRING,
|
|
G_TYPE_INT);
|
|
|
|
net_signals[BYTES_RECV] =
|
|
g_signal_new("bytes_recv",
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(GmNetClass, bytes_recv),
|
|
NULL, NULL,
|
|
gm_marshal_VOID__STRING_UINT,
|
|
G_TYPE_NONE,
|
|
2,
|
|
G_TYPE_STRING,
|
|
G_TYPE_UINT);
|
|
|
|
g_type_class_add_private(object_class, sizeof(GmNetPrivate));
|
|
}
|
|
|
|
static void
|
|
gm_net_init(GmNet *net) {
|
|
net->priv = GM_NET_GET_PRIVATE(net);
|
|
net->priv->state = GM_NET_STATE_DISCONNECTED;
|
|
net->priv->addr = NULL;
|
|
net->priv->current = NULL;
|
|
net->priv->channel = NULL;
|
|
|
|
net->priv->source = 0;
|
|
net->priv->connect_timeout_id = 0;
|
|
net->priv->connect_check_id = 0;
|
|
|
|
pthread_mutex_init(&(net->priv->mutex_idle), NULL);
|
|
}
|
|
|
|
static void
|
|
gm_net_set_host(GmNet *net, gchar const *host) {
|
|
g_free(net->priv->current_host);
|
|
net->priv->current_host = g_strdup(host);
|
|
}
|
|
|
|
static void
|
|
gm_net_set_port(GmNet *net, gchar const *port) {
|
|
g_free(net->priv->current_port);
|
|
net->priv->current_port = g_strdup(port);
|
|
}
|
|
|
|
void
|
|
gm_net_set_state(GmNet *net, GmNetState state) {
|
|
g_signal_emit(net, net_signals[STATE_CHANGING], 0, state);
|
|
net->priv->state = state;
|
|
g_signal_emit(net, net_signals[STATE_CHANGED], 0, state);
|
|
}
|
|
|
|
void
|
|
gm_net_clean_disconnection(GmNet *net) {
|
|
GError *err = NULL;
|
|
|
|
if (!net->priv->channel) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: NOT clean for %d",
|
|
net->priv->socket);
|
|
return;
|
|
}
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: clean disconnect for %d",
|
|
net->priv->socket);
|
|
|
|
// Shutdown the channel
|
|
g_io_channel_shutdown(net->priv->channel, TRUE, &err);
|
|
|
|
if (err) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: error on channel shutdown: "
|
|
"%s", err->message);
|
|
g_error_free(err);
|
|
err = NULL;
|
|
}
|
|
|
|
g_io_channel_unref(net->priv->channel);
|
|
|
|
if (net->priv->source) {
|
|
g_source_remove(net->priv->source);
|
|
net->priv->source = 0;
|
|
}
|
|
|
|
net->priv->channel = NULL;
|
|
net->priv->socket = -1;
|
|
|
|
net->priv->tn_last = 0;
|
|
net->priv->tn_subneg = 0;
|
|
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTED);
|
|
}
|
|
|
|
void
|
|
gm_net_dirty_disconnection(GmNet *net, int err) {
|
|
gchar *msg;
|
|
|
|
// Pff, stupid, we print a message and pass it on to clean_disconnection
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.DirtyDisconnection: dirty disconnect %d",
|
|
net->priv->socket);
|
|
|
|
msg = g_strdup_printf(_("Connection lost... (%s)"), strerror(err));
|
|
g_signal_emit(net, net_signals[NET_ERROR], 0, msg,
|
|
GM_NET_ERROR_DISCONNECTED);
|
|
g_free(msg);
|
|
|
|
gm_net_clean_disconnection(net);
|
|
}
|
|
|
|
void
|
|
gm_net_connect_succeed(GmNet *net) {
|
|
freeaddrinfo(net->priv->addr);
|
|
net->priv->addr = NULL;
|
|
net->priv->current = NULL;
|
|
|
|
net->priv->source = g_io_add_watch(net->priv->channel,
|
|
G_IO_IN | G_IO_HUP, (GIOFunc)on_gm_net_input_recv, net);
|
|
|
|
if (net->priv->connect_timeout_id != 0) {
|
|
g_source_remove(net->priv->connect_timeout_id);
|
|
net->priv->connect_timeout_id = 0;
|
|
}
|
|
|
|
if (net->priv->connect_check_id != 0) {
|
|
g_source_remove(net->priv->connect_check_id);
|
|
net->priv->connect_check_id = 0;
|
|
}
|
|
|
|
gettimeofday(&(net->priv->last_connected), NULL);
|
|
gm_net_set_state(net, GM_NET_STATE_CONNECTED);
|
|
}
|
|
|
|
void
|
|
gm_net_connect_failed(GmNet *net, gchar *err, gint code) {
|
|
if (net->priv->channel) {
|
|
g_io_channel_shutdown(net->priv->channel, TRUE, NULL);
|
|
g_io_channel_unref(net->priv->channel);
|
|
net->priv->channel = NULL;
|
|
}
|
|
|
|
g_signal_emit(net, net_signals[NET_ERROR], 0, err, GM_NET_ERROR_CONNECTING);
|
|
|
|
if (net->priv->addr && net->priv->current->ai_next) {
|
|
net->priv->current = net->priv->current->ai_next;
|
|
gm_net_connect_next(net);
|
|
} else {
|
|
net->priv->socket = -1;
|
|
|
|
if (net->priv->addr) {
|
|
freeaddrinfo(net->priv->addr);
|
|
net->priv->addr = NULL;
|
|
net->priv->current = NULL;
|
|
}
|
|
|
|
g_signal_emit(net, net_signals[NET_ERROR], 0,
|
|
_("Could not make connection..."), GM_NET_ERROR_CONNECTING);
|
|
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTED);
|
|
}
|
|
}
|
|
|
|
void
|
|
gm_net_handle_telnet(GmNet *net, unsigned char *buf, int *len) {
|
|
int i, j;
|
|
unsigned char c;
|
|
|
|
j = 0;
|
|
|
|
for (i = 0; i < *len; ++i) {
|
|
c = buf[i];
|
|
|
|
if (net->priv->tn_last) {
|
|
switch (net->priv->tn_last) {
|
|
case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT:
|
|
net->priv->tn_last = 0;
|
|
break;
|
|
case TN_IAC:
|
|
switch (c) {
|
|
case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT:
|
|
net->priv->tn_last = c;
|
|
break;
|
|
case TN_SB:
|
|
net->priv->tn_subneg = 1;
|
|
net->priv->tn_last = 0;
|
|
break;
|
|
case TN_SE:
|
|
net->priv->tn_subneg = 0;
|
|
net->priv->tn_last = 0;
|
|
break;
|
|
case TN_IAC:
|
|
if (!net->priv->tn_subneg) {
|
|
buf[j] = c;
|
|
++j;
|
|
}
|
|
net->priv->tn_last = 0;
|
|
break;
|
|
default:
|
|
net->priv->tn_last = 0;
|
|
break;
|
|
}
|
|
}
|
|
} else if (c == TN_IAC) {
|
|
net->priv->tn_last = TN_IAC;
|
|
} else if (net->priv->tn_subneg) {
|
|
continue;
|
|
} else {
|
|
buf[j] = c;
|
|
++j;
|
|
}
|
|
}
|
|
|
|
*len = j; //Since j-- is the last written char
|
|
}
|
|
|
|
static gboolean
|
|
idle_proceed_connect_next(gpointer user_data) {
|
|
threadinfo *info = (threadinfo *)user_data;
|
|
GmNet *net = info->net;
|
|
struct addrinfo *tmp;
|
|
gint ret = info->ret;
|
|
gint result;
|
|
|
|
info->idle_id = 0;
|
|
pthread_join(net->priv->thread, NULL);
|
|
net->priv->thread = 0;
|
|
|
|
if (info->ret != 0) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.ConnectNext: getnameinfo error: %s",
|
|
gai_strerror(info->ret));
|
|
gm_net_free_thread_info(net);
|
|
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
|
|
return FALSE;
|
|
}
|
|
|
|
gm_net_set_host(net, info->host);
|
|
gm_net_set_port(net, info->port);
|
|
gm_net_set_state(net, GM_NET_STATE_TRY_ADDRESS);
|
|
|
|
tmp = info->tmp;
|
|
gm_net_free_thread_info(net);
|
|
|
|
net->priv->socket = socket(tmp->ai_family, tmp->ai_socktype,
|
|
tmp->ai_protocol);
|
|
|
|
if (net->priv->socket < 0) {
|
|
gm_net_connect_failed(net, strerror(errno), errno);
|
|
} else {
|
|
fcntl(net->priv->socket, F_SETFL,
|
|
fcntl(net->priv->socket, F_GETFL) | O_NONBLOCK);
|
|
|
|
if ((result = connect(net->priv->socket, tmp->ai_addr,
|
|
net->priv->addr->ai_addrlen)) == -1 && errno != EINPROGRESS) {
|
|
gm_net_connect_failed(net, strerror(errno), errno);
|
|
} else {
|
|
net->priv->channel = g_io_channel_unix_new(net->priv->socket);
|
|
g_io_channel_set_close_on_unref(net->priv->channel, TRUE);
|
|
|
|
if (result == 0) {
|
|
gm_net_connect_succeed(net);
|
|
} else {
|
|
net->priv->connect_check_id = g_io_add_watch(net->priv->channel,
|
|
G_IO_OUT|G_IO_ERR, (GIOFunc)on_gm_net_connect_check, net);
|
|
net->priv->connect_timeout_id = g_timeout_add(5000,
|
|
(GSourceFunc)on_gm_net_connect_timeout, net);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
mutex_unlock(void *ptr) {
|
|
pthread_mutex_unlock((pthread_mutex_t *)ptr);
|
|
}
|
|
|
|
void *
|
|
nameinfo_thread(void *ptr) {
|
|
threadinfo *info = (threadinfo *)ptr;
|
|
char host[NI_MAXHOST], port[NI_MAXSERV];
|
|
info->tmp = info->current;
|
|
|
|
info->ret = getnameinfo(info->tmp->ai_addr, info->tmp->ai_addrlen, host,
|
|
NI_MAXHOST, port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
|
|
|
|
if (info->ret == 0) {
|
|
info->host = g_strdup(host);
|
|
info->port = g_strdup(port);
|
|
}
|
|
|
|
pthread_mutex_lock(info->mutex_idle);
|
|
pthread_cleanup_push(mutex_unlock, info->mutex_idle);
|
|
info->idle_id = g_idle_add(idle_proceed_connect_next, info);
|
|
pthread_cleanup_pop(1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gm_net_connect_next(GmNet *net) {
|
|
threadinfo *info;
|
|
|
|
if (net->priv->current == NULL) {
|
|
return;
|
|
} else {
|
|
info = g_new0(threadinfo, 1);
|
|
info->net = net;
|
|
info->current = net->priv->current;
|
|
info->mutex_idle = &(net->priv->mutex_idle);
|
|
|
|
net->priv->thread_info = info;
|
|
pthread_create(&(net->priv->thread), NULL, nameinfo_thread, (void *)info);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
idle_proceed_addrinfo(gpointer user_data) {
|
|
threadinfo *info = (threadinfo *)user_data;
|
|
GmNet *net = info->net;
|
|
|
|
info->idle_id = 0;
|
|
pthread_join(net->priv->thread, NULL);
|
|
net->priv->thread = 0;
|
|
|
|
if (info->ret != 0) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo failed: %s",
|
|
gai_strerror(info->ret));
|
|
gm_net_connect_failed(net, (gchar *)gai_strerror(info->ret),
|
|
info->ret);
|
|
gm_net_free_thread_info(net);
|
|
return FALSE;
|
|
}
|
|
|
|
net->priv->addr = info->addr;
|
|
|
|
if (info->addr != NULL) {
|
|
net->priv->current = info->addr;
|
|
gm_net_free_thread_info(net);
|
|
gm_net_connect_next(net);
|
|
} else {
|
|
gm_net_free_thread_info(net);
|
|
gm_net_connect_failed(net, _("No addresses available"), 0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void *
|
|
addrinfo_thread(void *ptr) {
|
|
threadinfo *info = (threadinfo *)ptr;
|
|
struct addrinfo hint;
|
|
|
|
memset(&hint, 0, sizeof(hint));
|
|
hint.ai_flags = 0;
|
|
hint.ai_family = AF_UNSPEC;
|
|
hint.ai_socktype = SOCK_STREAM;
|
|
hint.ai_protocol = IPPROTO_TCP;
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo: %s : %s",
|
|
info->host, info->port);
|
|
|
|
info->ret = getaddrinfo(info->host, info->port, &hint, &(info->addr));
|
|
|
|
pthread_mutex_lock(info->mutex_idle);
|
|
pthread_cleanup_push(mutex_unlock, info->mutex_idle);
|
|
info->idle_id = g_idle_add(idle_proceed_addrinfo, info);
|
|
pthread_cleanup_pop(1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Public */
|
|
GmNet *
|
|
gm_net_new() {
|
|
GmNet *net = GM_NET(g_object_new(GM_TYPE_NET, NULL));
|
|
|
|
return net;
|
|
}
|
|
|
|
GmNetState
|
|
gm_net_state(GmNet *net) {
|
|
return net->priv->state;
|
|
}
|
|
|
|
void
|
|
gm_net_connect(GmNet *net, const gchar *host, const gchar *port) {
|
|
char shost[NI_MAXHOST], sport[NI_MAXSERV];
|
|
threadinfo *info;
|
|
|
|
if (net->priv->state != GM_NET_STATE_DISCONNECTED) {
|
|
return;
|
|
}
|
|
|
|
snprintf(shost, NI_MAXHOST - 1, "%s", host);
|
|
snprintf(sport, NI_MAXSERV - 1, "%s", port);
|
|
|
|
gm_net_set_host(net, shost);
|
|
gm_net_set_port(net, sport);
|
|
|
|
gm_net_set_state(net, GM_NET_STATE_CONNECTING);
|
|
|
|
info = g_new0(threadinfo, 1);
|
|
info->host = g_strdup(shost);
|
|
info->port = g_strdup(sport);
|
|
info->net = net;
|
|
info->mutex_idle = &(net->priv->mutex_idle);
|
|
|
|
net->priv->thread_info = info;
|
|
pthread_create(&(net->priv->thread), NULL, addrinfo_thread, (void *)info);
|
|
}
|
|
|
|
void
|
|
gm_net_disconnect(GmNet *net) {
|
|
// thread running
|
|
if (net->priv->state != GM_NET_STATE_DISCONNECTED) {
|
|
gm_net_close_thread(net);
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
|
|
|
|
if (net->priv->connect_timeout_id != 0) {
|
|
g_source_remove(net->priv->connect_timeout_id);
|
|
net->priv->connect_timeout_id = 0;
|
|
}
|
|
|
|
if (net->priv->connect_check_id != 0) {
|
|
g_source_remove(net->priv->connect_check_id);
|
|
net->priv->connect_check_id = 0;
|
|
}
|
|
|
|
gm_net_clean_disconnection(net);
|
|
}
|
|
}
|
|
|
|
void
|
|
gm_net_send_line(GmNet *net, gchar *line) {
|
|
gchar *send_line;
|
|
|
|
send_line = (gchar *)(g_strconcat(line, "\r\n", NULL));
|
|
gm_net_send(net, send_line);
|
|
g_free(send_line);
|
|
}
|
|
|
|
void
|
|
gm_net_send(GmNet *net, gchar *text) {
|
|
int result;
|
|
fd_set connect_set;
|
|
|
|
if (net->priv->state == GM_NET_STATE_CONNECTED) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: %s", text);
|
|
|
|
if ((result = send(net->priv->socket, text, strlen(text), 0)) == -1
|
|
&& (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
|
FD_ZERO(&connect_set);
|
|
FD_SET(net->priv->socket, &connect_set);
|
|
|
|
// Wait for sending to be done
|
|
select(net->priv->socket + 1, NULL, &connect_set, NULL, NULL);
|
|
} else if (result == -1) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: error on sending line: %s", strerror(errno));
|
|
gm_net_dirty_disconnection(net, errno);
|
|
}
|
|
} else {
|
|
g_signal_emit(net, net_signals[NET_ERROR], 0, _("Not connected"),
|
|
GM_NET_ERROR);
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: not connected!");
|
|
}
|
|
}
|
|
|
|
const gchar *
|
|
gm_net_current_host(GmNet *net) {
|
|
return net->priv->current_host;
|
|
}
|
|
|
|
const gchar *
|
|
gm_net_current_port(GmNet *net) {
|
|
return net->priv->current_port;
|
|
}
|
|
|
|
/* Callbacks */
|
|
|
|
#define MAX_RECV_BUF 1024
|
|
|
|
gboolean
|
|
on_gm_net_input_recv(GIOChannel * source, GIOCondition condition, GmNet *net) {
|
|
unsigned char lbuf[MAX_RECV_BUF];
|
|
int len;
|
|
|
|
if (condition == G_IO_HUP) {
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
|
|
gm_net_clean_disconnection(net);
|
|
return FALSE;
|
|
}
|
|
|
|
if (net->priv->state != GM_NET_STATE_CONNECTED) {
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.OnInputRecv: not connected!");
|
|
return FALSE;
|
|
}
|
|
|
|
// Break the received line by newline (skip \r)
|
|
len = recv(net->priv->socket, lbuf, MAX_RECV_BUF - 2, 0);
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmNet.OnInputRecv: received %d bytes", len);
|
|
|
|
if (len < 1) {
|
|
// Disconnected, either clean or dirty
|
|
// (shouldn't this be caught by G_IO_HUP?)
|
|
if (len < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
return TRUE;
|
|
}
|
|
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
|
|
gm_net_dirty_disconnection(net, errno);
|
|
} else {
|
|
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
|
|
gm_net_clean_disconnection(net);
|
|
}
|
|
|
|
return FALSE;
|
|
} else {
|
|
// It's fine, we have text!
|
|
gm_net_handle_telnet(net, lbuf, &len);
|
|
g_signal_emit(net, net_signals[BYTES_RECV], 0, lbuf, len);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
on_gm_net_connect_check(GIOChannel * source, GIOCondition condition,
|
|
GmNet *net) {
|
|
int option = 0;
|
|
socklen_t optionsize = sizeof(option);
|
|
|
|
if (net->priv->connect_timeout_id != 0) {
|
|
g_source_remove(net->priv->connect_timeout_id);
|
|
net->priv->connect_timeout_id = 0;
|
|
}
|
|
|
|
if (condition == G_IO_ERR) {
|
|
getsockopt(net->priv->socket, SOL_SOCKET, SO_ERROR, &option, &optionsize);
|
|
gm_net_connect_failed(net, strerror(option), option);
|
|
} else {
|
|
gm_net_connect_succeed(net);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
on_gm_net_connect_timeout(GmNet *net) {
|
|
net->priv->connect_timeout_id = 0;
|
|
|
|
if (net->priv->connect_check_id != 0) {
|
|
g_source_remove(net->priv->connect_check_id);
|
|
net->priv->connect_check_id = 0;
|
|
}
|
|
|
|
gm_net_connect_failed(net, _("Connect timeout (5)"), 0);
|
|
return FALSE;
|
|
}
|