gnome-control-center/panels/printers/pp-host.c
Marek Kasik 142d2c65b4 printers: Add PpHost object for listing print devices
This commit adds PpHost object which represents a remote
host from which we want to get printers. It contains
asynchronous method for enumerating printers list from the host
using CUPS' SNMP backend and method for enumerating printers list
directly from the remote CUPS server running on the host. (#683229)
2012-09-04 14:09:05 +02:00

459 lines
13 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2012 Red Hat, Inc,
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Marek Kasik <mkasik@redhat.com>
*/
#include "pp-host.h"
struct _PpHostPrivate
{
gchar *hostname;
gint port;
};
G_DEFINE_TYPE (PpHost, pp_host, G_TYPE_OBJECT);
enum {
PROP_0 = 0,
PROP_HOSTNAME,
PROP_PORT,
};
static void
pp_host_finalize (GObject *object)
{
PpHostPrivate *priv;
priv = PP_HOST (object)->priv;
g_clear_pointer (&priv->hostname, g_free);
G_OBJECT_CLASS (pp_host_parent_class)->finalize (object);
}
static void
pp_host_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *param_spec)
{
PpHost *self;
self = PP_HOST (object);
switch (prop_id)
{
case PROP_HOSTNAME:
g_value_set_string (value, self->priv->hostname);
break;
case PROP_PORT:
g_value_set_int (value, self->priv->port);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
prop_id,
param_spec);
break;
}
}
static void
pp_host_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *param_spec)
{
PpHost *self = PP_HOST (object);
switch (prop_id)
{
case PROP_HOSTNAME:
g_free (self->priv->hostname);
self->priv->hostname = g_value_dup_string (value);
break;
case PROP_PORT:
self->priv->port = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
prop_id,
param_spec);
break;
}
}
static void
pp_host_class_init (PpHostClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (PpHostPrivate));
gobject_class->set_property = pp_host_set_property;
gobject_class->get_property = pp_host_get_property;
gobject_class->finalize = pp_host_finalize;
g_object_class_install_property (gobject_class, PROP_HOSTNAME,
g_param_spec_string ("hostname",
"Hostname",
"The hostname",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_PORT,
g_param_spec_int ("port",
"Port",
"The port",
0, G_MAXINT32, 631,
G_PARAM_READWRITE));
}
static void
pp_host_init (PpHost *host)
{
host->priv = G_TYPE_INSTANCE_GET_PRIVATE (host,
PP_TYPE_HOST,
PpHostPrivate);
}
PpHost *
pp_host_new (const gchar *hostname,
gint port)
{
return g_object_new (PP_TYPE_HOST,
"hostname", hostname,
"port", port,
NULL);
}
typedef struct
{
PpDevicesList *devices;
} GSDData;
static gchar **
line_split (gchar *line)
{
gboolean escaped = FALSE;
gboolean quoted = FALSE;
gboolean in_word = FALSE;
gchar **words = NULL;
gchar **result = NULL;
gchar *buffer = NULL;
gchar ch;
gint n = 0;
gint i, j = 0, k = 0;
if (line)
{
n = strlen (line);
words = g_new0 (gchar *, n + 1);
buffer = g_new0 (gchar, n + 1);
for (i = 0; i < n; i++)
{
ch = line[i];
if (escaped)
{
buffer[k++] = ch;
escaped = FALSE;
continue;
}
if (ch == '\\')
{
in_word = TRUE;
escaped = TRUE;
continue;
}
if (in_word)
{
if (quoted)
{
if (ch == '"')
quoted = FALSE;
else
buffer[k++] = ch;
}
else if (g_ascii_isspace (ch))
{
words[j++] = g_strdup (buffer);
memset (buffer, 0, n + 1);
k = 0;
in_word = FALSE;
}
else if (ch == '"')
quoted = TRUE;
else
buffer[k++] = ch;
}
else
{
if (ch == '"')
{
in_word = TRUE;
quoted = TRUE;
}
else if (!g_ascii_isspace (ch))
{
in_word = TRUE;
buffer[k++] = ch;
}
}
}
}
if (buffer && buffer[0] != '\0')
words[j++] = g_strdup (buffer);
result = g_strdupv (words);
g_strfreev (words);
g_free (buffer);
return result;
}
static void
_pp_host_get_snmp_devices_thread (GSimpleAsyncResult *res,
GObject *object,
GCancellable *cancellable)
{
PpHost *host = (PpHost *) object;
PpHostPrivate *priv = host->priv;
PpPrintDevice *device;
GSDData *data;
GError *error;
gchar **argv;
gchar *stdout_string = NULL;
gchar *stderr_string = NULL;
gint exit_status;
data = g_simple_async_result_get_op_res_gpointer (res);
data->devices = g_new0 (PpDevicesList, 1);
data->devices->devices = NULL;
argv = g_new0 (gchar *, 3);
argv[0] = g_strdup ("/usr/lib/cups/backend/snmp");
argv[1] = g_strdup (priv->hostname);
/* Use SNMP to get printer's informations */
g_spawn_sync (NULL,
argv,
NULL,
0,
NULL,
NULL,
&stdout_string,
&stderr_string,
&exit_status,
&error);
g_free (argv[1]);
g_free (argv[0]);
g_free (argv);
if (exit_status == 0 && stdout_string)
{
gchar **printer_informations = NULL;
gint length;
printer_informations = line_split (stdout_string);
length = g_strv_length (printer_informations);
if (length >= 4)
{
device = g_new0 (PpPrintDevice, 1);
device->device_class = g_strdup (printer_informations[0]);
device->device_uri = g_strdup (printer_informations[1]);
device->device_make_and_model = g_strdup (printer_informations[2]);
device->device_info = g_strdup (printer_informations[3]);
device->device_name = g_strdup (printer_informations[3]);
device->device_name =
g_strcanon (device->device_name, ALLOWED_CHARACTERS, '-');
device->acquisition_method = ACQUISITION_METHOD_SNMP;
if (length >= 5 && printer_informations[4][0] != '\0')
device->device_id = g_strdup (printer_informations[4]);
if (length >= 6 && printer_informations[5][0] != '\0')
device->device_location = g_strdup (printer_informations[5]);
data->devices->devices = g_list_append (data->devices->devices, device);
}
g_strfreev (printer_informations);
g_free (stdout_string);
}
}
static void
gsd_data_free (GSDData *data)
{
GList *iter;
if (data)
{
if (data->devices)
{
if (data->devices->devices)
{
for (iter = data->devices->devices; iter; iter = iter->next)
pp_print_device_free ((PpPrintDevice *) iter->data);
g_list_free (data->devices->devices);
}
g_free (data->devices);
}
g_free (data);
}
}
void
pp_host_get_snmp_devices_async (PpHost *host,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
GSDData *data;
res = g_simple_async_result_new (G_OBJECT (host), callback, user_data, pp_host_get_snmp_devices_async);
data = g_new0 (GSDData, 1);
data->devices = NULL;
g_simple_async_result_set_check_cancellable (res, cancellable);
g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify) gsd_data_free);
g_simple_async_result_run_in_thread (res, _pp_host_get_snmp_devices_thread, 0, cancellable);
g_object_unref (res);
}
PpDevicesList *
pp_host_get_snmp_devices_finish (PpHost *host,
GAsyncResult *res,
GError **error)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
GSDData *data;
PpDevicesList *result;
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == pp_host_get_snmp_devices_async);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
data = g_simple_async_result_get_op_res_gpointer (simple);
result = data->devices;
data->devices = NULL;
return result;
}
static void
_pp_host_get_remote_cups_devices_thread (GSimpleAsyncResult *res,
GObject *object,
GCancellable *cancellable)
{
cups_dest_t *dests = NULL;
GSDData *data;
PpHost *host = (PpHost *) object;
PpHostPrivate *priv = host->priv;
PpPrintDevice *device;
http_t *http;
gint num_of_devices = 0;
gint i;
data = g_simple_async_result_get_op_res_gpointer (res);
data->devices = g_new0 (PpDevicesList, 1);
data->devices->devices = NULL;
/* Connect to remote CUPS server and get its devices */
http = httpConnect (priv->hostname, priv->port);
if (http)
{
num_of_devices = cupsGetDests2 (http, &dests);
if (num_of_devices > 0)
{
for (i = 0; i < num_of_devices; i++)
{
device = g_new0 (PpPrintDevice, 1);
device->device_class = g_strdup ("network");
device->device_uri = g_strdup_printf ("ipp://%s:%d/printers/%s",
priv->hostname,
priv->port,
dests[i].name);
device->device_name = g_strdup (dests[i].name);
device->device_location = g_strdup (cupsGetOption ("printer-location",
dests[i].num_options,
dests[i].options));
device->host_name = g_strdup (priv->hostname);
device->host_port = priv->port;
device->acquisition_method = ACQUISITION_METHOD_REMOTE_CUPS_SERVER;
data->devices->devices = g_list_append (data->devices->devices, device);
}
}
httpClose (http);
}
}
void
pp_host_get_remote_cups_devices_async (PpHost *host,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
GSDData *data;
res = g_simple_async_result_new (G_OBJECT (host), callback, user_data, pp_host_get_remote_cups_devices_async);
data = g_new0 (GSDData, 1);
data->devices = NULL;
g_simple_async_result_set_check_cancellable (res, cancellable);
g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify) gsd_data_free);
g_simple_async_result_run_in_thread (res, _pp_host_get_remote_cups_devices_thread, 0, cancellable);
g_object_unref (res);
}
PpDevicesList *
pp_host_get_remote_cups_devices_finish (PpHost *host,
GAsyncResult *res,
GError **error)
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
GSDData *data;
PpDevicesList *result;
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == pp_host_get_remote_cups_devices_async);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
data = g_simple_async_result_get_op_res_gpointer (simple);
result = data->devices;
data->devices = NULL;
return result;
}