Pass all discovered devices to 'GroupPhysicalDevices' instead of just the devices already present in the list and the newly discovered ones. Replace an existing device in the list if there is a better device (e.g. replace 'usb:' by 'hp:' device). https://bugzilla.gnome.org/show_bug.cgi?id=693186
4255 lines
120 KiB
C
4255 lines
120 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 *tmp = NULL;
|
|
gchar *res = NULL;
|
|
gchar *result = NULL;
|
|
gint i, j = 0, k = -1;
|
|
|
|
if (input_string)
|
|
{
|
|
tmp = g_strstrip (g_ascii_strdown (input_string, -1));
|
|
if (tmp)
|
|
{
|
|
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);
|
|
g_free (tmp);
|
|
g_free (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;
|
|
}
|
|
|
|
/* Cancels subscription of given id */
|
|
void
|
|
cancel_cups_subscription (gint id)
|
|
{
|
|
http_t *http;
|
|
ipp_t *request;
|
|
|
|
if (id >= 0 &&
|
|
((http = httpConnectEncrypt (cupsServer (), ippPort (),
|
|
cupsEncryption ())) != NULL)) {
|
|
request = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
|
"printer-uri", NULL, "/");
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
|
|
"requesting-user-name", NULL, cupsUser ());
|
|
ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
|
|
"notify-subscription-id", id);
|
|
ippDelete (cupsDoRequest (http, request, "/"));
|
|
httpClose (http);
|
|
}
|
|
}
|
|
|
|
/* Returns id of renewed subscription or new id */
|
|
gint
|
|
renew_cups_subscription (gint id,
|
|
const char * const *events,
|
|
gint num_events,
|
|
gint lease_duration)
|
|
{
|
|
ipp_attribute_t *attr = NULL;
|
|
http_t *http;
|
|
ipp_t *request;
|
|
ipp_t *response = NULL;
|
|
gint result = -1;
|
|
|
|
if ((http = httpConnectEncrypt (cupsServer (), ippPort (),
|
|
cupsEncryption ())) == NULL) {
|
|
g_debug ("Connection to CUPS server \'%s\' failed.", cupsServer ());
|
|
}
|
|
else {
|
|
if (id >= 0) {
|
|
request = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
|
"printer-uri", NULL, "/");
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
|
|
"requesting-user-name", NULL, cupsUser ());
|
|
ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
|
|
"notify-subscription-id", id);
|
|
ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
|
|
"notify-lease-duration", lease_duration);
|
|
response = cupsDoRequest (http, request, "/");
|
|
if (response != NULL &&
|
|
ippGetStatusCode (response) <= IPP_OK_CONFLICT) {
|
|
if ((attr = ippFindAttribute (response, "notify-lease-duration",
|
|
IPP_TAG_INTEGER)) == NULL)
|
|
g_debug ("No notify-lease-duration in response!\n");
|
|
else
|
|
if (ippGetInteger (attr, 0) == lease_duration)
|
|
result = id;
|
|
}
|
|
}
|
|
|
|
if (result < 0) {
|
|
request = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
|
"printer-uri", NULL, "/");
|
|
ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
|
|
"requesting-user-name", NULL, cupsUser ());
|
|
ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
|
|
"notify-events", num_events, NULL, events);
|
|
ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
|
|
"notify-pull-method", NULL, "ippget");
|
|
ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
|
|
"notify-recipient-uri", NULL, "dbus://");
|
|
ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
|
|
"notify-lease-duration", lease_duration);
|
|
response = cupsDoRequest (http, request, "/");
|
|
|
|
if (response != NULL &&
|
|
ippGetStatusCode (response) <= IPP_OK_CONFLICT) {
|
|
if ((attr = ippFindAttribute (response, "notify-subscription-id",
|
|
IPP_TAG_INTEGER)) == NULL)
|
|
g_debug ("No notify-subscription-id in response!\n");
|
|
else
|
|
result = ippGetInteger (attr, 0);
|
|
}
|
|
}
|
|
|
|
if (response)
|
|
ippDelete (response);
|
|
|
|
httpClose (http);
|
|
}
|
|
|
|
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 char *printer_location = NULL;
|
|
const char *printer_info = NULL;
|
|
const char *printer_uri = NULL;
|
|
const char *device_uri = NULL;
|
|
const char *job_sheets = NULL;
|
|
gboolean result = FALSE;
|
|
gboolean accepting = TRUE;
|
|
gboolean printer_paused = FALSE;
|
|
gboolean default_printer = FALSE;
|
|
gboolean printer_shared = FALSE;
|
|
GError *error = NULL;
|
|
http_t *http;
|
|
gchar *ppd_link;
|
|
gchar *ppd_filename = NULL;
|
|
gchar **sheets = NULL;
|
|
gchar **users_allowed = NULL;
|
|
gchar **users_denied = NULL;
|
|
gchar **member_names = NULL;
|
|
gchar *start_sheet = NULL;
|
|
gchar *end_sheet = NULL;
|
|
gchar *error_policy = NULL;
|
|
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
|
|
*/
|
|
if ((http = httpConnectEncrypt (cupsServer (), ippPort (),
|
|
cupsEncryption ())) != 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);
|
|
g_error_free (error);
|
|
}
|
|
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;
|
|
|
|
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,
|
|
&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", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppd_link)
|
|
{
|
|
g_unlink (ppd_link);
|
|
g_free (ppd_link);
|
|
g_free (ppd_filename);
|
|
}
|
|
|
|
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);
|
|
g_free (op_policy);
|
|
g_free (error_policy);
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_set_accepting_jobs (const gchar *printer_name,
|
|
gboolean accepting_jobs,
|
|
const gchar *reason)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_set_enabled (const gchar *printer_name,
|
|
gboolean enabled)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_delete (const gchar *printer_name)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_set_default (const gchar *printer_name)
|
|
{
|
|
GDBusConnection *bus;
|
|
const char *cups_server;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_set_job_sheets (const gchar *printer_name,
|
|
const gchar *start_sheet,
|
|
const gchar *end_sheet)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
printer_set_policy (const gchar *printer_name,
|
|
const gchar *policy,
|
|
gboolean error_policy)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
class_add_printer (const gchar *class_name,
|
|
const gchar *printer_name)
|
|
{
|
|
GDBusConnection *bus;
|
|
GVariant *output;
|
|
gboolean result = FALSE;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
}
|
|
|
|
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;
|
|
gchar *printer_uri;
|
|
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);
|
|
|
|
g_free (printer_uri);
|
|
|
|
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;
|
|
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);
|
|
|
|
g_error_free (error);
|
|
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;
|
|
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 (error->code != G_IO_ERROR_CANCELLED)
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
/* 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;
|
|
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);
|
|
g_error_free (error);
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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);
|
|
g_error_free (error);
|
|
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;
|
|
gchar *ppd_filename;
|
|
gint i;
|
|
|
|
data->result = g_new0 (gchar *, g_strv_length (data->ppds_names) + 1);
|
|
for (i = 0; data->ppds_names[i]; i++)
|
|
{
|
|
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);
|
|
g_free (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;
|
|
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);
|
|
|
|
g_error_free (error);
|
|
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;
|
|
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;
|
|
gchar *driver;
|
|
gchar *match;
|
|
|
|
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)))
|
|
{
|
|
g_variant_get (item,
|
|
"(ss)",
|
|
&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_free (driver);
|
|
g_free (match);
|
|
g_variant_unref (item);
|
|
}
|
|
}
|
|
|
|
g_variant_unref (array);
|
|
}
|
|
|
|
g_variant_unref (output);
|
|
}
|
|
else
|
|
{
|
|
if (error->code != G_IO_ERROR_CANCELLED)
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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;
|
|
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)
|
|
{
|
|
gchar *key;
|
|
gchar *value;
|
|
gchar *number;
|
|
gchar *endptr;
|
|
gchar *suffix;
|
|
|
|
g_variant_get (devices_variant,
|
|
"a{ss}",
|
|
&iter);
|
|
|
|
while ((item = g_variant_iter_next_value (iter)))
|
|
{
|
|
g_variant_get (item,
|
|
"{ss}",
|
|
&key,
|
|
&value);
|
|
|
|
if (g_str_equal (value, data->device_uri))
|
|
{
|
|
number = g_strrstr (key, ":");
|
|
if (number != NULL)
|
|
{
|
|
number++;
|
|
index = g_ascii_strtoll (number, &endptr, 10);
|
|
if (index == 0 && endptr == (number))
|
|
index = -1;
|
|
}
|
|
}
|
|
|
|
g_free (key);
|
|
g_free (value);
|
|
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)))
|
|
{
|
|
gchar *key;
|
|
gchar *value;
|
|
|
|
g_variant_get (item,
|
|
"{ss}",
|
|
&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_free (key);
|
|
g_free (value);
|
|
g_variant_unref (item);
|
|
}
|
|
|
|
g_free (suffix);
|
|
}
|
|
|
|
g_variant_unref (devices_variant);
|
|
}
|
|
|
|
g_variant_unref (output);
|
|
}
|
|
else
|
|
{
|
|
if (error->code != G_IO_ERROR_CANCELLED)
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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;
|
|
const gchar *ppd_make_and_model;
|
|
const gchar *ppd_device_id;
|
|
const gchar *ppd_name;
|
|
const gchar *ppd_product;
|
|
const gchar *ppd_make;
|
|
gchar *mfg;
|
|
gchar *mfg_normalized;
|
|
gchar *mdl;
|
|
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))
|
|
{
|
|
while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
|
|
attr = ippNextAttribute (response);
|
|
|
|
if (attr == NULL)
|
|
break;
|
|
|
|
ppd_device_id = NULL;
|
|
ppd_make_and_model = NULL;
|
|
ppd_name = NULL;
|
|
ppd_product = NULL;
|
|
ppd_make = NULL;
|
|
mfg = NULL;
|
|
mfg_normalized = NULL;
|
|
mdl = NULL;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
g_free (mdl);
|
|
g_free (mfg);
|
|
g_free (mfg_normalized);
|
|
|
|
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;
|
|
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);
|
|
|
|
g_error_free (error);
|
|
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 (gchar *name)
|
|
{
|
|
gchar *normalized_name;
|
|
gchar *result = NULL;
|
|
gint i;
|
|
|
|
if (name)
|
|
{
|
|
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)
|
|
{
|
|
result = g_strdup (manufacturers_names[i].display_name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_free (normalized_name);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
|
|
http = httpConnect (data->host_name, data->port);
|
|
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;
|
|
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);
|
|
|
|
g_error_free (error);
|
|
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) pp_print_device_free);
|
|
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;
|
|
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);
|
|
|
|
g_error_free (error);
|
|
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;
|
|
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 (error->code != G_IO_ERROR_CANCELLED)
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
g_error_free (error);
|
|
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 (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;
|
|
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;
|
|
|
|
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;
|
|
gchar *key;
|
|
gchar *value;
|
|
gint index = -1, max_index = -1, i;
|
|
|
|
g_variant_get (devices_variant, "a{ss}", &iter);
|
|
while ((item = g_variant_iter_next_value (iter)))
|
|
{
|
|
g_variant_get (item, "{ss}", &key, &value);
|
|
|
|
index = get_suffix_index (key);
|
|
if (index > max_index)
|
|
max_index = index;
|
|
|
|
g_free (key);
|
|
g_free (value);
|
|
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)))
|
|
{
|
|
g_variant_get (item, "{ss}", &key, &value);
|
|
|
|
index = get_suffix_index (key);
|
|
if (index >= 0)
|
|
{
|
|
if (!devices[index])
|
|
devices[index] = g_new0 (PpPrintDevice, 1);
|
|
|
|
if (g_str_has_prefix (key, "device-class"))
|
|
devices[index]->device_class = g_strdup (value);
|
|
else if (g_str_has_prefix (key, "device-id"))
|
|
devices[index]->device_id = g_strdup (value);
|
|
else if (g_str_has_prefix (key, "device-info"))
|
|
devices[index]->device_info = g_strdup (value);
|
|
else if (g_str_has_prefix (key, "device-make-and-model"))
|
|
{
|
|
devices[index]->device_make_and_model = g_strdup (value);
|
|
devices[index]->device_name = g_strdup (value);
|
|
}
|
|
else if (g_str_has_prefix (key, "device-uri"))
|
|
devices[index]->device_uri = g_strdup (value);
|
|
else if (g_str_has_prefix (key, "device-location"))
|
|
devices[index]->device_location = g_strdup (value);
|
|
|
|
devices[index]->acquisition_method = ACQUISITION_METHOD_DEFAULT_CUPS_SERVER;
|
|
}
|
|
|
|
g_free (key);
|
|
g_free (value);
|
|
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 (error->domain != G_IO_ERROR ||
|
|
error->code != G_IO_ERROR_CANCELLED)
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
|
|
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;
|
|
gchar *backend_name;
|
|
|
|
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 ();
|
|
}
|
|
|
|
g_free (backend_name);
|
|
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;
|
|
GError *error = NULL;
|
|
gchar *backend_name;
|
|
|
|
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (!bus)
|
|
{
|
|
g_warning ("Failed to get system bus: %s", error->message);
|
|
g_error_free (error);
|
|
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);
|
|
|
|
g_free (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);
|
|
}
|
|
|
|
void
|
|
pp_print_device_free (PpPrintDevice *device)
|
|
{
|
|
if (device)
|
|
{
|
|
g_free (device->device_class);
|
|
g_free (device->device_id);
|
|
g_free (device->device_info);
|
|
g_free (device->device_make_and_model);
|
|
g_free (device->device_uri);
|
|
g_free (device->device_location);
|
|
g_free (device->device_name);
|
|
g_free (device->device_ppd);
|
|
g_free (device->host_name);
|
|
g_free (device->display_name);
|
|
g_free (device->device_original_name);
|
|
g_free (device);
|
|
}
|
|
}
|
|
|
|
PpPrintDevice *
|
|
pp_print_device_copy (PpPrintDevice *device)
|
|
{
|
|
PpPrintDevice *result = NULL;
|
|
|
|
if (device)
|
|
{
|
|
result = g_new (PpPrintDevice, 1);
|
|
|
|
result->is_authenticated_server = device->is_authenticated_server;
|
|
result->device_class = g_strdup (device->device_class);
|
|
result->device_id = g_strdup (device->device_id);
|
|
result->device_info = g_strdup (device->device_info);
|
|
result->device_make_and_model = g_strdup (device->device_make_and_model);
|
|
result->device_uri = g_strdup (device->device_uri);
|
|
result->device_location = g_strdup (device->device_location);
|
|
result->device_name = g_strdup (device->device_name);
|
|
result->device_ppd = g_strdup (device->device_ppd);
|
|
result->host_name = g_strdup (device->host_name);
|
|
result->host_port = device->host_port;
|
|
result->acquisition_method = device->acquisition_method;
|
|
result->display_name = g_strdup (device->display_name);
|
|
result->device_original_name = g_strdup (device->device_original_name);
|
|
result->network_device = device->network_device;
|
|
result->show = device->show;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
gchar *printer_name;
|
|
gboolean my_jobs;
|
|
gint which_jobs;
|
|
cups_job_t *jobs;
|
|
gint num_of_jobs;
|
|
CGJCallback callback;
|
|
gpointer user_data;
|
|
GMainContext *context;
|
|
} CGJData;
|
|
|
|
static gboolean
|
|
cups_get_jobs_idle_cb (gpointer user_data)
|
|
{
|
|
CGJData *data = (CGJData *) user_data;
|
|
|
|
data->callback (data->jobs,
|
|
data->num_of_jobs,
|
|
data->user_data);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
cups_get_jobs_data_free (gpointer user_data)
|
|
{
|
|
CGJData *data = (CGJData *) user_data;
|
|
|
|
if (data->context)
|
|
g_main_context_unref (data->context);
|
|
g_free (data->printer_name);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
cups_get_jobs_cb (gpointer user_data)
|
|
{
|
|
CGJData *data = (CGJData *) user_data;
|
|
GSource *idle_source;
|
|
|
|
idle_source = g_idle_source_new ();
|
|
g_source_set_callback (idle_source,
|
|
cups_get_jobs_idle_cb,
|
|
data,
|
|
cups_get_jobs_data_free);
|
|
g_source_attach (idle_source, data->context);
|
|
g_source_unref (idle_source);
|
|
}
|
|
|
|
static gpointer
|
|
cups_get_jobs_func (gpointer user_data)
|
|
{
|
|
CGJData *data = (CGJData *) user_data;
|
|
|
|
data->num_of_jobs = cupsGetJobs (&data->jobs,
|
|
data->printer_name,
|
|
data->my_jobs ? 1 : 0,
|
|
data->which_jobs);
|
|
|
|
cups_get_jobs_cb (data);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
cups_get_jobs_async (const gchar *printer_name,
|
|
gboolean my_jobs,
|
|
gint which_jobs,
|
|
CGJCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
CGJData *data;
|
|
GThread *thread;
|
|
GError *error = NULL;
|
|
|
|
data = g_new0 (CGJData, 1);
|
|
data->printer_name = g_strdup (printer_name);
|
|
data->my_jobs = my_jobs;
|
|
data->which_jobs = which_jobs;
|
|
data->callback = callback;
|
|
data->user_data = user_data;
|
|
data->context = g_main_context_ref_thread_default ();
|
|
|
|
thread = g_thread_try_new ("cups-get-jobs",
|
|
cups_get_jobs_func,
|
|
data,
|
|
&error);
|
|
|
|
if (!thread)
|
|
{
|
|
g_warning ("%s", error->message);
|
|
callback (NULL, 0, user_data);
|
|
|
|
g_error_free (error);
|
|
cups_get_jobs_data_free (data);
|
|
}
|
|
else
|
|
{
|
|
g_thread_unref (thread);
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GCancellable *cancellable;
|
|
JCPCallback callback;
|
|
gpointer user_data;
|
|
} JCPData;
|
|
|
|
static void
|
|
job_cancel_purge_async_dbus_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
GVariant *output;
|
|
JCPData *data = (JCPData *) user_data;
|
|
GError *error = NULL;
|
|
|
|
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
|
|
res,
|
|
&error);
|
|
g_object_unref (source_object);
|
|
|
|
if (output)
|
|
{
|
|
g_variant_unref (output);
|
|
}
|
|
else
|
|
{
|
|
if (!g_cancellable_is_cancelled (data->cancellable))
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
data->callback (data->user_data);
|
|
|
|
if (data->cancellable)
|
|
g_object_unref (data->cancellable);
|
|
g_free (data);
|
|
}
|
|
|
|
void
|
|
job_cancel_purge_async (gint job_id,
|
|
gboolean job_purge,
|
|
GCancellable *cancellable,
|
|
JCPCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GDBusConnection *bus;
|
|
JCPData *data;
|
|
GError *error = NULL;
|
|
|
|
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (!bus)
|
|
{
|
|
g_warning ("Failed to get session bus: %s", error->message);
|
|
g_error_free (error);
|
|
callback (user_data);
|
|
return;
|
|
}
|
|
|
|
data = g_new0 (JCPData, 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,
|
|
"JobCancelPurge",
|
|
g_variant_new ("(ib)",
|
|
job_id,
|
|
job_purge),
|
|
G_VARIANT_TYPE ("(s)"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
job_cancel_purge_async_dbus_cb,
|
|
data);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GCancellable *cancellable;
|
|
JSHUCallback callback;
|
|
gpointer user_data;
|
|
} JSHUData;
|
|
|
|
static void
|
|
job_set_hold_until_async_dbus_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
GVariant *output;
|
|
JSHUData *data = (JSHUData *) user_data;
|
|
GError *error = NULL;
|
|
|
|
output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
|
|
res,
|
|
&error);
|
|
g_object_unref (source_object);
|
|
|
|
if (output)
|
|
{
|
|
g_variant_unref (output);
|
|
}
|
|
else
|
|
{
|
|
if (!g_cancellable_is_cancelled (data->cancellable))
|
|
g_warning ("%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
data->callback (data->user_data);
|
|
|
|
if (data->cancellable)
|
|
g_object_unref (data->cancellable);
|
|
g_free (data);
|
|
}
|
|
|
|
void
|
|
job_set_hold_until_async (gint job_id,
|
|
const gchar *job_hold_until,
|
|
GCancellable *cancellable,
|
|
JSHUCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GDBusConnection *bus;
|
|
JSHUData *data;
|
|
GError *error = NULL;
|
|
|
|
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (!bus)
|
|
{
|
|
g_warning ("Failed to get session bus: %s", error->message);
|
|
g_error_free (error);
|
|
callback (user_data);
|
|
return;
|
|
}
|
|
|
|
data = g_new0 (JSHUData, 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,
|
|
"JobSetHoldUntil",
|
|
g_variant_new ("(is)",
|
|
job_id,
|
|
job_hold_until),
|
|
G_VARIANT_TYPE ("(s)"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
job_set_hold_until_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 && device->device_uri != NULL)
|
|
{
|
|
if (g_str_has_prefix (device->device_uri, "socket") ||
|
|
g_str_has_prefix (device->device_uri, "lpd") ||
|
|
g_str_has_prefix (device->device_uri, "ipp") ||
|
|
g_str_has_prefix (device->device_uri, "smb"))
|
|
{
|
|
status = httpSeparateURI (HTTP_URI_CODING_ALL,
|
|
device->device_uri,
|
|
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 (device->device_uri, "dnssd") ||
|
|
g_str_has_prefix (device->device_uri, "mdns")) &&
|
|
device->device_info != NULL)
|
|
{
|
|
/*
|
|
* CUPS browses its printers as
|
|
* "PrinterName @ ComputerName" or "PrinterInfo @ ComputerName"
|
|
* through DNS-SD.
|
|
*/
|
|
hostname_begin = g_strrstr (device->device_info, " @ ");
|
|
if (hostname_begin != NULL)
|
|
result = g_strdup (hostname_begin + 3);
|
|
}
|
|
else if (g_str_has_prefix (device->device_uri, "hp:/net/") ||
|
|
g_str_has_prefix (device->device_uri, "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 (device->device_uri, "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 *devices,
|
|
GList *local_cups_devices,
|
|
cups_dest_t *dests,
|
|
gint num_of_dests,
|
|
PpPrintDevice *device)
|
|
{
|
|
PpPrintDevice *item;
|
|
gboolean already_present;
|
|
GList *iter;
|
|
gsize len;
|
|
gchar *name = NULL;
|
|
gchar *new_name;
|
|
gchar *lower_name;
|
|
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 (device->device_id != NULL)
|
|
{
|
|
name = get_tag_value (device->device_id, "mdl");
|
|
if (name == NULL)
|
|
name = get_tag_value (device->device_id, "model");
|
|
}
|
|
|
|
if (name == NULL &&
|
|
device->device_make_and_model != NULL &&
|
|
device->device_make_and_model[0] != '\0')
|
|
{
|
|
name = g_strdup (device->device_make_and_model);
|
|
}
|
|
|
|
if (name == NULL &&
|
|
device->device_original_name != NULL &&
|
|
device->device_original_name[0] != '\0')
|
|
{
|
|
name = g_strdup (device->device_original_name);
|
|
}
|
|
|
|
if (name == NULL &&
|
|
device->device_info != NULL &&
|
|
device->device_info[0] != '\0')
|
|
{
|
|
name = g_strdup (device->device_info);
|
|
}
|
|
|
|
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++)
|
|
{
|
|
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';
|
|
}
|
|
|
|
g_free (lower_name);
|
|
}
|
|
|
|
/* 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;
|
|
do
|
|
{
|
|
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 = devices; iter; iter = iter->next)
|
|
{
|
|
item = (PpPrintDevice *) iter->data;
|
|
if (g_strcmp0 (item->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 (item->device_original_name, new_name) == 0)
|
|
already_present = TRUE;
|
|
}
|
|
|
|
if (already_present)
|
|
g_free (new_name);
|
|
|
|
} while (already_present);
|
|
|
|
g_free (name);
|
|
|
|
return 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);
|
|
}
|
|
}
|