diff --git a/adium/Makefile.am b/adium/Makefile.am
index c44d5a4..83bc862 100755
--- a/adium/Makefile.am
+++ b/adium/Makefile.am
@@ -1,92 +1,82 @@
-EXTRA_DIST = cvr/pn_direct_conn.c \
- cvr/pn_direct_conn.h
-
pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-MSNSOURCES = msn.c \
- nexus.c \
- notification.c \
- page.c \
- session.c \
- switchboard.c \
- sync.c \
- pn_log.c \
- pn_printf.c \
- pn_util.c \
- pn_buffer.c \
- pn_error.c \
- pn_status.c \
- pn_oim.c \
- pn_dp_manager.c \
- pn_siren7.c \
- cmd/cmdproc.c \
- cmd/command.c \
- cmd/msg.c \
- cmd/table.c \
- cmd/transaction.c \
- io/pn_parser.c \
- ab/pn_group.c \
- ab/pn_contact.c \
- ab/pn_contactlist.c \
- io/pn_stream.c \
- io/pn_node.c \
- io/pn_cmd_server.c \
- io/pn_http_server.c \
- io/pn_ssl_conn.c \
- cvr/pn_peer_call.c \
- cvr/pn_peer_link.c \
- cvr/pn_peer_msg.c \
- cvr/pn_msnobj.c \
- libpurple/xfer.c \
- ext/libsiren/common.c \
- ext/libsiren/dct4.c \
- ext/libsiren/decoder.c \
- ext/libsiren/huffman.c \
- ext/libsiren/rmlt.c \
- ext/libmspack/cabd.c \
- ext/libmspack/mszipd.c \
- ext/libmspack/lzxd.c \
- ext/libmspack/qtmd.c \
- ext/libmspack/system.c \
- fix_purple.c
+MSNSOURCES = \
+ msn.c \
+ nexus.c \
+ notification.c \
+ page.c \
+ session.c \
+ switchboard.c \
+ sync.c \
+ pn_log.c \
+ pn_printf.c \
+ pn_util.c \
+ pn_buffer.c \
+ pn_error.c \
+ pn_status.c \
+ pn_oim.c \
+ pn_dp_manager.c \
+ pn_siren7.c \
+ cmd/cmdproc.c \
+ cmd/command.c \
+ cmd/msg.c \
+ cmd/table.c \
+ cmd/transaction.c \
+ io/pn_parser.c \
+ ab/pn_group.c \
+ ab/pn_contact.c \
+ ab/pn_contactlist.c \
+ io/pn_stream.c \
+ io/pn_node.c \
+ io/pn_cmd_server.c \
+ io/pn_http_server.c \
+ io/pn_ssl_conn.c \
+ io/pn_dc_conn.c \
+ cvr/pn_peer_call.c \
+ cvr/pn_peer_link.c \
+ cvr/pn_peer_msg.c \
+ cvr/pn_msnobj.c \
+ cvr/pn_direct_conn.c \
+ libpurple/xfer.c \
+ ext/libsiren/common.c \
+ ext/libsiren/dct4.c \
+ ext/libsiren/decoder.c \
+ ext/libsiren/huffman.c \
+ ext/libsiren/rmlt.c \
+ ext/libmspack/cabd.c \
+ ext/libmspack/mszipd.c \
+ ext/libmspack/lzxd.c \
+ ext/libmspack/qtmd.c \
+ ext/libmspack/system.c \
+ fix_purple.c
AM_CFLAGS = $(st)
-version := $(shell $(srcdir)/get-version.sh)
+version := $(shell $(srcdir)/get-version)
libmsn_pecan_la_LDFLAGS = -module -avoid-version -lm
-if STATIC_MSN
-
st = -DPURPLE_STATIC_PRPL
noinst_LTLIBRARIES = libmsn_pecan.la
libmsn_pecan_la_SOURCES = $(MSNSOURCES)
libmsn_pecan_la_CFLAGS = $(AM_CFLAGS)
-else
-
-st =
-pkg_LTLIBRARIES = libmsn_pecan.la
-libmsn_pecan_la_SOURCES = $(MSNSOURCES)
-libmsn_pecan_la_LIBADD = $(GLIB_LIBS)
-
-endif
-
AM_CPPFLAGS = \
- -I$(top_srcdir)/libpurple \
- -I$(top_builddir)/libpurple \
- $(GLIB_CFLAGS) \
- $(DEBUG_CFLAGS) \
- -DSTATIC_PECAN \
- -DHAVE_LIBPURPLE \
- -D_XOPEN_SOURCE \
- -DADIUM \
- -DPURPLE_DEBUG \
- -DPECAN_DEBUG_SLP \
- -DPECAN_CVR \
- -DPECAN_USE_PSM \
- -DPECAN_LIBSIREN \
- -DRECEIVE_PLUS_SOUNDS \
- -DGETTEXT_PACKAGE='"pidgin"' \
- -DENABLE_NLS \
- -DVERSION='"$(version)"'
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ $(GLIB_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ -DSTATIC_PECAN \
+ -DHAVE_LIBPURPLE \
+ -D_XOPEN_SOURCE \
+ -DADIUM \
+ -DPURPLE_DEBUG \
+ -DPECAN_DEBUG_SLP \
+ -DPECAN_CVR \
+ -DMSN_DIRECTCONN \
+ -DPECAN_USE_PSM \
+ -DPECAN_LIBSIREN \
+ -DRECEIVE_PLUS_SOUNDS \
+ -DGETTEXT_PACKAGE='"pidgin"' \
+ -DENABLE_NLS \
+ -DVERSION='"$(version)"'
--
1.6.5
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
adium/Makefile.am | 1 +
session.c | 22 ++++++++++++++++++++++
session_private.h | 5 +++++
3 files changed, 28 insertions(+), 0 deletions(-)
mode change 100644 => 100755 session.c
mode change 100644 => 100755 session_private.h
diff --git a/adium/Makefile.am b/adium/Makefile.am
index 83bc862..e2c10d4 100755
--- a/adium/Makefile.am
+++ b/adium/Makefile.am
@@ -69,6 +69,7 @@ AM_CPPFLAGS = \
-DSTATIC_PECAN \
-DHAVE_LIBPURPLE \
-D_XOPEN_SOURCE \
+ -DINTERNAL_MAINLOOP \
-DADIUM \
-DPURPLE_DEBUG \
-DPECAN_DEBUG_SLP \
diff --git a/session.c b/session.c
old mode 100644
new mode 100755
index 6d44f17..8b16adf
--- a/session.c
+++ b/session.c
@@ -41,6 +41,9 @@
/* libpurple stuff. */
#include <account.h>
+#ifdef INTERNAL_MAINLOOP
+#include <eventloop.h>
+#endif
static void
conversation_created_cb (PurpleConversation *conv, gpointer data)
@@ -69,6 +72,15 @@ conversation_created_cb (PurpleConversation *conv, gpointer data)
}
}
+#ifdef INTERNAL_MAINLOOP
+static inline gboolean
+g_main_context_iteration_timer ()
+{
+ g_main_context_iteration (NULL, FALSE);
+ return TRUE;
+}
+#endif
+
MsnSession *
msn_session_new (const gchar *username,
const gchar *password,
@@ -81,6 +93,11 @@ msn_session_new (const gchar *username,
session->username = pn_normalize (username);
session->password = g_strndup (password, 16);
+#ifdef INTERNAL_MAINLOOP
+ session->g_main_loop = g_main_loop_new (NULL, FALSE);
+ session->g_main_loop_timer = purple_timeout_add (1000, g_main_context_iteration_timer, NULL);
+#endif
+
session->config = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/** @todo sb and ns need this here but should be updated on-the-fly. */
@@ -178,6 +195,11 @@ msn_session_destroy (MsnSession *session)
g_free (session->username);
g_free (session->password);
+#ifdef INTERNAL_MAINLOOP
+ purple_timeout_remove (session->g_main_loop_timer);
+ g_main_loop_unref (session->g_main_loop);
+#endif
+
g_free (session);
}
diff --git a/session_private.h b/session_private.h
old mode 100644
new mode 100755
index 404ea4e..5fb71d0
--- a/session_private.h
+++ b/session_private.h
@@ -44,6 +44,11 @@ struct MsnSession
gchar *username;
gchar *password;
+#ifdef INTERNAL_MAINLOOP
+ GMainLoop *g_main_loop;
+ guint g_main_loop_timer;
+#endif
+
void *user_data;
struct pn_contact *user; /**< Store contact information. */
--
1.6.5
diff --git a/Makefile b/Makefile
index 4cc9b53..ccde133 100644
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,7 @@ CFLAGS += -Wall # $(EXTRA_WARNINGS)
override CFLAGS += -D_XOPEN_SOURCE
override CFLAGS += -I. -DENABLE_NLS -DHAVE_LIBPURPLE -DPURPLE_DEBUG
+override CFLAGS += -DPIDGIN
ifdef CVR
override CFLAGS += -DPECAN_CVR
diff --git a/msn.c b/msn.c
index 99ec39e..9db608c 100644
--- a/msn.c
+++ b/msn.c
@@ -64,7 +64,7 @@
#endif /* PURPLE_VERSION_CHECK(2,5,0) */
#endif /* defined(PECAN_CVR) */
-#ifndef ADIUM
+#ifdef PIDGIN
#define PLUGIN_ID "prpl-msn-pecan"
#else
#define PLUGIN_ID "prpl-msn_pecan"
--
1.6.5
diff --git a/adium/Makefile.am b/adium/Makefile.am
deleted file mode 100755
index e2c10d4..0000000
--- a/adium/Makefile.am
+++ /dev/null
@@ -1,83 +0,0 @@
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-
-MSNSOURCES = \
- msn.c \
- io/pn_dc_conn.c \
- cvr/pn_peer_call.c \
- cvr/pn_peer_link.c \
- cvr/pn_peer_msg.c \
- cvr/pn_msnobj.c \
- cvr/pn_direct_conn.c \
- libpurple/xfer.c \
- ext/libsiren/common.c \
- ext/libsiren/dct4.c \
- ext/libsiren/decoder.c \
- ext/libsiren/huffman.c \
- ext/libsiren/rmlt.c \
- ext/libmspack/cabd.c \
- ext/libmspack/mszipd.c \
- ext/libmspack/lzxd.c \
- ext/libmspack/qtmd.c \
- ext/libmspack/system.c \
- fix_purple.c
-
-AM_CFLAGS = $(st)
-
-version := $(shell $(srcdir)/get-version)
-
-libmsn_pecan_la_LDFLAGS = -module -avoid-version -lm
-
-st = -DPURPLE_STATIC_PRPL
-noinst_LTLIBRARIES = libmsn_pecan.la
-libmsn_pecan_la_SOURCES = $(MSNSOURCES)
-libmsn_pecan_la_CFLAGS = $(AM_CFLAGS)
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/libpurple \
- -I$(top_builddir)/libpurple \
- $(GLIB_CFLAGS) \
- $(DEBUG_CFLAGS) \
- -DSTATIC_PECAN \
- -DHAVE_LIBPURPLE \
- -D_XOPEN_SOURCE \
- -DINTERNAL_MAINLOOP \
- -DADIUM \
- -DPURPLE_DEBUG \
- -DPECAN_DEBUG_SLP \
- -DPECAN_CVR \
- -DMSN_DIRECTCONN \
- -DPECAN_USE_PSM \
- -DPECAN_LIBSIREN \
- -DRECEIVE_PLUS_SOUNDS \
- -DGETTEXT_PACKAGE='"pidgin"' \
- -DENABLE_NLS \
- -DVERSION='"$(version)"'
diff --git a/clients/Adium/Makefile.am b/clients/Adium/Makefile.am
new file mode 100755
index 0000000..e2c10d4
--- /dev/null
+++ b/clients/Adium/Makefile.am
@@ -0,0 +1,83 @@
+pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+
+
+AM_CFLAGS = $(st)
+
+version := $(shell $(srcdir)/get-version)
+
+libmsn_pecan_la_LDFLAGS = -module -avoid-version -lm
+
+st = -DPURPLE_STATIC_PRPL
+noinst_LTLIBRARIES = libmsn_pecan.la
+libmsn_pecan_la_SOURCES = $(MSNSOURCES)
+libmsn_pecan_la_CFLAGS = $(AM_CFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/libpurple \
+ -I$(top_builddir)/libpurple \
+ $(GLIB_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ -DSTATIC_PECAN \
+ -DHAVE_LIBPURPLE \
+ -D_XOPEN_SOURCE \
+ -DINTERNAL_MAINLOOP \
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
clients/Instantbird/giochannel.c | 2316 ++++++++++++++++++++++++++++++++
clients/Instantbird/giounix.c | 598 ++++++++
clients/Instantbird/giowin32.c | 2129 +++++++++++++++++++++++++++++
clients/Instantbird/gstrcmp0.c | 22 +
clients/Instantbird/linux/Makefile.in | 62 +
clients/Instantbird/macosx/Makefile.in | 63 +
clients/Instantbird/win32/Makefile.in | 63 +
msn.c | 4 +
8 files changed, 5257 insertions(+), 0 deletions(-)
create mode 100755 clients/Instantbird/giochannel.c
create mode 100755 clients/Instantbird/giounix.c
create mode 100755 clients/Instantbird/giowin32.c
create mode 100755 clients/Instantbird/gstrcmp0.c
create mode 100755 clients/Instantbird/linux/Makefile.in
create mode 100755 clients/Instantbird/macosx/Makefile.in
create mode 100755 clients/Instantbird/win32/Makefile.in
diff --git a/clients/Instantbird/giochannel.c b/clients/Instantbird/giochannel.c
new file mode 100755
index 0000000..b067736
--- /dev/null
+++ b/clients/Instantbird/giochannel.c
@@ -0,0 +1,2316 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * giochannel.c: IO Channel abstraction
+ * Copyright 1998 Owen Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#ifndef EILSEQ
+# define EILSEQ 42
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#undef G_DISABLE_DEPRECATED
+
+#include "glib.h"
+
+#include "giochannel.h"
+
+#include "glibintl.h"
+
+#include "galias.h"
+
+#define G_IO_NICE_BUF_SIZE 1024
+
+/* This needs to be as wide as the largest character in any possible encoding */
+#define MAX_CHAR_SIZE 10
+
+/* Some simplifying macros, which reduce the need to worry whether the
+ * buffers have been allocated. These also make USE_BUF () an lvalue,
+ * which is used in g_io_channel_read_to_end ().
+ */
+#define USE_BUF(channel) ((channel)->encoding ? (channel)->encoded_read_buf \
+ : (channel)->read_buf)
+#define BUF_LEN(string) ((string) ? (string)->len : 0)
+
+static GIOError g_io_error_get_from_g_error (GIOStatus status,
+ GError *err);
+static void g_io_channel_purge (GIOChannel *channel);
+static GIOStatus g_io_channel_fill_buffer (GIOChannel *channel,
+ GError **err);
+static GIOStatus g_io_channel_read_line_backend (GIOChannel *channel,
+ gsize *length,
+ gsize *terminator_pos,
+ GError **error);
+
+void
+g_io_channel_init (GIOChannel *channel)
+{
+ channel->ref_count = 1;
+ channel->encoding = g_strdup ("UTF-8");
+ channel->line_term = NULL;
+ channel->line_term_len = 0;
+ channel->buf_size = G_IO_NICE_BUF_SIZE;
+ channel->read_cd = (GIConv) -1;
+ channel->write_cd = (GIConv) -1;
+ channel->read_buf = NULL; /* Lazy allocate buffers */
+ channel->encoded_read_buf = NULL;
+ channel->write_buf = NULL;
+ channel->partial_write_buf[0] = '\0';
+ channel->use_buffer = TRUE;
+ channel->do_encode = FALSE;
+ channel->close_on_unref = FALSE;
+}
+
+GIOChannel *
+g_io_channel_ref (GIOChannel *channel)
+{
+ g_return_val_if_fail (channel != NULL, NULL);
+
+ g_atomic_int_inc (&channel->ref_count);
+
+ return channel;
+}
+
+void
+g_io_channel_unref (GIOChannel *channel)
+{
+ gboolean is_zero;
+
+ g_return_if_fail (channel != NULL);
+
+ is_zero = g_atomic_int_dec_and_test (&channel->ref_count);
+
+ if (G_UNLIKELY (is_zero))
+ {
+ if (channel->close_on_unref)
+ g_io_channel_shutdown (channel, TRUE, NULL);
+ else
+ g_io_channel_purge (channel);
+ g_free (channel->encoding);
+ if (channel->read_cd != (GIConv) -1)
+ g_iconv_close (channel->read_cd);
+ if (channel->write_cd != (GIConv) -1)
+ g_iconv_close (channel->write_cd);
+ g_free (channel->line_term);
+ if (channel->read_buf)
+ g_string_free (channel->read_buf, TRUE);
+ if (channel->write_buf)
+ g_string_free (channel->write_buf, TRUE);
+ if (channel->encoded_read_buf)
+ g_string_free (channel->encoded_read_buf, TRUE);
+ channel->funcs->io_free (channel);
+ }
+}
+
+static GIOError
+g_io_error_get_from_g_error (GIOStatus status,
+ GError *err)
+{
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ case G_IO_STATUS_EOF:
+ return G_IO_ERROR_NONE;
+ case G_IO_STATUS_AGAIN:
+ return G_IO_ERROR_AGAIN;
+ case G_IO_STATUS_ERROR:
+ g_return_val_if_fail (err != NULL, G_IO_ERROR_UNKNOWN);
+
+ if (err->domain != G_IO_CHANNEL_ERROR)
+ return G_IO_ERROR_UNKNOWN;
+ switch (err->code)
+ {
+ case G_IO_CHANNEL_ERROR_INVAL:
+ return G_IO_ERROR_INVAL;
+ default:
+ return G_IO_ERROR_UNKNOWN;
+ }
+ default:
+ g_assert_not_reached ();
+ return G_IO_ERROR_UNKNOWN; /* Keep the compiler happy */
+ }
+}
+
+/**
+ * g_io_channel_read:
+ * @channel: a #GIOChannel
+ * @buf: a buffer to read the data into (which should be at least
+ * count bytes long)
+ * @count: the number of bytes to read from the #GIOChannel
+ * @bytes_read: returns the number of bytes actually read
+ *
+ * Reads data from a #GIOChannel.
+ *
+ * Return value: %G_IO_ERROR_NONE if the operation was successful.
+ *
+ * Deprecated:2.2: Use g_io_channel_read_chars() instead.
+ **/
+GIOError
+g_io_channel_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read)
+{
+ GError *err = NULL;
+ GIOError error;
+ GIOStatus status;
+
+ g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
+ g_return_val_if_fail (bytes_read != NULL, G_IO_ERROR_UNKNOWN);
+
+ if (count == 0)
+ {
+ if (bytes_read)
+ *bytes_read = 0;
+ return G_IO_ERROR_NONE;
+ }
+
+ g_return_val_if_fail (buf != NULL, G_IO_ERROR_UNKNOWN);
+
+ status = channel->funcs->io_read (channel, buf, count, bytes_read, &err);
+
+ error = g_io_error_get_from_g_error (status, err);
+
+ if (err)
+ g_error_free (err);
+
+ return error;
+}
+
+/**
+ * g_io_channel_write:
+ * @channel: a #GIOChannel
+ * @buf: the buffer containing the data to write
+ * @count: the number of bytes to write
+ * @bytes_written: the number of bytes actually written
+ *
+ * Writes data to a #GIOChannel.
+ *
+ * Return value: %G_IO_ERROR_NONE if the operation was successful.
+ *
+ * Deprecated:2.2: Use g_io_channel_write_chars() instead.
+ **/
+GIOError
+g_io_channel_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written)
+{
+ GError *err = NULL;
+ GIOError error;
+ GIOStatus status;
+
+ g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
+ g_return_val_if_fail (bytes_written != NULL, G_IO_ERROR_UNKNOWN);
+
+ status = channel->funcs->io_write (channel, buf, count, bytes_written, &err);
+
+ error = g_io_error_get_from_g_error (status, err);
+
+ if (err)
+ g_error_free (err);
+
+ return error;
+}
+
+/**
+ * g_io_channel_seek:
+ * @channel: a #GIOChannel
+ * @offset: an offset, in bytes, which is added to the position specified
+ * by @type
+ * @type: the position in the file, which can be %G_SEEK_CUR (the current
+ * position), %G_SEEK_SET (the start of the file), or %G_SEEK_END
+ * (the end of the file)
+ *
+ * Sets the current position in the #GIOChannel, similar to the standard
+ * library function fseek().
+ *
+ * Return value: %G_IO_ERROR_NONE if the operation was successful.
+ *
+ * Deprecated:2.2: Use g_io_channel_seek_position() instead.
+ **/
+GIOError
+g_io_channel_seek (GIOChannel *channel,
+ gint64 offset,
+ GSeekType type)
+{
+ GError *err = NULL;
+ GIOError error;
+ GIOStatus status;
+
+ g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
+ g_return_val_if_fail (channel->is_seekable, G_IO_ERROR_UNKNOWN);
+
+ switch (type)
+ {
+ case G_SEEK_CUR:
+ case G_SEEK_SET:
+ case G_SEEK_END:
+ break;
+ default:
+ g_warning ("g_io_channel_seek: unknown seek type");
+ return G_IO_ERROR_UNKNOWN;
+ }
+
+ status = channel->funcs->io_seek (channel, offset, type, &err);
+
+ error = g_io_error_get_from_g_error (status, err);
+
+ if (err)
+ g_error_free (err);
+
+ return error;
+}
+
+/* The function g_io_channel_new_file() is prototyped in both
+ * giounix.c and giowin32.c, so we stick its documentation here.
+ */
+
+/**
+ * g_io_channel_new_file:
+ * @filename: A string containing the name of a file
+ * @mode: One of "r", "w", "a", "r+", "w+", "a+". These have
+ * the same meaning as in fopen()
+ * @error: A location to return an error of type %G_FILE_ERROR
+ *
+ * Open a file @filename as a #GIOChannel using mode @mode. This
+ * channel will be closed when the last reference to it is dropped,
+ * so there is no need to call g_io_channel_close() (though doing
+ * so will not cause problems, as long as no attempt is made to
+ * access the channel after it is closed).
+ *
+ * Return value: A #GIOChannel on success, %NULL on failure.
+ **/
+
+/**
+ * g_io_channel_close:
+ * @channel: A #GIOChannel
+ *
+ * Close an IO channel. Any pending data to be written will be
+ * flushed, ignoring errors. The channel will not be freed until the
+ * last reference is dropped using g_io_channel_unref().
+ *
+ * Deprecated:2.2: Use g_io_channel_shutdown() instead.
+ **/
+void
+g_io_channel_close (GIOChannel *channel)
+{
+ GError *err = NULL;
+
+ g_return_if_fail (channel != NULL);
+
+ g_io_channel_purge (channel);
+
+ channel->funcs->io_close (channel, &err);
+
+ if (err)
+ { /* No way to return the error */
+ g_warning ("Error closing channel: %s", err->message);
+ g_error_free (err);
+ }
+
+ channel->close_on_unref = FALSE; /* Because we already did */
+ channel->is_readable = FALSE;
+ channel->is_writeable = FALSE;
+ channel->is_seekable = FALSE;
+}
+
+/**
+ * g_io_channel_shutdown:
+ * @channel: a #GIOChannel
+ * @flush: if %TRUE, flush pending
+ * @err: location to store a #GIOChannelError
+ *
+ * Close an IO channel. Any pending data to be written will be
+ * flushed if @flush is %TRUE. The channel will not be freed until the
+ * last reference is dropped using g_io_channel_unref().
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_shutdown (GIOChannel *channel,
+ gboolean flush,
+ GError **err)
+{
+ GIOStatus status, result;
+ GError *tmperr = NULL;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (err == NULL || *err == NULL, G_IO_STATUS_ERROR);
+
+ if (channel->write_buf && channel->write_buf->len > 0)
+ {
+ if (flush)
+ {
+ GIOFlags flags;
+
+ /* Set the channel to blocking, to avoid a busy loop
+ */
+ flags = g_io_channel_get_flags (channel);
+ /* Ignore any errors here, they're irrelevant */
+ g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
+
+ result = g_io_channel_flush (channel, &tmperr);
+ }
+ else
+ result = G_IO_STATUS_NORMAL;
+
+ g_string_truncate(channel->write_buf, 0);
+ }
+ else
+ result = G_IO_STATUS_NORMAL;
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ if (flush)
+ g_warning ("Partial character at end of write buffer not flushed.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+
+ status = channel->funcs->io_close (channel, err);
+
+ channel->close_on_unref = FALSE; /* Because we already did */
+ channel->is_readable = FALSE;
+ channel->is_writeable = FALSE;
+ channel->is_seekable = FALSE;
+
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ g_clear_error (&tmperr);
+ return status;
+ }
+ else if (result != G_IO_STATUS_NORMAL)
+ {
+ g_propagate_error (err, tmperr);
+ return result;
+ }
+ else
+ return G_IO_STATUS_NORMAL;
+}
+
+/* This function is used for the final flush on close or unref */
+static void
+g_io_channel_purge (GIOChannel *channel)
+{
+ GError *err = NULL;
+ GIOStatus status;
+
+ g_return_if_fail (channel != NULL);
+
+ if (channel->write_buf && channel->write_buf->len > 0)
+ {
+ GIOFlags flags;
+
+ /* Set the channel to blocking, to avoid a busy loop
+ */
+ flags = g_io_channel_get_flags (channel);
+ g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
+
+ status = g_io_channel_flush (channel, &err);
+
+ if (err)
+ { /* No way to return the error */
+ g_warning ("Error flushing string: %s", err->message);
+ g_error_free (err);
+ }
+ }
+
+ /* Flush these in case anyone tries to close without unrefing */
+
+ if (channel->read_buf)
+ g_string_truncate (channel->read_buf, 0);
+ if (channel->write_buf)
+ g_string_truncate (channel->write_buf, 0);
+ if (channel->encoding)
+ {
+ if (channel->encoded_read_buf)
+ g_string_truncate (channel->encoded_read_buf, 0);
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ g_warning ("Partial character at end of write buffer not flushed.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+ }
+}
+
+GSource *
+g_io_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ g_return_val_if_fail (channel != NULL, NULL);
+
+ return channel->funcs->io_create_watch (channel, condition);
+}
+
+guint
+g_io_add_watch_full (GIOChannel *channel,
+ gint priority,
+ GIOCondition condition,
+ GIOFunc func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GSource *source;
+ guint id;
+
+ g_return_val_if_fail (channel != NULL, 0);
+
+ source = g_io_create_watch (channel, condition);
+
+ if (priority != G_PRIORITY_DEFAULT)
+ g_source_set_priority (source, priority);
+ g_source_set_callback (source, (GSourceFunc)func, user_data, notify);
+
+ id = g_source_attach (source, NULL);
+ g_source_unref (source);
+
+ return id;
+}
+
+guint
+g_io_add_watch (GIOChannel *channel,
+ GIOCondition condition,
+ GIOFunc func,
+ gpointer user_data)
+{
+ return g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, condition, func, user_data, NULL);
+}
+
+/**
+ * g_io_channel_get_buffer_condition:
+ * @channel: A #GIOChannel
+ *
+ * This function returns a #GIOCondition depending on whether there
+ * is data to be read/space to write data in the internal buffers in
+ * the #GIOChannel. Only the flags %G_IO_IN and %G_IO_OUT may be set.
+ *
+ * Return value: A #GIOCondition
+ **/
+GIOCondition
+g_io_channel_get_buffer_condition (GIOChannel *channel)
+{
+ GIOCondition condition = 0;
+
+ if (channel->encoding)
+ {
+ if (channel->encoded_read_buf && (channel->encoded_read_buf->len > 0))
+ condition |= G_IO_IN; /* Only return if we have full characters */
+ }
+ else
+ {
+ if (channel->read_buf && (channel->read_buf->len > 0))
+ condition |= G_IO_IN;
+ }
+
+ if (channel->write_buf && (channel->write_buf->len < channel->buf_size))
+ condition |= G_IO_OUT;
+
+ return condition;
+}
+
+/**
+ * g_io_channel_error_from_errno:
+ * @en: an <literal>errno</literal> error number, e.g. %EINVAL
+ *
+ * Converts an <literal>errno</literal> error number to a #GIOChannelError.
+ *
+ * Return value: a #GIOChannelError error number, e.g.
+ * %G_IO_CHANNEL_ERROR_INVAL.
+ **/
+GIOChannelError
+g_io_channel_error_from_errno (gint en)
+{
+#ifdef EAGAIN
+ g_return_val_if_fail (en != EAGAIN, G_IO_CHANNEL_ERROR_FAILED);
+#endif
+
+ switch (en)
+ {
+#ifdef EBADF
+ case EBADF:
+ g_warning("Invalid file descriptor.\n");
+ return G_IO_CHANNEL_ERROR_FAILED;
+#endif
+
+#ifdef EFAULT
+ case EFAULT:
+ g_warning("Buffer outside valid address space.\n");
+ return G_IO_CHANNEL_ERROR_FAILED;
+#endif
+
+#ifdef EFBIG
+ case EFBIG:
+ return G_IO_CHANNEL_ERROR_FBIG;
+#endif
+
+#ifdef EINTR
+ /* In general, we should catch EINTR before we get here,
+ * but close() is allowed to return EINTR by POSIX, so
+ * we need to catch it here; EINTR from close() is
+ * unrecoverable, because it's undefined whether
+ * the fd was actually closed or not, so we just return
+ * a generic error code.
+ */
+ case EINTR:
+ return G_IO_CHANNEL_ERROR_FAILED;
+#endif
+
+#ifdef EINVAL
+ case EINVAL:
+ return G_IO_CHANNEL_ERROR_INVAL;
+#endif
+
+#ifdef EIO
+ case EIO:
+ return G_IO_CHANNEL_ERROR_IO;
+#endif
+
+#ifdef EISDIR
+ case EISDIR:
+ return G_IO_CHANNEL_ERROR_ISDIR;
+#endif
+
+#ifdef ENOSPC
+ case ENOSPC:
+ return G_IO_CHANNEL_ERROR_NOSPC;
+#endif
+
+#ifdef ENXIO
+ case ENXIO:
+ return G_IO_CHANNEL_ERROR_NXIO;
+#endif
+
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ return G_IO_CHANNEL_ERROR_OVERFLOW;
+#endif
+
+#ifdef EPIPE
+ case EPIPE:
+ return G_IO_CHANNEL_ERROR_PIPE;
+#endif
+
+ default:
+ return G_IO_CHANNEL_ERROR_FAILED;
+ }
+}
+
+/**
+ * g_io_channel_set_buffer_size:
+ * @channel: a #GIOChannel
+ * @size: the size of the buffer, or 0 to let GLib pick a good size
+ *
+ * Sets the buffer size.
+ **/
+void
+g_io_channel_set_buffer_size (GIOChannel *channel,
+ gsize size)
+{
+ g_return_if_fail (channel != NULL);
+
+ if (size == 0)
+ size = G_IO_NICE_BUF_SIZE;
+
+ if (size < MAX_CHAR_SIZE)
+ size = MAX_CHAR_SIZE;
+
+ channel->buf_size = size;
+}
+
+/**
+ * g_io_channel_get_buffer_size:
+ * @channel: a #GIOChannel
+ *
+ * Gets the buffer size.
+ *
+ * Return value: the size of the buffer.
+ **/
+gsize
+g_io_channel_get_buffer_size (GIOChannel *channel)
+{
+ g_return_val_if_fail (channel != NULL, 0);
+
+ return channel->buf_size;
+}
+
+/**
+ * g_io_channel_set_line_term:
+ * @channel: a #GIOChannel
+ * @line_term: The line termination string. Use %NULL for autodetect.
+ * Autodetection breaks on "\n", "\r\n", "\r", "\0", and
+ * the Unicode paragraph separator. Autodetection should
+ * not be used for anything other than file-based channels.
+ * @length: The length of the termination string. If -1 is passed, the
+ * string is assumed to be nul-terminated. This option allows
+ * termination strings with embedded nuls.
+ *
+ * This sets the string that #GIOChannel uses to determine
+ * where in the file a line break occurs.
+ **/
+void
+g_io_channel_set_line_term (GIOChannel *channel,
+ const gchar *line_term,
+ gint length)
+{
+ g_return_if_fail (channel != NULL);
+ g_return_if_fail (line_term == NULL || length != 0); /* Disallow "" */
+
+ if (line_term == NULL)
+ length = 0;
+ else if (length < 0)
+ length = strlen (line_term);
+
+ g_free (channel->line_term);
+ channel->line_term = line_term ? g_memdup (line_term, length) : NULL;
+ channel->line_term_len = length;
+}
+
+/**
+ * g_io_channel_get_line_term:
+ * @channel: a #GIOChannel
+ * @length: a location to return the length of the line terminator
+ *
+ * This returns the string that #GIOChannel uses to determine
+ * where in the file a line break occurs. A value of %NULL
+ * indicates autodetection.
+ *
+ * Return value: The line termination string. This value
+ * is owned by GLib and must not be freed.
+ **/
+G_CONST_RETURN gchar*
+g_io_channel_get_line_term (GIOChannel *channel,
+ gint *length)
+{
+ g_return_val_if_fail (channel != NULL, NULL);
+
+ if (length)
+ *length = channel->line_term_len;
+
+ return channel->line_term;
+}
+
+/**
+ * g_io_channel_set_flags:
+ * @channel: a #GIOChannel
+ * @flags: the flags to set on the IO channel
+ * @error: A location to return an error of type #GIOChannelError
+ *
+ * Sets the (writeable) flags in @channel to (@flags & %G_IO_CHANNEL_SET_MASK).
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **error)
+{
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+
+ return (*channel->funcs->io_set_flags) (channel,
+ flags & G_IO_FLAG_SET_MASK,
+ error);
+}
+
+/**
+ * g_io_channel_get_flags:
+ * @channel: a #GIOChannel
+ *
+ * Gets the current flags for a #GIOChannel, including read-only
+ * flags such as %G_IO_FLAG_IS_READABLE.
+ *
+ * The values of the flags %G_IO_FLAG_IS_READABLE and %G_IO_FLAG_IS_WRITEABLE
+ * are cached for internal use by the channel when it is created.
+ * If they should change at some later point (e.g. partial shutdown
+ * of a socket with the UNIX shutdown() function), the user
+ * should immediately call g_io_channel_get_flags() to update
+ * the internal values of these flags.
+ *
+ * Return value: the flags which are set on the channel
+ **/
+GIOFlags
+g_io_channel_get_flags (GIOChannel *channel)
+{
+ GIOFlags flags;
+
+ g_return_val_if_fail (channel != NULL, 0);
+
+ flags = (* channel->funcs->io_get_flags) (channel);
+
+ /* Cross implementation code */
+
+ if (channel->is_seekable)
+ flags |= G_IO_FLAG_IS_SEEKABLE;
+ if (channel->is_readable)
+ flags |= G_IO_FLAG_IS_READABLE;
+ if (channel->is_writeable)
+ flags |= G_IO_FLAG_IS_WRITEABLE;
+
+ return flags;
+}
+
+/**
+ * g_io_channel_set_close_on_unref:
+ * @channel: a #GIOChannel
+ * @do_close: Whether to close the channel on the final unref of
+ * the GIOChannel data structure. The default value of
+ * this is %TRUE for channels created by g_io_channel_new_file (),
+ * and %FALSE for all other channels.
+ *
+ * Setting this flag to %TRUE for a channel you have already closed
+ * can cause problems.
+ **/
+void
+g_io_channel_set_close_on_unref (GIOChannel *channel,
+ gboolean do_close)
+{
+ g_return_if_fail (channel != NULL);
+
+ channel->close_on_unref = do_close;
+}
+
+/**
+ * g_io_channel_get_close_on_unref:
+ * @channel: a #GIOChannel.
+ *
+ * Returns whether the file/socket/whatever associated with @channel
+ * will be closed when @channel receives its final unref and is
+ * destroyed. The default value of this is %TRUE for channels created
+ * by g_io_channel_new_file (), and %FALSE for all other channels.
+ *
+ * Return value: Whether the channel will be closed on the final unref of
+ * the GIOChannel data structure.
+ **/
+gboolean
+g_io_channel_get_close_on_unref (GIOChannel *channel)
+{
+ g_return_val_if_fail (channel != NULL, FALSE);
+
+ return channel->close_on_unref;
+}
+
+/**
+ * g_io_channel_seek_position:
+ * @channel: a #GIOChannel
+ * @offset: The offset in bytes from the position specified by @type
+ * @type: a #GSeekType. The type %G_SEEK_CUR is only allowed in those
+ * cases where a call to g_io_channel_set_encoding ()
+ * is allowed. See the documentation for
+ * g_io_channel_set_encoding () for details.
+ * @error: A location to return an error of type #GIOChannelError
+ *
+ * Replacement for g_io_channel_seek() with the new API.
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_seek_position (GIOChannel *channel,
+ gint64 offset,
+ GSeekType type,
+ GError **error)
+{
+ GIOStatus status;
+
+ /* For files, only one of the read and write buffers can contain data.
+ * For sockets, both can contain data.
+ */
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_seekable, G_IO_STATUS_ERROR);
+
+ switch (type)
+ {
+ case G_SEEK_CUR: /* The user is seeking relative to the head of the buffer */
+ if (channel->use_buffer)
+ {
+ if (channel->do_encode && channel->encoded_read_buf
+ && channel->encoded_read_buf->len > 0)
+ {
+ g_warning ("Seek type G_SEEK_CUR not allowed for this"
+ " channel's encoding.\n");
+ return G_IO_STATUS_ERROR;
+ }
+ if (channel->read_buf)
+ offset -= channel->read_buf->len;
+ if (channel->encoded_read_buf)
+ {
+ g_assert (channel->encoded_read_buf->len == 0 || !channel->do_encode);
+
+ /* If there's anything here, it's because the encoding is UTF-8,
+ * so we can just subtract the buffer length, the same as for
+ * the unencoded data.
+ */
+
+ offset -= channel->encoded_read_buf->len;
+ }
+ }
+ break;
+ case G_SEEK_SET:
+ case G_SEEK_END:
+ break;
+ default:
+ g_warning ("g_io_channel_seek_position: unknown seek type");
+ return G_IO_STATUS_ERROR;
+ }
+
+ if (channel->use_buffer)
+ {
+ status = g_io_channel_flush (channel, error);
+ if (status != G_IO_STATUS_NORMAL)
+ return status;
+ }
+
+ status = channel->funcs->io_seek (channel, offset, type, error);
+
+ if ((status == G_IO_STATUS_NORMAL) && (channel->use_buffer))
+ {
+ if (channel->read_buf)
+ g_string_truncate (channel->read_buf, 0);
+
+ /* Conversion state no longer matches position in file */
+ if (channel->read_cd != (GIConv) -1)
+ g_iconv (channel->read_cd, NULL, NULL, NULL, NULL);
+ if (channel->write_cd != (GIConv) -1)
+ g_iconv (channel->write_cd, NULL, NULL, NULL, NULL);
+
+ if (channel->encoded_read_buf)
+ {
+ g_assert (channel->encoded_read_buf->len == 0 || !channel->do_encode);
+ g_string_truncate (channel->encoded_read_buf, 0);
+ }
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ g_warning ("Partial character at end of write buffer not flushed.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+ }
+
+ return status;
+}
+
+/**
+ * g_io_channel_flush:
+ * @channel: a #GIOChannel
+ * @error: location to store an error of type #GIOChannelError
+ *
+ * Flushes the write buffer for the GIOChannel.
+ *
+ * Return value: the status of the operation: One of
+ * #G_IO_CHANNEL_NORMAL, #G_IO_CHANNEL_AGAIN, or
+ * #G_IO_CHANNEL_ERROR.
+ **/
+GIOStatus
+g_io_channel_flush (GIOChannel *channel,
+ GError **error)
+{
+ GIOStatus status;
+ gsize this_time = 1, bytes_written = 0;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
+
+ if (channel->write_buf == NULL || channel->write_buf->len == 0)
+ return G_IO_STATUS_NORMAL;
+
+ do
+ {
+ g_assert (this_time > 0);
+
+ status = channel->funcs->io_write (channel,
+ channel->write_buf->str + bytes_written,
+ channel->write_buf->len - bytes_written,
+ &this_time, error);
+ bytes_written += this_time;
+ }
+ while ((bytes_written < channel->write_buf->len)
+ && (status == G_IO_STATUS_NORMAL));
+
+ g_string_erase (channel->write_buf, 0, bytes_written);
+
+ return status;
+}
+
+/**
+ * g_io_channel_set_buffered:
+ * @channel: a #GIOChannel
+ * @buffered: whether to set the channel buffered or unbuffered
+ *
+ * The buffering state can only be set if the channel's encoding
+ * is %NULL. For any other encoding, the channel must be buffered.
+ *
+ * A buffered channel can only be set unbuffered if the channel's
+ * internal buffers have been flushed. Newly created channels or
+ * channels which have returned %G_IO_STATUS_EOF
+ * not require such a flush. For write-only channels, a call to
+ * g_io_channel_flush () is sufficient. For all other channels,
+ * the buffers may be flushed by a call to g_io_channel_seek_position ().
+ * This includes the possibility of seeking with seek type %G_SEEK_CUR
+ * and an offset of zero. Note that this means that socket-based
+ * channels cannot be set unbuffered once they have had data
+ * read from them.
+ *
+ * On unbuffered channels, it is safe to mix read and write
+ * calls from the new and old APIs, if this is necessary for
+ * maintaining old code.
+ *
+ * The default state of the channel is buffered.
+ **/
+void
+g_io_channel_set_buffered (GIOChannel *channel,
+ gboolean buffered)
+{
+ g_return_if_fail (channel != NULL);
+
+ if (channel->encoding != NULL)
+ {
+ g_warning ("Need to have NULL encoding to set the buffering state of the "
+ "channel.\n");
+ return;
+ }
+
+ g_return_if_fail (!channel->read_buf || channel->read_buf->len == 0);
+ g_return_if_fail (!channel->write_buf || channel->write_buf->len == 0);
+
+ channel->use_buffer = buffered;
+}
+
+/**
+ * g_io_channel_get_buffered:
+ * @channel: a #GIOChannel
+ *
+ * Returns whether @channel is buffered.
+ *
+ * Return Value: %TRUE if the @channel is buffered.
+ **/
+gboolean
+g_io_channel_get_buffered (GIOChannel *channel)
+{
+ g_return_val_if_fail (channel != NULL, FALSE);
+
+ return channel->use_buffer;
+}
+
+/**
+ * g_io_channel_set_encoding:
+ * @channel: a #GIOChannel
+ * @encoding: the encoding type
+ * @error: location to store an error of type #GConvertError
+ *
+ * Sets the encoding for the input/output of the channel.
+ * The internal encoding is always UTF-8. The default encoding
+ * for the external file is UTF-8.
+ *
+ * The encoding %NULL is safe to use with binary data.
+ *
+ * The encoding can only be set if one of the following conditions
+ * is true:
+ * <itemizedlist>
+ * <listitem><para>
+ * The channel was just created, and has not been written to or read
+ * from yet.
+ * </para></listitem>
+ * <listitem><para>
+ * The channel is write-only.
+ * </para></listitem>
+ * <listitem><para>
+ * The channel is a file, and the file pointer was just
+ * repositioned by a call to g_io_channel_seek_position().
+ * (This flushes all the internal buffers.)
+ * </para></listitem>
+ * <listitem><para>
+ * The current encoding is %NULL or UTF-8.
+ * </para></listitem>
+ * <listitem><para>
+ * One of the (new API) read functions has just returned %G_IO_STATUS_EOF
+ * (or, in the case of g_io_channel_read_to_end(), %G_IO_STATUS_NORMAL).
+ * </para></listitem>
+ * <listitem><para>
+ * One of the functions g_io_channel_read_chars() or
+ * g_io_channel_read_unichar() has returned %G_IO_STATUS_AGAIN or
+ * %G_IO_STATUS_ERROR. This may be useful in the case of
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE.
+ * Returning one of these statuses from g_io_channel_read_line(),
+ * g_io_channel_read_line_string(), or g_io_channel_read_to_end()
+ * does <emphasis>not</emphasis> guarantee that the encoding can
+ * be changed.
+ * </para></listitem>
+ * </itemizedlist>
+ * Channels which do not meet one of the above conditions cannot call
+ * g_io_channel_seek_position() with an offset of %G_SEEK_CUR, and, if
+ * they are "seekable", cannot call g_io_channel_write_chars() after
+ * calling one of the API "read" functions.
+ *
+ * Return Value: %G_IO_STATUS_NORMAL if the encoding was successfully set.
+ **/
+GIOStatus
+g_io_channel_set_encoding (GIOChannel *channel,
+ const gchar *encoding,
+ GError **error)
+{
+ GIConv read_cd, write_cd;
+ gboolean did_encode;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
+
+ /* Make sure the encoded buffers are empty */
+
+ g_return_val_if_fail (!channel->do_encode || !channel->encoded_read_buf ||
+ channel->encoded_read_buf->len == 0, G_IO_STATUS_ERROR);
+
+ if (!channel->use_buffer)
+ {
+ g_warning ("Need to set the channel buffered before setting the encoding.\n");
+ g_warning ("Assuming this is what you meant and acting accordingly.\n");
+
+ channel->use_buffer = TRUE;
+ }
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ g_warning ("Partial character at end of write buffer not flushed.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+
+ did_encode = channel->do_encode;
+
+ if (!encoding || strcmp (encoding, "UTF8") == 0 || strcmp (encoding, "UTF-8") == 0)
+ {
+ channel->do_encode = FALSE;
+ read_cd = write_cd = (GIConv) -1;
+ }
+ else
+ {
+ gint err = 0;
+ const gchar *from_enc = NULL, *to_enc = NULL;
+
+ if (channel->is_readable)
+ {
+ read_cd = g_iconv_open ("UTF-8", encoding);
+
+ if (read_cd == (GIConv) -1)
+ {
+ err = errno;
+ from_enc = encoding;
+ to_enc = "UTF-8";
+ }
+ }
+ else
+ read_cd = (GIConv) -1;
+
+ if (channel->is_writeable && err == 0)
+ {
+ write_cd = g_iconv_open (encoding, "UTF-8");
+
+ if (write_cd == (GIConv) -1)
+ {
+ err = errno;
+ from_enc = "UTF-8";
+ to_enc = encoding;
+ }
+ }
+ else
+ write_cd = (GIConv) -1;
+
+ if (err != 0)
+ {
+ g_assert (from_enc);
+ g_assert (to_enc);
+
+ if (err == EINVAL)
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
+ _("Conversion from character set '%s' to '%s' is not supported"),
+ from_enc, to_enc);
+ else
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Could not open converter from '%s' to '%s': %s"),
+ from_enc, to_enc, g_strerror (err));
+
+ if (read_cd != (GIConv) -1)
+ g_iconv_close (read_cd);
+ if (write_cd != (GIConv) -1)
+ g_iconv_close (write_cd);
+
+ return G_IO_STATUS_ERROR;
+ }
+
+ channel->do_encode = TRUE;
+ }
+
+ /* The encoding is ok, so set the fields in channel */
+
+ if (channel->read_cd != (GIConv) -1)
+ g_iconv_close (channel->read_cd);
+ if (channel->write_cd != (GIConv) -1)
+ g_iconv_close (channel->write_cd);
+
+ if (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)
+ {
+ g_assert (!did_encode); /* Encoding UTF-8, NULL doesn't use encoded_read_buf */
+
+ /* This is just validated UTF-8, so we can copy it back into read_buf
+ * so it can be encoded in whatever the new encoding is.
+ */
+
+ g_string_prepend_len (channel->read_buf, channel->encoded_read_buf->str,
+ channel->encoded_read_buf->len);
+ g_string_truncate (channel->encoded_read_buf, 0);
+ }
+
+ channel->read_cd = read_cd;
+ channel->write_cd = write_cd;
+
+ g_free (channel->encoding);
+ channel->encoding = g_strdup (encoding);
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_get_encoding:
+ * @channel: a #GIOChannel
+ *
+ * Gets the encoding for the input/output of the channel.
+ * The internal encoding is always UTF-8. The encoding %NULL
+ * makes the channel safe for binary data.
+ *
+ * Return value: A string containing the encoding, this string is
+ * owned by GLib and must not be freed.
+ **/
+G_CONST_RETURN gchar*
+g_io_channel_get_encoding (GIOChannel *channel)
+{
+ g_return_val_if_fail (channel != NULL, NULL);
+
+ return channel->encoding;
+}
+
+static GIOStatus
+g_io_channel_fill_buffer (GIOChannel *channel,
+ GError **err)
+{
+ gsize read_size, cur_len, oldlen;
+ GIOStatus status;
+
+ if (channel->is_seekable && channel->write_buf && channel->write_buf->len > 0)
+ {
+ status = g_io_channel_flush (channel, err);
+ if (status != G_IO_STATUS_NORMAL)
+ return status;
+ }
+ if (channel->is_seekable && channel->partial_write_buf[0] != '\0')
+ {
+ g_warning ("Partial character at end of write buffer not flushed.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+
+ if (!channel->read_buf)
+ channel->read_buf = g_string_sized_new (channel->buf_size);
+
+ cur_len = channel->read_buf->len;
+
+ g_string_set_size (channel->read_buf, channel->read_buf->len + channel->buf_size);
+
+ status = channel->funcs->io_read (channel, channel->read_buf->str + cur_len,
+ channel->buf_size, &read_size, err);
+
+ g_assert ((status == G_IO_STATUS_NORMAL) || (read_size == 0));
+
+ g_string_truncate (channel->read_buf, read_size + cur_len);
+
+ if ((status != G_IO_STATUS_NORMAL) &&
+ ((status != G_IO_STATUS_EOF) || (channel->read_buf->len == 0)))
+ return status;
+
+ g_assert (channel->read_buf->len > 0);
+
+ if (channel->encoded_read_buf)
+ oldlen = channel->encoded_read_buf->len;
+ else
+ {
+ oldlen = 0;
+ if (channel->encoding)
+ channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
+ }
+
+ if (channel->do_encode)
+ {
+ gsize errnum, inbytes_left, outbytes_left;
+ gchar *inbuf, *outbuf;
+ int errval;
+
+ g_assert (channel->encoded_read_buf);
+
+reencode:
+
+ inbytes_left = channel->read_buf->len;
+ outbytes_left = MAX (channel->read_buf->len,
+ channel->encoded_read_buf->allocated_len
+ - channel->encoded_read_buf->len - 1); /* 1 for NULL */
+ outbytes_left = MAX (outbytes_left, 6);
+
+ inbuf = channel->read_buf->str;
+ g_string_set_size (channel->encoded_read_buf,
+ channel->encoded_read_buf->len + outbytes_left);
+ outbuf = channel->encoded_read_buf->str + channel->encoded_read_buf->len
+ - outbytes_left;
+
+ errnum = g_iconv (channel->read_cd, &inbuf, &inbytes_left,
+ &outbuf, &outbytes_left);
+ errval = errno;
+
+ g_assert (inbuf + inbytes_left == channel->read_buf->str
+ + channel->read_buf->len);
+ g_assert (outbuf + outbytes_left == channel->encoded_read_buf->str
+ + channel->encoded_read_buf->len);
+
+ g_string_erase (channel->read_buf, 0,
+ channel->read_buf->len - inbytes_left);
+ g_string_truncate (channel->encoded_read_buf,
+ channel->encoded_read_buf->len - outbytes_left);
+
+ if (errnum == (gsize) -1)
+ {
+ switch (errval)
+ {
+ case EINVAL:
+ if ((oldlen == channel->encoded_read_buf->len)
+ && (status == G_IO_STATUS_EOF))
+ status = G_IO_STATUS_EOF;
+ else
+ status = G_IO_STATUS_NORMAL;
+ break;
+ case E2BIG:
+ /* Buffer size at least 6, wrote at least on character */
+ g_assert (inbuf != channel->read_buf->str);
+ goto reencode;
+ case EILSEQ:
+ if (oldlen < channel->encoded_read_buf->len)
+ status = G_IO_STATUS_NORMAL;
+ else
+ {
+ g_set_error (err, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ return G_IO_STATUS_ERROR;
+ }
+ break;
+ default:
+ g_assert (errval != EBADF); /* The converter should be open */
+ g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Error during conversion: %s"), g_strerror (errval));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+ g_assert ((status != G_IO_STATUS_NORMAL)
+ || (channel->encoded_read_buf->len > 0));
+ }
+ else if (channel->encoding) /* UTF-8 */
+ {
+ gchar *nextchar, *lastchar;
+
+ g_assert (channel->encoded_read_buf);
+
+ nextchar = channel->read_buf->str;
+ lastchar = channel->read_buf->str + channel->read_buf->len;
+
+ while (nextchar < lastchar)
+ {
+ gunichar val_char;
+
+ val_char = g_utf8_get_char_validated (nextchar, lastchar - nextchar);
+
+ switch (val_char)
+ {
+ case -2:
+ /* stop, leave partial character in buffer */
+ lastchar = nextchar;
+ break;
+ case -1:
+ if (oldlen < channel->encoded_read_buf->len)
+ status = G_IO_STATUS_NORMAL;
+ else
+ {
+ g_set_error (err, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ status = G_IO_STATUS_ERROR;
+ }
+ lastchar = nextchar;
+ break;
+ default:
+ nextchar = g_utf8_next_char (nextchar);
+ break;
+ }
+ }
+
+ if (lastchar > channel->read_buf->str)
+ {
+ gint copy_len = lastchar - channel->read_buf->str;
+
+ g_string_append_len (channel->encoded_read_buf, channel->read_buf->str,
+ copy_len);
+ g_string_erase (channel->read_buf, 0, copy_len);
+ }
+ }
+
+ return status;
+}
+
+/**
+ * g_io_channel_read_line:
+ * @channel: a #GIOChannel
+ * @str_return: The line read from the #GIOChannel, including the
+ * line terminator. This data should be freed with g_free()
+ * when no longer needed. This is a nul-terminated string.
+ * If a @length of zero is returned, this will be %NULL instead.
+ * @length: location to store length of the read data, or %NULL
+ * @terminator_pos: location to store position of line terminator, or %NULL
+ * @error: A location to return an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Reads a line, including the terminating character(s),
+ * from a #GIOChannel into a newly-allocated string.
+ * @str_return will contain allocated memory if the return
+ * is %G_IO_STATUS_NORMAL.
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_read_line (GIOChannel *channel,
+ gchar **str_return,
+ gsize *length,
+ gsize *terminator_pos,
+ GError **error)
+{
+ GIOStatus status;
+ gsize got_length;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (str_return != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
+
+ status = g_io_channel_read_line_backend (channel, &got_length, terminator_pos, error);
+
+ if (length)
+ *length = got_length;
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ g_assert (USE_BUF (channel));
+ *str_return = g_strndup (USE_BUF (channel)->str, got_length);
+ g_string_erase (USE_BUF (channel), 0, got_length);
+ }
+ else
+ *str_return = NULL;
+
+ return status;
+}
+
+/**
+ * g_io_channel_read_line_string:
+ * @channel: a #GIOChannel
+ * @buffer: a #GString into which the line will be written.
+ * If @buffer already contains data, the old data will
+ * be overwritten.
+ * @terminator_pos: location to store position of line terminator, or %NULL
+ * @error: a location to store an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Reads a line from a #GIOChannel, using a #GString as a buffer.
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_read_line_string (GIOChannel *channel,
+ GString *buffer,
+ gsize *terminator_pos,
+ GError **error)
+{
+ gsize length;
+ GIOStatus status;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (buffer != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
+
+ if (buffer->len > 0)
+ g_string_truncate (buffer, 0); /* clear out the buffer */
+
+ status = g_io_channel_read_line_backend (channel, &length, terminator_pos, error);
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ g_assert (USE_BUF (channel));
+ g_string_append_len (buffer, USE_BUF (channel)->str, length);
+ g_string_erase (USE_BUF (channel), 0, length);
+ }
+
+ return status;
+}
+
+
+static GIOStatus
+g_io_channel_read_line_backend (GIOChannel *channel,
+ gsize *length,
+ gsize *terminator_pos,
+ GError **error)
+{
+ GIOStatus status;
+ gsize checked_to, line_term_len, line_length, got_term_len;
+ gboolean first_time = TRUE;
+
+ if (!channel->use_buffer)
+ {
+ /* Can't do a raw read in read_line */
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Can't do a raw read in g_io_channel_read_line_string"));
+ return G_IO_STATUS_ERROR;
+ }
+
+ status = G_IO_STATUS_NORMAL;
+
+ if (channel->line_term)
+ line_term_len = channel->line_term_len;
+ else
+ line_term_len = 3;
+ /* This value used for setting checked_to, it's the longest of the four
+ * we autodetect for.
+ */
+
+ checked_to = 0;
+
+ while (TRUE)
+ {
+ gchar *nextchar, *lastchar;
+ GString *use_buf;
+
+ if (!first_time || (BUF_LEN (USE_BUF (channel)) == 0))
+ {
+read_again:
+ status = g_io_channel_fill_buffer (channel, error);
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ if (BUF_LEN (USE_BUF (channel)) == 0)
+ /* Can happen when using conversion and only read
+ * part of a character
+ */
+ {
+ first_time = FALSE;
+ continue;
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ if (BUF_LEN (USE_BUF (channel)) == 0)
+ {
+ if (length)
+ *length = 0;
+
+ if (channel->encoding && channel->read_buf->len != 0)
+ {
+ g_set_error (error, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Leftover unconverted data in read buffer"));
+ return G_IO_STATUS_ERROR;
+ }
+ else
+ return G_IO_STATUS_EOF;
+ }
+ break;
+ default:
+ if (length)
+ *length = 0;
+ return status;
+ }
+ }
+
+ g_assert (BUF_LEN (USE_BUF (channel)) != 0);
+
+ use_buf = USE_BUF (channel); /* The buffer has been created by this point */
+
+ first_time = FALSE;
+
+ lastchar = use_buf->str + use_buf->len;
+
+ for (nextchar = use_buf->str + checked_to; nextchar < lastchar;
+ channel->encoding ? nextchar = g_utf8_next_char (nextchar) : nextchar++)
+ {
+ if (channel->line_term)
+ {
+ if (memcmp (channel->line_term, nextchar, line_term_len) == 0)
+ {
+ line_length = nextchar - use_buf->str;
+ got_term_len = line_term_len;
+ goto done;
+ }
+ }
+ else /* auto detect */
+ {
+ switch (*nextchar)
+ {
+ case '\n': /* unix */
+ line_length = nextchar - use_buf->str;
+ got_term_len = 1;
+ goto done;
+ case '\r': /* Warning: do not use with sockets */
+ line_length = nextchar - use_buf->str;
+ if ((nextchar == lastchar - 1) && (status != G_IO_STATUS_EOF)
+ && (lastchar == use_buf->str + use_buf->len))
+ goto read_again; /* Try to read more data */
+ if ((nextchar < lastchar - 1) && (*(nextchar + 1) == '\n')) /* dos */
+ got_term_len = 2;
+ else /* mac */
+ got_term_len = 1;
+ goto done;
+ case '\xe2': /* Unicode paragraph separator */
+ if (strncmp ("\xe2\x80\xa9", nextchar, 3) == 0)
+ {
+ line_length = nextchar - use_buf->str;
+ got_term_len = 3;
+ goto done;
+ }
+ break;
+ case '\0': /* Embeded null in input */
+ line_length = nextchar - use_buf->str;
+ got_term_len = 1;
+ goto done;
+ default: /* no match */
+ break;
+ }
+ }
+ }
+
+ /* If encoding != NULL, valid UTF-8, didn't overshoot */
+ g_assert (nextchar == lastchar);
+
+ /* Check for EOF */
+
+ if (status == G_IO_STATUS_EOF)
+ {
+ if (channel->encoding && channel->read_buf->len > 0)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Channel terminates in a partial character"));
+ return G_IO_STATUS_ERROR;
+ }
+ line_length = use_buf->len;
+ got_term_len = 0;
+ break;
+ }
+
+ if (use_buf->len > line_term_len - 1)
+ checked_to = use_buf->len - (line_term_len - 1);
+ else
+ checked_to = 0;
+ }
+
+done:
+
+ if (terminator_pos)
+ *terminator_pos = line_length;
+
+ if (length)
+ *length = line_length + got_term_len;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_read_to_end:
+ * @channel: a #GIOChannel
+ * @str_return: Location to store a pointer to a string holding
+ * the remaining data in the #GIOChannel. This data should
+ * be freed with g_free() when no longer needed. This
+ * data is terminated by an extra nul character, but there
+ * may be other nuls in the intervening data.
+ * @length: location to store length of the data
+ * @error: location to return an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Reads all the remaining data from the file.
+ *
+ * Return value: %G_IO_STATUS_NORMAL on success.
+ * This function never returns %G_IO_STATUS_EOF.
+ **/
+GIOStatus
+g_io_channel_read_to_end (GIOChannel *channel,
+ gchar **str_return,
+ gsize *length,
+ GError **error)
+{
+ GIOStatus status;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
+
+ if (str_return)
+ *str_return = NULL;
+ if (length)
+ *length = 0;
+
+ if (!channel->use_buffer)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Can't do a raw read in g_io_channel_read_to_end"));
+ return G_IO_STATUS_ERROR;
+ }
+
+ do
+ status = g_io_channel_fill_buffer (channel, error);
+ while (status == G_IO_STATUS_NORMAL);
+
+ if (status != G_IO_STATUS_EOF)
+ return status;
+
+ if (channel->encoding && channel->read_buf->len > 0)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Channel terminates in a partial character"));
+ return G_IO_STATUS_ERROR;
+ }
+
+ if (USE_BUF (channel) == NULL)
+ {
+ /* length is already set to zero */
+ if (str_return)
+ *str_return = g_strdup ("");
+ }
+ else
+ {
+ if (length)
+ *length = USE_BUF (channel)->len;
+
+ if (str_return)
+ *str_return = g_string_free (USE_BUF (channel), FALSE);
+ else
+ g_string_free (USE_BUF (channel), TRUE);
+
+ if (channel->encoding)
+ channel->encoded_read_buf = NULL;
+ else
+ channel->read_buf = NULL;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_read_chars:
+ * @channel: a #GIOChannel
+ * @buf: a buffer to read data into
+ * @count: the size of the buffer. Note that the buffer may
+ * not be complelely filled even if there is data
+ * in the buffer if the remaining data is not a
+ * complete character.
+ * @bytes_read: The number of bytes read. This may be zero even on
+ * success if count < 6 and the channel's encoding is non-%NULL.
+ * This indicates that the next UTF-8 character is too wide for
+ * the buffer.
+ * @error: a location to return an error of type #GConvertError
+ * or #GIOChannelError.
+ *
+ * Replacement for g_io_channel_read() with the new API.
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_read_chars (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **error)
+{
+ GIOStatus status;
+ gsize got_bytes;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
+
+ if (count == 0)
+ {
+ *bytes_read = 0;
+ return G_IO_STATUS_NORMAL;
+ }
+ g_return_val_if_fail (buf != NULL, G_IO_STATUS_ERROR);
+
+ if (!channel->use_buffer)
+ {
+ gsize tmp_bytes;
+
+ g_assert (!channel->read_buf || channel->read_buf->len == 0);
+
+ status = channel->funcs->io_read (channel, buf, count, &tmp_bytes, error);
+
+ if (bytes_read)
+ *bytes_read = tmp_bytes;
+
+ return status;
+ }
+
+ status = G_IO_STATUS_NORMAL;
+
+ while (BUF_LEN (USE_BUF (channel)) < count && status == G_IO_STATUS_NORMAL)
+ status = g_io_channel_fill_buffer (channel, error);
+
+ /* Only return an error if we have no data */
+
+ if (BUF_LEN (USE_BUF (channel)) == 0)
+ {
+ g_assert (status != G_IO_STATUS_NORMAL);
+
+ if (status == G_IO_STATUS_EOF && channel->encoding
+ && BUF_LEN (channel->read_buf) > 0)
+ {
+ g_set_error (error, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Leftover unconverted data in read buffer"));
+ status = G_IO_STATUS_ERROR;
+ }
+
+ if (bytes_read)
+ *bytes_read = 0;
+
+ return status;
+ }
+
+ if (status == G_IO_STATUS_ERROR)
+ g_clear_error (error);
+
+ got_bytes = MIN (count, BUF_LEN (USE_BUF (channel)));
+
+ g_assert (got_bytes > 0);
+
+ if (channel->encoding)
+ /* Don't validate for NULL encoding, binary safe */
+ {
+ gchar *nextchar, *prevchar;
+
+ g_assert (USE_BUF (channel) == channel->encoded_read_buf);
+
+ nextchar = channel->encoded_read_buf->str;
+
+ do
+ {
+ prevchar = nextchar;
+ nextchar = g_utf8_next_char (nextchar);
+ g_assert (nextchar != prevchar); /* Possible for *prevchar of -1 or -2 */
+ }
+ while (nextchar < channel->encoded_read_buf->str + got_bytes);
+
+ if (nextchar > channel->encoded_read_buf->str + got_bytes)
+ got_bytes = prevchar - channel->encoded_read_buf->str;
+
+ g_assert (got_bytes > 0 || count < 6);
+ }
+
+ memcpy (buf, USE_BUF (channel)->str, got_bytes);
+ g_string_erase (USE_BUF (channel), 0, got_bytes);
+
+ if (bytes_read)
+ *bytes_read = got_bytes;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_read_unichar:
+ * @channel: a #GIOChannel
+ * @thechar: a location to return a character
+ * @error: a location to return an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Reads a Unicode character from @channel.
+ * This function cannot be called on a channel with %NULL encoding.
+ *
+ * Return value: a #GIOStatus
+ **/
+GIOStatus
+g_io_channel_read_unichar (GIOChannel *channel,
+ gunichar *thechar,
+ GError **error)
+{
+ GIOStatus status = G_IO_STATUS_NORMAL;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->encoding != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
+
+ while (BUF_LEN (channel->encoded_read_buf) == 0 && status == G_IO_STATUS_NORMAL)
+ status = g_io_channel_fill_buffer (channel, error);
+
+ /* Only return an error if we have no data */
+
+ if (BUF_LEN (USE_BUF (channel)) == 0)
+ {
+ g_assert (status != G_IO_STATUS_NORMAL);
+
+ if (status == G_IO_STATUS_EOF && BUF_LEN (channel->read_buf) > 0)
+ {
+ g_set_error (error, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Leftover unconverted data in read buffer"));
+ status = G_IO_STATUS_ERROR;
+ }
+
+ if (thechar)
+ *thechar = (gunichar) -1;
+
+ return status;
+ }
+
+ if (status == G_IO_STATUS_ERROR)
+ g_clear_error (error);
+
+ if (thechar)
+ *thechar = g_utf8_get_char (channel->encoded_read_buf->str);
+
+ g_string_erase (channel->encoded_read_buf, 0,
+ g_utf8_next_char (channel->encoded_read_buf->str)
+ - channel->encoded_read_buf->str);
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_write_chars:
+ * @channel: a #GIOChannel
+ * @buf: a buffer to write data from
+ * @count: the size of the buffer. If -1, the buffer
+ * is taken to be a nul-terminated string.
+ * @bytes_written: The number of bytes written. This can be nonzero
+ * even if the return value is not %G_IO_STATUS_NORMAL.
+ * If the return value is %G_IO_STATUS_NORMAL and the
+ * channel is blocking, this will always be equal
+ * to @count if @count >= 0.
+ * @error: a location to return an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Replacement for g_io_channel_write() with the new API.
+ *
+ * On seekable channels with encodings other than %NULL or UTF-8, generic
+ * mixing of reading and writing is not allowed. A call to g_io_channel_write_chars ()
+ * may only be made on a channel from which data has been read in the
+ * cases described in the documentation for g_io_channel_set_encoding ().
+ *
+ * Return value: the status of the operation.
+ **/
+GIOStatus
+g_io_channel_write_chars (GIOChannel *channel,
+ const gchar *buf,
+ gssize count,
+ gsize *bytes_written,
+ GError **error)
+{
+ GIOStatus status;
+ gssize wrote_bytes = 0;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_writeable, G_IO_STATUS_ERROR);
+
+ if ((count < 0) && buf)
+ count = strlen (buf);
+
+ if (count == 0)
+ {
+ if (bytes_written)
+ *bytes_written = 0;
+ return G_IO_STATUS_NORMAL;
+ }
+
+ g_return_val_if_fail (buf != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (count > 0, G_IO_STATUS_ERROR);
+
+ /* Raw write case */
+
+ if (!channel->use_buffer)
+ {
+ gsize tmp_bytes;
+
+ g_assert (!channel->write_buf || channel->write_buf->len == 0);
+ g_assert (channel->partial_write_buf[0] == '\0');
+
+ status = channel->funcs->io_write (channel, buf, count, &tmp_bytes, error);
+
+ if (bytes_written)
+ *bytes_written = tmp_bytes;
+
+ return status;
+ }
+
+ /* General case */
+
+ if (channel->is_seekable && (( BUF_LEN (channel->read_buf) > 0)
+ || (BUF_LEN (channel->encoded_read_buf) > 0)))
+ {
+ if (channel->do_encode && BUF_LEN (channel->encoded_read_buf) > 0)
+ {
+ g_warning("Mixed reading and writing not allowed on encoded files");
+ return G_IO_STATUS_ERROR;
+ }
+ status = g_io_channel_seek_position (channel, 0, G_SEEK_CUR, error);
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ if (bytes_written)
+ *bytes_written = 0;
+ return status;
+ }
+ }
+
+ if (!channel->write_buf)
+ channel->write_buf = g_string_sized_new (channel->buf_size);
+
+ while (wrote_bytes < count)
+ {
+ gsize space_in_buf;
+
+ /* If the buffer is full, try a write immediately. In
+ * the nonblocking case, this prevents the user from
+ * writing just a little bit to the buffer every time
+ * and never receiving an EAGAIN.
+ */
+
+ if (channel->write_buf->len >= channel->buf_size - MAX_CHAR_SIZE)
+ {
+ gsize did_write = 0, this_time;
+
+ do
+ {
+ status = channel->funcs->io_write (channel, channel->write_buf->str
+ + did_write, channel->write_buf->len
+ - did_write, &this_time, error);
+ did_write += this_time;
+ }
+ while (status == G_IO_STATUS_NORMAL &&
+ did_write < MIN (channel->write_buf->len, MAX_CHAR_SIZE));
+
+ g_string_erase (channel->write_buf, 0, did_write);
+
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ if (status == G_IO_STATUS_AGAIN && wrote_bytes > 0)
+ status = G_IO_STATUS_NORMAL;
+ if (bytes_written)
+ *bytes_written = wrote_bytes;
+ return status;
+ }
+ }
+
+ space_in_buf = MAX (channel->buf_size, channel->write_buf->allocated_len - 1)
+ - channel->write_buf->len; /* 1 for NULL */
+
+ /* This is only true because g_io_channel_set_buffer_size ()
+ * ensures that channel->buf_size >= MAX_CHAR_SIZE.
+ */
+ g_assert (space_in_buf >= MAX_CHAR_SIZE);
+
+ if (!channel->encoding)
+ {
+ gssize write_this = MIN (space_in_buf, count - wrote_bytes);
+
+ g_string_append_len (channel->write_buf, buf, write_this);
+ buf += write_this;
+ wrote_bytes += write_this;
+ }
+ else
+ {
+ const gchar *from_buf;
+ gsize from_buf_len, from_buf_old_len, left_len;
+ gsize err;
+ gint errnum;
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ g_assert (wrote_bytes == 0);
+
+ from_buf = channel->partial_write_buf;
+ from_buf_old_len = strlen (channel->partial_write_buf);
+ g_assert (from_buf_old_len > 0);
+ from_buf_len = MIN (6, from_buf_old_len + count);
+
+ memcpy (channel->partial_write_buf + from_buf_old_len, buf,
+ from_buf_len - from_buf_old_len);
+ }
+ else
+ {
+ from_buf = buf;
+ from_buf_len = count - wrote_bytes;
+ from_buf_old_len = 0;
+ }
+
+reconvert:
+
+ if (!channel->do_encode) /* UTF-8 encoding */
+ {
+ const gchar *badchar;
+ gsize try_len = MIN (from_buf_len, space_in_buf);
+
+ /* UTF-8, just validate, emulate g_iconv */
+
+ if (!g_utf8_validate (from_buf, try_len, &badchar))
+ {
+ gunichar try_char;
+ gsize incomplete_len = from_buf + try_len - badchar;
+
+ left_len = from_buf + from_buf_len - badchar;
+
+ try_char = g_utf8_get_char_validated (badchar, incomplete_len);
+
+ switch (try_char)
+ {
+ case -2:
+ g_assert (incomplete_len < 6);
+ if (try_len == from_buf_len)
+ {
+ errnum = EINVAL;
+ err = (gsize) -1;
+ }
+ else
+ {
+ errnum = 0;
+ err = (gsize) 0;
+ }
+ break;
+ case -1:
+ g_warning ("Invalid UTF-8 passed to g_io_channel_write_chars().");
+ /* FIXME bail here? */
+ errnum = EILSEQ;
+ err = (gsize) -1;
+ break;
+ default:
+ g_assert_not_reached ();
+ err = (gsize) -1;
+ errnum = 0; /* Don't confunse the compiler */
+ }
+ }
+ else
+ {
+ err = (gsize) 0;
+ errnum = 0;
+ left_len = from_buf_len - try_len;
+ }
+
+ g_string_append_len (channel->write_buf, from_buf,
+ from_buf_len - left_len);
+ from_buf += from_buf_len - left_len;
+ }
+ else
+ {
+ gchar *outbuf;
+
+ left_len = from_buf_len;
+ g_string_set_size (channel->write_buf, channel->write_buf->len
+ + space_in_buf);
+ outbuf = channel->write_buf->str + channel->write_buf->len
+ - space_in_buf;
+ err = g_iconv (channel->write_cd, (gchar **) &from_buf, &left_len,
+ &outbuf, &space_in_buf);
+ errnum = errno;
+ g_string_truncate (channel->write_buf, channel->write_buf->len
+ - space_in_buf);
+ }
+
+ if (err == (gsize) -1)
+ {
+ switch (errnum)
+ {
+ case EINVAL:
+ g_assert (left_len < 6);
+
+ if (from_buf_old_len == 0)
+ {
+ /* Not from partial_write_buf */
+
+ memcpy (channel->partial_write_buf, from_buf, left_len);
+ channel->partial_write_buf[left_len] = '\0';
+ if (bytes_written)
+ *bytes_written = count;
+ return G_IO_STATUS_NORMAL;
+ }
+
+ /* Working in partial_write_buf */
+
+ if (left_len == from_buf_len)
+ {
+ /* Didn't convert anything, must still have
+ * less than a full character
+ */
+
+ g_assert (count == from_buf_len - from_buf_old_len);
+
+ channel->partial_write_buf[from_buf_len] = '\0';
+
+ if (bytes_written)
+ *bytes_written = count;
+
+ return G_IO_STATUS_NORMAL;
+ }
+
+ g_assert (from_buf_len - left_len >= from_buf_old_len);
+
+ /* We converted all the old data. This is fine */
+
+ break;
+ case E2BIG:
+ if (from_buf_len == left_len)
+ {
+ /* Nothing was written, add enough space for
+ * at least one character.
+ */
+ space_in_buf += MAX_CHAR_SIZE;
+ goto reconvert;
+ }
+ break;
+ case EILSEQ:
+ g_set_error (error, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ if (from_buf_old_len > 0 && from_buf_len == left_len)
+ g_warning ("Illegal sequence due to partial character "
+ "at the end of a previous write.\n");
+ else
+ wrote_bytes += from_buf_len - left_len - from_buf_old_len;
+ if (bytes_written)
+ *bytes_written = wrote_bytes;
+ channel->partial_write_buf[0] = '\0';
+ return G_IO_STATUS_ERROR;
+ default:
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Error during conversion: %s"), g_strerror (errnum));
+ if (from_buf_len >= left_len + from_buf_old_len)
+ wrote_bytes += from_buf_len - left_len - from_buf_old_len;
+ if (bytes_written)
+ *bytes_written = wrote_bytes;
+ channel->partial_write_buf[0] = '\0';
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ g_assert (from_buf_len - left_len >= from_buf_old_len);
+
+ wrote_bytes += from_buf_len - left_len - from_buf_old_len;
+
+ if (from_buf_old_len > 0)
+ {
+ /* We were working in partial_write_buf */
+
+ buf += from_buf_len - left_len - from_buf_old_len;
+ channel->partial_write_buf[0] = '\0';
+ }
+ else
+ buf = from_buf;
+ }
+ }
+
+ if (bytes_written)
+ *bytes_written = count;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+/**
+ * g_io_channel_write_unichar:
+ * @channel: a #GIOChannel
+ * @thechar: a character
+ * @error: location to return an error of type #GConvertError
+ * or #GIOChannelError
+ *
+ * Writes a Unicode character to @channel.
+ * This function cannot be called on a channel with %NULL encoding.
+ *
+ * Return value: a #GIOStatus
+ **/
+GIOStatus
+g_io_channel_write_unichar (GIOChannel *channel,
+ gunichar thechar,
+ GError **error)
+{
+ GIOStatus status;
+ gchar static_buf[6];
+ gsize char_len, wrote_len;
+
+ g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->encoding != NULL, G_IO_STATUS_ERROR);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL),
+ G_IO_STATUS_ERROR);
+ g_return_val_if_fail (channel->is_writeable, G_IO_STATUS_ERROR);
+
+ char_len = g_unichar_to_utf8 (thechar, static_buf);
+
+ if (channel->partial_write_buf[0] != '\0')
+ {
+ g_warning ("Partial charater written before writing unichar.\n");
+ channel->partial_write_buf[0] = '\0';
+ }
+
+ status = g_io_channel_write_chars (channel, static_buf,
+ char_len, &wrote_len, error);
+
+ /* We validate UTF-8, so we can't get a partial write */
+
+ g_assert (wrote_len == char_len || status != G_IO_STATUS_NORMAL);
+
+ return status;
+}
+
+/**
+ * g_io_channel_error_quark:
+ *
+ * Return value: the quark used as %G_IO_CHANNEL_ERROR
+ **/
+GQuark
+g_io_channel_error_quark (void)
+{
+ return g_quark_from_static_string ("g-io-channel-error-quark");
+}
diff --git a/clients/Instantbird/giounix.c b/clients/Instantbird/giounix.c
new file mode 100755
index 0000000..87197ed
--- /dev/null
+++ b/clients/Instantbird/giounix.c
@@ -0,0 +1,598 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * giounix.c: IO Channels using unix file descriptors
+ * Copyright 1998 Owen Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+#define _POSIX_SOURCE /* for SSIZE_MAX */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "glib.h"
+#include "galias.h"
+
+/*
+ * Unix IO Channels
+ */
+
+typedef struct _GIOUnixChannel GIOUnixChannel;
+typedef struct _GIOUnixWatch GIOUnixWatch;
+
+struct _GIOUnixChannel
+{
+ GIOChannel channel;
+ gint fd;
+};
+
+struct _GIOUnixWatch
+{
+ GSource source;
+ GPollFD pollfd;
+ GIOChannel *channel;
+ GIOCondition condition;
+};
+
+
+static GIOStatus g_io_unix_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err);
+static GIOStatus g_io_unix_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err);
+static GIOStatus g_io_unix_seek (GIOChannel *channel,
+ gint64 offset,
+ GSeekType type,
+ GError **err);
+static GIOStatus g_io_unix_close (GIOChannel *channel,
+ GError **err);
+static void g_io_unix_free (GIOChannel *channel);
+static GSource* g_io_unix_create_watch (GIOChannel *channel,
+ GIOCondition condition);
+static GIOStatus g_io_unix_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **err);
+static GIOFlags g_io_unix_get_flags (GIOChannel *channel);
+
+static gboolean g_io_unix_prepare (GSource *source,
+ gint *timeout);
+static gboolean g_io_unix_check (GSource *source);
+static gboolean g_io_unix_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void g_io_unix_finalize (GSource *source);
+
+GSourceFuncs g_io_watch_funcs = {
+ g_io_unix_prepare,
+ g_io_unix_check,
+ g_io_unix_dispatch,
+ g_io_unix_finalize
+};
+
+static GIOFuncs unix_channel_funcs = {
+ g_io_unix_read,
+ g_io_unix_write,
+ g_io_unix_seek,
+ g_io_unix_close,
+ g_io_unix_create_watch,
+ g_io_unix_free,
+ g_io_unix_set_flags,
+ g_io_unix_get_flags,
+};
+
+static gboolean
+g_io_unix_prepare (GSource *source,
+ gint *timeout)
+{
+ GIOUnixWatch *watch = (GIOUnixWatch *)source;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+
+ *timeout = -1;
+
+ /* Only return TRUE here if _all_ bits in watch->condition will be set
+ */
+ return ((watch->condition & buffer_condition) == watch->condition);
+}
+
+static gboolean
+g_io_unix_check (GSource *source)
+{
+ GIOUnixWatch *watch = (GIOUnixWatch *)source;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+ GIOCondition poll_condition = watch->pollfd.revents;
+
+ return ((poll_condition | buffer_condition) & watch->condition);
+}
+
+static gboolean
+g_io_unix_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+
+{
+ GIOFunc func = (GIOFunc)callback;
+ GIOUnixWatch *watch = (GIOUnixWatch *)source;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+
+ if (!func)
+ {
+ g_warning ("IO watch dispatched without callback\n"
+ "You must call g_source_connect().");
+ return FALSE;
+ }
+
+ return (*func) (watch->channel,
+ (watch->pollfd.revents | buffer_condition) & watch->condition,
+ user_data);
+}
+
+static void
+g_io_unix_finalize (GSource *source)
+{
+ GIOUnixWatch *watch = (GIOUnixWatch *)source;
+
+ g_io_channel_unref (watch->channel);
+}
+
+static GIOStatus
+g_io_unix_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+ gssize result;
+
+ if (count > SSIZE_MAX) /* At least according to the Debian manpage for read */
+ count = SSIZE_MAX;
+
+ retry:
+ result = read (unix_channel->fd, buf, count);
+
+ if (result < 0)
+ {
+ *bytes_read = 0;
+
+ switch (errno)
+ {
+#ifdef EINTR
+ case EINTR:
+ goto retry;
+#endif
+#ifdef EAGAIN
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
+#endif
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ *bytes_read = result;
+
+ return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus
+g_io_unix_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+ gssize result;
+
+ retry:
+ result = write (unix_channel->fd, buf, count);
+
+ if (result < 0)
+ {
+ *bytes_written = 0;
+
+ switch (errno)
+ {
+#ifdef EINTR
+ case EINTR:
+ goto retry;
+#endif
+#ifdef EAGAIN
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
+#endif
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ *bytes_written = result;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus
+g_io_unix_seek (GIOChannel *channel,
+ gint64 offset,
+ GSeekType type,
+ GError **err)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+ int whence;
+ off_t tmp_offset;
+ off_t result;
+
+ switch (type)
+ {
+ case G_SEEK_SET:
+ whence = SEEK_SET;
+ break;
+ case G_SEEK_CUR:
+ whence = SEEK_CUR;
+ break;
+ case G_SEEK_END:
+ whence = SEEK_END;
+ break;
+ default:
+ whence = -1; /* Shut the compiler up */
+ g_assert_not_reached ();
+ }
+
+ tmp_offset = offset;
+ if (tmp_offset != offset)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (EINVAL),
+ g_strerror (EINVAL));
+ return G_IO_STATUS_ERROR;
+ }
+
+ result = lseek (unix_channel->fd, tmp_offset, whence);
+
+ if (result < 0)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+
+static GIOStatus
+g_io_unix_close (GIOChannel *channel,
+ GError **err)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+
+ if (close (unix_channel->fd) < 0)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static void
+g_io_unix_free (GIOChannel *channel)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+
+ g_free (unix_channel);
+}
+
+static GSource *
+g_io_unix_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+ GSource *source;
+ GIOUnixWatch *watch;
+
+
+ source = g_source_new (&g_io_watch_funcs, sizeof (GIOUnixWatch));
+ watch = (GIOUnixWatch *)source;
+
+ watch->channel = channel;
+ g_io_channel_ref (channel);
+
+ watch->condition = condition;
+
+ watch->pollfd.fd = unix_channel->fd;
+ watch->pollfd.events = condition;
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
+}
+
+static GIOStatus
+g_io_unix_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **err)
+{
+ glong fcntl_flags;
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *) channel;
+
+ fcntl_flags = 0;
+
+ if (flags & G_IO_FLAG_APPEND)
+ fcntl_flags |= O_APPEND;
+ if (flags & G_IO_FLAG_NONBLOCK)
+#ifdef O_NONBLOCK
+ fcntl_flags |= O_NONBLOCK;
+#else
+ fcntl_flags |= O_NDELAY;
+#endif
+
+ if (fcntl (unix_channel->fd, F_SETFL, fcntl_flags) == -1)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOFlags
+g_io_unix_get_flags (GIOChannel *channel)
+{
+ GIOFlags flags = 0;
+ glong fcntl_flags;
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *) channel;
+
+ fcntl_flags = fcntl (unix_channel->fd, F_GETFL);
+
+ if (fcntl_flags == -1)
+ {
+ g_warning (G_STRLOC "Error while getting flags for FD: %s (%d)\n",
+ g_strerror (errno), errno);
+ return 0;
+ }
+
+ if (fcntl_flags & O_APPEND)
+ flags |= G_IO_FLAG_APPEND;
+#ifdef O_NONBLOCK
+ if (fcntl_flags & O_NONBLOCK)
+#else
+ if (fcntl_flags & O_NDELAY)
+#endif
+ flags |= G_IO_FLAG_NONBLOCK;
+
+ switch (fcntl_flags & (O_RDONLY | O_WRONLY | O_RDWR))
+ {
+ case O_RDONLY:
+ channel->is_readable = TRUE;
+ channel->is_writeable = FALSE;
+ break;
+ case O_WRONLY:
+ channel->is_readable = FALSE;
+ channel->is_writeable = TRUE;
+ break;
+ case O_RDWR:
+ channel->is_readable = TRUE;
+ channel->is_writeable = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return flags;
+}
+
+GIOChannel *
+g_io_channel_new_file (const gchar *filename,
+ const gchar *mode,
+ GError **error)
+{
+ int fid, flags;
+ mode_t create_mode;
+ GIOChannel *channel;
+ enum { /* Cheesy hack */
+ MODE_R = 1 << 0,
+ MODE_W = 1 << 1,
+ MODE_A = 1 << 2,
+ MODE_PLUS = 1 << 3
+ } mode_num;
+ struct stat buffer;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (mode != NULL, NULL);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);
+
+ switch (mode[0])
+ {
+ case 'r':
+ mode_num = MODE_R;
+ break;
+ case 'w':
+ mode_num = MODE_W;
+ break;
+ case 'a':
+ mode_num = MODE_A;
+ break;
+ default:
+ g_warning ("Invalid GIOFileMode %s.\n", mode);
+ return NULL;
+ }
+
+ switch (mode[1])
+ {
+ case '\0':
+ break;
+ case '+':
+ if (mode[2] == '\0')
+ {
+ mode_num |= MODE_PLUS;
+ break;
+ }
+ /* Fall through */
+ default:
+ g_warning ("Invalid GIOFileMode %s.\n", mode);
+ return NULL;
+ }
+
+ switch (mode_num)
+ {
+ case MODE_R:
+ flags = O_RDONLY;
+ break;
+ case MODE_W:
+ flags = O_WRONLY | O_TRUNC | O_CREAT;
+ break;
+ case MODE_A:
+ flags = O_WRONLY | O_APPEND | O_CREAT;
+ break;
+ case MODE_R | MODE_PLUS:
+ flags = O_RDWR;
+ break;
+ case MODE_W | MODE_PLUS:
+ flags = O_RDWR | O_TRUNC | O_CREAT;
+ break;
+ case MODE_A | MODE_PLUS:
+ flags = O_RDWR | O_APPEND | O_CREAT;
+ break;
+ default:
+ g_assert_not_reached ();
+ flags = 0;
+ }
+
+ create_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ fid = open (filename, flags, create_mode);
+ if (fid == -1)
+ {
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ return (GIOChannel *)NULL;
+ }
+
+ if (fstat (fid, &buffer) == -1) /* In case someone opens a FIFO */
+ {
+ close (fid);
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ return (GIOChannel *)NULL;
+ }
+
+ channel = (GIOChannel *) g_new (GIOUnixChannel, 1);
+
+ channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
+ || S_ISBLK (buffer.st_mode);
+
+ switch (mode_num)
+ {
+ case MODE_R:
+ channel->is_readable = TRUE;
+ channel->is_writeable = FALSE;
+ break;
+ case MODE_W:
+ case MODE_A:
+ channel->is_readable = FALSE;
+ channel->is_writeable = TRUE;
+ break;
+ case MODE_R | MODE_PLUS:
+ case MODE_W | MODE_PLUS:
+ case MODE_A | MODE_PLUS:
+ channel->is_readable = TRUE;
+ channel->is_writeable = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_io_channel_init (channel);
+ channel->close_on_unref = TRUE; /* must be after g_io_channel_init () */
+ channel->funcs = &unix_channel_funcs;
+
+ ((GIOUnixChannel *) channel)->fd = fid;
+ return channel;
+}
+
+GIOChannel *
+g_io_channel_unix_new (gint fd)
+{
+ struct stat buffer;
+ GIOUnixChannel *unix_channel = g_new (GIOUnixChannel, 1);
+ GIOChannel *channel = (GIOChannel *)unix_channel;
+
+ g_io_channel_init (channel);
+ channel->funcs = &unix_channel_funcs;
+
+ unix_channel->fd = fd;
+
+ /* I'm not sure if fstat on a non-file (e.g., socket) works
+ * it should be safe to say if it fails, the fd isn't seekable.
+ */
+ /* Newer UNIX versions support S_ISSOCK(), fstat() will probably
+ * succeed in most cases.
+ */
+ if (fstat (unix_channel->fd, &buffer) == 0)
+ channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
+ || S_ISBLK (buffer.st_mode);
+ else /* Assume not seekable */
+ channel->is_seekable = FALSE;
+
+ g_io_unix_get_flags (channel); /* Sets is_readable, is_writeable */
+
+ return channel;
+}
+
+gint
+g_io_channel_unix_get_fd (GIOChannel *channel)
+{
+ GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
+ return unix_channel->fd;
+}
diff --git a/clients/Instantbird/giowin32.c b/clients/Instantbird/giowin32.c
new file mode 100755
index 0000000..83cb86c
--- /dev/null
+++ b/clients/Instantbird/giowin32.c
@@ -0,0 +1,2129 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * giowin32.c: IO Channels for Win32.
+ * Copyright 1998 Owen Taylor and Tor Lillqvist
+ * Copyright 1999-2000 Tor Lillqvist and Craig Setera
+ * Copyright 2001-2003 Andrew Lanoix
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "glib.h"
+
+#include <stdlib.h>
+#include <winsock2.h>
+#include <windows.h>
+#include <conio.h>
+#include <fcntl.h>
+#include <io.h>
+#include <process.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "gstdio.h"
+#include "glibintl.h"
+
+#include "galias.h"
+
+typedef struct _GIOWin32Channel GIOWin32Channel;
+typedef struct _GIOWin32Watch GIOWin32Watch;
+
+#define BUFFER_SIZE 4096
+
+typedef enum {
+ G_IO_WIN32_WINDOWS_MESSAGES, /* Windows messages */
+ G_IO_WIN32_FILE_DESC, /* Unix-like file descriptors from
+ * _open() or _pipe(), except for console IO.
+ * Have to create separate thread to read.
+ */
+ G_IO_WIN32_CONSOLE, /* Console IO (usually stdin, stdout, stderr) */
+ G_IO_WIN32_SOCKET /* Sockets. No separate thread */
+} GIOWin32ChannelType;
+
+struct _GIOWin32Channel {
+ GIOChannel channel;
+ gint fd; /* Either a Unix-like file handle as provided
+ * by the Microsoft C runtime, or a SOCKET
+ * as provided by WinSock.
+ */
+ GIOWin32ChannelType type;
+
+ gboolean debug;
+
+ /* This is used by G_IO_WIN32_WINDOWS_MESSAGES channels */
+ HWND hwnd; /* handle of window, or NULL */
+
+ /* Following fields are used by fd channels. */
+ CRITICAL_SECTION mutex;
+
+ int direction; /* 0 means we read from it,
+ * 1 means we write to it.
+ */
+
+ gboolean running; /* Is reader thread running. FALSE if
+ * EOF has been reached.
+ */
+ gboolean needs_close; /* If the channel has been closed while
+ * the reader thread was still running.
+ */
+ guint thread_id; /* If non-NULL has a reader thread, or has
+ * had.*/
+ HANDLE data_avail_event;
+
+ gushort revents;
+
+ /* Following fields used by fd channels for input */
+
+ /* Data is kept in a circular buffer. To be able to distinguish between
+ * empty and full buffer, we cannot fill it completely, but have to
+ * leave a one character gap.
+ *
+ * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE).
+ *
+ * Empty: wrp == rdp
+ * Full: (wrp + 1) % BUFFER_SIZE == rdp
+ * Partial: otherwise
+ */
+ guchar *buffer; /* (Circular) buffer */
+ gint wrp, rdp; /* Buffer indices for writing and reading */
+ HANDLE space_avail_event;
+
+ /* Following fields used by socket channels */
+ int event_mask;
+ int last_events;
+ int event;
+ gboolean write_would_have_blocked;
+};
+
+#define LOCK(mutex) EnterCriticalSection (&mutex)
+#define UNLOCK(mutex) LeaveCriticalSection (&mutex)
+
+struct _GIOWin32Watch {
+ GSource source;
+ GPollFD pollfd;
+ GIOChannel *channel;
+ GIOCondition condition;
+};
+
+static void
+g_win32_print_access_mode (int flags)
+{
+ g_print ("%s%s%s%s%s%s%s%s%s%s",
+ ((flags & 0x3) == _O_RDWR ? "O_RDWR" :
+ ((flags & 0x3) == _O_RDONLY ? "O_RDONLY" :
+ ((flags & 0x3) == _O_WRONLY ? "O_WRONLY" : "0"))),
+ (flags & _O_APPEND ? "|O_APPEND" : ""),
+ (flags & _O_RANDOM ? "|O_RANDOM" : ""),
+ (flags & _O_SEQUENTIAL ? "|O_SEQUENTIAL" : ""),
+ (flags & _O_TEMPORARY ? "|O_TEMPORARY" : ""),
+ (flags & _O_CREAT ? "|O_CREAT" : ""),
+ (flags & _O_TRUNC ? "|O_TRUNC" : ""),
+ (flags & _O_EXCL ? "|O_EXCL" : ""),
+ (flags & _O_TEXT ? "|O_TEXT" : ""),
+ (flags & _O_BINARY ? "|O_BINARY" : ""));
+}
+
+static void
+g_win32_print_gioflags (GIOFlags flags)
+{
+ char *bar = "";
+
+ if (flags & G_IO_FLAG_APPEND)
+ bar = "|", g_print ("APPEND");
+ if (flags & G_IO_FLAG_NONBLOCK)
+ g_print ("%sNONBLOCK", bar), bar = "|";
+ if (flags & G_IO_FLAG_IS_READABLE)
+ g_print ("%sREADABLE", bar), bar = "|";
+ if (flags & G_IO_FLAG_IS_WRITEABLE)
+ g_print ("%sWRITEABLE", bar), bar = "|";
+ if (flags & G_IO_FLAG_IS_SEEKABLE)
+ g_print ("%sSEEKABLE", bar), bar = "|";
+}
+
+static const char *
+event_mask_to_string (int mask)
+{
+ char buf[100];
+ int checked_bits = 0;
+ char *bufp = buf;
+
+ if (mask == 0)
+ return "";
+
+#define BIT(n) checked_bits |= FD_##n; if (mask & FD_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))
+
+ BIT (READ);
+ BIT (WRITE);
+ BIT (OOB);
+ BIT (ACCEPT);
+ BIT (CONNECT);
+ BIT (CLOSE);
+ BIT (QOS);
+ BIT (GROUP_QOS);
+ BIT (ROUTING_INTERFACE_CHANGE);
+ BIT (ADDRESS_LIST_CHANGE);
+
+#undef BIT
+
+ if ((mask & ~checked_bits) != 0)
+ bufp += sprintf (bufp, "|%#x", mask & ~checked_bits);
+
+ return g_quark_to_string (g_quark_from_string (buf));
+}
+
+static const char *
+condition_to_string (GIOCondition condition)
+{
+ char buf[100];
+ int checked_bits = 0;
+ char *bufp = buf;
+
+ if (condition == 0)
+ return "";
+
+#define BIT(n) checked_bits |= G_IO_##n; if (condition & G_IO_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))
+
+ BIT (IN);
+ BIT (OUT);
+ BIT (PRI);
+ BIT (ERR);
+ BIT (HUP);
+ BIT (NVAL);
+
+#undef BIT
+
+ if ((condition & ~checked_bits) != 0)
+ bufp += sprintf (bufp, "|%#x", condition & ~checked_bits);
+
+ return g_quark_to_string (g_quark_from_string (buf));
+}
+
+static gboolean
+g_io_win32_get_debug_flag (void)
+{
+ return (getenv ("G_IO_WIN32_DEBUG") != NULL);
+}
+
+static char *
+winsock_error_message (int number)
+{
+ static char unk[100];
+
+ switch (number) {
+ case WSAEINTR:
+ return "Interrupted function call";
+ case WSAEACCES:
+ return "Permission denied";
+ case WSAEFAULT:
+ return "Bad address";
+ case WSAEINVAL:
+ return "Invalid argument";
+ case WSAEMFILE:
+ return "Too many open sockets";
+ case WSAEWOULDBLOCK:
+ return "Resource temporarily unavailable";
+ case WSAEINPROGRESS:
+ return "Operation now in progress";
+ case WSAEALREADY:
+ return "Operation already in progress";
+ case WSAENOTSOCK:
+ return "Socket operation on nonsocket";
+ case WSAEDESTADDRREQ:
+ return "Destination address required";
+ case WSAEMSGSIZE:
+ return "Message too long";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported on transport endpoint";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case WSAEADDRINUSE:
+ return "Address already in use";
+ case WSAEADDRNOTAVAIL:
+ return "Address not available";
+ case WSAENETDOWN:
+ return "Network interface is not configured";
+ case WSAENETUNREACH:
+ return "Network is unreachable";
+ case WSAENETRESET:
+ return "Network dropped connection on reset";
+ case WSAECONNABORTED:
+ return "Software caused connection abort";
+ case WSAECONNRESET:
+ return "Connection reset by peer";
+ case WSAENOBUFS:
+ return "No buffer space available";
+ case WSAEISCONN:
+ return "Socket is already connected";
+ case WSAENOTCONN:
+ return "Socket is not connected";
+ case WSAESHUTDOWN:
+ return "Can't send after socket shutdown";
+ case WSAETIMEDOUT:
+ return "Connection timed out";
+ case WSAECONNREFUSED:
+ return "Connection refused";
+ case WSAEHOSTDOWN:
+ return "Host is down";
+ case WSAEHOSTUNREACH:
+ return "Host is unreachable";
+ case WSAEPROCLIM:
+ return "Too many processes";
+ case WSASYSNOTREADY:
+ return "Network subsystem is unavailable";
+ case WSAVERNOTSUPPORTED:
+ return "Winsock.dll version out of range";
+ case WSANOTINITIALISED:
+ return "Successful WSAStartup not yet performed";
+ case WSAEDISCON:
+ return "Graceful shutdown in progress";
+ case WSATYPE_NOT_FOUND:
+ return "Class type not found";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found";
+ case WSATRY_AGAIN:
+ return "Nonauthoritative host not found";
+ case WSANO_RECOVERY:
+ return "This is a nonrecoverable error";
+ case WSANO_DATA:
+ return "Valid name, no data record of requested type";
+ case WSA_INVALID_HANDLE:
+ return "Specified event object handle is invalid";
+ case WSA_INVALID_PARAMETER:
+ return "One or more parameters are invalid";
+ case WSA_IO_INCOMPLETE:
+ return "Overlapped I/O event object not in signaled state";
+ case WSA_NOT_ENOUGH_MEMORY:
+ return "Insufficient memory available";
+ case WSA_OPERATION_ABORTED:
+ return "Overlapped operation aborted";
+ case WSAEINVALIDPROCTABLE:
+ return "Invalid procedure table from service provider";
+ case WSAEINVALIDPROVIDER:
+ return "Invalid service provider version number";
+ case WSAEPROVIDERFAILEDINIT:
+ return "Unable to initialize a service provider";
+ case WSASYSCALLFAILURE:
+ return "System call failure";
+ default:
+ sprintf (unk, "Unknown WinSock error %d", number);
+ return unk;
+ }
+}
+
+static void
+g_io_channel_win32_init (GIOWin32Channel *channel)
+{
+ channel->debug = g_io_win32_get_debug_flag ();
+ channel->buffer = NULL;
+ channel->running = FALSE;
+ channel->needs_close = FALSE;
+ channel->thread_id = 0;
+ channel->data_avail_event = NULL;
+ channel->revents = 0;
+ channel->space_avail_event = NULL;
+ channel->event_mask = 0;
+ channel->last_events = 0;
+ channel->event = 0;
+ channel->write_would_have_blocked = FALSE;
+ InitializeCriticalSection (&channel->mutex);
+}
+
+static void
+create_events (GIOWin32Channel *channel)
+{
+ SECURITY_ATTRIBUTES sec_attrs;
+
+ sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sec_attrs.lpSecurityDescriptor = NULL;
+ sec_attrs.bInheritHandle = FALSE;
+
+ /* The data available event is manual reset, the space available event
+ * is automatic reset.
+ */
+ if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
+ || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL)))
+ {
+ gchar *emsg = g_win32_error_message (GetLastError ());
+ g_error ("Error creating event: %s", emsg);
+ g_free (emsg);
+ }
+}
+
+static unsigned __stdcall
+read_thread (void *parameter)
+{
+ GIOWin32Channel *channel = parameter;
+ guchar *buffer;
+ guint nbytes;
+
+ g_io_channel_ref ((GIOChannel *)channel);
+
+ if (channel->debug)
+ g_print ("read_thread %#x: start fd=%d, data_avail=%#x space_avail=%#x\n",
+ channel->thread_id,
+ channel->fd,
+ (guint) channel->data_avail_event,
+ (guint) channel->space_avail_event);
+
+ channel->direction = 0;
+ channel->buffer = g_malloc (BUFFER_SIZE);
+ channel->rdp = channel->wrp = 0;
+ channel->running = TRUE;
+
+ SetEvent (channel->space_avail_event);
+
+ LOCK (channel->mutex);
+ while (channel->running)
+ {
+ if (channel->debug)
+ g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+ {
+ /* Buffer is full */
+ if (channel->debug)
+ g_print ("read_thread %#x: resetting space_avail\n",
+ channel->thread_id);
+ ResetEvent (channel->space_avail_event);
+ if (channel->debug)
+ g_print ("read_thread %#x: waiting for space\n",
+ channel->thread_id);
+ UNLOCK (channel->mutex);
+ WaitForSingleObject (channel->space_avail_event, INFINITE);
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ }
+
+ buffer = channel->buffer + channel->wrp;
+
+ /* Always leave at least one byte unused gap to be able to
+ * distinguish between the full and empty condition...
+ */
+ nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
+ BUFFER_SIZE - channel->wrp);
+
+ if (channel->debug)
+ g_print ("read_thread %#x: calling read() for %d bytes\n",
+ channel->thread_id, nbytes);
+
+ UNLOCK (channel->mutex);
+
+ nbytes = read (channel->fd, buffer, nbytes);
+
+ LOCK (channel->mutex);
+
+ channel->revents = G_IO_IN;
+ if (nbytes == 0)
+ channel->revents |= G_IO_HUP;
+ else if (nbytes < 0)
+ channel->revents |= G_IO_ERR;
+
+ if (channel->debug)
+ g_print ("read_thread %#x: read() returned %d, rdp=%d, wrp=%d\n",
+ channel->thread_id, nbytes, channel->rdp, channel->wrp);
+
+ if (nbytes <= 0)
+ break;
+
+ channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
+ if (channel->debug)
+ g_print ("read_thread %#x: rdp=%d, wrp=%d, setting data_avail\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ SetEvent (channel->data_avail_event);
+ }
+
+ channel->running = FALSE;
+ if (channel->needs_close)
+ {
+ if (channel->debug)
+ g_print ("read_thread %#x: channel fd %d needs closing\n",
+ channel->thread_id, channel->fd);
+ close (channel->fd);
+ channel->fd = -1;
+ }
+
+ if (channel->debug)
+ g_print ("read_thread %#x: EOF, rdp=%d, wrp=%d, setting data_avail\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ SetEvent (channel->data_avail_event);
+ UNLOCK (channel->mutex);
+
+ g_io_channel_unref ((GIOChannel *)channel);
+
+ /* No need to call _endthreadex(), the actual thread starter routine
+ * in MSVCRT (see crt/src/threadex.c:_threadstartex) calls
+ * _endthreadex() for us.
+ */
+
+ return 0;
+}
+
+static unsigned __stdcall
+write_thread (void *parameter)
+{
+ GIOWin32Channel *channel = parameter;
+ guchar *buffer;
+ guint nbytes;
+
+ g_io_channel_ref ((GIOChannel *)channel);
+
+ if (channel->debug)
+ g_print ("write_thread %#x: start fd=%d, data_avail=%#x space_avail=%#x\n",
+ channel->thread_id,
+ channel->fd,
+ (guint) channel->data_avail_event,
+ (guint) channel->space_avail_event);
+
+ channel->direction = 1;
+ channel->buffer = g_malloc (BUFFER_SIZE);
+ channel->rdp = channel->wrp = 0;
+ channel->running = TRUE;
+
+ SetEvent (channel->space_avail_event);
+
+ /* We use the same event objects as for a reader thread, but with
+ * reversed meaning. So, space_avail is used if data is available
+ * for writing, and data_avail is used if space is available in the
+ * write buffer.
+ */
+
+ LOCK (channel->mutex);
+ while (channel->running || channel->rdp != channel->wrp)
+ {
+ if (channel->debug)
+ g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ if (channel->wrp == channel->rdp)
+ {
+ /* Buffer is empty. */
+ if (channel->debug)
+ g_print ("write_thread %#x: resetting space_avail\n",
+ channel->thread_id);
+ ResetEvent (channel->space_avail_event);
+ if (channel->debug)
+ g_print ("write_thread %#x: waiting for data\n",
+ channel->thread_id);
+ channel->revents = G_IO_OUT;
+ SetEvent (channel->data_avail_event);
+ UNLOCK (channel->mutex);
+ WaitForSingleObject (channel->space_avail_event, INFINITE);
+
+ LOCK (channel->mutex);
+ if (channel->rdp == channel->wrp)
+ break;
+
+ if (channel->debug)
+ g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ }
+
+ buffer = channel->buffer + channel->rdp;
+ if (channel->rdp < channel->wrp)
+ nbytes = channel->wrp - channel->rdp;
+ else
+ nbytes = BUFFER_SIZE - channel->rdp;
+
+ if (channel->debug)
+ g_print ("write_thread %#x: calling write() for %d bytes\n",
+ channel->thread_id, nbytes);
+
+ UNLOCK (channel->mutex);
+ nbytes = write (channel->fd, buffer, nbytes);
+ LOCK (channel->mutex);
+
+ if (channel->debug)
+ g_print ("write_thread %#x: write(%i) returned %d, rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->fd, nbytes, channel->rdp, channel->wrp);
+
+ channel->revents = 0;
+ if (nbytes > 0)
+ channel->revents |= G_IO_OUT;
+ else if (nbytes <= 0)
+ channel->revents |= G_IO_ERR;
+
+ channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
+
+ if (nbytes <= 0)
+ break;
+
+ if (channel->debug)
+ g_print ("write_thread: setting data_avail for thread %#x\n",
+ channel->thread_id);
+ SetEvent (channel->data_avail_event);
+ }
+
+ channel->running = FALSE;
+ if (channel->needs_close)
+ {
+ if (channel->debug)
+ g_print ("write_thread %#x: channel fd %d needs closing\n",
+ channel->thread_id, channel->fd);
+ close (channel->fd);
+ channel->fd = -1;
+ }
+
+ UNLOCK (channel->mutex);
+
+ g_io_channel_unref ((GIOChannel *)channel);
+
+ return 0;
+}
+
+static void
+create_thread (GIOWin32Channel *channel,
+ GIOCondition condition,
+ unsigned (__stdcall *thread) (void *parameter))
+{
+ HANDLE thread_handle;
+
+ thread_handle = (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0,
+ &channel->thread_id);
+ if (thread_handle == 0)
+ g_warning (G_STRLOC ": Error creating reader thread: %s",
+ g_strerror (errno));
+ else if (!CloseHandle (thread_handle))
+ g_warning (G_STRLOC ": Error closing thread handle: %s\n",
+ g_win32_error_message (GetLastError ()));
+
+ WaitForSingleObject (channel->space_avail_event, INFINITE);
+}
+
+static GIOStatus
+buffer_read (GIOWin32Channel *channel,
+ guchar *dest,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
+{
+ guint nbytes;
+ guint left = count;
+
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("reading from thread %#x %d bytes, rdp=%d, wrp=%d\n",
+ channel->thread_id, count, channel->rdp, channel->wrp);
+
+ if (channel->wrp == channel->rdp)
+ {
+ UNLOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("waiting for data from thread %#x\n", channel->thread_id);
+ WaitForSingleObject (channel->data_avail_event, INFINITE);
+ if (channel->debug)
+ g_print ("done waiting for data from thread %#x\n", channel->thread_id);
+ LOCK (channel->mutex);
+ if (channel->wrp == channel->rdp && !channel->running)
+ {
+ if (channel->debug)
+ g_print ("wrp==rdp, !running\n");
+ UNLOCK (channel->mutex);
+ *bytes_read = 0;
+ return G_IO_STATUS_EOF;
+ }
+ }
+
+ if (channel->rdp < channel->wrp)
+ nbytes = channel->wrp - channel->rdp;
+ else
+ nbytes = BUFFER_SIZE - channel->rdp;
+ UNLOCK (channel->mutex);
+ nbytes = MIN (left, nbytes);
+ if (channel->debug)
+ g_print ("moving %d bytes from thread %#x\n",
+ nbytes, channel->thread_id);
+ memcpy (dest, channel->buffer + channel->rdp, nbytes);
+ dest += nbytes;
+ left -= nbytes;
+ LOCK (channel->mutex);
+ channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
+ if (channel->debug)
+ g_print ("setting space_avail for thread %#x\n", channel->thread_id);
+ SetEvent (channel->space_avail_event);
+ if (channel->debug)
+ g_print ("for thread %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ if (channel->running && channel->wrp == channel->rdp)
+ {
+ if (channel->debug)
+ g_print ("resetting data_avail of thread %#x\n",
+ channel->thread_id);
+ ResetEvent (channel->data_avail_event);
+ };
+ UNLOCK (channel->mutex);
+
+ /* We have no way to indicate any errors form the actual
+ * read() or recv() call in the reader thread. Should we have?
+ */
+ *bytes_read = count - left;
+ return (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+
+static GIOStatus
+buffer_write (GIOWin32Channel *channel,
+ const guchar *dest,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ guint nbytes;
+ guint left = count;
+
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("buffer_write: writing to thread %#x %d bytes, rdp=%d, wrp=%d\n",
+ channel->thread_id, count, channel->rdp, channel->wrp);
+
+ if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+ {
+ /* Buffer is full */
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: resetting data_avail\n",
+ channel->thread_id);
+ ResetEvent (channel->data_avail_event);
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: waiting for space\n",
+ channel->thread_id);
+ UNLOCK (channel->mutex);
+ WaitForSingleObject (channel->data_avail_event, INFINITE);
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ }
+
+ nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
+ BUFFER_SIZE - channel->wrp);
+
+ UNLOCK (channel->mutex);
+ nbytes = MIN (left, nbytes);
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: writing %d bytes\n",
+ channel->thread_id, nbytes);
+ memcpy (channel->buffer + channel->wrp, dest, nbytes);
+ dest += nbytes;
+ left -= nbytes;
+ LOCK (channel->mutex);
+
+ channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d, setting space_avail\n",
+ channel->thread_id, channel->rdp, channel->wrp);
+ SetEvent (channel->space_avail_event);
+
+ if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+ {
+ /* Buffer is full */
+ if (channel->debug)
+ g_print ("buffer_write: tid %#x: resetting data_avail\n",
+ channel->thread_id);
+ ResetEvent (channel->data_avail_event);
+ }
+
+ UNLOCK (channel->mutex);
+
+ /* We have no way to indicate any errors form the actual
+ * write() call in the writer thread. Should we have?
+ */
+ *bytes_written = count - left;
+ return (*bytes_written > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+
+static gboolean
+g_io_win32_prepare (GSource *source,
+ gint *timeout)
+{
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+ GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+ int event_mask;
+
+ *timeout = -1;
+
+ switch (channel->type)
+ {
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ case G_IO_WIN32_CONSOLE:
+ break;
+
+ case G_IO_WIN32_FILE_DESC:
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for thread %#x buffer_condition:{%s}\n"
+ " watch->pollfd.events:{%s} watch->pollfd.revents:{%s} channel->revents:{%s}\n",
+ channel->thread_id, condition_to_string (buffer_condition),
+ condition_to_string (watch->pollfd.events),
+ condition_to_string (watch->pollfd.revents),
+ condition_to_string (channel->revents));
+
+ LOCK (channel->mutex);
+ if (channel->running)
+ {
+ if (channel->direction == 0 && channel->wrp == channel->rdp)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for thread %#x, setting channel->revents = 0\n",
+ channel->thread_id);
+ channel->revents = 0;
+ }
+ }
+ else
+ {
+ if (channel->direction == 1
+ && (channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: for thread %#x, setting channel->revents = %i\n",
+ channel->thread_id, 0);
+ channel->revents = 0;
+ }
+ }
+ UNLOCK (channel->mutex);
+ break;
+
+ case G_IO_WIN32_SOCKET:
+ event_mask = 0;
+ if (watch->condition & G_IO_IN)
+ event_mask |= (FD_READ | FD_ACCEPT);
+ if (watch->condition & G_IO_OUT)
+ event_mask |= (FD_WRITE | FD_CONNECT);
+ event_mask |= FD_CLOSE;
+
+ if (channel->event_mask != event_mask /* || channel->event != watch->pollfd.fd*/)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_prepare: WSAEventSelect(%d, %#x, {%s}\n",
+ channel->fd, watch->pollfd.fd,
+ event_mask_to_string (event_mask));
+ if (WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd,
+ event_mask) == SOCKET_ERROR)
+ ; /* What? */
+ channel->event_mask = event_mask;
+#if 0
+ channel->event = watch->pollfd.fd;
+#endif
+ channel->last_events = 0;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+ return ((watch->condition & buffer_condition) == watch->condition);
+}
+
+static gboolean
+g_io_win32_check (GSource *source)
+{
+ MSG msg;
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+ GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+ WSANETWORKEVENTS events;
+
+ switch (channel->type)
+ {
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE));
+
+ case G_IO_WIN32_FILE_DESC:
+ if (channel->debug)
+ g_print ("g_io_win32_check: for thread %#x buffer_condition=%s\n"
+ " watch->pollfd.events={%s} watch->pollfd.revents={%s} channel->revents={%s}\n",
+ channel->thread_id, condition_to_string (buffer_condition),
+ condition_to_string (watch->pollfd.events),
+ condition_to_string (watch->pollfd.revents),
+ condition_to_string (channel->revents));
+
+ watch->pollfd.revents = (watch->pollfd.events & channel->revents);
+
+ return ((watch->pollfd.revents | buffer_condition) & watch->condition);
+
+ case G_IO_WIN32_CONSOLE:
+ if (watch->channel->is_writeable)
+ return TRUE;
+ else if (watch->channel->is_readable)
+ {
+ INPUT_RECORD buffer;
+ DWORD n;
+ if (PeekConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n) &&
+ n == 1)
+ {
+ /* _kbhit() does quite complex processing to find out
+ * whether at least one of the key events pending corresponds
+ * to a "real" character that can be read.
+ */
+ if (_kbhit ())
+ return TRUE;
+
+ /* Discard all other kinds of events */
+ ReadConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n);
+ }
+ }
+ return FALSE;
+
+ case G_IO_WIN32_SOCKET:
+ if (channel->last_events & FD_WRITE)
+ {
+ if (channel->debug)
+ g_print ("g_io_win32_check: sock=%d event=%#x last_events has FD_WRITE\n",
+ channel->fd, watch->pollfd.fd);
+ }
+ else
+ {
+ WSAEnumNetworkEvents (channel->fd, 0, &events);
+
+ if (channel->debug)
+ g_print ("g_io_win32_check: WSAEnumNetworkEvents (%d, %#x) revents={%s} condition={%s} events={%s}\n",
+ channel->fd, watch->pollfd.fd,
+ condition_to_string (watch->pollfd.revents),
+ condition_to_string (watch->condition),
+ event_mask_to_string (events.lNetworkEvents));
+
+ if (watch->pollfd.revents != 0 &&
+ events.lNetworkEvents == 0 &&
+ !(channel->event_mask & FD_WRITE))
+ {
+ channel->event_mask = 0;
+ if (channel->debug)
+ g_print ("g_io_win32_check: WSAEventSelect(%d, %#x, {})\n",
+ channel->fd, watch->pollfd.fd);
+ WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, 0);
+ if (channel->debug)
+ g_print ("g_io_win32_check: ResetEvent(%#x)\n",
+ watch->pollfd.fd);
+ ResetEvent ((HANDLE) watch->pollfd.fd);
+ }
+ channel->last_events = events.lNetworkEvents;
+ }
+ watch->pollfd.revents = 0;
+ if (channel->last_events & (FD_READ | FD_ACCEPT))
+ watch->pollfd.revents |= G_IO_IN;
+ if (channel->last_events & FD_WRITE)
+ watch->pollfd.revents |= G_IO_OUT;
+ else
+ {
+ /* We have called WSAEnumNetworkEvents() above but it didn't
+ * set FD_WRITE.
+ */
+ if (events.lNetworkEvents & FD_CONNECT)
+ {
+ if (events.iErrorCode[FD_CONNECT_BIT] == 0)
+ watch->pollfd.revents |= G_IO_OUT;
+ else
+ watch->pollfd.revents |= (G_IO_HUP | G_IO_ERR);
+ }
+ if (watch->pollfd.revents == 0 && (channel->last_events & (FD_CLOSE)))
+ watch->pollfd.revents |= G_IO_HUP;
+ }
+
+ /* Regardless of WSAEnumNetworkEvents() result, if watching for
+ * writability, unless last write would have blocked set
+ * G_IO_OUT. But never set both G_IO_OUT and G_IO_HUP.
+ */
+ if (!(watch->pollfd.revents & G_IO_HUP) &&
+ !channel->write_would_have_blocked &&
+ (channel->event_mask & FD_WRITE))
+ watch->pollfd.revents |= G_IO_OUT;
+
+ return ((watch->pollfd.revents | buffer_condition) & watch->condition);
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+}
+
+static gboolean
+g_io_win32_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GIOFunc func = (GIOFunc)callback;
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+ GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+ GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+
+ if (!func)
+ {
+ g_warning (G_STRLOC ": GIOWin32Watch dispatched without callback\n"
+ "You must call g_source_connect().");
+ return FALSE;
+ }
+
+ if (channel->debug)
+ g_print ("g_io_win32_dispatch: pollfd.revents=%s condition=%s result=%s\n",
+ condition_to_string (watch->pollfd.revents),
+ condition_to_string (watch->condition),
+ condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition));
+
+ return (*func) (watch->channel,
+ (watch->pollfd.revents | buffer_condition) & watch->condition,
+ user_data);
+}
+
+static void
+g_io_win32_finalize (GSource *source)
+{
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+ GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+
+ switch (channel->type)
+ {
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ case G_IO_WIN32_CONSOLE:
+ break;
+
+ case G_IO_WIN32_FILE_DESC:
+ LOCK (channel->mutex);
+ if (channel->debug)
+ g_print ("g_io_win32_finalize: channel with thread %#x\n",
+ channel->thread_id);
+ UNLOCK (channel->mutex);
+ break;
+
+ case G_IO_WIN32_SOCKET:
+ if (channel->debug)
+ g_print ("g_io_win32_finalize: channel is for sock=%d\n", channel->fd);
+#if 0
+ CloseHandle ((HANDLE) watch->pollfd.fd);
+ channel->event = 0;
+ channel->event_mask = 0;
+#endif
+ break;
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+ g_io_channel_unref (watch->channel);
+}
+
+GSourceFuncs g_io_watch_funcs = {
+ g_io_win32_prepare,
+ g_io_win32_check,
+ g_io_win32_dispatch,
+ g_io_win32_finalize
+};
+
+static GIOStatus
+g_io_win32_msg_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ MSG msg; /* In case of alignment problems */
+
+ if (count < sizeof (MSG))
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
+ "Incorrect message size"); /* Informative enough error message? */
+ return G_IO_STATUS_ERROR;
+ }
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_msg_read: for %#x\n",
+ (guint) win32_channel->hwnd);
+ if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE))
+ return G_IO_STATUS_AGAIN;
+
+ memmove (buf, &msg, sizeof (MSG));
+ *bytes_read = sizeof (MSG);
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus
+g_io_win32_msg_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ MSG msg;
+
+ if (count != sizeof (MSG))
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
+ "Incorrect message size"); /* Informative enough error message? */
+ return G_IO_STATUS_ERROR;
+ }
+
+ /* In case of alignment problems */
+ memmove (&msg, buf, sizeof (MSG));
+ if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
+ {
+ gchar *emsg = g_win32_error_message (GetLastError ());
+ g_set_error (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, emsg);
+ g_free (emsg);
+ return G_IO_STATUS_ERROR;
+ }
+
+ *bytes_written = sizeof (MSG);
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus
+g_io_win32_msg_close (GIOChannel *channel,
+ GError **err)
+{
+ /* Nothing to be done. Or should we set hwnd to some invalid value? */
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static void
+g_io_win32_free (GIOChannel *channel)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_free channel fd=%d\n", win32_channel->fd);
+
+ if (win32_channel->data_avail_event)
+ CloseHandle (win32_channel->data_avail_event);
+ if (win32_channel->space_avail_event)
+ CloseHandle (win32_channel->space_avail_event);
+ if (win32_channel->type == G_IO_WIN32_SOCKET)
+ WSAEventSelect (win32_channel->fd, NULL, 0);
+ DeleteCriticalSection (&win32_channel->mutex);
+
+ g_free (win32_channel->buffer);
+ g_free (win32_channel);
+}
+
+static GSource *
+g_io_win32_msg_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOWin32Watch *watch;
+ GSource *source;
+
+ source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
+ watch = (GIOWin32Watch *)source;
+
+ watch->channel = channel;
+ g_io_channel_ref (channel);
+
+ watch->condition = condition;
+
+ watch->pollfd.fd = G_WIN32_MSG_HANDLE;
+ watch->pollfd.events = condition;
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
+}
+
+static GIOStatus
+g_io_win32_fd_and_console_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ gint result;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_fd_read: fd=%d count=%d\n",
+ win32_channel->fd, count);
+
+ if (win32_channel->thread_id)
+ {
+ return buffer_read (win32_channel, buf, count, bytes_read, err);
+ }
+
+ result = read (win32_channel->fd, buf, count);
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_fd_read: read() => %d\n", result);
+
+ if (result < 0)
+ {
+ *bytes_read = 0;
+
+ switch (errno)
+ {
+#ifdef EAGAIN
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
+#endif
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ *bytes_read = result;
+
+ return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus
+g_io_win32_fd_and_console_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ gint result;
+
+ if (win32_channel->thread_id)
+ {
+ return buffer_write (win32_channel, buf, count, bytes_written, err);
+ }
+
+ result = write (win32_channel->fd, buf, count);
+ if (win32_channel->debug)
+ g_print ("g_io_win32_fd_write: fd=%d count=%d => %d\n",
+ win32_channel->fd, count, result);
+
+ if (result < 0)
+ {
+ *bytes_written = 0;
+
+ switch (errno)
+ {
+#ifdef EAGAIN
+ case EAGAIN:
+ return G_IO_STATUS_AGAIN;
+#endif
+ default:
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ *bytes_written = result;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus
+g_io_win32_fd_seek (GIOChannel *channel,
+ gint64 offset,
+ GSeekType type,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ int whence;
+ off_t tmp_offset;
+ off_t result;
+
+ switch (type)
+ {
+ case G_SEEK_SET:
+ whence = SEEK_SET;
+ break;
+ case G_SEEK_CUR:
+ whence = SEEK_CUR;
+ break;
+ case G_SEEK_END:
+ whence = SEEK_END;
+ break;
+ default:
+ whence = -1; /* Keep the compiler quiet */
+ g_assert_not_reached ();
+ abort ();
+ }
+
+ tmp_offset = offset;
+ if (tmp_offset != offset)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (EINVAL),
+ g_strerror (EINVAL));
+ return G_IO_STATUS_ERROR;
+ }
+
+ result = lseek (win32_channel->fd, tmp_offset, whence);
+
+ if (result < 0)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus
+g_io_win32_fd_close (GIOChannel *channel,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (win32_channel->debug)
+ g_print ("thread %#x: closing fd %d\n",
+ win32_channel->thread_id,
+ win32_channel->fd);
+ LOCK (win32_channel->mutex);
+ if (win32_channel->running)
+ {
+ if (win32_channel->debug)
+ g_print ("thread %#x: running, marking fd %d for later close\n",
+ win32_channel->thread_id, win32_channel->fd);
+ win32_channel->running = FALSE;
+ win32_channel->needs_close = TRUE;
+ if (win32_channel->direction == 0)
+ SetEvent (win32_channel->data_avail_event);
+ else
+ SetEvent (win32_channel->space_avail_event);
+ }
+ else
+ {
+ if (win32_channel->debug)
+ g_print ("closing fd %d\n", win32_channel->fd);
+ close (win32_channel->fd);
+ if (win32_channel->debug)
+ g_print ("closed fd %d, setting to -1\n",
+ win32_channel->fd);
+ win32_channel->fd = -1;
+ }
+ UNLOCK (win32_channel->mutex);
+
+ /* FIXME error detection? */
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GSource *
+g_io_win32_fd_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+
+ watch->channel = channel;
+ g_io_channel_ref (channel);
+
+ watch->condition = condition;
+
+ if (win32_channel->data_avail_event == NULL)
+ create_events (win32_channel);
+
+ watch->pollfd.fd = (gint) win32_channel->data_avail_event;
+ watch->pollfd.events = condition;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_fd_create_watch: fd=%d condition={%s} handle=%#x\n",
+ win32_channel->fd, condition_to_string (condition), watch->pollfd.fd);
+
+ LOCK (win32_channel->mutex);
+ if (win32_channel->thread_id == 0)
+ {
+ if (condition & G_IO_IN)
+ create_thread (win32_channel, condition, read_thread);
+ else if (condition & G_IO_OUT)
+ create_thread (win32_channel, condition, write_thread);
+ }
+
+ g_source_add_poll (source, &watch->pollfd);
+ UNLOCK (win32_channel->mutex);
+
+ return source;
+}
+
+static GIOStatus
+g_io_win32_console_close (GIOChannel *channel,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (close (win32_channel->fd) < 0)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ g_io_channel_error_from_errno (errno),
+ g_strerror (errno));
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GSource *
+g_io_win32_console_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+
+ watch->channel = channel;
+ g_io_channel_ref (channel);
+
+ watch->condition = condition;
+
+ watch->pollfd.fd = (gint) _get_osfhandle (win32_channel->fd);
+ watch->pollfd.events = condition;
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
+}
+
+static GIOStatus
+g_io_win32_sock_read (GIOChannel *channel,
+ gchar *buf,
+ gsize count,
+ gsize *bytes_read,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ gint result;
+ GIOChannelError error;
+ int winsock_error;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_read: sockfd=%d count=%d\n",
+ win32_channel->fd, count);
+
+ result = recv (win32_channel->fd, buf, count, 0);
+ if (result == SOCKET_ERROR)
+ winsock_error = WSAGetLastError ();
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_read: recv=%d %s\n",
+ result,
+ (result == SOCKET_ERROR ? winsock_error_message (winsock_error) : ""));
+
+ if (result == SOCKET_ERROR)
+ {
+ *bytes_read = 0;
+
+ switch (winsock_error)
+ {
+ case WSAEINVAL:
+ error = G_IO_CHANNEL_ERROR_INVAL;
+ break;
+ case WSAEWOULDBLOCK:
+ return G_IO_STATUS_AGAIN;
+ default:
+ error = G_IO_CHANNEL_ERROR_FAILED;
+ break;
+ }
+ g_set_error (err, G_IO_CHANNEL_ERROR, error,
+ winsock_error_message (winsock_error));
+ return G_IO_STATUS_ERROR;
+ }
+ else
+ {
+ *bytes_read = result;
+ if (result == 0)
+ return G_IO_STATUS_EOF;
+ else
+ return G_IO_STATUS_NORMAL;
+ }
+}
+
+static GIOStatus
+g_io_win32_sock_write (GIOChannel *channel,
+ const gchar *buf,
+ gsize count,
+ gsize *bytes_written,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ gint result;
+ GIOChannelError error;
+ int winsock_error;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_write: sockfd=%d count=%d\n",
+ win32_channel->fd, count);
+
+ result = send (win32_channel->fd, buf, count, 0);
+ if (result == SOCKET_ERROR)
+ winsock_error = WSAGetLastError ();
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_write: send=%d %s\n",
+ result,
+ (result == SOCKET_ERROR ? winsock_error_message (winsock_error) : ""));
+
+ if (result == SOCKET_ERROR)
+ {
+ *bytes_written = 0;
+
+ switch (winsock_error)
+ {
+ case WSAEINVAL:
+ error = G_IO_CHANNEL_ERROR_INVAL;
+ break;
+ case WSAEWOULDBLOCK:
+ win32_channel->write_would_have_blocked = TRUE;
+ win32_channel->last_events = 0;
+ return G_IO_STATUS_AGAIN;
+ default:
+ error = G_IO_CHANNEL_ERROR_FAILED;
+ break;
+ }
+ g_set_error (err, G_IO_CHANNEL_ERROR, error,
+ winsock_error_message (winsock_error));
+
+ return G_IO_STATUS_ERROR;
+ }
+ else
+ {
+ *bytes_written = result;
+ win32_channel->write_would_have_blocked = FALSE;
+
+ return G_IO_STATUS_NORMAL;
+ }
+}
+
+static GIOStatus
+g_io_win32_sock_close (GIOChannel *channel,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (win32_channel->fd != -1)
+ {
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_close: closing socket %d\n",
+ win32_channel->fd);
+
+ closesocket (win32_channel->fd);
+ win32_channel->fd = -1;
+ }
+
+ /* FIXME error detection? */
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GSource *
+g_io_win32_sock_create_watch (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
+ GIOWin32Watch *watch = (GIOWin32Watch *)source;
+
+ watch->channel = channel;
+ g_io_channel_ref (channel);
+
+ watch->condition = condition;
+
+ if (win32_channel->event == 0)
+ win32_channel->event = (int) WSACreateEvent ();
+
+ watch->pollfd.fd = win32_channel->event;
+ watch->pollfd.events = condition;
+
+ if (win32_channel->debug)
+ g_print ("g_io_win32_sock_create_watch: sock=%d handle=%#x condition={%s}\n",
+ win32_channel->fd, watch->pollfd.fd,
+ condition_to_string (watch->condition));
+
+ g_source_add_poll (source, &watch->pollfd);
+
+ return source;
+}
+
+GIOChannel *
+g_io_channel_new_file (const gchar *filename,
+ const gchar *mode,
+ GError **error)
+{
+ int fid, flags, pmode;
+ GIOChannel *channel;
+
+ enum { /* Cheesy hack */
+ MODE_R = 1 << 0,
+ MODE_W = 1 << 1,
+ MODE_A = 1 << 2,
+ MODE_PLUS = 1 << 3,
+ } mode_num;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (mode != NULL, NULL);
+ g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);
+
+ switch (mode[0])
+ {
+ case 'r':
+ mode_num = MODE_R;
+ break;
+ case 'w':
+ mode_num = MODE_W;
+ break;
+ case 'a':
+ mode_num = MODE_A;
+ break;
+ default:
+ g_warning ("Invalid GIOFileMode %s.\n", mode);
+ return NULL;
+ }
+
+ switch (mode[1])
+ {
+ case '\0':
+ break;
+ case '+':
+ if (mode[2] == '\0')
+ {
+ mode_num |= MODE_PLUS;
+ break;
+ }
+ /* Fall through */
+ default:
+ g_warning ("Invalid GIOFileMode %s.\n", mode);
+ return NULL;
+ }
+
+ switch (mode_num)
+ {
+ case MODE_R:
+ flags = O_RDONLY;
+ pmode = _S_IREAD;
+ break;
+ case MODE_W:
+ flags = O_WRONLY | O_TRUNC | O_CREAT;
+ pmode = _S_IWRITE;
+ break;
+ case MODE_A:
+ flags = O_WRONLY | O_APPEND | O_CREAT;
+ pmode = _S_IWRITE;
+ break;
+ case MODE_R | MODE_PLUS:
+ flags = O_RDWR;
+ pmode = _S_IREAD | _S_IWRITE;
+ break;
+ case MODE_W | MODE_PLUS:
+ flags = O_RDWR | O_TRUNC | O_CREAT;
+ pmode = _S_IREAD | _S_IWRITE;
+ break;
+ case MODE_A | MODE_PLUS:
+ flags = O_RDWR | O_APPEND | O_CREAT;
+ pmode = _S_IREAD | _S_IWRITE;
+ break;
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+
+ /* always open 'untranslated' */
+ fid = g_open (filename, flags | _O_BINARY, pmode);
+
+ if (g_io_win32_get_debug_flag ())
+ {
+ g_print ("g_io_channel_win32_new_file: open(\"%s\", ", filename);
+ g_win32_print_access_mode (flags|_O_BINARY);
+ g_print (",%#o)=%d\n", pmode, fid);
+ }
+
+ if (fid < 0)
+ {
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ return (GIOChannel *)NULL;
+ }
+
+ channel = g_io_channel_win32_new_fd (fid);
+
+ /* XXX: move this to g_io_channel_win32_new_fd () */
+ channel->close_on_unref = TRUE;
+ channel->is_seekable = TRUE;
+
+ /* g_io_channel_win32_new_fd sets is_readable and is_writeable to
+ * correspond to actual readability/writeability. Set to FALSE those
+ * that mode doesn't allow
+ */
+ switch (mode_num)
+ {
+ case MODE_R:
+ channel->is_writeable = FALSE;
+ break;
+ case MODE_W:
+ case MODE_A:
+ channel->is_readable = FALSE;
+ break;
+ case MODE_R | MODE_PLUS:
+ case MODE_W | MODE_PLUS:
+ case MODE_A | MODE_PLUS:
+ break;
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+
+ return channel;
+}
+
+#ifdef G_OS_WIN32
+
+#undef g_io_channel_new_file
+
+/* Binary compatibility version. Not for newly compiled code. */
+
+GIOChannel *
+g_io_channel_new_file (const gchar *filename,
+ const gchar *mode,
+ GError **error)
+{
+ gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+ GIOChannel *retval;
+
+ if (utf8_filename == NULL)
+ return NULL;
+
+ retval = g_io_channel_new_file_utf8 (utf8_filename, mode, error);
+
+ g_free (utf8_filename);
+
+ return retval;
+}
+
+#endif
+
+static GIOStatus
+g_io_win32_unimpl_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ if (win32_channel->debug)
+ {
+ g_print ("g_io_win32_unimpl_set_flags: ");
+ g_win32_print_gioflags (flags);
+ g_print ("\n");
+ }
+
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ "Not implemented on Win32");
+
+ return G_IO_STATUS_ERROR;
+}
+
+static GIOFlags
+g_io_win32_fd_get_flags_internal (GIOChannel *channel,
+ struct stat *st)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+ gchar c;
+ DWORD count;
+
+ if (st->st_mode & _S_IFIFO)
+ {
+ channel->is_readable =
+ (PeekNamedPipe ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL, NULL) != 0) || GetLastError () == ERROR_BROKEN_PIPE;
+ channel->is_writeable =
+ (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
+ channel->is_seekable = FALSE;
+ }
+ else
+ {
+ channel->is_readable =
+ (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
+ channel->is_writeable =
+ (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
+ channel->is_seekable = TRUE;
+ }
+
+ /* XXX: G_IO_FLAG_APPEND */
+ /* XXX: G_IO_FLAG_NONBLOCK */
+
+ return 0;
+}
+
+static GIOFlags
+g_io_win32_fd_get_flags (GIOChannel *channel)
+{
+ struct stat st;
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ g_return_val_if_fail (win32_channel != NULL, 0);
+ g_return_val_if_fail (win32_channel->type == G_IO_WIN32_FILE_DESC, 0);
+
+ if (0 == fstat (win32_channel->fd, &st))
+ return g_io_win32_fd_get_flags_internal (channel, &st);
+ else
+ return 0;
+}
+
+static GIOFlags
+g_io_win32_console_get_flags_internal (GIOChannel *channel)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+ HANDLE handle = (HANDLE) _get_osfhandle (win32_channel->fd);
+ gchar c;
+ DWORD count;
+ INPUT_RECORD record;
+
+ channel->is_readable = PeekConsoleInput (handle, &record, 1, &count);
+ channel->is_writeable = WriteFile (handle, &c, 0, &count, NULL);
+ channel->is_seekable = FALSE;
+
+ return 0;
+}
+
+static GIOFlags
+g_io_win32_console_get_flags (GIOChannel *channel)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ g_return_val_if_fail (win32_channel != NULL, 0);
+ g_return_val_if_fail (win32_channel->type == G_IO_WIN32_CONSOLE, 0);
+
+ return g_io_win32_console_get_flags_internal (channel);
+}
+
+static GIOFlags
+g_io_win32_msg_get_flags (GIOChannel *channel)
+{
+ return 0;
+}
+
+static GIOStatus
+g_io_win32_sock_set_flags (GIOChannel *channel,
+ GIOFlags flags,
+ GError **err)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+ u_long arg;
+
+ if (win32_channel->debug)
+ {
+ g_print ("g_io_win32_sock_set_flags: ");
+ g_win32_print_gioflags (flags);
+ g_print ("\n");
+ }
+
+ if (flags & G_IO_FLAG_NONBLOCK)
+ {
+ arg = 1;
+ if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ winsock_error_message (WSAGetLastError ()));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+ else
+ {
+ arg = 0;
+ if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ g_set_error (err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED,
+ winsock_error_message (WSAGetLastError ()));
+ return G_IO_STATUS_ERROR;
+ }
+ }
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOFlags
+g_io_win32_sock_get_flags (GIOChannel *channel)
+{
+ /* Could we do something here? */
+ return 0;
+}
+
+static GIOFuncs win32_channel_msg_funcs = {
+ g_io_win32_msg_read,
+ g_io_win32_msg_write,
+ NULL,
+ g_io_win32_msg_close,
+ g_io_win32_msg_create_watch,
+ g_io_win32_free,
+ g_io_win32_unimpl_set_flags,
+ g_io_win32_msg_get_flags,
+};
+
+static GIOFuncs win32_channel_fd_funcs = {
+ g_io_win32_fd_and_console_read,
+ g_io_win32_fd_and_console_write,
+ g_io_win32_fd_seek,
+ g_io_win32_fd_close,
+ g_io_win32_fd_create_watch,
+ g_io_win32_free,
+ g_io_win32_unimpl_set_flags,
+ g_io_win32_fd_get_flags,
+};
+
+static GIOFuncs win32_channel_console_funcs = {
+ g_io_win32_fd_and_console_read,
+ g_io_win32_fd_and_console_write,
+ NULL,
+ g_io_win32_console_close,
+ g_io_win32_console_create_watch,
+ g_io_win32_free,
+ g_io_win32_unimpl_set_flags,
+ g_io_win32_console_get_flags,
+};
+
+static GIOFuncs win32_channel_sock_funcs = {
+ g_io_win32_sock_read,
+ g_io_win32_sock_write,
+ NULL,
+ g_io_win32_sock_close,
+ g_io_win32_sock_create_watch,
+ g_io_win32_free,
+ g_io_win32_sock_set_flags,
+ g_io_win32_sock_get_flags,
+};
+
+GIOChannel *
+g_io_channel_win32_new_messages (guint hwnd)
+{
+ GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
+ GIOChannel *channel = (GIOChannel *)win32_channel;
+
+ g_io_channel_init (channel);
+ g_io_channel_win32_init (win32_channel);
+ if (win32_channel->debug)
+ g_print ("g_io_channel_win32_new_messages: hwnd=%#x\n", hwnd);
+ channel->funcs = &win32_channel_msg_funcs;
+ win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES;
+ win32_channel->hwnd = (HWND) hwnd;
+
+ /* XXX: check this. */
+ channel->is_readable = IsWindow (win32_channel->hwnd);
+ channel->is_writeable = IsWindow (win32_channel->hwnd);
+
+ channel->is_seekable = FALSE;
+
+ return channel;
+}
+
+static GIOChannel *
+g_io_channel_win32_new_fd_internal (gint fd,
+ struct stat *st)
+{
+ GIOWin32Channel *win32_channel;
+ GIOChannel *channel;
+
+ win32_channel = g_new (GIOWin32Channel, 1);
+ channel = (GIOChannel *)win32_channel;
+
+ g_io_channel_init (channel);
+ g_io_channel_win32_init (win32_channel);
+
+ win32_channel->fd = fd;
+
+ if (win32_channel->debug)
+ g_print ("g_io_channel_win32_new_fd: %u\n", fd);
+ if (st->st_mode & _S_IFCHR) /* console */
+ {
+ channel->funcs = &win32_channel_console_funcs;
+ win32_channel->type = G_IO_WIN32_CONSOLE;
+ g_io_win32_console_get_flags_internal (channel);
+ }
+ else
+ {
+ channel->funcs = &win32_channel_fd_funcs;
+ win32_channel->type = G_IO_WIN32_FILE_DESC;
+ g_io_win32_fd_get_flags_internal (channel, st);
+ }
+
+ return channel;
+}
+
+GIOChannel *
+g_io_channel_win32_new_fd (gint fd)
+{
+ struct stat st;
+
+ if (fstat (fd, &st) == -1)
+ {
+ g_warning (G_STRLOC ": %d isn't a C library file descriptor", fd);
+ return NULL;
+ }
+
+ return g_io_channel_win32_new_fd_internal (fd, &st);
+}
+
+gint
+g_io_channel_win32_get_fd (GIOChannel *channel)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ return win32_channel->fd;
+}
+
+GIOChannel *
+g_io_channel_win32_new_socket (int socket)
+{
+ GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
+ GIOChannel *channel = (GIOChannel *)win32_channel;
+
+ g_io_channel_init (channel);
+ g_io_channel_win32_init (win32_channel);
+ if (win32_channel->debug)
+ g_print ("g_io_channel_win32_new_socket: sockfd=%d\n", socket);
+ channel->funcs = &win32_channel_sock_funcs;
+ win32_channel->type = G_IO_WIN32_SOCKET;
+ win32_channel->fd = socket;
+
+ channel->is_readable = TRUE;
+ channel->is_writeable = TRUE;
+ channel->is_seekable = FALSE;
+
+ return channel;
+}
+
+GIOChannel *
+g_io_channel_unix_new (gint fd)
+{
+ gboolean is_fd, is_socket;
+ struct stat st;
+ int optval, optlen;
+
+ is_fd = (fstat (fd, &st) == 0);
+
+ optlen = sizeof (optval);
+ is_socket = (getsockopt (fd, SOL_SOCKET, SO_TYPE, (char *) &optval, &optlen) != SOCKET_ERROR);
+
+ if (is_fd && is_socket)
+ g_warning (G_STRLOC ": %d is both a file descriptor and a socket, file descriptor interpretation assumed.", fd);
+
+ if (is_fd)
+ return g_io_channel_win32_new_fd_internal (fd, &st);
+
+ if (is_socket)
+ return g_io_channel_win32_new_socket(fd);
+
+ g_warning (G_STRLOC ": %d is neither a file descriptor or a socket", fd);
+
+ return NULL;
+}
+
+gint
+g_io_channel_unix_get_fd (GIOChannel *channel)
+{
+ return g_io_channel_win32_get_fd (channel);
+}
+
+void
+g_io_channel_win32_set_debug (GIOChannel *channel,
+ gboolean flag)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ win32_channel->debug = flag;
+}
+
+gint
+g_io_channel_win32_poll (GPollFD *fds,
+ gint n_fds,
+ gint timeout)
+{
+ int result;
+
+ g_return_val_if_fail (n_fds >= 0, 0);
+
+ result = (*g_main_context_get_poll_func (NULL)) (fds, n_fds, timeout);
+
+ return result;
+}
+
+void
+g_io_channel_win32_make_pollfd (GIOChannel *channel,
+ GIOCondition condition,
+ GPollFD *fd)
+{
+ GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+ switch (win32_channel->type)
+ {
+ case G_IO_WIN32_FILE_DESC:
+ if (win32_channel->data_avail_event == NULL)
+ create_events (win32_channel);
+
+ fd->fd = (gint) win32_channel->data_avail_event;
+
+ if (win32_channel->thread_id == 0 && (condition & G_IO_IN))
+ {
+ if (condition & G_IO_IN)
+ create_thread (win32_channel, condition, read_thread);
+ else if (condition & G_IO_OUT)
+ create_thread (win32_channel, condition, write_thread);
+ }
+ break;
+
+ case G_IO_WIN32_CONSOLE:
+ fd->fd = (gint) _get_osfhandle (win32_channel->fd);
+ break;
+
+ case G_IO_WIN32_SOCKET:
+ fd->fd = (int) WSACreateEvent ();
+ break;
+
+ case G_IO_WIN32_WINDOWS_MESSAGES:
+ fd->fd = G_WIN32_MSG_HANDLE;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ abort ();
+ }
+
+ fd->events = condition;
+}
+
+/* Binary compatibility */
+GIOChannel *
+g_io_channel_win32_new_stream_socket (int socket)
+{
+ return g_io_channel_win32_new_socket (socket);
+}
+
+#define __G_IO_WIN32_C__
+#include "galiasdef.c"
diff --git a/clients/Instantbird/gstrcmp0.c b/clients/Instantbird/gstrcmp0.c
new file mode 100755
index 0000000..d21c31a
--- /dev/null
+++ b/clients/Instantbird/gstrcmp0.c
@@ -0,0 +1,22 @@
+/**
+ * g_strcmp0:
+ * @str1: a C string or %NULL
+ * @str2: another C string or %NULL
+ *
+ * Compares @str1 and @str2 like strcmp(). Handles %NULL
+ * gracefully by sorting it before non-%NULL strings.
+ *
+ * Returns: -1, 0 or 1, if @str1 is <, == or > than @str2.
+ *
+ * Since: 2.16
+ */
+int
+g_strcmp0 (const char *str1,
+ const char *str2)
+{
+ if (!str1)
+ return -(str1 != str2);
+ if (!str2)
+ return str1 != str2;
+ return strcmp (str1, str2);
+}
diff --git a/clients/Instantbird/linux/Makefile.in b/clients/Instantbird/linux/Makefile.in
new file mode 100755
index 0000000..2916764
--- /dev/null
+++ b/clients/Instantbird/linux/Makefile.in
@@ -0,0 +1,62 @@
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+PROTOCOL = msn_pecan
+
+include $(srcdir)/../prpl.mk
+
+DEFINES += \
+ -DHAVE_LIBPURPLE \
+ -D_XOPEN_SOURCE \
+ -DINSTANTBIRD \
+ -DPECAN_CVR \
+ -DPECAN_USE_PSM
+
+CSRCS = \
+ giochannel.c giounix.c gstrcmp0.c \
+ msn.c \
+ nexus.c \
+ notification.c \
+ page.c \
+ session.c \
+ switchboard.c \
+ sync.c \
+ pn_log.c \
+ pn_printf.c \
+ pn_util.c \
+ pn_buffer.c \
+ pn_error.c \
+ pn_status.c \
+ pn_oim.c \
+ pn_dp_manager.c \
+ cmd/cmdproc.c \
+ cmd/command.c \
+ cmd/msg.c \
+ cmd/table.c \
+ cmd/transaction.c \
+ io/pn_parser.c \
+ ab/pn_group.c \
+ ab/pn_contact.c \
+ ab/pn_contactlist.c \
+ io/pn_stream.c \
+ io/pn_node.c \
+ io/pn_cmd_server.c \
+ io/pn_http_server.c \
+ io/pn_ssl_conn.c \
+ cvr/pn_peer_call.c \
+ cvr/pn_peer_link.c \
+ cvr/pn_peer_msg.c \
+ cvr/pn_msnobj.c \
+ libpurple/xfer.c \
+ fix_purple.c \
+ $(NULL)
+
+export::
+ mkdir -p cmd
+ mkdir -p io
+ mkdir -p ab
+ mkdir -p cvr
+ mkdir -p libpurple
+
+include $(srcdir)/../prpl-rules.mk
diff --git a/clients/Instantbird/macosx/Makefile.in b/clients/Instantbird/macosx/Makefile.in
new file mode 100755
index 0000000..7f55408
--- /dev/null
+++ b/clients/Instantbird/macosx/Makefile.in
@@ -0,0 +1,63 @@
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+PROTOCOL = msn_pecan
+
+include $(srcdir)/../prpl.mk
+
+DEFINES += \
+ -DHAVE_LIBPURPLE \
+ -D_XOPEN_SOURCE \
+ -DINSTANTBIRD \
+ -DINTERNAL_MAINLOOP \
+ -DPECAN_CVR \
+ -DPECAN_USE_PSM
+
+CSRCS = \
+ giochannel.c giounix.c gstrcmp0.c \
+ msn.c \
+ nexus.c \
+ notification.c \
+ page.c \
+ session.c \
+ switchboard.c \
+ sync.c \
+ pn_log.c \
+ pn_printf.c \
+ pn_util.c \
+ pn_buffer.c \
+ pn_error.c \
+ pn_status.c \
+ pn_oim.c \
+ pn_dp_manager.c \
+ cmd/cmdproc.c \
+ cmd/command.c \
+ cmd/msg.c \
+ cmd/table.c \
+ cmd/transaction.c \
+ io/pn_parser.c \
+ ab/pn_group.c \
+ ab/pn_contact.c \
+ ab/pn_contactlist.c \
+ io/pn_stream.c \
+ io/pn_node.c \
+ io/pn_cmd_server.c \
+ io/pn_http_server.c \
+ io/pn_ssl_conn.c \
+ cvr/pn_peer_call.c \
+ cvr/pn_peer_link.c \
+ cvr/pn_peer_msg.c \
+ cvr/pn_msnobj.c \
+ libpurple/xfer.c \
+ fix_purple.c \
+ $(NULL)
+
+export::
+ mkdir -p cmd
+ mkdir -p io
+ mkdir -p ab
+ mkdir -p cvr
+ mkdir -p libpurple
+
+include $(srcdir)/../prpl-rules.mk
diff --git a/clients/Instantbird/win32/Makefile.in b/clients/Instantbird/win32/Makefile.in
new file mode 100755
index 0000000..11c136c
--- /dev/null
+++ b/clients/Instantbird/win32/Makefile.in
@@ -0,0 +1,63 @@
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+PROTOCOL = msn_pecan
+
+include $(srcdir)/../prpl.mk
+
+DEFINES += \
+ -DHAVE_LIBPURPLE \
+ -D_XOPEN_SOURCE \
+ -DINSTANTBIRD \
+ -DINTERNAL_MAINLOOP \
+ -DPECAN_CVR \
+ -DPECAN_USE_PSM
+
+CSRCS = \
+ giochannel.c giowin32.c gstrcmp0.c \
+ msn.c \
+ nexus.c \
+ notification.c \
+ page.c \
+ session.c \
+ switchboard.c \
+ sync.c \
+ pn_log.c \
+ pn_printf.c \
+ pn_util.c \
+ pn_buffer.c \
+ pn_error.c \
+ pn_status.c \
+ pn_oim.c \
+ pn_dp_manager.c \
+ cmd/cmdproc.c \
+ cmd/command.c \
+ cmd/msg.c \
+ cmd/table.c \
+ cmd/transaction.c \
+ io/pn_parser.c \
+ ab/pn_group.c \
+ ab/pn_contact.c \
+ ab/pn_contactlist.c \
+ io/pn_stream.c \
+ io/pn_node.c \
+ io/pn_cmd_server.c \
+ io/pn_http_server.c \
+ io/pn_ssl_conn.c \
+ cvr/pn_peer_call.c \
+ cvr/pn_peer_link.c \
+ cvr/pn_peer_msg.c \
+ cvr/pn_msnobj.c \
+ libpurple/xfer.c \
+ fix_purple.c \
+ $(NULL)
+
+export::
+ mkdir -p cmd
+ mkdir -p io
+ mkdir -p ab
+ mkdir -p cvr
+ mkdir -p libpurple
+
+include $(srcdir)/../prpl-rules.mk
diff --git a/msn.c b/msn.c
index 9db608c..560da52 100644
--- a/msn.c
+++ b/msn.c
@@ -1922,6 +1922,9 @@ init_plugin (PurplePlugin *plugin)
purple_prefs_remove ("/plugins/prpl/msn");
}
+#ifdef INSTANTBIRD
+PURPLE_INIT_PLUGIN(msn_pecan, init_plugin, info);
+#else
#ifndef STATIC_PECAN
G_MODULE_EXPORT gboolean
purple_init_plugin(PurplePlugin *plugin)
@@ -1941,3 +1944,4 @@ purple_init_msn_pecan_plugin(void)
return purple_plugin_register(plugin);
}
#endif
+#endif /* INSTANTBIRD */
--
1.6.5
* Safari doesn't allow to open file:// links. Now the link is shown, so you
can copy/paste it in Safari.
* Safari doesn't open .swf file directly, so added a workaround for this.
* Added another possible fix to Safari not showing the wink preview.
* Added src attribute to the wink preview image, for Adium compatibility.
* Now the temporary html file has a .html extension.
Signed-off-by: Andrea Piccinelli <fra...@gmail.com>
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
switchboard.c | 42 +++++++++++++++++++++++++++++++++---------
1 files changed, 33 insertions(+), 9 deletions(-)
diff --git a/switchboard.c b/switchboard.c
index 71166a6..77ebab7 100644
--- a/switchboard.c
+++ b/switchboard.c
@@ -1338,29 +1338,53 @@ extract_wink(struct pn_peer_call *call, const guchar *data, gsize size)
emot_name = swf_msg = NULL;
if (swf_path)
{
- if ((f = purple_mkstemp(&html_path, FALSE)))
+ if (purple_mkstemp(&html_path, FALSE))
{
- g_fprintf(f, "<script type='text/javascript'>\n" \
+ char *htmldata, *flashaction, *html_path_full;
+
+ html_path_full = g_strconcat (html_path, ".html", NULL);
+ g_free(html_path);
+ f = fopen (html_path_full, "wb");
+
+#ifndef ADIUM
+ flashaction = g_strdup("location.href = 'file://%s';\n");
+#else
+ flashaction = g_strdup("swfobject.embedSWF('file://%s', 'wink', '400', '300', '9.0.0');\n");
+#endif /* ADIUM */
+
+ htmldata = g_strdup_printf("<script type='text/javascript'>\n" \
SWFOBJECT "\n</script>\n" \
"<script type='text/javascript'>\n" \
"setTimeout('Redirect()',0);\n" \
"function Redirect() {\n" \
- "if (swfobject.hasFlashPlayerVersion('9.0.0')) location.href = 'file://%s';\n" \
+ "if (swfobject.hasFlashPlayerVersion('9.0.0'))\n" \
+ "%s" \
"else document.getElementById('wink').style.visibility = '';\n" \
"}\n" \
"</script>\n" \
+ "<center>\n" \
"<div style='visibility:hidden' id='wink'>\n" \
"<h2>Your browser does not support Shockwave Flash.</h2>\n" \
- "This software is required to play winks.<p><img src='%s'/>\n" \
+ "This software is required to play winks.<p><img src='%%s'/>\n" \
"<a href='http://www.adobe.com/go/getflashplayer'>\n" \
"<img src='http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif' " \
"alt='Get Adobe Flash player' /></a></p>\n" \
- "</div>", swf_path, img_path);
- fclose(f);
+ "</div></center>", flashaction);
+ g_free(flashaction);
+
+ g_fprintf(f, htmldata, swf_path, img_path);
+ fclose(f);
+ g_free(htmldata);
+#ifndef ADIUM
swf_msg = g_strdup_printf(
_("<a href=\"file://%s\">Click here to view the wink in your web browser</a>"),
- html_path);
- g_free(html_path);
+ html_path_full);
+#else
+ swf_msg = g_strdup_printf(
+ _("Copy the following link in your web browser to view it: file://%s"),
+ html_path_full);
+#endif /* ADIUM */
+ g_free(html_path_full);
}
else
{
@@ -1387,7 +1411,7 @@ extract_wink(struct pn_peer_call *call, const guchar *data, gsize size)
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pn_peer_link_get_passport(call->link));
imgid = purple_imgstore_add_with_id(emot, emot_len, NULL);
- emot_name = g_strdup_printf ("<IMG ID='%d'/>", imgid);
+ emot_name = g_strdup_printf (_("<IMG ID=\"%d\" src=\"%s\" alt=\"Wink Preview\">"), imgid, img_path);
}
else
--
1.6.5
diff --git a/switchboard.c b/switchboard.c
index 77ebab7..0a9046d 100644
--- a/switchboard.c
+++ b/switchboard.c
@@ -1186,12 +1186,10 @@ switchboard_show_ink (MsnSwitchBoard *swboard, const char *passport,
#else
if ((f = purple_mkstemp (&file, TRUE)))
{
- const gchar alt_text = _("received handwritten message");
-
fwrite (image_data, image_len, 1, f);
fclose (f);
- image_msg = g_strdup_printf ("<img src=\"file://%s\" alt=\"(%s)\" />", file, alt_text);
+ image_msg = g_strdup_printf ("<img src=\"file://%s\" alt=\"(%s)\" />", file, _("received handwritten message"));
g_free (file);
}
--
1.6.5
diff --git a/switchboard.c b/switchboard.c
index 0a9046d..b06bcc3 100644
--- a/switchboard.c
+++ b/switchboard.c
@@ -1283,151 +1283,157 @@ got_voice_clip(struct pn_peer_call *call, const guchar *data, gsize size)
static gboolean
extract_wink(struct pn_peer_call *call, const guchar *data, gsize size)
{
+ FILE *f;
struct mscab_decompressor *dec;
struct mscabd_cabinet *cab;
- struct mscabd_file *fileincab;
- FILE *f;
- char *msg, *swf_msg, *emot_name, *emot;
- size_t emot_len;
- const gchar *tmpdir;
- char *swf_path, *img_path, *html_path;
- char *path, *craff;
- int imgid;
-
- if (!(f = purple_mkstemp(&path, TRUE)))
+ struct mscabd_file *cab_files;
+ gchar *path, *craff, *swf_path = NULL, *preview_path = NULL, *msg, *swf_msg = NULL, *preview_name = NULL;
+ const gchar *tmp_dir;
+#ifndef ADIUM
+ int preview_id = -1;
+#endif /* !ADIUM */
+
+ if (!(f = purple_mkstemp (&path, TRUE)))
{
- pn_error("Couldn't open temp file for .cab image.\n");
+ pn_error ("wink receiving: couldn't open temp file for .cab image");
return FALSE;
}
- fwrite(data, size, 1, f);
- fclose(f);
+ fwrite (data, size, 1, f);
+ fclose (f);
- if (!(dec = mspack_create_cab_decompressor(NULL)))
+ if (!(dec = mspack_create_cab_decompressor (NULL)))
{
- pn_error("Couldn't create decompressor.\n");
+ pn_error ("wink receiving: could not create cab decompressor.\n");
return FALSE;
}
- if (!(cab = dec->open(dec, path)))
+
+ if (!(cab = dec->open (dec, path)))
{
- pn_error("Couldn't open .cab file.\n");
+ pn_error ("wink receiving: could not open .cab file\n");
return FALSE;
}
- tmpdir = (gchar*)g_get_tmp_dir();
- fileincab = cab->files;
- swf_path = img_path = NULL;
- while (fileincab)
+
+ tmp_dir = g_get_tmp_dir();
+ cab_files = cab->files;
+
+ while (cab_files)
{
- craff = g_build_filename(tmpdir, fileincab->filename, NULL);
- dec->extract(dec, fileincab, craff);
- if (strstr(fileincab->filename, ".swf")) swf_path = craff;
- else if (strstr(fileincab->filename, ".png") || strstr(fileincab->filename, ".jpg") ||
- strstr(fileincab->filename, ".gif"))
- img_path = craff;
- else g_free(craff);
- fileincab = fileincab->next;
- }
- /* don't g_free(tmpdir) - it's just a ref to a global */
- dec->close(dec, cab);
- mspack_destroy_cab_decompressor(dec);
- g_free(path);
+ craff = g_build_filename (tmp_dir, cab_files->filename, NULL);
+ dec->extract (dec, cab_files, craff);
+
+ if (strstr (cab_files->filename, ".swf"))
+ swf_path = craff;
+ else if (strstr (cab_files->filename, ".png") ||
+ strstr (cab_files->filename, ".jpg") ||
+ strstr (cab_files->filename, ".gif"))
+ preview_path = craff;
+ else
+ g_free (craff);
+
+ cab_files = cab_files->next;
+ }
+
+ dec->close (dec, cab);
+ mspack_destroy_cab_decompressor (dec);
+ g_free (path);
- pn_info("swf_path %s\n", swf_path);
- emot_name = swf_msg = NULL;
if (swf_path)
{
- if (purple_mkstemp(&html_path, FALSE))
+ gchar *html_path;
+
+ pn_info ("wink receiving: .swf path: %s", swf_path);
+
+ if (purple_mkstemp (&html_path, FALSE))
{
- char *htmldata, *flashaction, *html_path_full;
+ gchar *tmp = html_path;
+ html_path = g_strconcat (tmp, ".html", NULL);
+ g_free (tmp);
- html_path_full = g_strconcat (html_path, ".html", NULL);
- g_free(html_path);
- f = fopen (html_path_full, "wb");
+ f = fopen (html_path, "wb");
#ifndef ADIUM
- flashaction = g_strdup("location.href = 'file://%s';\n");
+ tmp = g_strdup_printf ("location.href = 'file://%s';\n", swf_path);
#else
- flashaction = g_strdup("swfobject.embedSWF('file://%s', 'wink', '400', '300', '9.0.0');\n");
+ tmp = g_strdup_printf ("swfobject.embedSWF('file://%s', 'wink', '400', '300', '9.0.0');\n", swf_path);
#endif /* ADIUM */
- htmldata = g_strdup_printf("<script type='text/javascript'>\n" \
- SWFOBJECT "\n</script>\n" \
- "<script type='text/javascript'>\n" \
- "setTimeout('Redirect()',0);\n" \
- "function Redirect() {\n" \
- "if (swfobject.hasFlashPlayerVersion('9.0.0'))\n" \
- "%s" \
- "else document.getElementById('wink').style.visibility = '';\n" \
- "}\n" \
- "</script>\n" \
- "<center>\n" \
- "<div style='visibility:hidden' id='wink'>\n" \
- "<h2>Your browser does not support Shockwave Flash.</h2>\n" \
- "This software is required to play winks.<p><img src='%%s'/>\n" \
- "<a href='http://www.adobe.com/go/getflashplayer'>\n" \
- "<img src='http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif' " \
- "alt='Get Adobe Flash player' /></a></p>\n" \
- "</div></center>", flashaction);
- g_free(flashaction);
-
- g_fprintf(f, htmldata, swf_path, img_path);
- fclose(f);
- g_free(htmldata);
+ g_fprintf (f, "<script type='text/javascript'>\n" \
+ SWFOBJECT "\n</script>\n" \
+ "<script type='text/javascript'>\n" \
+ "setTimeout('Redirect()',0);\n" \
+ "function Redirect() {\n" \
+ "if (swfobject.hasFlashPlayerVersion('9.0.0'))\n" \
+ "%s" \
+ "else document.getElementById('wink').style.visibility = '';\n" \
+ "}\n" \
+ "</script>\n" \
+ "<center>\n" \
+ "<div style='visibility:hidden' id='wink'>\n" \
+ "<h2>Your browser does not support Shockwave Flash.</h2>\n" \
+ "This software is required to play winks.<p>%s%s%s\n" \
+ "<a href='http://www.adobe.com/go/getflashplayer'>\n" \
+ "<img src='http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif' " \
+ "alt='Get Adobe Flash player' /></a></p>\n" \
+ "</div></center>",
+ tmp,
+ preview_path ? "<img src='" : "",
+ preview_path ? preview_path : "",
+ preview_path ? "'/>" : "");
+
+ g_free (tmp);
+ fclose (f);
+ }
#ifndef ADIUM
- swf_msg = g_strdup_printf(
- _("<a href=\"file://%s\">Click here to view the wink in your web browser</a>"),
- html_path_full);
+ swf_msg = g_strdup_printf (_("Click <a href=\"file://%s\">here</a> to view received wink in your web browser"),
+ html_path ? html_path : swf_path);
#else
- swf_msg = g_strdup_printf(
- _("Copy the following link in your web browser to view it: file://%s"),
- html_path_full);
+ swf_msg = g_strdup_printf (_("Copy the following link in your web browser to view received wink: file://%s"),
+ html_path ? html_path : swf_path);
#endif /* ADIUM */
- g_free(html_path_full);
- }
- else
- {
- swf_msg = g_strdup_printf(
- _("<a href=\"file://%s\">Click here to view the wink in your web browser</a>"),
- swf_path);
- }
+
+ if (html_path)
+ g_free (html_path);
+ g_free (swf_path);
}
- imgid = 0;
- if (img_path)
+ if (preview_path)
{
- if (g_file_get_contents(img_path, &emot, &emot_len, NULL))
- {
- PurpleConversation *conv;
- MsnSwitchBoard *swboard;
- PurpleAccount *account;
-
- swboard = call->swboard;
- conv = swboard->conv;
- account = msn_session_get_user_data(swboard->session);
-
- if (!conv)
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pn_peer_link_get_passport(call->link));
-
- imgid = purple_imgstore_add_with_id(emot, emot_len, NULL);
- emot_name = g_strdup_printf (_("<IMG ID=\"%d\" src=\"%s\" alt=\"Wink Preview\">"), imgid, img_path);
+#ifndef ADIUM
+ gchar *preview;
+ size_t preview_len;
- }
- else
+ if (g_file_get_contents (preview_path, &preview, &preview_len, NULL))
{
- emot = NULL;
+ preview_id = purple_imgstore_add_with_id (preview, preview_len, NULL);
+ preview_name = g_strdup_printf ("<img id=\"%d\" />", preview_id);
}
+#else
+ preview_name = g_strdup_printf ("<img src=\"file://%s\" alt=\"(%s)\" />", preview_path, _("received wink preview"));
+#endif /* ADIUM */
+
+ g_free (preview_path);
+ }
+
+ if (preview_name)
+ {
+ msg = g_strdup_printf(_("sent a wink:\n%s\n%s"), preview_name, swf_msg);
+ g_free (preview_name);
}
- if (emot_name)
- msg = g_strdup_printf(_("sent a wink:\n%s\n%s"), emot_name, swf_msg);
else
msg = g_strdup_printf(_("sent a wink.\n%s"), swf_msg);
- notify_user (call->swboard->cmdproc, pn_peer_link_get_passport(call->link),
- msg);
- purple_imgstore_unref_by_id (imgid);
- g_free (emot_name);
- /* Blows: probably the smiley code doesn't copy it.. g_free(emot); */
- g_free(msg); g_free(swf_msg); g_free(img_path); g_free(swf_path);
+ g_free (swf_msg);
+
+ notify_user (call->swboard->cmdproc, pn_peer_link_get_passport (call->link), msg);
+
+#ifndef ADIUM
+ if (preview_id != -1)
+ purple_imgstore_unref_by_id (preview_id);
+#endif /* !ADIUM */
+
+ g_free(msg);
+
return TRUE;
}
--
1.6.5
diff --git a/TODO b/TODO
deleted file mode 100644
index b5dc3bc..0000000
--- a/TODO
+++ /dev/null
@@ -1,73 +0,0 @@
-== Miscellaneous ==
-
- * remove sync.c, it only complicates things. Also, there's issues with the
- initial login (new account).
-
-== GObjectification ==
-
-The objective is that all of the important structures in msn-pecan are
-developed using GObject.
-
- * Network I/O
- * Command server (done)
- * HTTP server (done)
- * Notification server
- * Switchboard server
- * Direct Connection (p2p)
- * Nexus (authentication)
-
- * AddressBook
- * Contact
- * Group
- * Contact List
-
- * CVR
-
-== Unit Testing ==
-
-For this better modularization is required so individual modules can tested
-independently. Also, to be able to work without libpurple.
-
- * Modularization
- * Network I/O
- * AddressBook
- * CVR
-
- * Unit Tests
- * Utilities
- * Command parsing
- * Message parsing
- * Transaction handling
- * Network I/O
- * AddressBook
- * CVR
-
-== Utilities ==
-
-=== printf ===
-
-GLib's printf utility is sub-obtimal so a custom one has been developed in
-msn-pecan. It's far from being complete, and probably should be pushed into
-mainline GLib.
-
-So that g_print ("%s", NULL) works on all architectures. Currently that crashes
-in Windows.
-
-== Documentation ==
-
- * Overview, architecture
- * Create design diagrams
- * Use doxygen
- * Add documentation to: http://imfreedom.org/wiki/index.php/MSN
- * Examples of communications (client/server, p2p)
-
-== Other ideas ==
-
- * Create a standalone library.
- * Don't rely on GLib's mainloop. (for Adium X)
- * Use gio
- * Use libsoup
- * Properly use and test pn_buffer.
- * Port offline message support from libgmsn.
- * Allow cancel in authorization dialog.
- * Allow alias from the add buddy dialog.
--
1.6.5
diff --git a/README b/README
index 1e96c90..4540ab8 100644
--- a/README
+++ b/README
@@ -4,28 +4,8 @@ libpurple.
It's based on the code from 2.2.2 but slowly becoming a completely different
code.
-Compared to Pidgin's official MSN plug-in:
-
- * Faster log-in
- * Fewer connection issues
- * Experimental direct connection support (fast file transfers)
- * Server-side storage for display names (private alias)
- * Support for handwritten messages (read-only)
- * Support for voice clips (receive-only)
- * Support for Plus! sounds (receive-only)
- * Option to hide Plus! tags
-
-Other features:
-
- * Support for personal status messages
- * Support for offline messaging (read-only)
- * Send custom emoticons (Pidgin >= 2.5)
-
-Future plans:
-
- * Complete direct connection (fast file-transfers)
- * MSNP15 support
- * Complete GObjectification
+You can find more information at:
+http://code.google.com/p/msn-pecan/
For questions, comments or patches contact:
http://groups.google.com/group/msn-pecan
--
1.6.5
diff --git a/AUTHORS b/AUTHORS
index 29f3747..82fb9fa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,12 +15,3 @@ Original patch to add support for handwritten messages (read-only):
Original patch to add support for voice clips (receive-only):
* Chris Stafford
-
-Translators:
- * Marco de Moulin <ma...@point.nl> (Dutch)
- * Simo Mattila <simo.h....@gmail.com> (Finnish)
- * Alexandre André (SGC.Alex) <sgc....@gmail.com> (French)
- * Devid Antonio Filoni <devi...@gmail.com> (Italian)
- * Jovan Turanjanin <jovan_tu...@yahoo.com> (Serbian)
- * Edgardo Fredz <mede...@gmail.com> (Spanish)
- * Erik Fredriksen <tapi...@gmail.com> (Swedish)
--
1.6.5
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
msn.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/msn.c b/msn.c
index 560da52..3c4114b 100644
--- a/msn.c
+++ b/msn.c
@@ -1762,7 +1762,7 @@ static PurplePluginProtocolInfo prpl_info =
OPT_PROTO_MAIL_CHECK,
NULL, /* user_splits */
NULL, /* protocol_options */
- {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
+ {"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
list_icon, /* list_icon */
list_emblems, /* list_emblems */
status_text, /* status_text */
--
1.6.5
Fixes CVE-2010-0013
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
cvr/pn_peer_msg.c | 43 +++++++++++++++++++++++++++++++++++++++----
1 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/cvr/pn_peer_msg.c b/cvr/pn_peer_msg.c
index 95858b9..4e5a6ba 100644
--- a/cvr/pn_peer_msg.c
+++ b/cvr/pn_peer_msg.c
@@ -434,6 +434,43 @@ set_image(struct pn_peer_msg *peer_msg,
peer_msg->buffer = g_memdup(image->data, peer_msg->size);
}
+#ifdef HAVE_LIBPURPLE
+#if PURPLE_VERSION_CHECK(2,5,0)
+/* XXX: this could be improved if we tracked custom smileys
+ * per-protocol, per-account, per-session or (ideally) per-conversation
+ */
+static PurpleStoredImage *
+find_valid_emoticon(PurpleAccount *account, const char *path)
+{
+ GList *smileys;
+
+ if (!purple_account_get_bool (account, "custom_smileys", TRUE))
+ return NULL;
+
+ smileys = purple_smileys_get_all ();
+
+ for (; smileys; smileys = g_list_delete_link (smileys, smileys)) {
+ PurpleSmiley *smiley;
+ PurpleStoredImage *img;
+
+ smiley = smileys->data;
+ img = purple_smiley_get_stored_image (smiley);
+
+ if (purple_strequal (path, purple_imgstore_get_filename (img))) {
+ g_list_free (smileys);
+ return img;
+ }
+
+ purple_imgstore_unref (img);
+ }
+
+ pn_error ("received illegal request for file %s\n", path);
+
+ return NULL;
+}
+#endif /* PURPLE_VERSION_CHECK(2,5,0) */
+#endif /* HAVE_LIBPURPLE */
+
static void
got_sessionreq(struct pn_peer_call *call,
const char *branch,
@@ -495,13 +532,11 @@ got_sessionreq(struct pn_peer_call *call,
#if PURPLE_VERSION_CHECK(2,5,0)
else if (type == PN_MSNOBJ_EMOTICON) {
PurpleStoredImage *img;
- char *path;
- path = g_build_filename(purple_smileys_get_storing_dir(), pn_msnobj_get_location(obj), NULL);
- img = purple_imgstore_new_from_file(path);
+ MsnSession *session = pn_peer_link_get_session (call->link);
+ img = find_valid_emoticon (msn_session_get_user_data (session), pn_msnobj_get_location (obj));
image = pn_buffer_new_memdup((const gpointer) purple_imgstore_get_data(img),
purple_imgstore_get_size(img));
purple_imgstore_unref(img);
- g_free(path);
}
#endif /* PURPLE_VERSION_CHECK(2,5,0) */
#endif /* HAVE_LIBPURPLE */
--
1.6.5
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
switchboard.c | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/switchboard.c b/switchboard.c
index b06bcc3..bb45a9a 100644
--- a/switchboard.c
+++ b/switchboard.c
@@ -1611,6 +1611,21 @@ emoticon_msg(MsnCmdProc *cmdproc,
swboard = cmdproc->data;
conv = swboard->conv;
+ if (msn_session_find_swboard (session, pn_peer_link_get_passport (link)) != swboard)
+ {
+ if (msn_session_find_swboard (session, pn_peer_link_get_passport (link)))
+ {
+ /*
+ * Apparently we're using a different switchboard now or
+ * something? I don't know if this is normal, but it
+ * definitely happens. So make sure the old switchboard
+ * doesn't still have a reference to us.
+ */
+ g_hash_table_remove (session->conversations, pn_peer_link_get_passport (link));
+ }
+ g_hash_table_insert (session->conversations, g_strdup (pn_peer_link_get_passport (link)), swboard);
+ }
+
/* If the conversation doesn't exist then this is a custom smiley
* used in the first message in a MSN conversation: we need to create
* the conversation now, otherwise the custom smiley won't be shown.
--
1.6.5
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
Makefile | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index ccde133..440ad38 100644
--- a/Makefile
+++ b/Makefile
@@ -145,7 +145,6 @@ endif
sources := $(objects:.o=.c)
deps := $(objects:.o=.d)
-PO_TEMPLATE := po/messages.pot
CATALOGS := ar da de eo es fi fr tr hu it nb nl pt_BR pt sr sv tr zh_CN zh_TW
ifeq ($(PLATFORM),darwin)
@@ -203,7 +202,7 @@ $(plugin): CFLAGS := $(CFLAGS) $(PURPLE_CFLAGS) $(GIO_CFLAGS) $(FALLBACK_CFLAGS)
$(plugin): LIBS := $(plugin_libs)
messages.pot: $(sources)
- $(XGETTEXT) -m -c --keyword --keyword=_ --keyword=N_ -o $@ $<
+ $(XGETTEXT) -m -c --keyword --keyword=_ --keyword=N_ -o $@ $^
%.dylib::
$(QUIET_LINK)$(CC) $(LDFLAGS) -dynamiclib -o $@ $^ $(LIBS)
--
1.6.5
Should have made been committed.
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
cvr/pn_peer_msg.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/cvr/pn_peer_msg.c b/cvr/pn_peer_msg.c
index 4e5a6ba..d485b3a 100644
--- a/cvr/pn_peer_msg.c
+++ b/cvr/pn_peer_msg.c
@@ -316,7 +316,6 @@ got_transresp(struct pn_peer_call *call,
direct_conn->nonce = g_strdup(nonce);
for (c = list; c; c = c->next) {
- pn_test("adding host = %s", (char *) c->data);
pn_direct_conn_add_addr(direct_conn, c->data);
g_free(c->data);
}
--
1.6.5
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
pn_oim.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/pn_oim.c b/pn_oim.c
index ab87d1d..5c996be 100644
--- a/pn_oim.c
+++ b/pn_oim.c
@@ -576,6 +576,8 @@ process_body_receive (OimRequest *oim_request,
gchar *end;
cur += 2;
end = strstr (cur, "\r\n\r\n");
+ if (!end)
+ end = strstr (cur, "</GetMessageResult>");
if (end)
*end = '\0';
message = (gchar *) purple_base64_decode (cur, NULL);
--
1.6.5
Seems to cause memory corruption on ARM platform.
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
pn_util.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/pn_util.c b/pn_util.c
index 10eb774..30c59d4 100644
--- a/pn_util.c
+++ b/pn_util.c
@@ -948,7 +948,7 @@ pn_rand_guid(void)
time_t
pn_parse_date(const char *str)
{
- gchar month[3], *months[13] = {
+ gchar month[4], *months[13] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
int d, m, y, hour, min, sec, tz;
--
1.6.5
Signed-off-by: Devid Antonio Filoni <devi...@gmail.com>
---
ab/pn_contact.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++
ab/pn_contact.h | 6 ++++
ab/pn_contact_priv.h | 1 +
msn.c | 6 ++++
pn_global.h | 5 +++
switchboard.c | 20 +++++++++++--
6 files changed, 112 insertions(+), 3 deletions(-)
diff --git a/ab/pn_contact.c b/ab/pn_contact.c
index fa8d885..e6d835b 100644
--- a/ab/pn_contact.c
+++ b/ab/pn_contact.c
@@ -17,12 +17,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "pn_global.h"
+
#include "pn_contact.h"
#include "pn_contact_priv.h"
#include "pn_contactlist.h"
#include "pn_contactlist_priv.h"
#include "pn_group.h"
#include "pn_log.h"
+#include "pn_locale.h"
#include "pn_util.h"
#include "pn_dp_manager.h" /* for pn_dp_manager_contact_set_object */
@@ -201,6 +204,65 @@ pn_contact_set_passport (struct pn_contact *contact,
}
}
+
+static void
+set_client_name (struct pn_contact *contact)
+{
+#if defined(PECAN_CVR)
+#ifdef HAVE_LIBPURPLE
+ if (!contact->client_name)
+ {
+ struct pn_msnobj *obj = pn_contact_get_object (contact);
+
+ if (obj)
+ {
+ const gchar *location;
+
+ location = pn_msnobj_get_location (obj);
+
+ if (strcmp (location, "amsn.tmp") == 0)
+ contact->client_name = g_strdup_printf ("aMSN (%s)", _("unknown version"));
+ else if (strcmp (location, "KMess.tmp") == 0)
+ contact->client_name = g_strdup_printf ("KMess (%s)", _("unknown version"));
+ else if (strcmp (location, "Mercury.tmp") == 0)
+ contact->client_name = g_strdup_printf ("Mercury (%s)", _("unknown version"));
+ else if (strcmp (location, "kopete.tmp") == 0)
+ contact->client_name = g_strdup_printf ("Kopete (%s)", _("unknown version"));
+ else if (strcmp (location, "TFR2C2.tmp") == 0)
+ contact->client_name = g_strdup_printf ("libpurple (%s)", _("unknown version"));
+ else
+ {
+ gulong client_id = pn_contact_get_client_id (contact);
+
+ if (client_id & PN_CLIENT_VER_5_0)
+ contact->client_name = g_strdup ("MSN Messenger 5.0");
+ if (client_id & PN_CLIENT_VER_6_0)
+ contact->client_name = g_strdup ("MSN Messenger 6.0");
+ if (client_id & PN_CLIENT_VER_6_1)
+ contact->client_name = g_strdup ("MSN Messenger 6.1");
+ if (client_id & PN_CLIENT_VER_6_2)
+ contact->client_name = g_strdup ("MSN Messenger 6.2");
+ if (client_id & PN_CLIENT_VER_7_0)
+ contact->client_name = g_strdup ("MSN Messenger 7.0");
+ if (client_id & PN_CLIENT_VER_7_5)
+ contact->client_name = g_strdup ("MSN Messenger 7.5");
+ if (client_id & PN_CLIENT_VER_8_0)
+ contact->client_name = g_strdup ("Windows Live Messenger 8.0");
+ if (client_id & PN_CLIENT_VER_8_1)
+ contact->client_name = g_strdup ("Windows Live Messenger 8.1");
+ if (client_id & PN_CLIENT_VER_8_5)
+ contact->client_name = g_strdup ("Windows Live Messenger 2008");
+ if (client_id & PN_CLIENT_VER_9_BETA)
+ contact->client_name = g_strdup ("Windows Live Messenger 2009 (beta)");
+ if (client_id & PN_CLIENT_VER_9_0)
+ contact->client_name = g_strdup ("Windows Live Messenger 2009");
+ }
+ }
+ }
+#endif /* HAVE_LIBPURPLE */
+#endif /* defined(PECAN_CVR) */
+}
+
void
pn_contact_set_client_id (struct pn_contact *contact,
gulong client_id)
@@ -604,6 +666,8 @@ pn_contact_set_object (struct pn_contact *contact,
old_obj = contact->msnobj;
contact->msnobj = obj;
+ set_client_name (contact);
+
if (!pn_msnobj_equal(old_obj, obj)) {
gboolean prioritize;
@@ -641,6 +705,19 @@ pn_contact_update_object (struct pn_contact *contact)
}
#endif /* defined(PECAN_CVR) */
+void
+pn_contact_set_client_name (struct pn_contact *contact,
+ const gchar *client_name)
+{
+ contact->client_name = g_strdup (client_name);
+}
+
+const gchar *
+pn_contact_get_client_name (struct pn_contact *contact)
+{
+ return contact->client_name;
+}
+
const gchar *
pn_contact_get_passport (const struct pn_contact *contact)
{
diff --git a/ab/pn_contact.h b/ab/pn_contact.h
index d37dc97..94c9ce4 100644
--- a/ab/pn_contact.h
+++ b/ab/pn_contact.h
@@ -312,4 +312,10 @@ gboolean pn_contact_is_blocked (const struct pn_contact *contact);
gboolean pn_contact_can_receive (const struct pn_contact *contact);
+
+const gchar *pn_contact_get_client_name (struct pn_contact *contact);
+
+void pn_contact_set_client_name (struct pn_contact *contact,
+ const gchar *client_name);
+
#endif /* PN_CONTACT_H */
diff --git a/ab/pn_contact_priv.h b/ab/pn_contact_priv.h
index 5d2f468..c4187f7 100644
--- a/ab/pn_contact_priv.h
+++ b/ab/pn_contact_priv.h
@@ -59,6 +59,7 @@ struct pn_contact {
gchar *store_name; /**< The name stored in the server. */
gchar *friendly_name; /**< The friendly name. */
gchar *personal_message; /**< The personal message. */
+ gchar *client_name; /**< The client name/version. */
CurrentMedia media; /**< The current media. */
gchar *guid; /**< The GUID. Only present for contacts in our FL. */
diff --git a/msn.c b/msn.c
index 3c4114b..e83aaeb 100644
--- a/msn.c
+++ b/msn.c
@@ -715,6 +715,12 @@ tooltip_text (PurpleBuddy *buddy,
}
}
+ if (pn_contact_get_client_name (user))
+ {
+ purple_notify_user_info_add_pair (user_info, _("Client"),
+ pn_contact_get_client_name (user));
+ }
+
purple_notify_user_info_add_pair (user_info, _("Has you"),
((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No")));
}
diff --git a/pn_global.h b/pn_global.h
index f1a84a8..c98e977 100644
--- a/pn_global.h
+++ b/pn_global.h
@@ -57,6 +57,11 @@ typedef enum {
PN_CLIENT_VER_6_2 = 0x30,
PN_CLIENT_VER_7_0 = 0x40,
PN_CLIENT_VER_7_5 = 0x50,
+ PN_CLIENT_VER_8_0 = 0x60,
+ PN_CLIENT_VER_8_1 = 0x70,
+ PN_CLIENT_VER_8_5 = 0x80,
+ PN_CLIENT_VER_9_BETA = 0x90,
+ PN_CLIENT_VER_9_0 = 0xa0,
} PnClientVerId;
#endif /* PN_GLOBAL_H */
diff --git a/switchboard.c b/switchboard.c
index bb45a9a..710cb3b 100644
--- a/switchboard.c
+++ b/switchboard.c
@@ -1045,12 +1045,26 @@ plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
return;
}
-#if 0
if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
{
- pn_debug ("user-agent=[%s]", value);
+ struct pn_contact *contact;
+
+ /* pn_debug ("user-agent=[%s]", value); */
+
+ contact = pn_contactlist_find_contact (cmdproc->session->contactlist, passport);
+
+ if (contact)
+ {
+ gchar *client_name = g_strdup (value), *space;
+
+ if ((space = strstr (client_name, "/")))
+ space[0] = ' ';
+
+ pn_contact_set_client_name (contact, client_name);
+
+ g_free (client_name);
+ }
}
-#endif
if ((value = msn_message_get_attr(msg, "P4-Context")) != NULL)
{
--
1.6.5
diff --git a/clients/Adium/Makefile.am b/clients/Adium/Makefile.am
index e2c10d4..96b25ab 100755
--- a/clients/Adium/Makefile.am
+++ b/clients/Adium/Makefile.am
@@ -77,6 +77,7 @@ AM_CPPFLAGS = \
-DMSN_DIRECTCONN \
-DPECAN_USE_PSM \
-DPECAN_LIBSIREN \
+ -DPECAN_LIBMSPACK \
-DRECEIVE_PLUS_SOUNDS \
-DGETTEXT_PACKAGE='"pidgin"' \
-DENABLE_NLS \
--
1.6.5
I don't think this patch needs any reviewing since you are the Adium
maintainer... you can just send a pull request.
Also, it seems you forgot a cover letter to describe your changes:
http://code.google.com/p/msn-pecan/wiki/HowToContribute
Anyway, will pickup this one. Thanks.
--
Felipe Contreras
Let's remember this is mostly a hack. Also, have you tried to play
with different values? 2 seconds, half a second, etc.
Otherwise looks good. Thanks!
--
Felipe Contreras
I guess the motivation behind this is that Instantbird also requires
an underscore. If so, I wonder why. Perhaps it's somehow related to
the way libpurple handles static plugins, and if that's the case, then
we can use the STATIC_PECAN instead.
--
Felipe Contreras
Ok.
--
Felipe Contreras
This patch is *way* too big to be sent by email, and besides, there's
no point in reviewing most of this; we are only interested in the
changes in msn.c.
Also, GIOChannel has been part of GLib since forever, it seems Florian
decided to strip it from his Instantbird distribution which seems like
a *huge* hack to me.
This is not acceptable for the master branch, however, we can have a
special branch for instantbird. And I hope instantbird changes this.
--
Felipe Contreras
All these Adium specific changes are starting to look a bit ugly, but
it's ok for now I guess. Will apply this. Thanks!
--
Felipe Contreras
Actually, strike my previous comment, this patch is not ok... see below:
Shouldn't this text be translated?
> + g_free(flashaction);
> +
> + g_fprintf(f, htmldata, swf_path, img_path);
> + fclose(f);
> + g_free(htmldata);
> +#ifndef ADIUM
> swf_msg = g_strdup_printf(
> _("<a href=\"file://%s\">Click here to view the wink in your web browser</a>"),
> - html_path);
> - g_free(html_path);
> + html_path_full);
> +#else
> + swf_msg = g_strdup_printf(
> + _("Copy the following link in your web browser to view it: file://%s"),
> + html_path_full);
That's a change in the strings, and we are now in a string freeze.
> +#endif /* ADIUM */
> + g_free(html_path_full);
> }
> else
> {
> @@ -1387,7 +1411,7 @@ extract_wink(struct pn_peer_call *call, const guchar *data, gsize size)
> conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, pn_peer_link_get_passport(call->link));
>
> imgid = purple_imgstore_add_with_id(emot, emot_len, NULL);
> - emot_name = g_strdup_printf ("<IMG ID='%d'/>", imgid);
> + emot_name = g_strdup_printf (_("<IMG ID=\"%d\" src=\"%s\" alt=\"Wink Preview\">"), imgid, img_path);
And another string change.
IMO the best way to go forward is split the patch in two:
1) changes that don't modify strings
2) changes that modify strings
We can apply 1) into 'master', but 2) would have to wait after 0.1.
--
Felipe Contreras
Strictly speaking this patch is not only fixing the crash, but also
reorganizing some code. This is not that bad (although it should be
mentioned in the commit message), but I wonder if changing this to
'const gchar *' would also get rid of the crash.
Otherwise looks good. Thanks.
--
Felipe Contreras
Too many changes; this patch is very hard to review. At least it
should be split into one that changes the code-style, and another one
that reorganizes the code.
Although msn-pecan's code-style hasn't been clarified, I'm leaning
towards a style similar to the kernel (see pn_timer.h). This is not
set in stone but I would hold on the code-style cleaning patches for
now.
--
Felipe Contreras
Yeah, but the one on the wiki is higher level (and also not updated).
I should update both instead.
--
Felipe Contreras
Makes sense. I would consider keeping some translators in that list if
they are actively communicating, but it seems they are just generic
launchpad translators.
--
Felipe Contreras
Makes sense. Although we should keep the description for tarball
releases and github, and that description should be updated.
Still, I'll pick this one.
--
Felipe Contreras
Looks ok to me, but you might want to remove Elliott from the cc list
before sending this kind of patches (unless he is ok with it).
Thanks.
--
Felipe Contreras
Hell yeah!
> +static PurpleStoredImage *
> +find_valid_emoticon(PurpleAccount *account, const char *path)
> +{
> + GList *smileys;
> +
> + if (!purple_account_get_bool (account, "custom_smileys", TRUE))
> + return NULL;
> +
> + smileys = purple_smileys_get_all ();
> +
> + for (; smileys; smileys = g_list_delete_link (smileys, smileys)) {
This is a bit inefficient, I would prefer to replace that
g_list_delete_link with l->next and free the list at the end.
> + PurpleSmiley *smiley;
> + PurpleStoredImage *img;
> +
> + smiley = smileys->data;
> + img = purple_smiley_get_stored_image (smiley);
> +
> + if (purple_strequal (path, purple_imgstore_get_filename (img))) {
> + g_list_free (smileys);
> + return img;
> + }
> +
> + purple_imgstore_unref (img);
> + }
> +
> + pn_error ("received illegal request for file %s\n", path);
> +
> + return NULL;
> +}
> +#endif /* PURPLE_VERSION_CHECK(2,5,0) */
> +#endif /* HAVE_LIBPURPLE */
Still, I think this function is too inefficient, it's probably faster
and equally safe to just check if the path is inside
purple_smileys_get_storing_dir().
> static void
> got_sessionreq(struct pn_peer_call *call,
> const char *branch,
> @@ -495,13 +532,11 @@ got_sessionreq(struct pn_peer_call *call,
> #if PURPLE_VERSION_CHECK(2,5,0)
> else if (type == PN_MSNOBJ_EMOTICON) {
> PurpleStoredImage *img;
> - char *path;
> - path = g_build_filename(purple_smileys_get_storing_dir(), pn_msnobj_get_location(obj), NULL);
> - img = purple_imgstore_new_from_file(path);
> + MsnSession *session = pn_peer_link_get_session (call->link);
> + img = find_valid_emoticon (msn_session_get_user_data (session), pn_msnobj_get_location (obj));
You should check if img is NULL before using it.
> image = pn_buffer_new_memdup((const gpointer) purple_imgstore_get_data(img),
> purple_imgstore_get_size(img));
> purple_imgstore_unref(img);
> - g_free(path);
> }
> #endif /* PURPLE_VERSION_CHECK(2,5,0) */
> #endif /* HAVE_LIBPURPLE */
> --
Please give priority to this patch. I can make the changes if you
wish; up to you.
And again, consult with Elliott if he wants to be notified of these reviews.
Cheers.
--
Felipe Contreras
I'm not entirely happy with this pach. Can you try running with
valgrind to see if there are no problems?
We definitely should pick this one for 0.1, but please check that before.
--
Felipe Contreras
This is already on master.
--
Felipe Contreras
Also on master, please don't send these patches.
--
Felipe Contreras
Unnecessary extra space.
> +static void
> +set_client_name (struct pn_contact *contact)
> +{
> +#if defined(PECAN_CVR)
> +#ifdef HAVE_LIBPURPLE
> + if (!contact->client_name)
> + {
No need for this extra indentation:
if (!contact->client_name)
return;
> + struct pn_msnobj *obj = pn_contact_get_object (contact);
> +
> + if (obj)
> + {
No need for this indentation either.
> + const gchar *location;
> +
> + location = pn_msnobj_get_location (obj);
> +
> + if (strcmp (location, "amsn.tmp") == 0)
> + contact->client_name = g_strdup_printf ("aMSN (%s)", _("unknown version"));
> + else if (strcmp (location, "KMess.tmp") == 0)
> + contact->client_name = g_strdup_printf ("KMess (%s)", _("unknown version"));
> + else if (strcmp (location, "Mercury.tmp") == 0)
> + contact->client_name = g_strdup_printf ("Mercury (%s)", _("unknown version"));
> + else if (strcmp (location, "kopete.tmp") == 0)
> + contact->client_name = g_strdup_printf ("Kopete (%s)", _("unknown version"));
> + else if (strcmp (location, "TFR2C2.tmp") == 0)
> + contact->client_name = g_strdup_printf ("libpurple (%s)", _("unknown version"));
> + else
> + {
No need for this indentation:
if (!contact->client_name)
return;
Also, I wonder if TFR2C2 is only used in libpurple; AFAICR it was
taken from some reverse engineering, so maybe old MSN clients use it.
> + gulong client_id = pn_contact_get_client_id (contact);
> +
if (!client_id)
return;
This way we can avoid checking unnecessarily.
> + if (client_id & PN_CLIENT_VER_5_0)
> + contact->client_name = g_strdup ("MSN Messenger 5.0");
> + if (client_id & PN_CLIENT_VER_6_0)
> + contact->client_name = g_strdup ("MSN Messenger 6.0");
> + if (client_id & PN_CLIENT_VER_6_1)
> + contact->client_name = g_strdup ("MSN Messenger 6.1");
> + if (client_id & PN_CLIENT_VER_6_2)
> + contact->client_name = g_strdup ("MSN Messenger 6.2");
> + if (client_id & PN_CLIENT_VER_7_0)
> + contact->client_name = g_strdup ("MSN Messenger 7.0");
> + if (client_id & PN_CLIENT_VER_7_5)
> + contact->client_name = g_strdup ("MSN Messenger 7.5");
> + if (client_id & PN_CLIENT_VER_8_0)
> + contact->client_name = g_strdup ("Windows Live Messenger 8.0");
> + if (client_id & PN_CLIENT_VER_8_1)
> + contact->client_name = g_strdup ("Windows Live Messenger 8.1");
> + if (client_id & PN_CLIENT_VER_8_5)
> + contact->client_name = g_strdup ("Windows Live Messenger 2008");
> + if (client_id & PN_CLIENT_VER_9_BETA)
> + contact->client_name = g_strdup ("Windows Live Messenger 2009 (beta)");
> + if (client_id & PN_CLIENT_VER_9_0)
> + contact->client_name = g_strdup ("Windows Live Messenger 2009");
All these seem like a fallback if everything else fails but it would
only happen after receiving the first display picture.
Instead, I think it makes more sense to check these while generating
the tooltip.
> + }
> + }
> + }
> +#endif /* HAVE_LIBPURPLE */
> +#endif /* defined(PECAN_CVR) */
> +}
> +
> void
> pn_contact_set_client_id (struct pn_contact *contact,
> gulong client_id)
> @@ -604,6 +666,8 @@ pn_contact_set_object (struct pn_contact *contact,
> old_obj = contact->msnobj;
> contact->msnobj = obj;
>
> + set_client_name (contact);
You are not really setting anything; perhaps update_client_name()
makes more sense.
> +
> if (!pn_msnobj_equal(old_obj, obj)) {
> gboolean prioritize;
>
> @@ -641,6 +705,19 @@ pn_contact_update_object (struct pn_contact *contact)
> }
> #endif /* defined(PECAN_CVR) */
>
> +void
> +pn_contact_set_client_name (struct pn_contact *contact,
> + const gchar *client_name)
> +{
What happens if two set_client_name() are issued? I think the old one
should be freed, or we should just return.
No need to comment, remove completely.
> + contact = pn_contactlist_find_contact (cmdproc->session->contactlist, passport);
> +
> + if (contact)
> + {
> + gchar *client_name = g_strdup (value), *space;
> +
> + if ((space = strstr (client_name, "/")))
> + space[0] = ' ';
> +
What happens if the user-agent is malformed and there's no "/"?
I think we shouldn't set the client name at all. Therefore the
previous debug still make sense.
And it doesn't make sense to strdup the string in that case, so:
end = strchr(value, '/');
if (end) {
client_name = g_strndup(value, end - value);
...
g_free(client_name);
}
> + pn_contact_set_client_name (contact, client_name);
> +
> + g_free (client_name);
> + }
> }
> -#endif
>
> if ((value = msn_message_get_attr(msg, "P4-Context")) != NULL)
> {
> --
> 1.6.5
This patch would be really useful :) Thanks!
--
Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
cvr/pn_peer_msg.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/cvr/pn_peer_msg.c b/cvr/pn_peer_msg.c
index b830049..a6d4c8b 100644
--- a/cvr/pn_peer_msg.c
+++ b/cvr/pn_peer_msg.c
@@ -498,12 +498,14 @@ got_sessionreq(struct pn_peer_call *call,
#if PURPLE_VERSION_CHECK(2,5,0)
else if (type == PN_MSNOBJ_EMOTICON) {
PurpleStoredImage *img;
- char *path;
- path = g_build_filename(purple_smileys_get_storing_dir(), pn_msnobj_get_location(obj), NULL);
+ char *path, *loc;
+ loc = g_path_get_basename(pn_msnobj_get_location(obj));
+ path = g_build_filename(purple_smileys_get_storing_dir(), loc, NULL);
img = purple_imgstore_new_from_file(path);
image = pn_buffer_new_memdup((const gpointer) purple_imgstore_get_data(img),
purple_imgstore_get_size(img));
purple_imgstore_unref(img);
+ g_free(loc);
g_free(path);
}
#endif /* PURPLE_VERSION_CHECK(2,5,0) */
--
1.7.0
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
cvr/pn_peer_msg.c | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/cvr/pn_peer_msg.c b/cvr/pn_peer_msg.c
index a6d4c8b..e6eaa84 100644
--- a/cvr/pn_peer_msg.c
+++ b/cvr/pn_peer_msg.c
@@ -502,9 +502,11 @@ got_sessionreq(struct pn_peer_call *call,
loc = g_path_get_basename(pn_msnobj_get_location(obj));
path = g_build_filename(purple_smileys_get_storing_dir(), loc, NULL);
img = purple_imgstore_new_from_file(path);
- image = pn_buffer_new_memdup((const gpointer) purple_imgstore_get_data(img),
- purple_imgstore_get_size(img));
- purple_imgstore_unref(img);
+ if (img) {
+ image = pn_buffer_new_memdup((const gpointer) purple_imgstore_get_data(img),
+ purple_imgstore_get_size(img));
+ purple_imgstore_unref(img);
+ }
g_free(loc);
g_free(path);
}
--
1.7.0