gnome-control-center/panels/printers/pp-utils.c
Philip Chimento 389ee0dfe5 printers: Use CUPS httpConnect2() if available
In CUPS 1.7 httpConnect() and httpConnectEncrypt() were deprecated and
replaced with httpConnect2(). This checks if httpConnect2() is available
and if so, replaces the uses of the deprecated functions.

In the CUPS source code, httpConnect() and httpConnectEncrypt() are now
wrappers around httpConnect2(), so we make sure to use the same
arguments as in the CUPS source code so the two code paths are sure to
be identical:
2c030c7a06/cups/http.c (L412)
2c030c7a06/cups/http.c (L477)
2019-09-16 14:09:40 -07:00

3772 lines
108 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2009-2010 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 2 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <cups/cups.h>
#include <cups/ppd.h>
#include "pp-utils.h"
#define DBUS_TIMEOUT 120000
#define DBUS_TIMEOUT_LONG 600000
#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
#define HAVE_CUPS_1_6 1
#endif
#ifndef HAVE_CUPS_1_6
#define ippGetCount(attr) attr->num_values
#define ippGetGroupTag(attr) attr->group_tag
#define ippGetValueTag(attr) attr->value_tag
#define ippGetName(attr) attr->name
#define ippGetStatusCode(ipp) ipp->request.status.status_code
#define ippGetInteger(attr, element) attr->values[element].integer
#define ippGetString(attr, element, language) attr->values[element].string.text
#define ippGetBoolean(attr, element) attr->values[element].boolean
static int
ippGetRange (ipp_attribute_t *attr,
int element,
int *upper)
{
*upper = attr->values[element].range.upper;
return (attr->values[element].range.lower);
}
static ipp_attribute_t *
ippFirstAttribute (ipp_t *ipp)
{
if (!ipp)
return (NULL);
return (ipp->current = ipp->attrs);
}
static ipp_attribute_t *
ippNextAttribute (ipp_t *ipp)
{
if (!ipp || !ipp->current)
return (NULL);
return (ipp->current = ipp->current->next);
}
#endif
#if (CUPS_VERSION_MAJOR == 1) && (CUPS_VERSION_MINOR <= 6)
#define HTTP_URI_STATUS_OK HTTP_URI_OK
#endif
gchar *
get_tag_value (const gchar *tag_string, const gchar *tag_name)
{
gchar **tag_string_splitted = NULL;
gchar *tag_value = NULL;
gint tag_name_length;
gint i;
if (tag_string && tag_name)
{
tag_name_length = strlen (tag_name);
tag_string_splitted = g_strsplit (tag_string, ";", 0);
if (tag_string_splitted)
{
for (i = 0; i < g_strv_length (tag_string_splitted); i++)
if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0)
if (strlen (tag_string_splitted[i]) > tag_name_length + 1)
tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1);
g_strfreev (tag_string_splitted);
}
}
return tag_value;
}
/*
* Normalize given string so that it is lowercase, doesn't
* have trailing or leading whitespaces and digits doesn't
* neighbour with alphabetic.
* (see cupshelpers/ppds.py from system-config-printer)
*/
static gchar *
normalize (const gchar *input_string)
{
gchar *result = NULL;
gint i, j = 0, k = -1;
if (input_string)
{
g_autofree gchar *tmp = g_strstrip (g_ascii_strdown (input_string, -1));
if (tmp)
{
g_autofree gchar *res = g_new (gchar, 2 * strlen (tmp));
for (i = 0; i < strlen (tmp); i++)
{
if ((g_ascii_isalpha (tmp[i]) && k >= 0 && g_ascii_isdigit (res[k])) ||
(g_ascii_isdigit (tmp[i]) && k >= 0 && g_ascii_isalpha (res[k])))
{
res[j] = ' ';
k = j++;
res[j] = tmp[i];
k = j++;
}
else
{
if (g_ascii_isspace (tmp[i]) || !g_ascii_isalnum (tmp[i]))
{
if (!(k >= 0 && res[k] == ' '))
{
res[j] = ' ';
k = j++;
}
}
else
{
res[j] = tmp[i];
k = j++;
}
}
}
res[j] = '\0';
result = g_strdup (res);
}
}
return result;
}
char *
get_dest_attr (const char *dest_name,
const char *attr)
{
cups_dest_t *dests;
int num_dests;
cups_dest_t *dest;
const char *value;
char *ret;
if (dest_name == NULL)
return NULL;
ret = NULL;
num_dests = cupsGetDests (&dests);
if (num_dests < 1) {
g_debug ("Unable to get printer destinations");
return NULL;
}
dest = cupsGetDest (dest_name, NULL, num_dests, dests);
if (dest == NULL) {
g_debug ("Unable to find a printer named '%s'", dest_name);
goto out;
}
value = cupsGetOption (attr, dest->num_options, dest->options);
if (value == NULL) {
g_debug ("Unable to get %s for '%s'", attr, dest_name);
goto out;
}
ret = g_strdup (value);
out:
cupsFreeDests (num_dests, dests);
return ret;
}
gchar *
get_ppd_attribute (const gchar *ppd_file_name,
const gchar *attribute_name)
{
ppd_file_t *ppd_file = NULL;
ppd_attr_t *ppd_attr = NULL;
gchar *result = NULL;
if (ppd_file_name)
{
ppd_file = ppdOpenFile (ppd_file_name);
if (ppd_file)
{
ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL);
if (ppd_attr != NULL)
result = g_strdup (ppd_attr->value);
ppdClose (ppd_file);
}
}
return result;
}
/* Set default destination in ~/.cups/lpoptions.
* Unset default destination if "dest" is NULL.
*/
void
set_local_default_printer (const gchar *printer_name)
{
cups_dest_t *dests = NULL;
int num_dests = 0;
int i;
num_dests = cupsGetDests (&dests);
for (i = 0; i < num_dests; i ++)
{
if (printer_name && g_strcmp0 (dests[i].name, printer_name) == 0)
dests[i].is_default = 1;
else
dests[i].is_default = 0;
}
cupsSetDests (num_dests, dests);
}
/*
* This function does something which should be provided by CUPS...
* It returns FALSE if the renaming fails.
*/
gboolean
printer_rename (const gchar *old_name,
const gchar *new_name)
{
ipp_attribute_t *attr = NULL;
cups_ptype_t printer_type = 0;
cups_dest_t *dests = NULL;
cups_dest_t *dest = NULL;
cups_job_t *jobs = NULL;
GDBusConnection *bus;
const gchar *printer_location = NULL;
const gchar *printer_info = NULL;
const gchar *printer_uri = NULL;
const gchar *device_uri = NULL;
const gchar *job_sheets = NULL;
gboolean result = FALSE;
gboolean accepting = TRUE;
gboolean printer_paused = FALSE;
gboolean default_printer = FALSE;
gboolean printer_shared = FALSE;
g_autoptr(GError) error = NULL;
http_t *http;
g_autofree gchar *ppd_link = NULL;
g_autofree gchar *ppd_filename = NULL;
gchar **sheets = NULL;
gchar **users_allowed = NULL;
gchar **users_denied = NULL;
gchar **member_names = NULL;
const gchar *start_sheet = NULL;
const gchar *end_sheet = NULL;
g_autofree gchar *error_policy = NULL;
g_autofree gchar *op_policy = NULL;
ipp_t *request;
ipp_t *response;
gint i;
int num_dests = 0;
int num_jobs = 0;
static const char * const requested_attrs[] = {
"printer-error-policy",
"printer-op-policy",
"requesting-user-name-allowed",
"requesting-user-name-denied",
"member-names"};
if (old_name == NULL ||
old_name[0] == '\0' ||
new_name == NULL ||
new_name[0] == '\0' ||
g_strcmp0 (old_name, new_name) == 0)
return FALSE;
num_dests = cupsGetDests (&dests);
dest = cupsGetDest (new_name, NULL, num_dests, dests);
if (dest)
{
cupsFreeDests (num_dests, dests);
return FALSE;
}
num_jobs = cupsGetJobs (&jobs, old_name, 0, CUPS_WHICHJOBS_ACTIVE);
cupsFreeJobs (num_jobs, jobs);
if (num_jobs > 1)
{
g_warning ("There are queued jobs on printer %s!", old_name);
cupsFreeDests (num_dests, dests);
return FALSE;
}
/*
* Gather some informations about the original printer
*/
dest = cupsGetDest (old_name, NULL, num_dests, dests);
if (dest)
{
for (i = 0; i < dest->num_options; i++)
{
if (g_strcmp0 (dest->options[i].name, "printer-is-accepting-jobs") == 0)
accepting = g_strcmp0 (dest->options[i].value, "true") == 0;
else if (g_strcmp0 (dest->options[i].name, "printer-is-shared") == 0)
printer_shared = g_strcmp0 (dest->options[i].value, "true") == 0;
else if (g_strcmp0 (dest->options[i].name, "device-uri") == 0)
device_uri = dest->options[i].value;
else if (g_strcmp0 (dest->options[i].name, "printer-uri-supported") == 0)
printer_uri = dest->options[i].value;
else if (g_strcmp0 (dest->options[i].name, "printer-info") == 0)
printer_info = dest->options[i].value;
else if (g_strcmp0 (dest->options[i].name, "printer-location") == 0)
printer_location = dest->options[i].value;
else if (g_strcmp0 (dest->options[i].name, "printer-state") == 0)
printer_paused = g_strcmp0 (dest->options[i].value, "5") == 0;
else if (g_strcmp0 (dest->options[i].name, "job-sheets") == 0)
job_sheets = dest->options[i].value;
else if (g_strcmp0 (dest->options[i].name, "printer-type") == 0)
printer_type = atoi (dest->options[i].value);
}
default_printer = dest->is_default;
}
cupsFreeDests (num_dests, dests);
if (accepting)
{
printer_set_accepting_jobs (old_name, FALSE, NULL);
num_jobs = cupsGetJobs (&jobs, old_name, 0, CUPS_WHICHJOBS_ACTIVE);
cupsFreeJobs (num_jobs, jobs);
if (num_jobs > 1)
{
printer_set_accepting_jobs (old_name, accepting, NULL);
g_warning ("There are queued jobs on printer %s!", old_name);
return FALSE;
}
}
/*
* Gather additional informations about the original printer
*/
#ifdef HAVE_CUPS_HTTPCONNECT2
http = httpConnect2 (cupsServer (), ippPort (), NULL, AF_UNSPEC,
cupsEncryption (), 1, 30000, NULL);
#else
http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ());
#endif
if (http != NULL)
{
request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, printer_uri);
ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", G_N_ELEMENTS (requested_attrs), NULL, requested_attrs);
response = cupsDoRequest (http, request, "/");
if (response)
{
if (ippGetStatusCode (response) <= IPP_OK_CONFLICT)
{
attr = ippFindAttribute (response, "printer-error-policy", IPP_TAG_NAME);
if (attr)
error_policy = g_strdup (ippGetString (attr, 0, NULL));
attr = ippFindAttribute (response, "printer-op-policy", IPP_TAG_NAME);
if (attr)
op_policy = g_strdup (ippGetString (attr, 0, NULL));
attr = ippFindAttribute (response, "requesting-user-name-allowed", IPP_TAG_NAME);
if (attr && ippGetCount (attr) > 0)
{
users_allowed = g_new0 (gchar *, ippGetCount (attr) + 1);
for (i = 0; i < ippGetCount (attr); i++)
users_allowed[i] = g_strdup (ippGetString (attr, i, NULL));
}
attr = ippFindAttribute (response, "requesting-user-name-denied", IPP_TAG_NAME);
if (attr && ippGetCount (attr) > 0)
{
users_denied = g_new0 (gchar *, ippGetCount (attr) + 1);
for (i = 0; i < ippGetCount (attr); i++)
users_denied[i] = g_strdup (ippGetString (attr, i, NULL));
}
attr = ippFindAttribute (response, "member-names", IPP_TAG_NAME);
if (attr && ippGetCount (attr) > 0)
{
member_names = g_new0 (gchar *, ippGetCount (attr) + 1);
for (i = 0; i < ippGetCount (attr); i++)
member_names[i] = g_strdup (ippGetString (attr, i, NULL));
}
}
ippDelete (response);
}
httpClose (http);
}
if (job_sheets)
{
sheets = g_strsplit (job_sheets, ",", 0);
if (g_strv_length (sheets) > 1)
{
start_sheet = sheets[0];
end_sheet = sheets[1];
}
}
ppd_link = g_strdup (cupsGetPPD (old_name));
if (ppd_link)
{
ppd_filename = g_file_read_link (ppd_link, NULL);
if (!ppd_filename)
ppd_filename = g_strdup (ppd_link);
}
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
}
else
{
if (printer_type & CUPS_PRINTER_CLASS)
{
if (member_names)
for (i = 0; i < g_strv_length (member_names); i++)
class_add_printer (new_name, member_names[i]);
}
else
{
GVariant *output;
g_autoptr(GError) add_error = NULL;
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterAddWithPpdFile",
g_variant_new ("(sssss)",
new_name,
device_uri ? device_uri : "",
ppd_filename ? ppd_filename : "",
printer_info ? printer_info : "",
printer_location ? printer_location : ""),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&add_error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: rename of printer %s to %s failed: %s", old_name, new_name, ret_error);
g_variant_unref (output);
}
else
{
g_warning ("%s", add_error->message);
}
}
}
if (ppd_link)
{
g_unlink (ppd_link);
}
num_dests = cupsGetDests (&dests);
dest = cupsGetDest (new_name, NULL, num_dests, dests);
if (dest)
{
printer_set_accepting_jobs (new_name, accepting, NULL);
printer_set_enabled (new_name, !printer_paused);
printer_set_shared (new_name, printer_shared);
printer_set_job_sheets (new_name, start_sheet, end_sheet);
printer_set_policy (new_name, op_policy, FALSE);
printer_set_policy (new_name, error_policy, TRUE);
printer_set_users (new_name, users_allowed, TRUE);
printer_set_users (new_name, users_denied, FALSE);
if (default_printer)
printer_set_default (new_name);
printer_delete (old_name);
result = TRUE;
}
else
printer_set_accepting_jobs (old_name, accepting, NULL);
cupsFreeDests (num_dests, dests);
if (sheets)
g_strfreev (sheets);
if (users_allowed)
g_strfreev (users_allowed);
if (users_denied)
g_strfreev (users_denied);
return result;
}
gboolean
printer_set_location (const gchar *printer_name,
const gchar *location)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name || !location)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetLocation",
g_variant_new ("(ss)", printer_name, location),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of location for printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_accepting_jobs (const gchar *printer_name,
gboolean accepting_jobs,
const gchar *reason)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetAcceptJobs",
g_variant_new ("(sbs)",
printer_name,
accepting_jobs,
reason ? reason : ""),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of acceptance of jobs for printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_enabled (const gchar *printer_name,
gboolean enabled)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetEnabled",
g_variant_new ("(sb)", printer_name, enabled),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of enablement of printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_delete (const gchar *printer_name)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterDelete",
g_variant_new ("(s)", printer_name),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: removing of printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_default (const gchar *printer_name)
{
GDBusConnection *bus;
const char *cups_server;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name)
return TRUE;
cups_server = cupsServer ();
if (g_ascii_strncasecmp (cups_server, "localhost", 9) == 0 ||
g_ascii_strncasecmp (cups_server, "127.0.0.1", 9) == 0 ||
g_ascii_strncasecmp (cups_server, "::1", 3) == 0 ||
cups_server[0] == '/')
{
/* Clean .cups/lpoptions before setting
* default printer on local CUPS server.
*/
set_local_default_printer (NULL);
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
}
else
{
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetDefault",
g_variant_new ("(s)", printer_name),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting default printer to %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
}
}
else
/* Store default printer to .cups/lpoptions
* if we are connected to a remote CUPS server.
*/
{
set_local_default_printer (printer_name);
}
return result;
}
gboolean
printer_set_shared (const gchar *printer_name,
gboolean shared)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetShared",
g_variant_new ("(sb)", printer_name, shared),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of sharing of printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_job_sheets (const gchar *printer_name,
const gchar *start_sheet,
const gchar *end_sheet)
{
GDBusConnection *bus;
GVariant *output;
g_autoptr(GError) error = NULL;
gboolean result = FALSE;
if (!printer_name || !start_sheet || !end_sheet)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetJobSheets",
g_variant_new ("(sss)", printer_name, start_sheet, end_sheet),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of job sheets for printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_policy (const gchar *printer_name,
const gchar *policy,
gboolean error_policy)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name || !policy)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
if (error_policy)
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetErrorPolicy",
g_variant_new ("(ss)", printer_name, policy),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
else
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetOpPolicy",
g_variant_new ("(ss)", printer_name, policy),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of a policy for printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_set_users (const gchar *printer_name,
gchar **users,
gboolean allowed)
{
GDBusConnection *bus;
GVariantBuilder array_builder;
gint i;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!printer_name || !users)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as"));
for (i = 0; users[i]; i++)
g_variant_builder_add (&array_builder, "s", users[i]);
if (allowed)
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetUsersAllowed",
g_variant_new ("(sas)", printer_name, &array_builder),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
else
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterSetUsersDenied",
g_variant_new ("(sas)", printer_name, &array_builder),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of access list for printer %s failed: %s", printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
class_add_printer (const gchar *class_name,
const gchar *printer_name)
{
GDBusConnection *bus;
GVariant *output;
gboolean result = FALSE;
g_autoptr(GError) error = NULL;
if (!class_name || !printer_name)
return TRUE;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
return TRUE;
}
output = g_dbus_connection_call_sync (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"ClassAddPrinter",
g_variant_new ("(ss)", class_name, printer_name),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
g_object_unref (bus);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: adding of printer %s to class %s failed: %s", printer_name, class_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
g_warning ("%s", error->message);
}
return result;
}
gboolean
printer_is_local (cups_ptype_t printer_type,
const gchar *device_uri)
{
gboolean result = TRUE;
char scheme[HTTP_MAX_URI];
char username[HTTP_MAX_URI];
char hostname[HTTP_MAX_URI];
char resource[HTTP_MAX_URI];
int port;
if (printer_type &
(CUPS_PRINTER_DISCOVERED |
CUPS_PRINTER_REMOTE |
CUPS_PRINTER_IMPLICIT))
result = FALSE;
if (device_uri == NULL || !result)
return result;
httpSeparateURI (HTTP_URI_CODING_ALL, device_uri,
scheme, sizeof (scheme),
username, sizeof (username),
hostname, sizeof (hostname),
&port,
resource, sizeof (resource));
if (g_str_equal (scheme, "ipp") ||
g_str_equal (scheme, "smb") ||
g_str_equal (scheme, "socket") ||
g_str_equal (scheme, "lpd"))
result = FALSE;
return result;
}
gchar*
printer_get_hostname (cups_ptype_t printer_type,
const gchar *device_uri,
const gchar *printer_uri)
{
gboolean local = TRUE;
gchar *result = NULL;
char scheme[HTTP_MAX_URI];
char username[HTTP_MAX_URI];
char hostname[HTTP_MAX_URI];
char resource[HTTP_MAX_URI];
int port;
if (device_uri == NULL)
return result;
if (printer_type & (CUPS_PRINTER_DISCOVERED |
CUPS_PRINTER_REMOTE |
CUPS_PRINTER_IMPLICIT))
{
if (printer_uri)
{
httpSeparateURI (HTTP_URI_CODING_ALL, printer_uri,
scheme, sizeof (scheme),
username, sizeof (username),
hostname, sizeof (hostname),
&port,
resource, sizeof (resource));
if (hostname[0] != '\0')
result = g_strdup (hostname);
}
local = FALSE;
}
if (result == NULL && device_uri)
{
httpSeparateURI (HTTP_URI_CODING_ALL, device_uri,
scheme, sizeof (scheme),
username, sizeof (username),
hostname, sizeof (hostname),
&port,
resource, sizeof (resource));
if (g_str_equal (scheme, "ipp") ||
g_str_equal (scheme, "smb") ||
g_str_equal (scheme, "socket") ||
g_str_equal (scheme, "lpd"))
{
if (hostname[0] != '\0')
result = g_strdup (hostname);
local = FALSE;
}
}
if (local)
result = g_strdup ("localhost");
return result;
}
/* Returns default page size for current locale */
const gchar *
get_page_size_from_locale (void)
{
if (g_str_equal (gtk_paper_size_get_default (), GTK_PAPER_NAME_LETTER))
return "Letter";
else
return "A4";
}
typedef struct
{
gchar *printer_name;
gchar **attributes_names;
GHashTable *result;
GIACallback callback;
gpointer user_data;
GMainContext *context;
} GIAData;
static gboolean
get_ipp_attributes_idle_cb (gpointer user_data)
{
GIAData *data = (GIAData *) user_data;
data->callback (data->result, data->user_data);
return FALSE;
}
static void
get_ipp_attributes_data_free (gpointer user_data)
{
GIAData *data = (GIAData *) user_data;
if (data->context)
g_main_context_unref (data->context);
g_free (data->printer_name);
if (data->attributes_names)
g_strfreev (data->attributes_names);
g_free (data);
}
static void
get_ipp_attributes_cb (gpointer user_data)
{
GIAData *data = (GIAData *) user_data;
GSource *idle_source;
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
get_ipp_attributes_idle_cb,
data,
get_ipp_attributes_data_free);
g_source_attach (idle_source, data->context);
g_source_unref (idle_source);
}
static void
ipp_attribute_free2 (gpointer attr)
{
IPPAttribute *attribute = (IPPAttribute *) attr;
ipp_attribute_free (attribute);
}
static gpointer
get_ipp_attributes_func (gpointer user_data)
{
ipp_attribute_t *attr = NULL;
GIAData *data = (GIAData *) user_data;
ipp_t *request;
ipp_t *response = NULL;
g_autofree gchar *printer_uri = NULL;
char **requested_attrs = NULL;
gint i, j, length = 0;
printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", data->printer_name);
if (data->attributes_names)
{
length = g_strv_length (data->attributes_names);
requested_attrs = g_new0 (char *, length);
for (i = 0; data->attributes_names[i]; i++)
requested_attrs[i] = g_strdup (data->attributes_names[i]);
request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, printer_uri);
ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", length, NULL, (const char **) requested_attrs);
response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
}
if (response)
{
if (ippGetStatusCode (response) <= IPP_OK_CONFLICT)
{
for (j = 0; j < length; j++)
{
attr = ippFindAttribute (response, requested_attrs[j], IPP_TAG_ZERO);
if (attr && ippGetCount (attr) > 0 && ippGetValueTag (attr) != IPP_TAG_NOVALUE)
{
IPPAttribute *attribute;
attribute = g_new0 (IPPAttribute, 1);
attribute->attribute_name = g_strdup (requested_attrs[j]);
attribute->attribute_values = g_new0 (IPPAttributeValue, ippGetCount (attr));
attribute->num_of_values = ippGetCount (attr);
if (ippGetValueTag (attr) == IPP_TAG_INTEGER ||
ippGetValueTag (attr) == IPP_TAG_ENUM)
{
attribute->attribute_type = IPP_ATTRIBUTE_TYPE_INTEGER;
for (i = 0; i < ippGetCount (attr); i++)
attribute->attribute_values[i].integer_value = ippGetInteger (attr, i);
}
else if (ippGetValueTag (attr) == IPP_TAG_NAME ||
ippGetValueTag (attr) == IPP_TAG_STRING ||
ippGetValueTag (attr) == IPP_TAG_TEXT ||
ippGetValueTag (attr) == IPP_TAG_URI ||
ippGetValueTag (attr) == IPP_TAG_KEYWORD ||
ippGetValueTag (attr) == IPP_TAG_URISCHEME)
{
attribute->attribute_type = IPP_ATTRIBUTE_TYPE_STRING;
for (i = 0; i < ippGetCount (attr); i++)
attribute->attribute_values[i].string_value = g_strdup (ippGetString (attr, i, NULL));
}
else if (ippGetValueTag (attr) == IPP_TAG_RANGE)
{
attribute->attribute_type = IPP_ATTRIBUTE_TYPE_RANGE;
for (i = 0; i < ippGetCount (attr); i++)
{
attribute->attribute_values[i].lower_range =
ippGetRange (attr, i, &(attribute->attribute_values[i].upper_range));
}
}
else if (ippGetValueTag (attr) == IPP_TAG_BOOLEAN)
{
attribute->attribute_type = IPP_ATTRIBUTE_TYPE_BOOLEAN;
for (i = 0; i < ippGetCount (attr); i++)
attribute->attribute_values[i].boolean_value = ippGetBoolean (attr, i);
}
if (!data->result)
data->result = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ipp_attribute_free2);
g_hash_table_insert (data->result, g_strdup (requested_attrs[j]), attribute);
}
}
}
ippDelete (response);
}
for (i = 0; i < length; i++)
g_free (requested_attrs[i]);
g_free (requested_attrs);
get_ipp_attributes_cb (data);
return NULL;
}
void
get_ipp_attributes_async (const gchar *printer_name,
gchar **attributes_names,
GIACallback callback,
gpointer user_data)
{
GIAData *data;
GThread *thread;
g_autoptr(GError) error = NULL;
data = g_new0 (GIAData, 1);
data->printer_name = g_strdup (printer_name);
data->attributes_names = g_strdupv (attributes_names);
data->callback = callback;
data->user_data = user_data;
data->context = g_main_context_ref_thread_default ();
thread = g_thread_try_new ("get-ipp-attributes",
get_ipp_attributes_func,
data,
&error);
if (!thread)
{
g_warning ("%s", error->message);
callback (NULL, user_data);
get_ipp_attributes_data_free (data);
}
else
{
g_thread_unref (thread);
}
}
IPPAttribute *
ipp_attribute_copy (IPPAttribute *attr)
{
IPPAttribute *result = NULL;
gint i;
if (attr)
{
result = g_new0 (IPPAttribute, 1);
*result = *attr;
result->attribute_name = g_strdup (attr->attribute_name);
result->attribute_values = g_new0 (IPPAttributeValue, attr->num_of_values);
for (i = 0; i < attr->num_of_values; i++)
{
result->attribute_values[i] = attr->attribute_values[i];
if (attr->attribute_values[i].string_value)
result->attribute_values[i].string_value = g_strdup (attr->attribute_values[i].string_value);
}
}
return result;
}
void
ipp_attribute_free (IPPAttribute *attr)
{
gint i;
if (attr)
{
for (i = 0; i < attr->num_of_values; i++)
g_free (attr->attribute_values[i].string_value);
g_free (attr->attribute_values);
g_free (attr->attribute_name);
g_free (attr);
}
}
typedef struct
{
gchar *printer_name;
gchar *ppd_copy;
GCancellable *cancellable;
PSPCallback callback;
gpointer user_data;
} PSPData;
static void
printer_set_ppd_async_dbus_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GVariant *output;
gboolean result = FALSE;
PSPData *data = (PSPData *) user_data;
g_autoptr(GError) error = NULL;
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
g_object_unref (source_object);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of driver for printer %s failed: %s", data->printer_name, ret_error);
else
result = TRUE;
g_variant_unref (output);
}
else
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("%s", error->message);
}
/* Don't call callback if cancelled */
if (!data->cancellable ||
!g_cancellable_is_cancelled (data->cancellable))
data->callback (g_strdup (data->printer_name),
result,
data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
if (data->ppd_copy)
{
g_unlink (data->ppd_copy);
g_free (data->ppd_copy);
}
g_free (data->printer_name);
g_free (data);
}
/*
* Set ppd for given printer.
* Don't use this for classes, just for printers.
*/
void
printer_set_ppd_async (const gchar *printer_name,
const gchar *ppd_name,
GCancellable *cancellable,
PSPCallback callback,
gpointer user_data)
{
GDBusConnection *bus;
PSPData *data;
g_autoptr(GError) error = NULL;
data = g_new0 (PSPData, 1);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
data->printer_name = g_strdup (printer_name);
if (printer_name == NULL ||
printer_name[0] == '\0')
{
goto out;
}
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
goto out;
}
g_dbus_connection_call (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterAdd",
g_variant_new ("(sssss)",
printer_name,
"",
ppd_name,
"",
""),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
data->cancellable,
printer_set_ppd_async_dbus_cb,
data);
return;
out:
callback (g_strdup (printer_name), FALSE, user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data);
}
static void
printer_set_ppd_file_async_scb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GDBusConnection *bus;
gboolean success;
PSPData *data = (PSPData *) user_data;
g_autoptr(GError) error = NULL;
success = g_file_copy_finish (G_FILE (source_object),
res,
&error);
g_object_unref (source_object);
if (!success)
{
g_warning ("%s", error->message);
goto out;
}
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
goto out;
}
g_dbus_connection_call (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"PrinterAddWithPpdFile",
g_variant_new ("(sssss)",
data->printer_name,
"",
data->ppd_copy,
"",
""),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
data->cancellable,
printer_set_ppd_async_dbus_cb,
data);
return;
out:
data->callback (g_strdup (data->printer_name), FALSE, data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data->ppd_copy);
g_free (data);
}
/*
* Set ppd for given printer.
* Don't use this for classes, just for printers.
*/
void
printer_set_ppd_file_async (const gchar *printer_name,
const gchar *ppd_filename,
GCancellable *cancellable,
PSPCallback callback,
gpointer user_data)
{
GFileIOStream *stream;
PSPData *data;
GFile *source_ppd_file;
GFile *destination_ppd_file;
data = g_new0 (PSPData, 1);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
data->printer_name = g_strdup (printer_name);
if (printer_name == NULL ||
printer_name[0] == '\0')
{
goto out;
}
/*
* We need to copy the PPD to temp directory at first.
* This is needed because of SELinux.
*/
source_ppd_file = g_file_new_for_path (ppd_filename);
destination_ppd_file = g_file_new_tmp ("g-c-c-XXXXXX.ppd", &stream, NULL);
g_object_unref (stream);
data->ppd_copy = g_strdup (g_file_get_path (destination_ppd_file));
g_file_copy_async (source_ppd_file,
destination_ppd_file,
G_FILE_COPY_OVERWRITE,
G_PRIORITY_DEFAULT,
cancellable,
NULL,
NULL,
printer_set_ppd_file_async_scb,
data);
g_object_unref (destination_ppd_file);
return;
out:
callback (g_strdup (printer_name), FALSE, user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data);
}
typedef void (*GPACallback) (gchar **attribute_values,
gpointer user_data);
typedef struct
{
gchar *attribute_name;
gchar **ppds_names;
gchar **result;
GPACallback callback;
gpointer user_data;
GMainContext *context;
} GPAData;
static gboolean
get_ppds_attribute_idle_cb (gpointer user_data)
{
GPAData *data = (GPAData *) user_data;
data->callback (data->result, data->user_data);
return FALSE;
}
static void
get_ppds_attribute_data_free (gpointer user_data)
{
GPAData *data = (GPAData *) user_data;
if (data->context)
g_main_context_unref (data->context);
g_free (data->attribute_name);
g_strfreev (data->ppds_names);
g_free (data);
}
static void
get_ppds_attribute_cb (gpointer user_data)
{
GPAData *data = (GPAData *) user_data;
GSource *idle_source;
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
get_ppds_attribute_idle_cb,
data,
get_ppds_attribute_data_free);
g_source_attach (idle_source, data->context);
g_source_unref (idle_source);
}
static gpointer
get_ppds_attribute_func (gpointer user_data)
{
ppd_file_t *ppd_file;
ppd_attr_t *ppd_attr;
GPAData *data = (GPAData *) user_data;
gint i;
data->result = g_new0 (gchar *, g_strv_length (data->ppds_names) + 1);
for (i = 0; data->ppds_names[i]; i++)
{
g_autofree gchar *ppd_filename = g_strdup (cupsGetServerPPD (CUPS_HTTP_DEFAULT, data->ppds_names[i]));
if (ppd_filename)
{
ppd_file = ppdOpenFile (ppd_filename);
if (ppd_file)
{
ppd_attr = ppdFindAttr (ppd_file, data->attribute_name, NULL);
if (ppd_attr != NULL)
data->result[i] = g_strdup (ppd_attr->value);
ppdClose (ppd_file);
}
g_unlink (ppd_filename);
}
}
get_ppds_attribute_cb (data);
return NULL;
}
/*
* Get values of requested PPD attribute for given PPDs.
*/
static void
get_ppds_attribute_async (gchar **ppds_names,
gchar *attribute_name,
GPACallback callback,
gpointer user_data)
{
GPAData *data;
GThread *thread;
g_autoptr(GError) error = NULL;
if (!ppds_names || !attribute_name)
{
callback (NULL, user_data);
return;
}
data = g_new0 (GPAData, 1);
data->ppds_names = g_strdupv (ppds_names);
data->attribute_name = g_strdup (attribute_name);
data->callback = callback;
data->user_data = user_data;
data->context = g_main_context_ref_thread_default ();
thread = g_thread_try_new ("get-ppds-attribute",
get_ppds_attribute_func,
data,
&error);
if (!thread)
{
g_warning ("%s", error->message);
callback (NULL, user_data);
get_ppds_attribute_data_free (data);
}
else
{
g_thread_unref (thread);
}
}
typedef void (*GDACallback) (gchar *device_id,
gchar *device_make_and_model,
gchar *device_uri,
gpointer user_data);
typedef struct
{
gchar *printer_name;
gchar *device_uri;
GCancellable *cancellable;
GList *backend_list;
GDACallback callback;
gpointer user_data;
} GDAData;
typedef struct
{
gchar *printer_name;
gint count;
PPDName **result;
GCancellable *cancellable;
GPNCallback callback;
gpointer user_data;
} GPNData;
static void
get_ppd_names_async_cb (gchar **attribute_values,
gpointer user_data)
{
GPNData *data = (GPNData *) user_data;
gint i;
if (g_cancellable_is_cancelled (data->cancellable))
{
g_strfreev (attribute_values);
for (i = 0; data->result[i]; i++)
{
g_free (data->result[i]->ppd_name);
g_free (data->result[i]);
}
g_free (data->result);
data->result = NULL;
goto out;
}
if (attribute_values)
{
for (i = 0; attribute_values[i]; i++)
data->result[i]->ppd_display_name = attribute_values[i];
g_free (attribute_values);
}
out:
data->callback (data->result,
data->printer_name,
g_cancellable_is_cancelled (data->cancellable),
data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data);
}
static void
get_ppd_names_async_dbus_scb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GVariant *output;
PPDName *ppd_item;
PPDName **result = NULL;
GPNData *data = (GPNData *) user_data;
g_autoptr(GError) error = NULL;
GList *driver_list = NULL;
GList *iter;
gint i, j, n = 0;
static const char * const match_levels[] = {
"exact-cmd",
"exact",
"close",
"generic",
"none"};
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
g_object_unref (source_object);
if (output)
{
GVariant *array;
g_variant_get (output, "(@a(ss))",
&array);
if (array)
{
GVariantIter *iter;
GVariant *item;
for (j = 0; j < G_N_ELEMENTS (match_levels) && n < data->count; j++)
{
g_variant_get (array,
"a(ss)",
&iter);
while ((item = g_variant_iter_next_value (iter)))
{
const gchar *driver, *match;
g_variant_get (item,
"(&s&s)",
&driver,
&match);
if (g_str_equal (match, match_levels[j]) && n < data->count)
{
ppd_item = g_new0 (PPDName, 1);
ppd_item->ppd_name = g_strdup (driver);
if (g_strcmp0 (match, "exact-cmd") == 0)
ppd_item->ppd_match_level = PPD_EXACT_CMD_MATCH;
else if (g_strcmp0 (match, "exact") == 0)
ppd_item->ppd_match_level = PPD_EXACT_MATCH;
else if (g_strcmp0 (match, "close") == 0)
ppd_item->ppd_match_level = PPD_CLOSE_MATCH;
else if (g_strcmp0 (match, "generic") == 0)
ppd_item->ppd_match_level = PPD_GENERIC_MATCH;
else if (g_strcmp0 (match, "none") == 0)
ppd_item->ppd_match_level = PPD_NO_MATCH;
driver_list = g_list_append (driver_list, ppd_item);
n++;
}
g_variant_unref (item);
}
}
g_variant_unref (array);
}
g_variant_unref (output);
}
else
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("%s", error->message);
}
if (n > 0)
{
result = g_new0 (PPDName *, n + 1);
i = 0;
for (iter = driver_list; iter; iter = iter->next)
{
result[i] = iter->data;
i++;
}
}
if (result)
{
gchar **ppds_names;
data->result = result;
ppds_names = g_new0 (gchar *, n + 1);
for (i = 0; i < n; i++)
ppds_names[i] = g_strdup (result[i]->ppd_name);
get_ppds_attribute_async (ppds_names,
"NickName",
get_ppd_names_async_cb,
data);
g_strfreev (ppds_names);
}
else
{
data->callback (NULL,
data->printer_name,
g_cancellable_is_cancelled (data->cancellable),
data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data);
}
}
static void
get_device_attributes_cb (gchar *device_id,
gchar *device_make_and_model,
gchar *device_uri,
gpointer user_data)
{
GDBusConnection *bus;
g_autoptr(GError) error = NULL;
GPNData *data = (GPNData *) user_data;
if (g_cancellable_is_cancelled (data->cancellable))
goto out;
if (!device_id || !device_make_and_model || !device_uri)
goto out;
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
goto out;
}
g_dbus_connection_call (bus,
SCP_BUS,
SCP_PATH,
SCP_IFACE,
"GetBestDrivers",
g_variant_new ("(sss)",
device_id,
device_make_and_model,
device_uri),
G_VARIANT_TYPE ("(a(ss))"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_LONG,
data->cancellable,
get_ppd_names_async_dbus_scb,
data);
return;
out:
data->callback (NULL,
data->printer_name,
g_cancellable_is_cancelled (data->cancellable),
data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->printer_name);
g_free (data);
}
/*
* Special item for the list of backends. It represents
* backends not present in the list itself.
*/
#define OTHER_BACKENDS "other-backends"
/*
* List of CUPS backends sorted according to their speed,
* the fastest is the first one. The last item represents
* backends not present in the list.
*/
const gchar *cups_backends[] = {
"usb",
"socket",
"serial",
"parallel",
"lpd",
"ipp",
"hp",
"dnssd",
"snmp",
"bluetooth",
"beh",
"ncp",
"hpfax",
OTHER_BACKENDS
};
static GList *
create_backends_list ()
{
GList *list = NULL;
gint i;
for (i = 0; i < G_N_ELEMENTS (cups_backends); i++)
list = g_list_prepend (list, g_strdup (cups_backends[i]));
list = g_list_reverse (list);
return list;
}
static GVariantBuilder *
create_other_backends_array ()
{
GVariantBuilder *builder;
gint i;
builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
for (i = 0; i < G_N_ELEMENTS (cups_backends) - 1; i++)
g_variant_builder_add (builder, "s", cups_backends[i]);
return builder;
}
static void
get_device_attributes_async_dbus_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GVariant *output;
GDAData *data = (GDAData *) user_data;
g_autoptr(GError) error = NULL;
GList *tmp;
gchar *device_id = NULL;
gchar *device_make_and_model = NULL;
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
g_object_unref (source_object);
if (output)
{
const gchar *ret_error;
GVariant *devices_variant = NULL;
g_variant_get (output, "(&s@a{ss})",
&ret_error,
&devices_variant);
if (ret_error[0] != '\0')
{
g_warning ("cups-pk-helper: getting of attributes for printer %s failed: %s", data->printer_name, ret_error);
}
if (devices_variant)
{
GVariantIter *iter;
GVariant *item;
gint index = -1;
if (data->device_uri)
{
g_autofree gchar *suffix = NULL;
g_variant_get (devices_variant,
"a{ss}",
&iter);
while ((item = g_variant_iter_next_value (iter)))
{
const gchar *key, *value;
g_variant_get (item,
"{&s&s}",
&key,
&value);
if (g_str_equal (value, data->device_uri))
{
gchar *number = g_strrstr (key, ":");
if (number != NULL)
{
gchar *endptr;
number++;
index = g_ascii_strtoll (number, &endptr, 10);
if (index == 0 && endptr == (number))
index = -1;
}
}
g_variant_unref (item);
}
suffix = g_strdup_printf (":%d", index);
g_variant_get (devices_variant,
"a{ss}",
&iter);
while ((item = g_variant_iter_next_value (iter)))
{
const gchar *key, *value;
g_variant_get (item,
"{&s&s}",
&key,
&value);
if (g_str_has_suffix (key, suffix))
{
if (g_str_has_prefix (key, "device-id"))
{
device_id = g_strdup (value);
}
if (g_str_has_prefix (key, "device-make-and-model"))
{
device_make_and_model = g_strdup (value);
}
}
g_variant_unref (item);
}
}
g_variant_unref (devices_variant);
}
g_variant_unref (output);
}
else
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("%s", error->message);
}
if (!device_id || !device_make_and_model)
{
GVariantBuilder *include_scheme_builder = NULL;
GVariantBuilder *exclude_scheme_builder = NULL;
g_free (device_id);
g_free (device_make_and_model);
device_id = NULL;
device_make_and_model = NULL;
if (data->backend_list && !g_cancellable_is_cancelled (data->cancellable))
{
const gchar *backend_name;
backend_name = data->backend_list->data;
if (g_strcmp0 (backend_name, OTHER_BACKENDS) != 0)
{
include_scheme_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
g_variant_builder_add (include_scheme_builder, "s", backend_name);
}
else
{
exclude_scheme_builder = create_other_backends_array ();
}
tmp = data->backend_list;
data->backend_list = g_list_remove_link (data->backend_list, tmp);
g_list_free_full (tmp, g_free);
g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source_object)),
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"DevicesGet",
g_variant_new ("(iiasas)",
0,
0,
include_scheme_builder,
exclude_scheme_builder),
G_VARIANT_TYPE ("(sa{ss})"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT,
data->cancellable,
get_device_attributes_async_dbus_cb,
user_data);
if (include_scheme_builder)
g_variant_builder_unref (include_scheme_builder);
if (exclude_scheme_builder)
g_variant_builder_unref (exclude_scheme_builder);
return;
}
}
g_object_unref (source_object);
if (data->backend_list)
{
g_list_free_full (data->backend_list, g_free);
data->backend_list = NULL;
}
data->callback (device_id,
device_make_and_model,
data->device_uri,
data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->device_uri);
g_free (data->printer_name);
g_free (data);
}
static void
get_device_attributes_async_scb (GHashTable *result,
gpointer user_data)
{
GDBusConnection *bus;
GVariantBuilder include_scheme_builder;
IPPAttribute *attr;
GDAData *data = (GDAData *) user_data;
g_autoptr(GError) error = NULL;
GList *tmp;
if (result)
{
attr = g_hash_table_lookup (result, "device-uri");
if (attr && attr->attribute_type == IPP_ATTRIBUTE_TYPE_STRING &&
attr->num_of_values > 0)
data->device_uri = g_strdup (attr->attribute_values[0].string_value);
g_hash_table_unref (result);
}
if (g_cancellable_is_cancelled (data->cancellable))
goto out;
if (!data->device_uri)
goto out;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
goto out;
}
data->backend_list = create_backends_list ();
g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&include_scheme_builder, "s", data->backend_list->data);
tmp = data->backend_list;
data->backend_list = g_list_remove_link (data->backend_list, tmp);
g_list_free_full (tmp, g_free);
g_dbus_connection_call (g_object_ref (bus),
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"DevicesGet",
g_variant_new ("(iiasas)",
0,
0,
&include_scheme_builder,
NULL),
G_VARIANT_TYPE ("(sa{ss})"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT,
data->cancellable,
get_device_attributes_async_dbus_cb,
data);
return;
out:
data->callback (NULL, NULL, NULL, data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data->device_uri);
g_free (data->printer_name);
g_free (data);
}
/*
* Get device-id, device-make-and-model and device-uri for given printer.
*/
static void
get_device_attributes_async (const gchar *printer_name,
GCancellable *cancellable,
GDACallback callback,
gpointer user_data)
{
GDAData *data;
gchar **attributes;
if (!printer_name)
{
callback (NULL, NULL, NULL, user_data);
return;
}
data = g_new0 (GDAData, 1);
data->printer_name = g_strdup (printer_name);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
attributes = g_new0 (gchar *, 2);
attributes[0] = g_strdup ("device-uri");
get_ipp_attributes_async (printer_name,
attributes,
get_device_attributes_async_scb,
data);
g_strfreev (attributes);
}
/*
* Return "count" best matching driver names for given printer.
*/
void
get_ppd_names_async (gchar *printer_name,
gint count,
GCancellable *cancellable,
GPNCallback callback,
gpointer user_data)
{
GPNData *data;
if (!printer_name)
{
callback (NULL, NULL, TRUE, user_data);
return;
}
data = g_new0 (GPNData, 1);
data->printer_name = g_strdup (printer_name);
data->count = count;
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
/*
* We have to find out device-id for this printer at first.
*/
get_device_attributes_async (printer_name,
cancellable,
get_device_attributes_cb,
data);
}
typedef struct
{
PPDList *result;
GCancellable *cancellable;
GAPCallback callback;
gpointer user_data;
GMainContext *context;
} GAPData;
static gboolean
get_all_ppds_idle_cb (gpointer user_data)
{
GAPData *data = (GAPData *) user_data;
/* Don't call callback if cancelled */
if (data->cancellable &&
g_cancellable_is_cancelled (data->cancellable))
{
ppd_list_free (data->result);
data->result = NULL;
}
else
{
data->callback (data->result, data->user_data);
}
return FALSE;
}
static void
get_all_ppds_data_free (gpointer user_data)
{
GAPData *data = (GAPData *) user_data;
if (data->context)
g_main_context_unref (data->context);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data);
}
static void
get_all_ppds_cb (gpointer user_data)
{
GAPData *data = (GAPData *) user_data;
GSource *idle_source;
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
get_all_ppds_idle_cb,
data,
get_all_ppds_data_free);
g_source_attach (idle_source, data->context);
g_source_unref (idle_source);
}
static const struct {
const char *normalized_name;
const char *display_name;
} manufacturers_names[] = {
{ "alps", "Alps" },
{ "anitech", "Anitech" },
{ "apple", "Apple" },
{ "apollo", "Apollo" },
{ "brother", "Brother" },
{ "canon", "Canon" },
{ "citizen", "Citizen" },
{ "citoh", "Citoh" },
{ "compaq", "Compaq" },
{ "dec", "DEC" },
{ "dell", "Dell" },
{ "dnp", "DNP" },
{ "dymo", "Dymo" },
{ "epson", "Epson" },
{ "fujifilm", "Fujifilm" },
{ "fujitsu", "Fujitsu" },
{ "gelsprinter", "Ricoh" },
{ "generic", "Generic" },
{ "genicom", "Genicom" },
{ "gestetner", "Gestetner" },
{ "hewlett packard", "Hewlett-Packard" },
{ "heidelberg", "Heidelberg" },
{ "hitachi", "Hitachi" },
{ "hp", "Hewlett-Packard" },
{ "ibm", "IBM" },
{ "imagen", "Imagen" },
{ "imagistics", "Imagistics" },
{ "infoprint", "InfoPrint" },
{ "infotec", "Infotec" },
{ "intellitech", "Intellitech" },
{ "kodak", "Kodak" },
{ "konica minolta", "Minolta" },
{ "kyocera", "Kyocera" },
{ "kyocera mita", "Kyocera" },
{ "lanier", "Lanier" },
{ "lexmark international", "Lexmark" },
{ "lexmark", "Lexmark" },
{ "minolta", "Minolta" },
{ "minolta qms", "Minolta" },
{ "mitsubishi", "Mitsubishi" },
{ "nec", "NEC" },
{ "nrg", "NRG" },
{ "oce", "Oce" },
{ "oki", "Oki" },
{ "oki data corp", "Oki" },
{ "olivetti", "Olivetti" },
{ "olympus", "Olympus" },
{ "panasonic", "Panasonic" },
{ "pcpi", "PCPI" },
{ "pentax", "Pentax" },
{ "qms", "QMS" },
{ "raven", "Raven" },
{ "raw", "Raw" },
{ "ricoh", "Ricoh" },
{ "samsung", "Samsung" },
{ "savin", "Savin" },
{ "seiko", "Seiko" },
{ "sharp", "Sharp" },
{ "shinko", "Shinko" },
{ "sipix", "SiPix" },
{ "sony", "Sony" },
{ "star", "Star" },
{ "tally", "Tally" },
{ "tektronix", "Tektronix" },
{ "texas instruments", "Texas Instruments" },
{ "toshiba", "Toshiba" },
{ "toshiba tec corp.", "Toshiba" },
{ "xante", "Xante" },
{ "xerox", "Xerox" },
{ "zebra", "Zebra" },
};
static gpointer
get_all_ppds_func (gpointer user_data)
{
ipp_attribute_t *attr;
GHashTable *ppds_hash = NULL;
GHashTable *manufacturers_hash = NULL;
GAPData *data = (GAPData *) user_data;
PPDName *item;
ipp_t *request;
ipp_t *response;
GList *list;
gchar *manufacturer_display_name;
gint i, j;
request = ippNewRequest (CUPS_GET_PPDS);
response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
if (response &&
ippGetStatusCode (response) <= IPP_OK_CONFLICT)
{
/*
* This hash contains names of manufacturers as keys and
* values are GLists of PPD names.
*/
ppds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/*
* This hash contains all possible names of manufacturers as keys
* and values are just first occurences of their equivalents.
* This is for mapping of e.g. "Hewlett Packard" and "HP" to the same name
* (the one which comes first).
*/
manufacturers_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++)
{
g_hash_table_insert (manufacturers_hash,
g_strdup (manufacturers_names[i].normalized_name),
g_strdup (manufacturers_names[i].display_name));
}
for (attr = ippFirstAttribute (response); attr != NULL; attr = ippNextAttribute (response))
{
const gchar *ppd_device_id = NULL;
const gchar *ppd_make_and_model = NULL;
const gchar *ppd_name = NULL;
const gchar *ppd_product = NULL;
const gchar *ppd_make = NULL;
g_autofree gchar *mdl = NULL;
g_autofree gchar *mfg = NULL;
g_autofree gchar *mfg_normalized = NULL;
while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
attr = ippNextAttribute (response);
if (attr == NULL)
break;
while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
{
if (g_strcmp0 (ippGetName (attr), "ppd-device-id") == 0 &&
ippGetValueTag (attr) == IPP_TAG_TEXT)
ppd_device_id = ippGetString (attr, 0, NULL);
else if (g_strcmp0 (ippGetName (attr), "ppd-make-and-model") == 0 &&
ippGetValueTag (attr) == IPP_TAG_TEXT)
ppd_make_and_model = ippGetString (attr, 0, NULL);
else if (g_strcmp0 (ippGetName (attr), "ppd-name") == 0 &&
ippGetValueTag (attr) == IPP_TAG_NAME)
ppd_name = ippGetString (attr, 0, NULL);
else if (g_strcmp0 (ippGetName (attr), "ppd-product") == 0 &&
ippGetValueTag (attr) == IPP_TAG_TEXT)
ppd_product = ippGetString (attr, 0, NULL);
else if (g_strcmp0 (ippGetName (attr), "ppd-make") == 0 &&
ippGetValueTag (attr) == IPP_TAG_TEXT)
ppd_make = ippGetString (attr, 0, NULL);
attr = ippNextAttribute (response);
}
/* Get manufacturer's name */
if (ppd_device_id && ppd_device_id[0] != '\0')
{
mfg = get_tag_value (ppd_device_id, "mfg");
if (!mfg)
mfg = get_tag_value (ppd_device_id, "manufacturer");
mfg_normalized = normalize (mfg);
}
if (!mfg &&
ppd_make &&
ppd_make[0] != '\0')
{
mfg = g_strdup (ppd_make);
mfg_normalized = normalize (ppd_make);
}
/* Get model */
if (ppd_make_and_model &&
ppd_make_and_model[0] != '\0')
{
mdl = g_strdup (ppd_make_and_model);
}
if (!mdl &&
ppd_product &&
ppd_product[0] != '\0')
{
mdl = g_strdup (ppd_product);
}
if (!mdl &&
ppd_device_id &&
ppd_device_id[0] != '\0')
{
mdl = get_tag_value (ppd_device_id, "mdl");
if (!mdl)
mdl = get_tag_value (ppd_device_id, "model");
}
if (ppd_name && ppd_name[0] != '\0' &&
mdl && mdl[0] != '\0' &&
mfg && mfg[0] != '\0')
{
manufacturer_display_name = g_hash_table_lookup (manufacturers_hash, mfg_normalized);
if (!manufacturer_display_name)
{
g_hash_table_insert (manufacturers_hash, g_strdup (mfg_normalized), g_strdup (mfg));
}
else
{
g_free (mfg_normalized);
mfg_normalized = normalize (manufacturer_display_name);
}
item = g_new0 (PPDName, 1);
item->ppd_name = g_strdup (ppd_name);
item->ppd_display_name = g_strdup (mdl);
item->ppd_match_level = -1;
list = g_hash_table_lookup (ppds_hash, mfg_normalized);
if (list)
{
list = g_list_append (list, item);
}
else
{
list = g_list_append (list, item);
g_hash_table_insert (ppds_hash, g_strdup (mfg_normalized), list);
}
}
if (attr == NULL)
break;
}
}
if (response)
ippDelete(response);
if (ppds_hash &&
manufacturers_hash)
{
GHashTableIter iter;
gpointer key;
gpointer value;
GList *ppd_item;
GList *sort_list = NULL;
GList *list_iter;
gchar *name;
data->result = g_new0 (PPDList, 1);
data->result->num_of_manufacturers = g_hash_table_size (ppds_hash);
data->result->manufacturers = g_new0 (PPDManufacturerItem *, data->result->num_of_manufacturers);
g_hash_table_iter_init (&iter, ppds_hash);
while (g_hash_table_iter_next (&iter, &key, &value))
{
sort_list = g_list_append (sort_list, g_strdup (key));
}
/* Sort list of manufacturers */
sort_list = g_list_sort (sort_list, (GCompareFunc) g_strcmp0);
/*
* Fill resulting list of lists (list of manufacturers where
* each item contains list of PPD names)
*/
i = 0;
for (list_iter = sort_list; list_iter; list_iter = list_iter->next)
{
name = (gchar *) list_iter->data;
value = g_hash_table_lookup (ppds_hash, name);
data->result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1);
data->result->manufacturers[i]->manufacturer_name = g_strdup (name);
data->result->manufacturers[i]->manufacturer_display_name = g_strdup (g_hash_table_lookup (manufacturers_hash, name));
data->result->manufacturers[i]->num_of_ppds = g_list_length ((GList *) value);
data->result->manufacturers[i]->ppds = g_new0 (PPDName *, data->result->manufacturers[i]->num_of_ppds);
for (ppd_item = (GList *) value, j = 0; ppd_item; ppd_item = ppd_item->next, j++)
{
data->result->manufacturers[i]->ppds[j] = ppd_item->data;
}
g_list_free ((GList *) value);
i++;
}
g_list_free_full (sort_list, g_free);
g_hash_table_destroy (ppds_hash);
g_hash_table_destroy (manufacturers_hash);
}
get_all_ppds_cb (data);
return NULL;
}
/*
* Get names of all installed PPDs sorted by manufacturers names.
*/
void
get_all_ppds_async (GCancellable *cancellable,
GAPCallback callback,
gpointer user_data)
{
GAPData *data;
GThread *thread;
g_autoptr(GError) error = NULL;
data = g_new0 (GAPData, 1);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
data->context = g_main_context_ref_thread_default ();
thread = g_thread_try_new ("get-all-ppds",
get_all_ppds_func,
data,
&error);
if (!thread)
{
g_warning ("%s", error->message);
callback (NULL, user_data);
get_all_ppds_data_free (data);
}
else
{
g_thread_unref (thread);
}
}
PPDList *
ppd_list_copy (PPDList *list)
{
PPDList *result = NULL;
gint i, j;
if (list)
{
result = g_new0 (PPDList, 1);
result->num_of_manufacturers = list->num_of_manufacturers;
result->manufacturers = g_new0 (PPDManufacturerItem *, list->num_of_manufacturers);
for (i = 0; i < result->num_of_manufacturers; i++)
{
result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1);
result->manufacturers[i]->num_of_ppds = list->manufacturers[i]->num_of_ppds;
result->manufacturers[i]->ppds = g_new0 (PPDName *, result->manufacturers[i]->num_of_ppds);
result->manufacturers[i]->manufacturer_display_name =
g_strdup (list->manufacturers[i]->manufacturer_display_name);
result->manufacturers[i]->manufacturer_name =
g_strdup (list->manufacturers[i]->manufacturer_name);
for (j = 0; j < result->manufacturers[i]->num_of_ppds; j++)
{
result->manufacturers[i]->ppds[j] = g_new0 (PPDName, 1);
result->manufacturers[i]->ppds[j]->ppd_display_name =
g_strdup (list->manufacturers[i]->ppds[j]->ppd_display_name);
result->manufacturers[i]->ppds[j]->ppd_name =
g_strdup (list->manufacturers[i]->ppds[j]->ppd_name);
result->manufacturers[i]->ppds[j]->ppd_match_level =
list->manufacturers[i]->ppds[j]->ppd_match_level;
}
}
}
return result;
}
void
ppd_list_free (PPDList *list)
{
gint i, j;
if (list)
{
for (i = 0; i < list->num_of_manufacturers; i++)
{
for (j = 0; j < list->manufacturers[i]->num_of_ppds; j++)
{
g_free (list->manufacturers[i]->ppds[j]->ppd_name);
g_free (list->manufacturers[i]->ppds[j]->ppd_display_name);
g_free (list->manufacturers[i]->ppds[j]);
}
g_free (list->manufacturers[i]->manufacturer_name);
g_free (list->manufacturers[i]->manufacturer_display_name);
g_free (list->manufacturers[i]->ppds);
g_free (list->manufacturers[i]);
}
g_free (list->manufacturers);
g_free (list);
}
}
gchar *
get_standard_manufacturers_name (const gchar *name)
{
g_autofree gchar *normalized_name = NULL;
gint i;
if (name == NULL)
return NULL;
normalized_name = normalize (name);
for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++)
{
if (g_strcmp0 (manufacturers_names[i].normalized_name, normalized_name) == 0)
{
return g_strdup (manufacturers_names[i].display_name);
}
}
return NULL;
}
typedef struct
{
gchar *printer_name;
gchar *host_name;
gint port;
gchar *result;
PGPCallback callback;
gpointer user_data;
GMainContext *context;
} PGPData;
static gboolean
printer_get_ppd_idle_cb (gpointer user_data)
{
PGPData *data = (PGPData *) user_data;
data->callback (data->result, data->user_data);
return FALSE;
}
static void
printer_get_ppd_data_free (gpointer user_data)
{
PGPData *data = (PGPData *) user_data;
if (data->context)
g_main_context_unref (data->context);
g_free (data->result);
g_free (data->printer_name);
g_free (data->host_name);
g_free (data);
}
static void
printer_get_ppd_cb (gpointer user_data)
{
PGPData *data = (PGPData *) user_data;
GSource *idle_source;
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
printer_get_ppd_idle_cb,
data,
printer_get_ppd_data_free);
g_source_attach (idle_source, data->context);
g_source_unref (idle_source);
}
static gpointer
printer_get_ppd_func (gpointer user_data)
{
PGPData *data = (PGPData *) user_data;
if (data->host_name)
{
http_t *http;
#ifdef HAVE_CUPS_HTTPCONNECT2
http = httpConnect2 (data->host_name, data->port, NULL, AF_UNSPEC,
HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
#else
http = httpConnect (data->host_name, data->port);
#endif
if (http)
{
data->result = g_strdup (cupsGetPPD2 (http, data->printer_name));
httpClose (http);
}
}
else
{
data->result = g_strdup (cupsGetPPD (data->printer_name));
}
printer_get_ppd_cb (data);
return NULL;
}
void
printer_get_ppd_async (const gchar *printer_name,
const gchar *host_name,
gint port,
PGPCallback callback,
gpointer user_data)
{
PGPData *data;
GThread *thread;
g_autoptr(GError) error = NULL;
data = g_new0 (PGPData, 1);
data->printer_name = g_strdup (printer_name);
data->host_name = g_strdup (host_name);
data->port = port;
data->callback = callback;
data->user_data = user_data;
data->context = g_main_context_ref_thread_default ();
thread = g_thread_try_new ("printer-get-ppd",
printer_get_ppd_func,
data,
&error);
if (!thread)
{
g_warning ("%s", error->message);
callback (NULL, user_data);
printer_get_ppd_data_free (data);
}
else
{
g_thread_unref (thread);
}
}
void
pp_devices_list_free (PpDevicesList *result)
{
if (result)
{
g_list_free_full (result->devices, (GDestroyNotify) g_object_unref);
g_free (result);
}
}
typedef struct
{
gchar *printer_name;
cups_dest_t *result;
GNDCallback callback;
gpointer user_data;
GMainContext *context;
} GNDData;
static gboolean
get_named_dest_idle_cb (gpointer user_data)
{
GNDData *data = (GNDData *) user_data;
data->callback (data->result, data->user_data);
return FALSE;
}
static void
get_named_dest_data_free (gpointer user_data)
{
GNDData *data = (GNDData *) user_data;
if (data->context)
g_main_context_unref (data->context);
g_free (data->printer_name);
g_free (data);
}
static void
get_named_dest_cb (gpointer user_data)
{
GNDData *data = (GNDData *) user_data;
GSource *idle_source;
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
get_named_dest_idle_cb,
data,
get_named_dest_data_free);
g_source_attach (idle_source, data->context);
g_source_unref (idle_source);
}
static gpointer
get_named_dest_func (gpointer user_data)
{
GNDData *data = (GNDData *) user_data;
data->result = cupsGetNamedDest (CUPS_HTTP_DEFAULT, data->printer_name, NULL);
get_named_dest_cb (data);
return NULL;
}
void
get_named_dest_async (const gchar *printer_name,
GNDCallback callback,
gpointer user_data)
{
GNDData *data;
GThread *thread;
g_autoptr(GError) error = NULL;
data = g_new0 (GNDData, 1);
data->printer_name = g_strdup (printer_name);
data->callback = callback;
data->user_data = user_data;
data->context = g_main_context_ref_thread_default ();
thread = g_thread_try_new ("get-named-dest",
get_named_dest_func,
data,
&error);
if (!thread)
{
g_warning ("%s", error->message);
callback (NULL, user_data);
get_named_dest_data_free (data);
}
else
{
g_thread_unref (thread);
}
}
typedef struct
{
GCancellable *cancellable;
PAOCallback callback;
gpointer user_data;
} PAOData;
static void
printer_add_option_async_dbus_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GVariant *output;
gboolean success = FALSE;
PAOData *data = (PAOData *) user_data;
g_autoptr(GError) error = NULL;
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
g_object_unref (source_object);
if (output)
{
const gchar *ret_error;
g_variant_get (output, "(&s)", &ret_error);
if (ret_error[0] != '\0')
g_warning ("cups-pk-helper: setting of an option failed: %s", ret_error);
else
success = TRUE;
g_variant_unref (output);
}
else
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("%s", error->message);
}
if (!g_cancellable_is_cancelled (data->cancellable))
data->callback (success, data->user_data);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data);
}
void
printer_add_option_async (const gchar *printer_name,
const gchar *option_name,
gchar **values,
gboolean set_default,
GCancellable *cancellable,
PAOCallback callback,
gpointer user_data)
{
GVariantBuilder array_builder;
GDBusConnection *bus;
PAOData *data;
g_autoptr(GError) error = NULL;
gint i;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
callback (FALSE, user_data);
return;
}
g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as"));
if (values)
{
for (i = 0; values[i]; i++)
g_variant_builder_add (&array_builder, "s", values[i]);
}
data = g_new0 (PAOData, 1);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
g_dbus_connection_call (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
set_default ? "PrinterAddOptionDefault" :
"PrinterAddOption",
g_variant_new ("(ssas)",
printer_name,
option_name,
&array_builder),
G_VARIANT_TYPE ("(s)"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT,
cancellable,
printer_add_option_async_dbus_cb,
data);
}
typedef struct
{
GCancellable *cancellable;
GCDCallback callback;
gpointer user_data;
GList *backend_list;
} GCDData;
static gint
get_suffix_index (const gchar *string)
{
gchar *number;
gchar *endptr;
gint index = -1;
number = g_strrstr (string, ":");
if (number)
{
number++;
index = g_ascii_strtoll (number, &endptr, 10);
if (index == 0 && endptr == number)
index = -1;
}
return index;
}
static void
get_cups_devices_async_dbus_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
PpPrintDevice **devices = NULL;
GVariant *output;
GCDData *data = (GCDData *) user_data;
g_autoptr(GError) error = NULL;
GList *result = NULL;
gint num_of_devices = 0;
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
res,
&error);
if (output)
{
const gchar *ret_error;
GVariant *devices_variant = NULL;
gboolean is_network_device;
g_variant_get (output, "(&s@a{ss})",
&ret_error,
&devices_variant);
if (ret_error[0] != '\0')
{
g_warning ("cups-pk-helper: getting of CUPS devices failed: %s", ret_error);
}
if (devices_variant)
{
GVariantIter *iter;
GVariant *item;
gint index = -1, max_index = -1, i;
g_variant_get (devices_variant, "a{ss}", &iter);
while ((item = g_variant_iter_next_value (iter)))
{
const gchar *key, *value;
g_variant_get (item, "{&s&s}", &key, &value);
index = get_suffix_index (key);
if (index > max_index)
max_index = index;
g_variant_unref (item);
}
if (max_index >= 0)
{
num_of_devices = max_index + 1;
devices = g_new0 (PpPrintDevice *, num_of_devices);
g_variant_get (devices_variant, "a{ss}", &iter);
while ((item = g_variant_iter_next_value (iter)))
{
const gchar *key, *value;
g_variant_get (item, "{&s&s}", &key, &value);
index = get_suffix_index (key);
if (index >= 0)
{
if (!devices[index])
devices[index] = pp_print_device_new ();
if (g_str_has_prefix (key, "device-class"))
{
is_network_device = g_strcmp0 (value, "network") == 0;
g_object_set (devices[index], "is-network-device", is_network_device, NULL);
}
else if (g_str_has_prefix (key, "device-id"))
g_object_set (devices[index], "device-id", value, NULL);
else if (g_str_has_prefix (key, "device-info"))
g_object_set (devices[index], "device-info", value, NULL);
else if (g_str_has_prefix (key, "device-make-and-model"))
{
g_object_set (devices[index],
"device-make-and-model", value,
"device-name", value,
NULL);
}
else if (g_str_has_prefix (key, "device-uri"))
g_object_set (devices[index], "device-uri", value, NULL);
else if (g_str_has_prefix (key, "device-location"))
g_object_set (devices[index], "device-location", value, NULL);
g_object_set (devices[index], "acquisition-method", ACQUISITION_METHOD_DEFAULT_CUPS_SERVER, NULL);
}
g_variant_unref (item);
}
for (i = 0; i < num_of_devices; i++)
result = g_list_append (result, devices[i]);
g_free (devices);
}
g_variant_unref (devices_variant);
}
g_variant_unref (output);
}
else
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("%s", error->message);
data->callback (result,
TRUE,
g_cancellable_is_cancelled (data->cancellable),
data->user_data);
g_list_free_full (data->backend_list, g_free);
data->backend_list = NULL;
g_object_unref (source_object);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data);
return;
}
if (data->backend_list)
{
if (!g_cancellable_is_cancelled (data->cancellable))
{
GVariantBuilder *include_scheme_builder = NULL;
GVariantBuilder *exclude_scheme_builder = NULL;
g_autofree gchar *backend_name = NULL;
backend_name = data->backend_list->data;
data->callback (result,
FALSE,
FALSE,
data->user_data);
if (g_strcmp0 (backend_name, OTHER_BACKENDS) != 0)
{
include_scheme_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
g_variant_builder_add (include_scheme_builder, "s", backend_name);
}
else
{
exclude_scheme_builder = create_other_backends_array ();
}
data->backend_list = g_list_remove_link (data->backend_list, data->backend_list);
g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source_object)),
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"DevicesGet",
g_variant_new ("(iiasas)",
0,
0,
include_scheme_builder,
exclude_scheme_builder),
G_VARIANT_TYPE ("(sa{ss})"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT,
data->cancellable,
get_cups_devices_async_dbus_cb,
user_data);
if (include_scheme_builder)
g_variant_builder_unref (include_scheme_builder);
if (exclude_scheme_builder)
g_variant_builder_unref (exclude_scheme_builder);
return;
}
else
{
data->callback (result,
TRUE,
TRUE,
data->user_data);
g_list_free_full (data->backend_list, g_free);
data->backend_list = NULL;
}
}
else
{
data->callback (result,
TRUE,
g_cancellable_is_cancelled (data->cancellable),
data->user_data);
}
g_object_unref (source_object);
if (data->cancellable)
g_object_unref (data->cancellable);
g_free (data);
}
void
get_cups_devices_async (GCancellable *cancellable,
GCDCallback callback,
gpointer user_data)
{
GDBusConnection *bus;
GVariantBuilder include_scheme_builder;
GCDData *data;
g_autoptr(GError) error = NULL;
g_autofree gchar *backend_name = NULL;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus)
{
g_warning ("Failed to get system bus: %s", error->message);
callback (NULL, TRUE, FALSE, user_data);
return;
}
data = g_new0 (GCDData, 1);
if (cancellable)
data->cancellable = g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
data->backend_list = create_backends_list ();
backend_name = data->backend_list->data;
g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&include_scheme_builder, "s", backend_name);
data->backend_list = g_list_remove_link (data->backend_list, data->backend_list);
g_dbus_connection_call (bus,
MECHANISM_BUS,
"/",
MECHANISM_BUS,
"DevicesGet",
g_variant_new ("(iiasas)",
0,
0,
&include_scheme_builder,
NULL),
G_VARIANT_TYPE ("(sa{ss})"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT,
cancellable,
get_cups_devices_async_dbus_cb,
data);
}
gchar *
guess_device_hostname (PpPrintDevice *device)
{
http_uri_status_t status;
char scheme[HTTP_MAX_URI];
char username[HTTP_MAX_URI];
char hostname[HTTP_MAX_URI];
char resource[HTTP_MAX_URI];
int port;
gchar *result = NULL;
gchar *hostname_begin;
gchar *hostname_end = NULL;
if (device != NULL && pp_print_device_get_device_uri (device) != NULL)
{
if (g_str_has_prefix (pp_print_device_get_device_uri (device), "socket") ||
g_str_has_prefix (pp_print_device_get_device_uri (device), "lpd") ||
g_str_has_prefix (pp_print_device_get_device_uri (device), "ipp") ||
g_str_has_prefix (pp_print_device_get_device_uri (device), "smb"))
{
status = httpSeparateURI (HTTP_URI_CODING_ALL,
pp_print_device_get_device_uri (device),
scheme, HTTP_MAX_URI,
username, HTTP_MAX_URI,
hostname, HTTP_MAX_URI,
&port,
resource, HTTP_MAX_URI);
if (status >= HTTP_URI_STATUS_OK &&
hostname[0] != '\0')
result = g_strdup (hostname);
}
else if ((g_str_has_prefix (pp_print_device_get_device_uri (device), "dnssd") ||
g_str_has_prefix (pp_print_device_get_device_uri (device), "mdns")) &&
pp_print_device_get_device_info (device) != NULL)
{
/*
* CUPS browses its printers as
* "PrinterName @ ComputerName" or "PrinterInfo @ ComputerName"
* through DNS-SD.
*/
hostname_begin = g_strrstr (pp_print_device_get_device_info (device), " @ ");
if (hostname_begin != NULL)
result = g_strdup (hostname_begin + 3);
}
else if (g_str_has_prefix (pp_print_device_get_device_uri (device), "hp:/net/") ||
g_str_has_prefix (pp_print_device_get_device_uri (device), "hpfax:/net/"))
{
/*
* HPLIP printers have URI of form hp:/net/%s?ip=%s&port=%d
* or hp:/net/%s?ip=%s.
*/
hostname_begin = g_strrstr (pp_print_device_get_device_uri (device), "ip=");
if (hostname_begin != NULL)
{
hostname_begin += 3;
hostname_end = strstr (hostname_begin, "&");
}
if (hostname_end != NULL)
result = g_strndup (hostname_begin, hostname_end - hostname_begin);
else
result = g_strdup (hostname_begin);
}
}
return result;
}
gchar *
canonicalize_device_name (GList *device_names,
GList *local_cups_devices,
cups_dest_t *dests,
gint num_of_dests,
PpPrintDevice *device)
{
PpPrintDevice *item;
gboolean already_present;
GList *iter;
gsize len;
g_autofree gchar *name = NULL;
gchar *occurrence;
gint name_index, j;
static const char * const residues[] = {
"-foomatic",
"-hpijs",
"-hpcups",
"-cups",
"-gutenprint",
"-series",
"-label-printer",
"-dot-matrix",
"-ps3",
"-ps2",
"-br-script",
"-kpdl",
"-pcl3",
"-pcl",
"-zxs",
"-pxl"};
if (pp_print_device_get_device_id (device) != NULL)
{
name = get_tag_value (pp_print_device_get_device_id (device), "mdl");
if (name == NULL)
name = get_tag_value (pp_print_device_get_device_id (device), "model");
}
if (name == NULL &&
pp_print_device_get_device_make_and_model (device) != NULL &&
pp_print_device_get_device_make_and_model (device)[0] != '\0')
{
name = g_strdup (pp_print_device_get_device_make_and_model (device));
}
if (name == NULL &&
pp_print_device_get_device_original_name (device) != NULL &&
pp_print_device_get_device_original_name (device)[0] != '\0')
{
name = g_strdup (pp_print_device_get_device_original_name (device));
}
if (name == NULL &&
pp_print_device_get_device_info (device) != NULL &&
pp_print_device_get_device_info (device)[0] != '\0')
{
name = g_strdup (pp_print_device_get_device_info (device));
}
if (name == NULL)
return NULL;
g_strstrip (name);
g_strcanon (name, ALLOWED_CHARACTERS, '-');
/* Remove common strings found in driver names */
for (j = 0; j < G_N_ELEMENTS (residues); j++)
{
g_autofree gchar *lower_name = g_ascii_strdown (name, -1);
occurrence = g_strrstr (lower_name, residues[j]);
if (occurrence != NULL)
{
occurrence[0] = '\0';
name[strlen (lower_name)] = '\0';
}
}
/* Remove trailing "-" */
len = strlen (name);
while (len-- && name[len] == '-')
name[len] = '\0';
/* Merge "--" to "-" */
occurrence = g_strrstr (name, "--");
while (occurrence != NULL)
{
shift_string_left (occurrence);
occurrence = g_strrstr (name, "--");
}
/* Remove leading "-" */
if (name[0] == '-')
shift_string_left (name);
name_index = 2;
already_present = FALSE;
while (TRUE)
{
g_autofree gchar *new_name = NULL;
if (already_present)
{
new_name = g_strdup_printf ("%s-%d", name, name_index);
name_index++;
}
else
{
new_name = g_strdup (name);
}
already_present = FALSE;
for (j = 0; j < num_of_dests; j++)
if (g_strcmp0 (dests[j].name, new_name) == 0)
already_present = TRUE;
for (iter = device_names; iter; iter = iter->next)
{
gchar *device_original_name = iter->data;
if (g_strcmp0 (device_original_name, new_name) == 0)
already_present = TRUE;
}
for (iter = local_cups_devices; iter; iter = iter->next)
{
item = (PpPrintDevice *) iter->data;
if (g_strcmp0 (pp_print_device_get_device_original_name (item), new_name) == 0)
already_present = TRUE;
}
if (!already_present)
return g_steal_pointer (&new_name);
}
}
void
shift_string_left (gchar *str)
{
gchar *next;
if (str != NULL && str[0] != '\0')
{
next = g_utf8_find_next_char (str, NULL);
memmove (str, next, strlen (next) + 1);
}
}