summaryrefslogtreecommitdiffstats
path: root/plug-ins/script-fu/script-fu-server.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/script-fu/script-fu-server.c1113
1 files changed, 1113 insertions, 0 deletions
diff --git a/plug-ins/script-fu/script-fu-server.c b/plug-ins/script-fu/script-fu-server.c
new file mode 100644
index 0000000..74270bf
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-server.c
@@ -0,0 +1,1113 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0502
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+typedef short sa_family_t; /* Not defined by winsock */
+
+#ifndef AI_ADDRCONFIG
+/* Missing from mingw headers, but value is publicly documented
+ * on http://msdn.microsoft.com/en-us/library/ms737530%28v=VS.85%29.aspx
+ */
+#define AI_ADDRCONFIG 0x0400
+#endif
+#include <libgimpbase/gimpwin32-io.h>
+#else
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+#endif
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "script-fu-intl.h"
+
+#include "scheme-wrapper.h"
+#include "script-fu-server.h"
+
+#ifdef G_OS_WIN32
+#define CLOSESOCKET(fd) closesocket(fd)
+#else
+#define CLOSESOCKET(fd) close(fd)
+#endif
+
+#define COMMAND_HEADER 3
+#define RESPONSE_HEADER 4
+#define MAGIC 'G'
+
+#ifndef HAVE_DIFFTIME
+#define difftime(a,b) (((gdouble)(a)) - ((gdouble)(b)))
+#endif
+
+#ifndef NO_FD_SET
+# define SELECT_MASK fd_set
+#else
+# ifndef _AIX
+ typedef long fd_mask;
+# endif
+# if defined(_IBMR2)
+# define SELECT_MASK void
+# else
+# define SELECT_MASK int
+# endif
+#endif
+
+
+/* image information */
+
+/* Header format for incoming commands...
+ * bytes: 1 2 3
+ * MAGIC CMD_LEN_H CMD_LEN_L
+ */
+
+/* Header format for outgoing responses...
+ * bytes: 1 2 3 4
+ * MAGIC ERROR? RSP_LEN_H RSP_LEN_L
+ */
+
+#define MAGIC_BYTE 0
+
+#define CMD_LEN_H_BYTE 1
+#define CMD_LEN_L_BYTE 2
+
+#define ERROR_BYTE 1
+#define RSP_LEN_H_BYTE 2
+#define RSP_LEN_L_BYTE 3
+
+/*
+ * Local Types
+ */
+
+typedef struct
+{
+ gchar *command;
+ gint filedes;
+ gint request_no;
+} SFCommand;
+
+typedef struct
+{
+ GtkWidget *ip_entry;
+ GtkWidget *port_entry;
+ GtkWidget *log_entry;
+
+ gchar *listen_ip;
+ gint port;
+ gchar *logfile;
+
+ gboolean run;
+} ServerInterface;
+
+typedef union
+{
+ sa_family_t family;
+ struct sockaddr_storage ss;
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+ struct sockaddr_in6 sa_in6;
+} sa_union;
+
+/*
+ * Local Functions
+ */
+
+static void server_start (const gchar *listen_ip,
+ gint port,
+ const gchar *logfile);
+static gboolean execute_command (SFCommand *cmd);
+static gint read_from_client (gint filedes);
+static gint make_socket (const struct addrinfo
+ *ai);
+static void server_log (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
+static void server_quit (void);
+
+static gboolean server_interface (void);
+static void response_callback (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void print_socket_api_error (const gchar *api_name);
+
+/*
+ * Local variables
+ */
+static gint server_socks[2],
+ server_socks_used = 0;
+static const gint server_socks_len = sizeof (server_socks) /
+ sizeof (server_socks[0]);
+static GList *command_queue = NULL;
+static gint queue_length = 0;
+static gint request_no = 0;
+static FILE *server_log_file = NULL;
+static GHashTable *clients = NULL;
+static gboolean script_fu_done = FALSE;
+static gboolean server_mode = FALSE;
+
+static ServerInterface sint =
+{
+ NULL, /* port entry widget */
+ NULL, /* log entry widget */
+ NULL, /* ip entry widget */
+
+ NULL, /* ip to bind to */
+ 10008, /* default port number */
+ NULL, /* use stdout */
+
+ FALSE /* run */
+};
+
+/*
+ * Server interface functions
+ */
+
+void
+script_fu_server_quit (void)
+{
+ script_fu_done = TRUE;
+}
+
+gint
+script_fu_server_get_mode (void)
+{
+ return server_mode;
+}
+
+void
+script_fu_server_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+
+ run_mode = params[0].data.d_int32;
+
+ ts_set_run_mode (run_mode);
+ ts_set_print_flag (1);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (server_interface ())
+ {
+ server_mode = TRUE;
+
+ /* Start the server */
+ server_start (sint.listen_ip, sint.port, sint.logfile);
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Set server_mode to TRUE */
+ server_mode = TRUE;
+
+ /* Start the server */
+ server_start ((params[1].data.d_string &&
+ strlen (params[1].data.d_string)) ?
+ params[1].data.d_string : "127.0.0.1",
+ params[2].data.d_int32,
+ params[3].data.d_string);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ status = GIMP_PDB_CALLING_ERROR;
+ g_warning ("Script-Fu server does not handle \"GIMP_RUN_WITH_LAST_VALS\"");
+
+ default:
+ break;
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static void
+script_fu_server_add_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ FD_SET (GPOINTER_TO_INT (key), (SELECT_MASK *) data);
+}
+
+static gboolean
+script_fu_server_read_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gint fd = GPOINTER_TO_INT (key);
+
+ if (FD_ISSET (fd, (SELECT_MASK *) data))
+ {
+ if (read_from_client (fd) < 0)
+ {
+ GList *list;
+
+ server_log ("Server: disconnect from host %s.\n", (gchar *) value);
+
+ CLOSESOCKET (fd);
+
+ /* Invalidate the file descriptor for pending commands
+ from the disconnected client. */
+ for (list = command_queue; list; list = list->next)
+ {
+ SFCommand *cmd = (SFCommand *) command_queue->data;
+
+ if (cmd->filedes == fd)
+ cmd->filedes = -1;
+ }
+
+ return TRUE; /* remove this client from the hash table */
+ }
+ }
+
+ return FALSE;
+}
+
+void
+script_fu_server_listen (gint timeout)
+{
+ struct timeval tv;
+ struct timeval *tvp = NULL;
+ SELECT_MASK fds;
+ gint sockno;
+
+ /* Set time struct */
+ if (timeout)
+ {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = timeout % 1000;
+ tvp = &tv;
+ }
+
+ FD_ZERO (&fds);
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ FD_SET (server_socks[sockno], &fds);
+ }
+ g_hash_table_foreach (clients, script_fu_server_add_fd, &fds);
+
+ /* Block until input arrives on one or more active sockets
+ or timeout occurs. */
+
+ if (select (FD_SETSIZE, &fds, NULL, NULL, tvp) < 0)
+ {
+ print_socket_api_error ("select");
+ return;
+ }
+
+ /* Service the server sockets if any has input pending. */
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ sa_union client;
+ gchar clientname[NI_MAXHOST];
+
+ /* Connection request on original socket. */
+ socklen_t size = sizeof (client);
+ gint new;
+ guint portno;
+
+ if (! FD_ISSET (server_socks[sockno], &fds))
+ {
+ continue;
+ }
+
+ new = accept (server_socks[sockno], &(client.sa), &size);
+
+ if (new < 0)
+ {
+ print_socket_api_error ("accept");
+ return;
+ }
+
+ /* Associate the client address with the socket */
+
+ /* If all else fails ... */
+ strncpy (clientname, "(error during host address lookup)", NI_MAXHOST-1);
+
+ /* Lookup address */
+ (void) getnameinfo (&(client.sa), size, clientname, sizeof (clientname),
+ NULL, 0, NI_NUMERICHOST);
+
+ g_hash_table_insert (clients, GINT_TO_POINTER (new),
+ g_strdup (clientname));
+
+ /* Determine port number */
+ switch (client.family)
+ {
+ case AF_INET:
+ portno = (guint) g_ntohs (client.sa_in.sin_port);
+ break;
+ case AF_INET6:
+ portno = (guint) g_ntohs (client.sa_in6.sin6_port);
+ break;
+ default:
+ portno = 0;
+ }
+
+ server_log ("Server: connect from host %s, port %d.\n",
+ clientname, portno);
+ }
+
+ /* Service the client sockets. */
+ g_hash_table_foreach_remove (clients, script_fu_server_read_fd, &fds);
+}
+
+static void
+server_progress_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_end (gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_set_text (const gchar *message,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_set_value (gdouble percentage,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+
+/*
+ * Suppress progress popups by installing progress handlers that do nothing.
+ */
+static const gchar *
+server_progress_install (void)
+{
+ GimpProgressVtable vtable = { 0, };
+
+ vtable.start = server_progress_start;
+ vtable.end = server_progress_end;
+ vtable.set_text = server_progress_set_text;
+ vtable.set_value = server_progress_set_value;
+
+ return gimp_progress_install_vtable (&vtable, NULL);
+}
+
+static void
+server_progress_uninstall (const gchar *progress)
+{
+ gimp_progress_uninstall (progress);
+}
+
+static void
+server_start (const gchar *listen_ip,
+ gint port,
+ const gchar *logfile)
+{
+ struct addrinfo *ai;
+ struct addrinfo *ai_curr;
+ struct addrinfo hints;
+ gint e;
+ gint sockno;
+ gchar *port_s;
+ const gchar *progress;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ port_s = g_strdup_printf ("%d", port);
+ e = getaddrinfo (listen_ip, port_s, &hints, &ai);
+ g_free (port_s);
+
+ if (e != 0)
+ {
+ g_printerr ("getaddrinfo: %s\n", gai_strerror (e));
+ return;
+ }
+
+ for (ai_curr = ai, sockno = 0;
+ ai_curr != NULL && sockno < server_socks_len;
+ ai_curr = ai_curr->ai_next, sockno++)
+ {
+ /* Create the socket and set it up to accept connections. */
+ /* This may fail if there's a server running on this port already. */
+ server_socks[sockno] = make_socket (ai_curr);
+
+ if (listen (server_socks[sockno], 5) < 0)
+ {
+ print_socket_api_error ("listen");
+ freeaddrinfo (ai);
+ return;
+ }
+ }
+
+ server_socks_used = sockno;
+
+ /* Setup up the server log file */
+ if (logfile && *logfile)
+ server_log_file = g_fopen (logfile, "a");
+ else
+ server_log_file = NULL;
+
+ if (! server_log_file)
+ server_log_file = stdout;
+
+ /* Set up the clientname hash table */
+ clients = g_hash_table_new_full (g_direct_hash, NULL,
+ NULL, (GDestroyNotify) g_free);
+
+ progress = server_progress_install ();
+
+ server_log ("Script-Fu server initialized and listening...\n");
+
+ /* Loop until the server is finished */
+ while (! script_fu_done)
+ {
+ script_fu_server_listen (0);
+
+ while (command_queue)
+ {
+ SFCommand *cmd = (SFCommand *) command_queue->data;
+
+ /* Process the command */
+ execute_command (cmd);
+
+ /* Remove the command from the list */
+ command_queue = g_list_remove (command_queue, cmd);
+ queue_length--;
+
+ /* Free the request */
+ g_free (cmd->command);
+ g_free (cmd);
+ }
+ }
+
+ server_progress_uninstall (progress);
+
+ freeaddrinfo (ai);
+ server_quit ();
+}
+
+static gboolean
+execute_command (SFCommand *cmd)
+{
+ guchar buffer[RESPONSE_HEADER];
+ GString *response;
+ time_t clocknow;
+ gboolean error;
+ gint i;
+ gdouble total_time;
+ GTimer *timer;
+
+ server_log ("Processing request #%d\n", cmd->request_no);
+ timer = g_timer_new ();
+
+ response = g_string_new (NULL);
+ ts_register_output_func (ts_gstring_output_func, response);
+
+ /* run the command */
+ if (ts_interpret_string (cmd->command) != 0)
+ {
+ error = TRUE;
+
+ server_log ("%s\n", response->str);
+ }
+ else
+ {
+ error = FALSE;
+
+ if (response->len == 0)
+ g_string_assign (response, ts_get_success_msg ());
+
+ total_time = g_timer_elapsed (timer, NULL);
+ time (&clocknow);
+ server_log ("Request #%d processed in %.3f seconds, finishing on %s",
+ cmd->request_no, total_time, ctime (&clocknow));
+ }
+ g_timer_destroy (timer);
+
+ buffer[MAGIC_BYTE] = MAGIC;
+ buffer[ERROR_BYTE] = error ? TRUE : FALSE;
+ buffer[RSP_LEN_H_BYTE] = (guchar) (response->len >> 8);
+ buffer[RSP_LEN_L_BYTE] = (guchar) (response->len & 0xFF);
+
+ /* Write the response to the client */
+ for (i = 0; i < RESPONSE_HEADER; i++)
+ if (cmd->filedes > 0 && send (cmd->filedes, (const void *) (buffer + i), 1, 0) < 0)
+ {
+ /* Write error */
+ print_socket_api_error ("send");
+ return FALSE;
+ }
+
+ for (i = 0; i < response->len; i++)
+ if (cmd->filedes > 0 && send (cmd->filedes, response->str + i, 1, 0) < 0)
+ {
+ /* Write error */
+ print_socket_api_error ("send");
+ return FALSE;
+ }
+
+ g_string_free (response, TRUE);
+
+ return FALSE;
+}
+
+static gint
+read_from_client (gint filedes)
+{
+ SFCommand *cmd;
+ guchar buffer[COMMAND_HEADER];
+ gchar *command;
+ gchar *clientaddr;
+ time_t clock;
+ gint command_len;
+ gint nbytes;
+ gint i;
+
+ for (i = 0; i < COMMAND_HEADER;)
+ {
+ nbytes = recv (filedes, (void *) (buffer + i), COMMAND_HEADER - i, 0);
+
+ if (nbytes < 0)
+ {
+#ifndef G_OS_WIN32
+ if (errno == EINTR)
+ continue;
+#endif
+ server_log ("Error reading command header.\n");
+ return -1;
+ }
+
+ if (nbytes == 0)
+ return -1; /* EOF */
+
+ i += nbytes;
+ }
+
+ if (buffer[MAGIC_BYTE] != MAGIC)
+ {
+ server_log ("Error in script-fu command transmission.\n");
+ return -1;
+ }
+
+ command_len = (buffer [CMD_LEN_H_BYTE] << 8) | buffer [CMD_LEN_L_BYTE];
+ command = g_new (gchar, command_len + 1);
+
+ for (i = 0; i < command_len;)
+ {
+ nbytes = recv (filedes, command + i, command_len - i, 0);
+
+ if (nbytes <= 0)
+ {
+#ifndef G_OS_WIN32
+ if (nbytes < 0 && errno == EINTR)
+ continue;
+#endif
+ server_log ("Error reading command. Read %d out of %d bytes.\n",
+ i, command_len);
+ g_free (command);
+ return -1;
+ }
+
+ i += nbytes;
+ }
+
+ command[command_len] = '\0';
+ cmd = g_new (SFCommand, 1);
+
+ cmd->filedes = filedes;
+ cmd->command = command;
+ cmd->request_no = request_no ++;
+
+ /* Add the command to the queue */
+ command_queue = g_list_append (command_queue, cmd);
+ queue_length ++;
+
+ /* Get the client address from the address/socket table */
+ clientaddr = g_hash_table_lookup (clients, GINT_TO_POINTER (cmd->filedes));
+ time (&clock);
+ server_log ("Received request #%d from IP address %s: %s on %s,"
+ "[Request queue length: %d]",
+ cmd->request_no,
+ clientaddr ? clientaddr : "<invalid>",
+ cmd->command, ctime (&clock), queue_length);
+
+ return 0;
+}
+
+static gint
+make_socket (const struct addrinfo *ai)
+{
+ gint sock;
+ gint v = 1;
+
+ /* Win32 needs the winsock library initialized. */
+#ifdef G_OS_WIN32
+ static gboolean winsock_initialized = FALSE;
+
+ if (! winsock_initialized)
+ {
+ WORD wVersionRequested = MAKEWORD (2, 2);
+ WSADATA wsaData;
+
+ if (WSAStartup (wVersionRequested, &wsaData) == 0)
+ {
+ winsock_initialized = TRUE;
+ }
+ else
+ {
+ print_socket_api_error ("WSAStartup");
+ gimp_quit ();
+ }
+ }
+#endif
+
+ /* Create the socket. */
+ sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0)
+ {
+ print_socket_api_error ("socket");
+ gimp_quit ();
+ }
+
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &v, sizeof(v));
+
+#ifdef IPV6_V6ONLY
+ /* Only listen on IPv6 addresses, otherwise bind() will fail. */
+ if (ai->ai_family == AF_INET6)
+ {
+ v = 1;
+ if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &v, sizeof(v)) < 0)
+ {
+ print_socket_api_error ("setsockopt");
+ gimp_quit();
+ }
+ }
+#endif
+
+ if (bind (sock, ai->ai_addr, ai->ai_addrlen) < 0)
+ {
+ print_socket_api_error ("bind");
+ gimp_quit ();
+ }
+
+ return sock;
+}
+
+static void
+server_log (const gchar *format,
+ ...)
+{
+ va_list args;
+ gchar *buf;
+
+ va_start (args, format);
+ buf = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ fputs (buf, server_log_file);
+ g_free (buf);
+
+ if (server_log_file != stdout)
+ fflush (server_log_file);
+}
+
+static void
+script_fu_server_shutdown_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ shutdown (GPOINTER_TO_INT (key), 2);
+}
+
+static void
+server_quit (void)
+{
+ gint sockno;
+
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ CLOSESOCKET (server_socks[sockno]);
+ }
+
+ if (clients)
+ {
+ g_hash_table_foreach (clients, script_fu_server_shutdown_fd, NULL);
+ g_hash_table_destroy (clients);
+ clients = NULL;
+ }
+
+ while (command_queue)
+ {
+ SFCommand *cmd = command_queue->data;
+
+ g_free (cmd->command);
+ g_free (cmd);
+ }
+
+ g_list_free (command_queue);
+ command_queue = NULL;
+ queue_length = 0;
+
+ /* Close the server log file */
+ if (server_log_file != stdout)
+ fclose (server_log_file);
+
+ server_log_file = NULL;
+}
+
+static gboolean
+server_interface (void)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *label;
+
+ INIT_I18N();
+
+ gimp_ui_init ("script-fu", FALSE);
+
+ dlg = gimp_dialog_new (_("Script-Fu Server Options"), "gimp-script-fu",
+ NULL, 0,
+ gimp_standard_help_func, "plug-in-script-fu-server",
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Start Server"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (response_callback),
+ NULL);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* The table to hold port, logfile and listen-to entries */
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* The server ip to listen to */
+ sint.ip_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (sint.ip_entry), "127.0.0.1");
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Listen on IP:"), 0.0, 0.5,
+ sint.ip_entry, 1, FALSE);
+
+ /* The server port */
+ sint.port_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (sint.port_entry), "10008");
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Server port:"), 0.0, 0.5,
+ sint.port_entry, 1, FALSE);
+
+ /* The server logfile */
+ sint.log_entry = gtk_entry_new ();
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Server logfile:"), 0.0, 0.5,
+ sint.log_entry, 1, FALSE);
+
+ /* Warning */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_WARNING,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
+ gtk_widget_show (image);
+
+ label = gtk_label_new (_("Listening on an IP address other than "
+ "127.0.0.1 (especially 0.0.0.0) can allow "
+ "attackers to remotely execute arbitrary code "
+ "on this machine."));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dlg);
+
+ gtk_main ();
+
+ return sint.run;
+}
+
+static void
+response_callback (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ g_free (sint.logfile);
+ g_free (sint.listen_ip);
+
+ sint.port = atoi (gtk_entry_get_text (GTK_ENTRY (sint.port_entry)));
+ sint.logfile = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.log_entry)));
+ sint.listen_ip = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.ip_entry)));
+ sint.run = TRUE;
+ }
+
+ gtk_widget_destroy (widget);
+}
+
+static void
+print_socket_api_error (const gchar *api_name)
+{
+#ifdef G_OS_WIN32
+ /* Yes, this functionality really belongs to GLib. */
+ const gchar *emsg;
+ gchar unk[100];
+ int number = WSAGetLastError ();
+
+ switch (number)
+ {
+ case WSAEINTR:
+ emsg = "Interrupted function call";
+ break;
+ case WSAEACCES:
+ emsg = "Permission denied";
+ break;
+ case WSAEFAULT:
+ emsg = "Bad address";
+ break;
+ case WSAEINVAL:
+ emsg = "Invalid argument";
+ break;
+ case WSAEMFILE:
+ emsg = "Too many open sockets";
+ break;
+ case WSAEWOULDBLOCK:
+ emsg = "Resource temporarily unavailable";
+ break;
+ case WSAEINPROGRESS:
+ emsg = "Operation now in progress";
+ break;
+ case WSAEALREADY:
+ emsg = "Operation already in progress";
+ break;
+ case WSAENOTSOCK:
+ emsg = "Socket operation on nonsocket";
+ break;
+ case WSAEDESTADDRREQ:
+ emsg = "Destination address required";
+ break;
+ case WSAEMSGSIZE:
+ emsg = "Message too long";
+ break;
+ case WSAEPROTOTYPE:
+ emsg = "Protocol wrong type for socket";
+ break;
+ case WSAENOPROTOOPT:
+ emsg = "Bad protocol option";
+ break;
+ case WSAEPROTONOSUPPORT:
+ emsg = "Protocol not supported";
+ break;
+ case WSAESOCKTNOSUPPORT:
+ emsg = "Socket type not supported";
+ break;
+ case WSAEOPNOTSUPP:
+ emsg = "Operation not supported on transport endpoint";
+ break;
+ case WSAEPFNOSUPPORT:
+ emsg = "Protocol family not supported";
+ break;
+ case WSAEAFNOSUPPORT:
+ emsg = "Address family not supported by protocol family";
+ break;
+ case WSAEADDRINUSE:
+ emsg = "Address already in use";
+ break;
+ case WSAEADDRNOTAVAIL:
+ emsg = "Address not available";
+ break;
+ case WSAENETDOWN:
+ emsg = "Network interface is not configured";
+ break;
+ case WSAENETUNREACH:
+ emsg = "Network is unreachable";
+ break;
+ case WSAENETRESET:
+ emsg = "Network dropped connection on reset";
+ break;
+ case WSAECONNABORTED:
+ emsg = "Software caused connection abort";
+ break;
+ case WSAECONNRESET:
+ emsg = "Connection reset by peer";
+ break;
+ case WSAENOBUFS:
+ emsg = "No buffer space available";
+ break;
+ case WSAEISCONN:
+ emsg = "Socket is already connected";
+ break;
+ case WSAENOTCONN:
+ emsg = "Socket is not connected";
+ break;
+ case WSAESHUTDOWN:
+ emsg = "Can't send after socket shutdown";
+ break;
+ case WSAETIMEDOUT:
+ emsg = "Connection timed out";
+ break;
+ case WSAECONNREFUSED:
+ emsg = "Connection refused";
+ break;
+ case WSAEHOSTDOWN:
+ emsg = "Host is down";
+ break;
+ case WSAEHOSTUNREACH:
+ emsg = "Host is unreachable";
+ break;
+ case WSAEPROCLIM:
+ emsg = "Too many processes";
+ break;
+ case WSASYSNOTREADY:
+ emsg = "Network subsystem is unavailable";
+ break;
+ case WSAVERNOTSUPPORTED:
+ emsg = "Winsock.dll version out of range";
+ break;
+ case WSANOTINITIALISED:
+ emsg = "Successful WSAStartup not yet performed";
+ break;
+ case WSAEDISCON:
+ emsg = "Graceful shutdown in progress";
+ break;
+ case WSATYPE_NOT_FOUND:
+ emsg = "Class type not found";
+ break;
+ case WSAHOST_NOT_FOUND:
+ emsg = "Host not found";
+ break;
+ case WSATRY_AGAIN:
+ emsg = "Nonauthoritative host not found";
+ break;
+ case WSANO_RECOVERY:
+ emsg = "This is a nonrecoverable error";
+ break;
+ case WSANO_DATA:
+ emsg = "Valid name, no data record of requested type";
+ break;
+ case WSA_INVALID_HANDLE:
+ emsg = "Specified event object handle is invalid";
+ break;
+ case WSA_INVALID_PARAMETER:
+ emsg = "One or more parameters are invalid";
+ break;
+ case WSA_IO_INCOMPLETE:
+ emsg = "Overlapped I/O event object not in signaled state";
+ break;
+ case WSA_NOT_ENOUGH_MEMORY:
+ emsg = "Insufficient memory available";
+ break;
+ case WSA_OPERATION_ABORTED:
+ emsg = "Overlapped operation aborted";
+ break;
+ case WSAEINVALIDPROCTABLE:
+ emsg = "Invalid procedure table from service provider";
+ break;
+ case WSAEINVALIDPROVIDER:
+ emsg = "Invalid service provider version number";
+ break;
+ case WSAEPROVIDERFAILEDINIT:
+ emsg = "Unable to initialize a service provider";
+ break;
+ case WSASYSCALLFAILURE:
+ emsg = "System call failure";
+ break;
+ default:
+ sprintf (unk, "Unknown WinSock error %d", number);
+ emsg = unk;
+ break;
+ }
+
+ g_printerr ("%s failed: %s\n", api_name, emsg);
+#else
+ perror (api_name);
+#endif
+}