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)
3772 lines
108 KiB
C
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);
|
|
}
|
|
}
|