Line | Branch | Exec | Source |
---|---|---|---|
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- | ||
2 | * | ||
3 | * Copyright 2009-2010 Red Hat, Inc, | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include "config.h" | ||
21 | |||
22 | #include <glib.h> | ||
23 | #include <glib/gi18n.h> | ||
24 | #include <glib/gstdio.h> | ||
25 | #include <gtk/gtk.h> | ||
26 | #include <cups/cups.h> | ||
27 | #include <cups/ppd.h> | ||
28 | |||
29 | #include "pp-utils.h" | ||
30 | |||
31 | #define DBUS_TIMEOUT 120000 | ||
32 | #define DBUS_TIMEOUT_LONG 600000 | ||
33 | |||
34 | #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) | ||
35 | #define HAVE_CUPS_1_6 1 | ||
36 | #endif | ||
37 | |||
38 | #ifndef HAVE_CUPS_1_6 | ||
39 | #define ippGetCount(attr) attr->num_values | ||
40 | #define ippGetGroupTag(attr) attr->group_tag | ||
41 | #define ippGetValueTag(attr) attr->value_tag | ||
42 | #define ippGetName(attr) attr->name | ||
43 | #define ippGetStatusCode(ipp) ipp->request.status.status_code | ||
44 | #define ippGetInteger(attr, element) attr->values[element].integer | ||
45 | #define ippGetString(attr, element, language) attr->values[element].string.text | ||
46 | #define ippGetBoolean(attr, element) attr->values[element].boolean | ||
47 | |||
48 | static int | ||
49 | ippGetRange (ipp_attribute_t *attr, | ||
50 | int element, | ||
51 | int *upper) | ||
52 | { | ||
53 | *upper = attr->values[element].range.upper; | ||
54 | return (attr->values[element].range.lower); | ||
55 | } | ||
56 | |||
57 | static ipp_attribute_t * | ||
58 | ippFirstAttribute (ipp_t *ipp) | ||
59 | { | ||
60 | if (!ipp) | ||
61 | return (NULL); | ||
62 | return (ipp->current = ipp->attrs); | ||
63 | } | ||
64 | |||
65 | static ipp_attribute_t * | ||
66 | ippNextAttribute (ipp_t *ipp) | ||
67 | { | ||
68 | if (!ipp || !ipp->current) | ||
69 | return (NULL); | ||
70 | return (ipp->current = ipp->current->next); | ||
71 | } | ||
72 | #endif | ||
73 | |||
74 | #if (CUPS_VERSION_MAJOR == 1) && (CUPS_VERSION_MINOR <= 6) | ||
75 | #define HTTP_URI_STATUS_OK HTTP_URI_OK | ||
76 | #endif | ||
77 | |||
78 | gchar * | ||
79 | ✗ | get_tag_value (const gchar *tag_string, const gchar *tag_name) | |
80 | { | ||
81 | ✗ | gchar **tag_string_splitted = NULL; | |
82 | ✗ | gchar *tag_value = NULL; | |
83 | gint tag_name_length; | ||
84 | gint i; | ||
85 | |||
86 | ✗ | if (tag_string && tag_name) | |
87 | { | ||
88 | ✗ | tag_name_length = strlen (tag_name); | |
89 | ✗ | tag_string_splitted = g_strsplit (tag_string, ";", 0); | |
90 | ✗ | if (tag_string_splitted) | |
91 | { | ||
92 | ✗ | for (i = 0; i < g_strv_length (tag_string_splitted); i++) | |
93 | ✗ | if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0) | |
94 | ✗ | if (strlen (tag_string_splitted[i]) > tag_name_length + 1) | |
95 | ✗ | tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1); | |
96 | |||
97 | ✗ | g_strfreev (tag_string_splitted); | |
98 | } | ||
99 | } | ||
100 | |||
101 | ✗ | return tag_value; | |
102 | } | ||
103 | |||
104 | |||
105 | /* | ||
106 | * Normalize given string so that it is lowercase, doesn't | ||
107 | * have trailing or leading whitespaces and digits doesn't | ||
108 | * neighbour with alphabetic. | ||
109 | * (see cupshelpers/ppds.py from system-config-printer) | ||
110 | */ | ||
111 | static gchar * | ||
112 | ✗ | normalize (const gchar *input_string) | |
113 | { | ||
114 | ✗ | gchar *result = NULL; | |
115 | ✗ | gint i, j = 0, k = -1; | |
116 | |||
117 | ✗ | if (input_string) | |
118 | { | ||
119 | ✗ | g_autofree gchar *tmp = g_strstrip (g_ascii_strdown (input_string, -1)); | |
120 | ✗ | if (tmp) | |
121 | { | ||
122 | ✗ | g_autofree gchar *res = g_new (gchar, 2 * strlen (tmp) + 1); | |
123 | |||
124 | ✗ | for (i = 0; i < strlen (tmp); i++) | |
125 | { | ||
126 | ✗ | if ((g_ascii_isalpha (tmp[i]) && k >= 0 && g_ascii_isdigit (res[k])) || | |
127 | ✗ | (g_ascii_isdigit (tmp[i]) && k >= 0 && g_ascii_isalpha (res[k]))) | |
128 | { | ||
129 | ✗ | res[j] = ' '; | |
130 | ✗ | k = j++; | |
131 | ✗ | res[j] = tmp[i]; | |
132 | ✗ | k = j++; | |
133 | } | ||
134 | else | ||
135 | { | ||
136 | ✗ | if (g_ascii_isspace (tmp[i]) || !g_ascii_isalnum (tmp[i])) | |
137 | { | ||
138 | ✗ | if (!(k >= 0 && res[k] == ' ')) | |
139 | { | ||
140 | ✗ | res[j] = ' '; | |
141 | ✗ | k = j++; | |
142 | } | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | ✗ | res[j] = tmp[i]; | |
147 | ✗ | k = j++; | |
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | ✗ | res[j] = '\0'; | |
153 | |||
154 | ✗ | result = g_strdup (res); | |
155 | } | ||
156 | } | ||
157 | |||
158 | ✗ | return result; | |
159 | } | ||
160 | |||
161 | |||
162 | char * | ||
163 | ✗ | get_dest_attr (const char *dest_name, | |
164 | const char *attr) | ||
165 | { | ||
166 | cups_dest_t *dests; | ||
167 | int num_dests; | ||
168 | cups_dest_t *dest; | ||
169 | const char *value; | ||
170 | char *ret; | ||
171 | |||
172 | ✗ | if (dest_name == NULL) | |
173 | ✗ | return NULL; | |
174 | |||
175 | ✗ | ret = NULL; | |
176 | |||
177 | ✗ | num_dests = cupsGetDests (&dests); | |
178 | ✗ | if (num_dests < 1) { | |
179 | ✗ | g_debug ("Unable to get printer destinations"); | |
180 | ✗ | return NULL; | |
181 | } | ||
182 | |||
183 | ✗ | dest = cupsGetDest (dest_name, NULL, num_dests, dests); | |
184 | ✗ | if (dest == NULL) { | |
185 | ✗ | g_debug ("Unable to find a printer named '%s'", dest_name); | |
186 | ✗ | goto out; | |
187 | } | ||
188 | |||
189 | ✗ | value = cupsGetOption (attr, dest->num_options, dest->options); | |
190 | ✗ | if (value == NULL) { | |
191 | ✗ | g_debug ("Unable to get %s for '%s'", attr, dest_name); | |
192 | ✗ | goto out; | |
193 | } | ||
194 | ✗ | ret = g_strdup (value); | |
195 | ✗ | out: | |
196 | ✗ | cupsFreeDests (num_dests, dests); | |
197 | |||
198 | ✗ | return ret; | |
199 | } | ||
200 | |||
201 | gchar * | ||
202 | ✗ | get_ppd_attribute (const gchar *ppd_file_name, | |
203 | const gchar *attribute_name) | ||
204 | { | ||
205 | ✗ | ppd_file_t *ppd_file = NULL; | |
206 | ✗ | ppd_attr_t *ppd_attr = NULL; | |
207 | ✗ | gchar *result = NULL; | |
208 | |||
209 | ✗ | if (ppd_file_name) | |
210 | { | ||
211 | ✗ | ppd_file = ppdOpenFile (ppd_file_name); | |
212 | |||
213 | ✗ | if (ppd_file) | |
214 | { | ||
215 | ✗ | ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL); | |
216 | ✗ | if (ppd_attr != NULL) | |
217 | ✗ | result = g_strdup (ppd_attr->value); | |
218 | ✗ | ppdClose (ppd_file); | |
219 | } | ||
220 | } | ||
221 | |||
222 | ✗ | return result; | |
223 | } | ||
224 | |||
225 | /* Set default destination in ~/.cups/lpoptions. | ||
226 | * Unset default destination if "dest" is NULL. | ||
227 | */ | ||
228 | void | ||
229 | ✗ | set_local_default_printer (const gchar *printer_name) | |
230 | { | ||
231 | ✗ | cups_dest_t *dests = NULL; | |
232 | ✗ | int num_dests = 0; | |
233 | int i; | ||
234 | |||
235 | ✗ | num_dests = cupsGetDests (&dests); | |
236 | |||
237 | ✗ | for (i = 0; i < num_dests; i ++) | |
238 | { | ||
239 | ✗ | if (printer_name && g_strcmp0 (dests[i].name, printer_name) == 0) | |
240 | ✗ | dests[i].is_default = 1; | |
241 | else | ||
242 | ✗ | dests[i].is_default = 0; | |
243 | } | ||
244 | |||
245 | ✗ | cupsSetDests (num_dests, dests); | |
246 | ✗ | } | |
247 | |||
248 | /* | ||
249 | * This function does something which should be provided by CUPS... | ||
250 | * It returns FALSE if the renaming fails. | ||
251 | */ | ||
252 | gboolean | ||
253 | ✗ | printer_rename (const gchar *old_name, | |
254 | const gchar *new_name) | ||
255 | { | ||
256 | ✗ | ipp_attribute_t *attr = NULL; | |
257 | ✗ | cups_ptype_t printer_type = 0; | |
258 | ✗ | cups_dest_t *dests = NULL; | |
259 | ✗ | cups_dest_t *dest = NULL; | |
260 | ✗ | cups_job_t *jobs = NULL; | |
261 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
262 | ✗ | const gchar *printer_location = NULL; | |
263 | ✗ | const gchar *printer_info = NULL; | |
264 | ✗ | const gchar *printer_uri = NULL; | |
265 | ✗ | const gchar *device_uri = NULL; | |
266 | ✗ | const gchar *job_sheets = NULL; | |
267 | ✗ | gboolean result = FALSE; | |
268 | ✗ | gboolean accepting = TRUE; | |
269 | ✗ | gboolean printer_paused = FALSE; | |
270 | ✗ | gboolean default_printer = FALSE; | |
271 | ✗ | gboolean printer_shared = FALSE; | |
272 | ✗ | g_autoptr(GError) error = NULL; | |
273 | http_t *http; | ||
274 | ✗ | g_autofree gchar *ppd_link = NULL; | |
275 | ✗ | g_autofree gchar *ppd_filename = NULL; | |
276 | ✗ | gchar **sheets = NULL; | |
277 | ✗ | gchar **users_allowed = NULL; | |
278 | ✗ | gchar **users_denied = NULL; | |
279 | ✗ | gchar **member_names = NULL; | |
280 | ✗ | const gchar *start_sheet = NULL; | |
281 | ✗ | const gchar *end_sheet = NULL; | |
282 | ✗ | g_autofree gchar *error_policy = NULL; | |
283 | ✗ | g_autofree gchar *op_policy = NULL; | |
284 | ipp_t *request; | ||
285 | ipp_t *response; | ||
286 | gint i; | ||
287 | ✗ | int num_dests = 0; | |
288 | ✗ | int num_jobs = 0; | |
289 | static const char * const requested_attrs[] = { | ||
290 | "printer-error-policy", | ||
291 | "printer-op-policy", | ||
292 | "requesting-user-name-allowed", | ||
293 | "requesting-user-name-denied", | ||
294 | "member-names"}; | ||
295 | |||
296 | ✗ | if (old_name == NULL || | |
297 | ✗ | old_name[0] == '\0' || | |
298 | ✗ | new_name == NULL || | |
299 | ✗ | new_name[0] == '\0' || | |
300 | ✗ | g_strcmp0 (old_name, new_name) == 0) | |
301 | ✗ | return FALSE; | |
302 | |||
303 | ✗ | num_dests = cupsGetDests (&dests); | |
304 | |||
305 | ✗ | dest = cupsGetDest (new_name, NULL, num_dests, dests); | |
306 | ✗ | if (dest) | |
307 | { | ||
308 | ✗ | cupsFreeDests (num_dests, dests); | |
309 | ✗ | return FALSE; | |
310 | } | ||
311 | |||
312 | ✗ | num_jobs = cupsGetJobs (&jobs, old_name, 0, CUPS_WHICHJOBS_ACTIVE); | |
313 | ✗ | cupsFreeJobs (num_jobs, jobs); | |
314 | ✗ | if (num_jobs > 1) | |
315 | { | ||
316 | ✗ | g_warning ("There are queued jobs on printer %s!", old_name); | |
317 | ✗ | cupsFreeDests (num_dests, dests); | |
318 | ✗ | return FALSE; | |
319 | } | ||
320 | |||
321 | /* | ||
322 | * Gather some informations about the original printer | ||
323 | */ | ||
324 | ✗ | dest = cupsGetDest (old_name, NULL, num_dests, dests); | |
325 | ✗ | if (dest) | |
326 | { | ||
327 | ✗ | for (i = 0; i < dest->num_options; i++) | |
328 | { | ||
329 | ✗ | if (g_strcmp0 (dest->options[i].name, "printer-is-accepting-jobs") == 0) | |
330 | ✗ | accepting = g_strcmp0 (dest->options[i].value, "true") == 0; | |
331 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-is-shared") == 0) | |
332 | ✗ | printer_shared = g_strcmp0 (dest->options[i].value, "true") == 0; | |
333 | ✗ | else if (g_strcmp0 (dest->options[i].name, "device-uri") == 0) | |
334 | ✗ | device_uri = dest->options[i].value; | |
335 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-uri-supported") == 0) | |
336 | ✗ | printer_uri = dest->options[i].value; | |
337 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-info") == 0) | |
338 | ✗ | printer_info = dest->options[i].value; | |
339 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-location") == 0) | |
340 | ✗ | printer_location = dest->options[i].value; | |
341 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-state") == 0) | |
342 | ✗ | printer_paused = g_strcmp0 (dest->options[i].value, "5") == 0; | |
343 | ✗ | else if (g_strcmp0 (dest->options[i].name, "job-sheets") == 0) | |
344 | ✗ | job_sheets = dest->options[i].value; | |
345 | ✗ | else if (g_strcmp0 (dest->options[i].name, "printer-type") == 0) | |
346 | ✗ | printer_type = atoi (dest->options[i].value); | |
347 | } | ||
348 | ✗ | default_printer = dest->is_default; | |
349 | } | ||
350 | ✗ | cupsFreeDests (num_dests, dests); | |
351 | |||
352 | ✗ | if (accepting) | |
353 | { | ||
354 | ✗ | printer_set_accepting_jobs (old_name, FALSE, NULL); | |
355 | |||
356 | ✗ | num_jobs = cupsGetJobs (&jobs, old_name, 0, CUPS_WHICHJOBS_ACTIVE); | |
357 | ✗ | cupsFreeJobs (num_jobs, jobs); | |
358 | ✗ | if (num_jobs > 1) | |
359 | { | ||
360 | ✗ | printer_set_accepting_jobs (old_name, accepting, NULL); | |
361 | ✗ | g_warning ("There are queued jobs on printer %s!", old_name); | |
362 | ✗ | return FALSE; | |
363 | } | ||
364 | } | ||
365 | |||
366 | |||
367 | /* | ||
368 | * Gather additional informations about the original printer | ||
369 | */ | ||
370 | #ifdef HAVE_CUPS_HTTPCONNECT2 | ||
371 | ✗ | http = httpConnect2 (cupsServer (), ippPort (), NULL, AF_UNSPEC, | |
372 | cupsEncryption (), 1, 30000, NULL); | ||
373 | #else | ||
374 | http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); | ||
375 | #endif | ||
376 | ✗ | if (http != NULL) | |
377 | { | ||
378 | ✗ | request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES); | |
379 | ✗ | ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, | |
380 | "printer-uri", NULL, printer_uri); | ||
381 | ✗ | ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
382 | "requested-attributes", G_N_ELEMENTS (requested_attrs), NULL, requested_attrs); | ||
383 | ✗ | response = cupsDoRequest (http, request, "/"); | |
384 | |||
385 | ✗ | if (response) | |
386 | { | ||
387 | ✗ | if (ippGetStatusCode (response) <= IPP_OK_CONFLICT) | |
388 | { | ||
389 | ✗ | attr = ippFindAttribute (response, "printer-error-policy", IPP_TAG_NAME); | |
390 | ✗ | if (attr) | |
391 | ✗ | error_policy = g_strdup (ippGetString (attr, 0, NULL)); | |
392 | |||
393 | ✗ | attr = ippFindAttribute (response, "printer-op-policy", IPP_TAG_NAME); | |
394 | ✗ | if (attr) | |
395 | ✗ | op_policy = g_strdup (ippGetString (attr, 0, NULL)); | |
396 | |||
397 | ✗ | attr = ippFindAttribute (response, "requesting-user-name-allowed", IPP_TAG_NAME); | |
398 | ✗ | if (attr && ippGetCount (attr) > 0) | |
399 | { | ||
400 | ✗ | users_allowed = g_new0 (gchar *, ippGetCount (attr) + 1); | |
401 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
402 | ✗ | users_allowed[i] = g_strdup (ippGetString (attr, i, NULL)); | |
403 | } | ||
404 | |||
405 | ✗ | attr = ippFindAttribute (response, "requesting-user-name-denied", IPP_TAG_NAME); | |
406 | ✗ | if (attr && ippGetCount (attr) > 0) | |
407 | { | ||
408 | ✗ | users_denied = g_new0 (gchar *, ippGetCount (attr) + 1); | |
409 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
410 | ✗ | users_denied[i] = g_strdup (ippGetString (attr, i, NULL)); | |
411 | } | ||
412 | |||
413 | ✗ | attr = ippFindAttribute (response, "member-names", IPP_TAG_NAME); | |
414 | ✗ | if (attr && ippGetCount (attr) > 0) | |
415 | { | ||
416 | ✗ | member_names = g_new0 (gchar *, ippGetCount (attr) + 1); | |
417 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
418 | ✗ | member_names[i] = g_strdup (ippGetString (attr, i, NULL)); | |
419 | } | ||
420 | } | ||
421 | ✗ | ippDelete (response); | |
422 | } | ||
423 | ✗ | httpClose (http); | |
424 | } | ||
425 | |||
426 | ✗ | if (job_sheets) | |
427 | { | ||
428 | ✗ | sheets = g_strsplit (job_sheets, ",", 0); | |
429 | ✗ | if (g_strv_length (sheets) > 1) | |
430 | { | ||
431 | ✗ | start_sheet = sheets[0]; | |
432 | ✗ | end_sheet = sheets[1]; | |
433 | } | ||
434 | } | ||
435 | |||
436 | ✗ | ppd_link = g_strdup (cupsGetPPD (old_name)); | |
437 | ✗ | if (ppd_link) | |
438 | { | ||
439 | ✗ | ppd_filename = g_file_read_link (ppd_link, NULL); | |
440 | |||
441 | ✗ | if (!ppd_filename) | |
442 | ✗ | ppd_filename = g_strdup (ppd_link); | |
443 | } | ||
444 | |||
445 | |||
446 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
447 | ✗ | if (!bus) | |
448 | { | ||
449 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
450 | } | ||
451 | else | ||
452 | { | ||
453 | ✗ | if (printer_type & CUPS_PRINTER_CLASS) | |
454 | { | ||
455 | ✗ | if (member_names) | |
456 | ✗ | for (i = 0; i < g_strv_length (member_names); i++) | |
457 | ✗ | class_add_printer (new_name, member_names[i]); | |
458 | } | ||
459 | else | ||
460 | { | ||
461 | ✗ | g_autoptr(GVariant) output = NULL; | |
462 | ✗ | g_autoptr(GError) add_error = NULL; | |
463 | |||
464 | ✗ | output = g_dbus_connection_call_sync (bus, | |
465 | MECHANISM_BUS, | ||
466 | "/", | ||
467 | MECHANISM_BUS, | ||
468 | "PrinterAddWithPpdFile", | ||
469 | g_variant_new ("(sssss)", | ||
470 | new_name, | ||
471 | device_uri ? device_uri : "", | ||
472 | ✗ | ppd_filename ? ppd_filename : "", | |
473 | printer_info ? printer_info : "", | ||
474 | printer_location ? printer_location : ""), | ||
475 | G_VARIANT_TYPE ("(s)"), | ||
476 | G_DBUS_CALL_FLAGS_NONE, | ||
477 | -1, | ||
478 | NULL, | ||
479 | &add_error); | ||
480 | |||
481 | ✗ | if (output) | |
482 | { | ||
483 | const gchar *ret_error; | ||
484 | |||
485 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
486 | ✗ | if (ret_error[0] != '\0') | |
487 | ✗ | g_warning ("cups-pk-helper: rename of printer %s to %s failed: %s", old_name, new_name, ret_error); | |
488 | } | ||
489 | else | ||
490 | { | ||
491 | ✗ | g_warning ("%s", add_error->message); | |
492 | } | ||
493 | } | ||
494 | } | ||
495 | |||
496 | ✗ | if (ppd_link) | |
497 | { | ||
498 | ✗ | g_unlink (ppd_link); | |
499 | } | ||
500 | |||
501 | ✗ | num_dests = cupsGetDests (&dests); | |
502 | ✗ | dest = cupsGetDest (new_name, NULL, num_dests, dests); | |
503 | ✗ | if (dest) | |
504 | { | ||
505 | ✗ | printer_set_accepting_jobs (new_name, accepting, NULL); | |
506 | ✗ | printer_set_enabled (new_name, !printer_paused); | |
507 | ✗ | printer_set_shared (new_name, printer_shared); | |
508 | ✗ | printer_set_job_sheets (new_name, start_sheet, end_sheet); | |
509 | ✗ | printer_set_policy (new_name, op_policy, FALSE); | |
510 | ✗ | printer_set_policy (new_name, error_policy, TRUE); | |
511 | ✗ | printer_set_users (new_name, users_allowed, TRUE); | |
512 | ✗ | printer_set_users (new_name, users_denied, FALSE); | |
513 | ✗ | if (default_printer) | |
514 | ✗ | printer_set_default (new_name); | |
515 | |||
516 | ✗ | printer_delete (old_name); | |
517 | |||
518 | ✗ | result = TRUE; | |
519 | } | ||
520 | else | ||
521 | ✗ | printer_set_accepting_jobs (old_name, accepting, NULL); | |
522 | |||
523 | ✗ | cupsFreeDests (num_dests, dests); | |
524 | ✗ | if (sheets) | |
525 | ✗ | g_strfreev (sheets); | |
526 | ✗ | if (users_allowed) | |
527 | ✗ | g_strfreev (users_allowed); | |
528 | ✗ | if (users_denied) | |
529 | ✗ | g_strfreev (users_denied); | |
530 | |||
531 | ✗ | return result; | |
532 | } | ||
533 | |||
534 | gboolean | ||
535 | ✗ | printer_set_location (const gchar *printer_name, | |
536 | const gchar *location) | ||
537 | { | ||
538 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
539 | ✗ | g_autoptr(GVariant) output = NULL; | |
540 | const gchar *ret_error; | ||
541 | ✗ | g_autoptr(GError) error = NULL; | |
542 | |||
543 | ✗ | if (!printer_name || !location) | |
544 | ✗ | return TRUE; | |
545 | |||
546 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
547 | ✗ | if (!bus) | |
548 | { | ||
549 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
550 | ✗ | return TRUE; | |
551 | } | ||
552 | |||
553 | ✗ | output = g_dbus_connection_call_sync (bus, | |
554 | MECHANISM_BUS, | ||
555 | "/", | ||
556 | MECHANISM_BUS, | ||
557 | "PrinterSetLocation", | ||
558 | g_variant_new ("(ss)", printer_name, location), | ||
559 | G_VARIANT_TYPE ("(s)"), | ||
560 | G_DBUS_CALL_FLAGS_NONE, | ||
561 | -1, | ||
562 | NULL, | ||
563 | &error); | ||
564 | |||
565 | ✗ | if (output == NULL) | |
566 | { | ||
567 | ✗ | g_warning ("%s", error->message); | |
568 | ✗ | return FALSE; | |
569 | } | ||
570 | |||
571 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
572 | ✗ | if (ret_error[0] != '\0') | |
573 | { | ||
574 | ✗ | g_warning ("cups-pk-helper: setting of location for printer %s failed: %s", printer_name, ret_error); | |
575 | ✗ | return FALSE; | |
576 | } | ||
577 | |||
578 | ✗ | return TRUE; | |
579 | } | ||
580 | |||
581 | gboolean | ||
582 | ✗ | printer_set_accepting_jobs (const gchar *printer_name, | |
583 | gboolean accepting_jobs, | ||
584 | const gchar *reason) | ||
585 | { | ||
586 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
587 | ✗ | g_autoptr(GVariant) output = NULL; | |
588 | const gchar *ret_error; | ||
589 | ✗ | g_autoptr(GError) error = NULL; | |
590 | |||
591 | ✗ | if (!printer_name) | |
592 | ✗ | return TRUE; | |
593 | |||
594 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
595 | ✗ | if (!bus) | |
596 | { | ||
597 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
598 | ✗ | return TRUE; | |
599 | } | ||
600 | |||
601 | ✗ | output = g_dbus_connection_call_sync (bus, | |
602 | MECHANISM_BUS, | ||
603 | "/", | ||
604 | MECHANISM_BUS, | ||
605 | "PrinterSetAcceptJobs", | ||
606 | g_variant_new ("(sbs)", | ||
607 | printer_name, | ||
608 | accepting_jobs, | ||
609 | reason ? reason : ""), | ||
610 | G_VARIANT_TYPE ("(s)"), | ||
611 | G_DBUS_CALL_FLAGS_NONE, | ||
612 | -1, | ||
613 | NULL, | ||
614 | &error); | ||
615 | |||
616 | ✗ | if (output == NULL) | |
617 | { | ||
618 | ✗ | g_warning ("%s", error->message); | |
619 | ✗ | return FALSE; | |
620 | } | ||
621 | |||
622 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
623 | ✗ | if (ret_error[0] != '\0') | |
624 | { | ||
625 | ✗ | g_warning ("cups-pk-helper: setting of acceptance of jobs for printer %s failed: %s", printer_name, ret_error); | |
626 | ✗ | return FALSE; | |
627 | } | ||
628 | |||
629 | ✗ | return TRUE; | |
630 | } | ||
631 | |||
632 | gboolean | ||
633 | ✗ | printer_set_enabled (const gchar *printer_name, | |
634 | gboolean enabled) | ||
635 | { | ||
636 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
637 | ✗ | g_autoptr(GVariant) output = NULL; | |
638 | const gchar *ret_error; | ||
639 | ✗ | g_autoptr(GError) error = NULL; | |
640 | |||
641 | ✗ | if (!printer_name) | |
642 | ✗ | return TRUE; | |
643 | |||
644 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
645 | ✗ | if (!bus) | |
646 | { | ||
647 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
648 | ✗ | return TRUE; | |
649 | } | ||
650 | |||
651 | ✗ | output = g_dbus_connection_call_sync (bus, | |
652 | MECHANISM_BUS, | ||
653 | "/", | ||
654 | MECHANISM_BUS, | ||
655 | "PrinterSetEnabled", | ||
656 | g_variant_new ("(sb)", printer_name, enabled), | ||
657 | G_VARIANT_TYPE ("(s)"), | ||
658 | G_DBUS_CALL_FLAGS_NONE, | ||
659 | -1, | ||
660 | NULL, | ||
661 | &error); | ||
662 | |||
663 | ✗ | if (output == NULL) | |
664 | { | ||
665 | ✗ | g_warning ("%s", error->message); | |
666 | ✗ | return FALSE; | |
667 | } | ||
668 | |||
669 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
670 | ✗ | if (ret_error[0] != '\0') | |
671 | { | ||
672 | ✗ | g_warning ("cups-pk-helper: setting of enablement of printer %s failed: %s", printer_name, ret_error); | |
673 | ✗ | return FALSE; | |
674 | } | ||
675 | |||
676 | ✗ | return TRUE; | |
677 | } | ||
678 | |||
679 | gboolean | ||
680 | ✗ | printer_delete (const gchar *printer_name) | |
681 | { | ||
682 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
683 | ✗ | g_autoptr(GVariant) output = NULL; | |
684 | const gchar *ret_error; | ||
685 | ✗ | g_autoptr(GError) error = NULL; | |
686 | |||
687 | ✗ | if (!printer_name) | |
688 | ✗ | return TRUE; | |
689 | |||
690 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
691 | ✗ | if (!bus) | |
692 | { | ||
693 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
694 | ✗ | return TRUE; | |
695 | } | ||
696 | |||
697 | ✗ | output = g_dbus_connection_call_sync (bus, | |
698 | MECHANISM_BUS, | ||
699 | "/", | ||
700 | MECHANISM_BUS, | ||
701 | "PrinterDelete", | ||
702 | g_variant_new ("(s)", printer_name), | ||
703 | G_VARIANT_TYPE ("(s)"), | ||
704 | G_DBUS_CALL_FLAGS_NONE, | ||
705 | -1, | ||
706 | NULL, | ||
707 | &error); | ||
708 | |||
709 | ✗ | if (output == NULL) | |
710 | { | ||
711 | ✗ | g_warning ("%s", error->message); | |
712 | ✗ | return FALSE; | |
713 | } | ||
714 | |||
715 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
716 | ✗ | if (ret_error[0] != '\0') | |
717 | { | ||
718 | ✗ | g_warning ("cups-pk-helper: removing of printer %s failed: %s", printer_name, ret_error); | |
719 | ✗ | return FALSE; | |
720 | } | ||
721 | |||
722 | ✗ | return TRUE; | |
723 | } | ||
724 | |||
725 | gboolean | ||
726 | ✗ | printer_set_default (const gchar *printer_name) | |
727 | { | ||
728 | const char *cups_server; | ||
729 | ✗ | g_autoptr(GError) error = NULL; | |
730 | |||
731 | ✗ | if (!printer_name) | |
732 | ✗ | return TRUE; | |
733 | |||
734 | ✗ | cups_server = cupsServer (); | |
735 | ✗ | if (g_ascii_strncasecmp (cups_server, "localhost", 9) == 0 || | |
736 | ✗ | g_ascii_strncasecmp (cups_server, "127.0.0.1", 9) == 0 || | |
737 | ✗ | g_ascii_strncasecmp (cups_server, "::1", 3) == 0 || | |
738 | ✗ | cups_server[0] == '/') | |
739 | { | ||
740 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
741 | ✗ | g_autoptr(GVariant) output = NULL; | |
742 | const gchar *ret_error; | ||
743 | |||
744 | /* Clean .cups/lpoptions before setting | ||
745 | * default printer on local CUPS server. | ||
746 | */ | ||
747 | ✗ | set_local_default_printer (NULL); | |
748 | |||
749 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
750 | ✗ | if (!bus) | |
751 | { | ||
752 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
753 | ✗ | return FALSE; | |
754 | } | ||
755 | |||
756 | ✗ | output = g_dbus_connection_call_sync (bus, | |
757 | MECHANISM_BUS, | ||
758 | "/", | ||
759 | MECHANISM_BUS, | ||
760 | "PrinterSetDefault", | ||
761 | g_variant_new ("(s)", printer_name), | ||
762 | G_VARIANT_TYPE ("(s)"), | ||
763 | G_DBUS_CALL_FLAGS_NONE, | ||
764 | -1, | ||
765 | NULL, | ||
766 | &error); | ||
767 | |||
768 | ✗ | if (output == NULL) | |
769 | { | ||
770 | ✗ | g_warning ("%s", error->message); | |
771 | ✗ | return FALSE; | |
772 | } | ||
773 | |||
774 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
775 | ✗ | if (ret_error[0] != '\0') | |
776 | { | ||
777 | ✗ | g_warning ("cups-pk-helper: setting default printer to %s failed: %s", printer_name, ret_error); | |
778 | ✗ | return FALSE; | |
779 | } | ||
780 | |||
781 | ✗ | return TRUE; | |
782 | } | ||
783 | else | ||
784 | /* Store default printer to .cups/lpoptions | ||
785 | * if we are connected to a remote CUPS server. | ||
786 | */ | ||
787 | { | ||
788 | ✗ | set_local_default_printer (printer_name); | |
789 | ✗ | return TRUE; | |
790 | } | ||
791 | } | ||
792 | |||
793 | gboolean | ||
794 | ✗ | printer_set_shared (const gchar *printer_name, | |
795 | gboolean shared) | ||
796 | { | ||
797 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
798 | ✗ | g_autoptr(GVariant) output = NULL; | |
799 | const gchar *ret_error; | ||
800 | ✗ | g_autoptr(GError) error = NULL; | |
801 | |||
802 | ✗ | if (!printer_name) | |
803 | ✗ | return TRUE; | |
804 | |||
805 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
806 | ✗ | if (!bus) | |
807 | { | ||
808 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
809 | ✗ | return TRUE; | |
810 | } | ||
811 | |||
812 | ✗ | output = g_dbus_connection_call_sync (bus, | |
813 | MECHANISM_BUS, | ||
814 | "/", | ||
815 | MECHANISM_BUS, | ||
816 | "PrinterSetShared", | ||
817 | g_variant_new ("(sb)", printer_name, shared), | ||
818 | G_VARIANT_TYPE ("(s)"), | ||
819 | G_DBUS_CALL_FLAGS_NONE, | ||
820 | -1, | ||
821 | NULL, | ||
822 | &error); | ||
823 | |||
824 | ✗ | if (output == NULL) | |
825 | { | ||
826 | ✗ | g_warning ("%s", error->message); | |
827 | ✗ | return FALSE; | |
828 | } | ||
829 | |||
830 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
831 | ✗ | if (ret_error[0] != '\0') | |
832 | { | ||
833 | ✗ | g_warning ("cups-pk-helper: setting of sharing of printer %s failed: %s", printer_name, ret_error); | |
834 | ✗ | return FALSE; | |
835 | } | ||
836 | |||
837 | ✗ | return TRUE; | |
838 | } | ||
839 | |||
840 | gboolean | ||
841 | ✗ | printer_set_job_sheets (const gchar *printer_name, | |
842 | const gchar *start_sheet, | ||
843 | const gchar *end_sheet) | ||
844 | { | ||
845 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
846 | ✗ | g_autoptr(GVariant) output = NULL; | |
847 | const gchar *ret_error; | ||
848 | ✗ | g_autoptr(GError) error = NULL; | |
849 | |||
850 | ✗ | if (!printer_name || !start_sheet || !end_sheet) | |
851 | ✗ | return TRUE; | |
852 | |||
853 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
854 | ✗ | if (!bus) | |
855 | { | ||
856 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
857 | ✗ | return TRUE; | |
858 | } | ||
859 | |||
860 | ✗ | output = g_dbus_connection_call_sync (bus, | |
861 | MECHANISM_BUS, | ||
862 | "/", | ||
863 | MECHANISM_BUS, | ||
864 | "PrinterSetJobSheets", | ||
865 | g_variant_new ("(sss)", printer_name, start_sheet, end_sheet), | ||
866 | G_VARIANT_TYPE ("(s)"), | ||
867 | G_DBUS_CALL_FLAGS_NONE, | ||
868 | -1, | ||
869 | NULL, | ||
870 | &error); | ||
871 | |||
872 | ✗ | if (output == NULL) | |
873 | { | ||
874 | ✗ | g_warning ("%s", error->message); | |
875 | ✗ | return FALSE; | |
876 | } | ||
877 | |||
878 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
879 | ✗ | if (ret_error[0] != '\0') | |
880 | { | ||
881 | ✗ | g_warning ("cups-pk-helper: setting of job sheets for printer %s failed: %s", printer_name, ret_error); | |
882 | ✗ | return FALSE; | |
883 | } | ||
884 | |||
885 | ✗ | return TRUE; | |
886 | } | ||
887 | |||
888 | gboolean | ||
889 | ✗ | printer_set_policy (const gchar *printer_name, | |
890 | const gchar *policy, | ||
891 | gboolean error_policy) | ||
892 | { | ||
893 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
894 | ✗ | g_autoptr(GVariant) output = NULL; | |
895 | const gchar *ret_error; | ||
896 | ✗ | g_autoptr(GError) error = NULL; | |
897 | |||
898 | ✗ | if (!printer_name || !policy) | |
899 | ✗ | return TRUE; | |
900 | |||
901 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
902 | ✗ | if (!bus) | |
903 | { | ||
904 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
905 | ✗ | return TRUE; | |
906 | } | ||
907 | |||
908 | ✗ | if (error_policy) | |
909 | ✗ | output = g_dbus_connection_call_sync (bus, | |
910 | MECHANISM_BUS, | ||
911 | "/", | ||
912 | MECHANISM_BUS, | ||
913 | "PrinterSetErrorPolicy", | ||
914 | g_variant_new ("(ss)", printer_name, policy), | ||
915 | G_VARIANT_TYPE ("(s)"), | ||
916 | G_DBUS_CALL_FLAGS_NONE, | ||
917 | -1, | ||
918 | NULL, | ||
919 | &error); | ||
920 | else | ||
921 | ✗ | output = g_dbus_connection_call_sync (bus, | |
922 | MECHANISM_BUS, | ||
923 | "/", | ||
924 | MECHANISM_BUS, | ||
925 | "PrinterSetOpPolicy", | ||
926 | g_variant_new ("(ss)", printer_name, policy), | ||
927 | G_VARIANT_TYPE ("(s)"), | ||
928 | G_DBUS_CALL_FLAGS_NONE, | ||
929 | -1, | ||
930 | NULL, | ||
931 | &error); | ||
932 | |||
933 | ✗ | if (output == NULL) | |
934 | { | ||
935 | ✗ | g_warning ("%s", error->message); | |
936 | ✗ | return FALSE; | |
937 | } | ||
938 | |||
939 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
940 | ✗ | if (ret_error[0] != '\0') | |
941 | { | ||
942 | ✗ | g_warning ("cups-pk-helper: setting of a policy for printer %s failed: %s", printer_name, ret_error); | |
943 | ✗ | return FALSE; | |
944 | } | ||
945 | |||
946 | ✗ | return TRUE; | |
947 | } | ||
948 | |||
949 | gboolean | ||
950 | ✗ | printer_set_users (const gchar *printer_name, | |
951 | gchar **users, | ||
952 | gboolean allowed) | ||
953 | { | ||
954 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
955 | GVariantBuilder array_builder; | ||
956 | gint i; | ||
957 | ✗ | g_autoptr(GVariant) output = NULL; | |
958 | const gchar *ret_error; | ||
959 | ✗ | g_autoptr(GError) error = NULL; | |
960 | |||
961 | ✗ | if (!printer_name || !users) | |
962 | ✗ | return TRUE; | |
963 | |||
964 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
965 | ✗ | if (!bus) | |
966 | { | ||
967 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
968 | ✗ | return TRUE; | |
969 | } | ||
970 | |||
971 | ✗ | g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); | |
972 | ✗ | for (i = 0; users[i]; i++) | |
973 | ✗ | g_variant_builder_add (&array_builder, "s", users[i]); | |
974 | |||
975 | ✗ | if (allowed) | |
976 | ✗ | output = g_dbus_connection_call_sync (bus, | |
977 | MECHANISM_BUS, | ||
978 | "/", | ||
979 | MECHANISM_BUS, | ||
980 | "PrinterSetUsersAllowed", | ||
981 | g_variant_new ("(sas)", printer_name, &array_builder), | ||
982 | G_VARIANT_TYPE ("(s)"), | ||
983 | G_DBUS_CALL_FLAGS_NONE, | ||
984 | -1, | ||
985 | NULL, | ||
986 | &error); | ||
987 | else | ||
988 | ✗ | output = g_dbus_connection_call_sync (bus, | |
989 | MECHANISM_BUS, | ||
990 | "/", | ||
991 | MECHANISM_BUS, | ||
992 | "PrinterSetUsersDenied", | ||
993 | g_variant_new ("(sas)", printer_name, &array_builder), | ||
994 | G_VARIANT_TYPE ("(s)"), | ||
995 | G_DBUS_CALL_FLAGS_NONE, | ||
996 | -1, | ||
997 | NULL, | ||
998 | &error); | ||
999 | |||
1000 | ✗ | if (output == NULL) | |
1001 | { | ||
1002 | ✗ | g_warning ("%s", error->message); | |
1003 | ✗ | return FALSE; | |
1004 | } | ||
1005 | |||
1006 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
1007 | ✗ | if (ret_error[0] != '\0') | |
1008 | { | ||
1009 | ✗ | g_warning ("cups-pk-helper: setting of access list for printer %s failed: %s", printer_name, ret_error); | |
1010 | ✗ | return FALSE; | |
1011 | } | ||
1012 | |||
1013 | ✗ | return TRUE; | |
1014 | } | ||
1015 | |||
1016 | gboolean | ||
1017 | ✗ | class_add_printer (const gchar *class_name, | |
1018 | const gchar *printer_name) | ||
1019 | { | ||
1020 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
1021 | ✗ | g_autoptr(GVariant) output = NULL; | |
1022 | const gchar *ret_error; | ||
1023 | ✗ | g_autoptr(GError) error = NULL; | |
1024 | |||
1025 | ✗ | if (!class_name || !printer_name) | |
1026 | ✗ | return TRUE; | |
1027 | |||
1028 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
1029 | ✗ | if (!bus) | |
1030 | { | ||
1031 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
1032 | ✗ | return TRUE; | |
1033 | } | ||
1034 | |||
1035 | ✗ | output = g_dbus_connection_call_sync (bus, | |
1036 | MECHANISM_BUS, | ||
1037 | "/", | ||
1038 | MECHANISM_BUS, | ||
1039 | "ClassAddPrinter", | ||
1040 | g_variant_new ("(ss)", class_name, printer_name), | ||
1041 | G_VARIANT_TYPE ("(s)"), | ||
1042 | G_DBUS_CALL_FLAGS_NONE, | ||
1043 | -1, | ||
1044 | NULL, | ||
1045 | &error); | ||
1046 | |||
1047 | ✗ | if (output == NULL) | |
1048 | { | ||
1049 | ✗ | g_warning ("%s", error->message); | |
1050 | ✗ | return FALSE; | |
1051 | } | ||
1052 | |||
1053 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
1054 | ✗ | if (ret_error[0] != '\0') | |
1055 | { | ||
1056 | ✗ | g_warning ("cups-pk-helper: adding of printer %s to class %s failed: %s", printer_name, class_name, ret_error); | |
1057 | ✗ | return FALSE; | |
1058 | } | ||
1059 | |||
1060 | ✗ | return TRUE; | |
1061 | } | ||
1062 | |||
1063 | gboolean | ||
1064 | ✗ | printer_is_local (cups_ptype_t printer_type, | |
1065 | const gchar *device_uri) | ||
1066 | { | ||
1067 | ✗ | gboolean result = TRUE; | |
1068 | char scheme[HTTP_MAX_URI]; | ||
1069 | char username[HTTP_MAX_URI]; | ||
1070 | char hostname[HTTP_MAX_URI]; | ||
1071 | char resource[HTTP_MAX_URI]; | ||
1072 | int port; | ||
1073 | |||
1074 | ✗ | if (printer_type & | |
1075 | (CUPS_PRINTER_DISCOVERED | | ||
1076 | CUPS_PRINTER_REMOTE | | ||
1077 | CUPS_PRINTER_IMPLICIT)) | ||
1078 | ✗ | result = FALSE; | |
1079 | |||
1080 | ✗ | if (device_uri == NULL || !result) | |
1081 | ✗ | return result; | |
1082 | |||
1083 | ✗ | httpSeparateURI (HTTP_URI_CODING_ALL, device_uri, | |
1084 | scheme, sizeof (scheme), | ||
1085 | username, sizeof (username), | ||
1086 | hostname, sizeof (hostname), | ||
1087 | &port, | ||
1088 | resource, sizeof (resource)); | ||
1089 | |||
1090 | ✗ | if (g_str_equal (scheme, "ipp") || | |
1091 | ✗ | g_str_equal (scheme, "smb") || | |
1092 | ✗ | g_str_equal (scheme, "socket") || | |
1093 | ✗ | g_str_equal (scheme, "lpd")) | |
1094 | ✗ | result = FALSE; | |
1095 | |||
1096 | ✗ | return result; | |
1097 | } | ||
1098 | |||
1099 | gchar* | ||
1100 | ✗ | printer_get_hostname (cups_ptype_t printer_type, | |
1101 | const gchar *device_uri, | ||
1102 | const gchar *printer_uri) | ||
1103 | { | ||
1104 | ✗ | gboolean local = TRUE; | |
1105 | ✗ | gchar *result = NULL; | |
1106 | char scheme[HTTP_MAX_URI]; | ||
1107 | char username[HTTP_MAX_URI]; | ||
1108 | char hostname[HTTP_MAX_URI]; | ||
1109 | char resource[HTTP_MAX_URI]; | ||
1110 | int port; | ||
1111 | |||
1112 | ✗ | if (device_uri == NULL) | |
1113 | ✗ | return result; | |
1114 | |||
1115 | ✗ | if (printer_type & (CUPS_PRINTER_DISCOVERED | | |
1116 | CUPS_PRINTER_REMOTE | | ||
1117 | CUPS_PRINTER_IMPLICIT)) | ||
1118 | { | ||
1119 | ✗ | if (printer_uri) | |
1120 | { | ||
1121 | ✗ | httpSeparateURI (HTTP_URI_CODING_ALL, printer_uri, | |
1122 | scheme, sizeof (scheme), | ||
1123 | username, sizeof (username), | ||
1124 | hostname, sizeof (hostname), | ||
1125 | &port, | ||
1126 | resource, sizeof (resource)); | ||
1127 | |||
1128 | ✗ | if (hostname[0] != '\0') | |
1129 | ✗ | result = g_strdup (hostname); | |
1130 | } | ||
1131 | |||
1132 | ✗ | local = FALSE; | |
1133 | } | ||
1134 | |||
1135 | ✗ | if (result == NULL && device_uri) | |
1136 | { | ||
1137 | ✗ | httpSeparateURI (HTTP_URI_CODING_ALL, device_uri, | |
1138 | scheme, sizeof (scheme), | ||
1139 | username, sizeof (username), | ||
1140 | hostname, sizeof (hostname), | ||
1141 | &port, | ||
1142 | resource, sizeof (resource)); | ||
1143 | |||
1144 | ✗ | if (g_str_equal (scheme, "ipp") || | |
1145 | ✗ | g_str_equal (scheme, "smb") || | |
1146 | ✗ | g_str_equal (scheme, "socket") || | |
1147 | ✗ | g_str_equal (scheme, "lpd")) | |
1148 | { | ||
1149 | ✗ | if (hostname[0] != '\0') | |
1150 | ✗ | result = g_strdup (hostname); | |
1151 | |||
1152 | ✗ | local = FALSE; | |
1153 | } | ||
1154 | } | ||
1155 | |||
1156 | ✗ | if (local) | |
1157 | ✗ | result = g_strdup ("localhost"); | |
1158 | |||
1159 | ✗ | return result; | |
1160 | } | ||
1161 | |||
1162 | /* Returns default page size for current locale */ | ||
1163 | const gchar * | ||
1164 | ✗ | get_page_size_from_locale (void) | |
1165 | { | ||
1166 | ✗ | if (g_str_equal (gtk_paper_size_get_default (), GTK_PAPER_NAME_LETTER)) | |
1167 | ✗ | return "Letter"; | |
1168 | else | ||
1169 | ✗ | return "A4"; | |
1170 | } | ||
1171 | |||
1172 | typedef struct | ||
1173 | { | ||
1174 | gchar *printer_name; | ||
1175 | gchar **attributes_names; | ||
1176 | GHashTable *result; | ||
1177 | GIACallback callback; | ||
1178 | gpointer user_data; | ||
1179 | GMainContext *context; | ||
1180 | } GIAData; | ||
1181 | |||
1182 | static GIAData * | ||
1183 | ✗ | gia_data_new (const gchar *printer_name, gchar **attributes_names, GIACallback callback, gpointer user_data) | |
1184 | { | ||
1185 | GIAData *data; | ||
1186 | |||
1187 | ✗ | data = g_new0 (GIAData, 1); | |
1188 | ✗ | data->printer_name = g_strdup (printer_name); | |
1189 | ✗ | data->attributes_names = g_strdupv (attributes_names); | |
1190 | ✗ | data->callback = callback; | |
1191 | ✗ | data->user_data = user_data; | |
1192 | ✗ | data->context = g_main_context_ref_thread_default (); | |
1193 | |||
1194 | ✗ | return data; | |
1195 | } | ||
1196 | |||
1197 | static void | ||
1198 | ✗ | gia_data_free (GIAData *data) | |
1199 | { | ||
1200 | ✗ | g_free (data->printer_name); | |
1201 | ✗ | if (data->attributes_names) | |
1202 | ✗ | g_strfreev (data->attributes_names); | |
1203 | ✗ | if (data->result) | |
1204 | ✗ | g_hash_table_unref (data->result); | |
1205 | ✗ | if (data->context) | |
1206 | ✗ | g_main_context_unref (data->context); | |
1207 | ✗ | g_free (data); | |
1208 | ✗ | } | |
1209 | |||
1210 | static gboolean | ||
1211 | ✗ | get_ipp_attributes_idle_cb (gpointer user_data) | |
1212 | { | ||
1213 | ✗ | GIAData *data = (GIAData *) user_data; | |
1214 | |||
1215 | ✗ | data->callback (data->result, data->user_data); | |
1216 | ✗ | data->result = NULL; | |
1217 | |||
1218 | ✗ | return FALSE; | |
1219 | } | ||
1220 | |||
1221 | static void | ||
1222 | ✗ | get_ipp_attributes_cb (gpointer user_data) | |
1223 | { | ||
1224 | ✗ | GIAData *data = user_data; | |
1225 | ✗ | g_autoptr(GSource) idle_source = NULL; | |
1226 | |||
1227 | ✗ | idle_source = g_idle_source_new (); | |
1228 | ✗ | g_source_set_callback (idle_source, | |
1229 | get_ipp_attributes_idle_cb, | ||
1230 | data, | ||
1231 | (GDestroyNotify) gia_data_free); | ||
1232 | ✗ | g_source_attach (idle_source, data->context); | |
1233 | ✗ | } | |
1234 | |||
1235 | static void | ||
1236 | ✗ | ipp_attribute_free2 (gpointer attr) | |
1237 | { | ||
1238 | ✗ | IPPAttribute *attribute = (IPPAttribute *) attr; | |
1239 | ✗ | ipp_attribute_free (attribute); | |
1240 | ✗ | } | |
1241 | |||
1242 | static gpointer | ||
1243 | ✗ | get_ipp_attributes_func (gpointer user_data) | |
1244 | { | ||
1245 | ✗ | ipp_attribute_t *attr = NULL; | |
1246 | ✗ | GIAData *data = user_data; | |
1247 | ipp_t *request; | ||
1248 | ✗ | ipp_t *response = NULL; | |
1249 | ✗ | g_autofree gchar *printer_uri = NULL; | |
1250 | ✗ | char **requested_attrs = NULL; | |
1251 | ✗ | gint i, j, length = 0; | |
1252 | |||
1253 | ✗ | printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", data->printer_name); | |
1254 | |||
1255 | ✗ | if (data->attributes_names) | |
1256 | { | ||
1257 | ✗ | length = g_strv_length (data->attributes_names); | |
1258 | |||
1259 | ✗ | requested_attrs = g_new0 (char *, length); | |
1260 | ✗ | for (i = 0; data->attributes_names[i]; i++) | |
1261 | ✗ | requested_attrs[i] = g_strdup (data->attributes_names[i]); | |
1262 | |||
1263 | ✗ | request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES); | |
1264 | ✗ | ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, | |
1265 | "printer-uri", NULL, printer_uri); | ||
1266 | ✗ | ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
1267 | "requested-attributes", length, NULL, (const char **) requested_attrs); | ||
1268 | ✗ | response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/"); | |
1269 | } | ||
1270 | |||
1271 | ✗ | if (response) | |
1272 | { | ||
1273 | ✗ | if (ippGetStatusCode (response) <= IPP_OK_CONFLICT) | |
1274 | { | ||
1275 | ✗ | for (j = 0; j < length; j++) | |
1276 | { | ||
1277 | ✗ | attr = ippFindAttribute (response, requested_attrs[j], IPP_TAG_ZERO); | |
1278 | ✗ | if (attr && ippGetCount (attr) > 0 && ippGetValueTag (attr) != IPP_TAG_NOVALUE) | |
1279 | { | ||
1280 | IPPAttribute *attribute; | ||
1281 | |||
1282 | ✗ | attribute = g_new0 (IPPAttribute, 1); | |
1283 | ✗ | attribute->attribute_name = g_strdup (requested_attrs[j]); | |
1284 | ✗ | attribute->attribute_values = g_new0 (IPPAttributeValue, ippGetCount (attr)); | |
1285 | ✗ | attribute->num_of_values = ippGetCount (attr); | |
1286 | |||
1287 | ✗ | if (ippGetValueTag (attr) == IPP_TAG_INTEGER || | |
1288 | ✗ | ippGetValueTag (attr) == IPP_TAG_ENUM) | |
1289 | { | ||
1290 | ✗ | attribute->attribute_type = IPP_ATTRIBUTE_TYPE_INTEGER; | |
1291 | |||
1292 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
1293 | ✗ | attribute->attribute_values[i].integer_value = ippGetInteger (attr, i); | |
1294 | } | ||
1295 | ✗ | else if (ippGetValueTag (attr) == IPP_TAG_NAME || | |
1296 | ✗ | ippGetValueTag (attr) == IPP_TAG_STRING || | |
1297 | ✗ | ippGetValueTag (attr) == IPP_TAG_TEXT || | |
1298 | ✗ | ippGetValueTag (attr) == IPP_TAG_URI || | |
1299 | ✗ | ippGetValueTag (attr) == IPP_TAG_KEYWORD || | |
1300 | ✗ | ippGetValueTag (attr) == IPP_TAG_URISCHEME) | |
1301 | { | ||
1302 | ✗ | attribute->attribute_type = IPP_ATTRIBUTE_TYPE_STRING; | |
1303 | |||
1304 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
1305 | ✗ | attribute->attribute_values[i].string_value = g_strdup (ippGetString (attr, i, NULL)); | |
1306 | } | ||
1307 | ✗ | else if (ippGetValueTag (attr) == IPP_TAG_RANGE) | |
1308 | { | ||
1309 | ✗ | attribute->attribute_type = IPP_ATTRIBUTE_TYPE_RANGE; | |
1310 | |||
1311 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
1312 | { | ||
1313 | ✗ | attribute->attribute_values[i].lower_range = | |
1314 | ✗ | ippGetRange (attr, i, &(attribute->attribute_values[i].upper_range)); | |
1315 | } | ||
1316 | } | ||
1317 | ✗ | else if (ippGetValueTag (attr) == IPP_TAG_BOOLEAN) | |
1318 | { | ||
1319 | ✗ | attribute->attribute_type = IPP_ATTRIBUTE_TYPE_BOOLEAN; | |
1320 | |||
1321 | ✗ | for (i = 0; i < ippGetCount (attr); i++) | |
1322 | ✗ | attribute->attribute_values[i].boolean_value = ippGetBoolean (attr, i); | |
1323 | } | ||
1324 | |||
1325 | ✗ | if (!data->result) | |
1326 | ✗ | data->result = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ipp_attribute_free2); | |
1327 | |||
1328 | ✗ | g_hash_table_insert (data->result, g_strdup (requested_attrs[j]), attribute); | |
1329 | } | ||
1330 | } | ||
1331 | } | ||
1332 | |||
1333 | ✗ | ippDelete (response); | |
1334 | } | ||
1335 | |||
1336 | |||
1337 | ✗ | for (i = 0; i < length; i++) | |
1338 | ✗ | g_free (requested_attrs[i]); | |
1339 | ✗ | g_free (requested_attrs); | |
1340 | |||
1341 | ✗ | get_ipp_attributes_cb (data); | |
1342 | |||
1343 | ✗ | return NULL; | |
1344 | } | ||
1345 | |||
1346 | void | ||
1347 | ✗ | get_ipp_attributes_async (const gchar *printer_name, | |
1348 | gchar **attributes_names, | ||
1349 | GIACallback callback, | ||
1350 | gpointer user_data) | ||
1351 | { | ||
1352 | GIAData *data; | ||
1353 | ✗ | g_autoptr(GThread) thread = NULL; | |
1354 | ✗ | g_autoptr(GError) error = NULL; | |
1355 | |||
1356 | ✗ | data = gia_data_new (printer_name, attributes_names, callback, user_data); | |
1357 | |||
1358 | ✗ | thread = g_thread_try_new ("get-ipp-attributes", | |
1359 | get_ipp_attributes_func, | ||
1360 | data, | ||
1361 | &error); | ||
1362 | |||
1363 | ✗ | if (!thread) | |
1364 | { | ||
1365 | ✗ | g_warning ("%s", error->message); | |
1366 | ✗ | callback (NULL, user_data); | |
1367 | |||
1368 | ✗ | gia_data_free (data); | |
1369 | } | ||
1370 | ✗ | } | |
1371 | |||
1372 | IPPAttribute * | ||
1373 | ✗ | ipp_attribute_copy (IPPAttribute *attr) | |
1374 | { | ||
1375 | ✗ | IPPAttribute *result = NULL; | |
1376 | gint i; | ||
1377 | |||
1378 | ✗ | if (attr) | |
1379 | { | ||
1380 | ✗ | result = g_new0 (IPPAttribute, 1); | |
1381 | |||
1382 | ✗ | *result = *attr; | |
1383 | ✗ | result->attribute_name = g_strdup (attr->attribute_name); | |
1384 | ✗ | result->attribute_values = g_new0 (IPPAttributeValue, attr->num_of_values); | |
1385 | ✗ | for (i = 0; i < attr->num_of_values; i++) | |
1386 | { | ||
1387 | ✗ | result->attribute_values[i] = attr->attribute_values[i]; | |
1388 | ✗ | if (attr->attribute_values[i].string_value) | |
1389 | ✗ | result->attribute_values[i].string_value = g_strdup (attr->attribute_values[i].string_value); | |
1390 | } | ||
1391 | } | ||
1392 | |||
1393 | ✗ | return result; | |
1394 | } | ||
1395 | |||
1396 | void | ||
1397 | ✗ | ipp_attribute_free (IPPAttribute *attr) | |
1398 | { | ||
1399 | gint i; | ||
1400 | |||
1401 | ✗ | if (attr) | |
1402 | { | ||
1403 | ✗ | for (i = 0; i < attr->num_of_values; i++) | |
1404 | ✗ | g_free (attr->attribute_values[i].string_value); | |
1405 | |||
1406 | ✗ | g_free (attr->attribute_values); | |
1407 | ✗ | g_free (attr->attribute_name); | |
1408 | ✗ | g_free (attr); | |
1409 | } | ||
1410 | ✗ | } | |
1411 | |||
1412 | typedef struct | ||
1413 | { | ||
1414 | gchar *printer_name; | ||
1415 | gchar *ppd_copy; | ||
1416 | GCancellable *cancellable; | ||
1417 | PSPCallback callback; | ||
1418 | gpointer user_data; | ||
1419 | } PSPData; | ||
1420 | |||
1421 | static PSPData * | ||
1422 | ✗ | psp_data_new (const gchar *printer_name, const gchar *ppd_copy, GCancellable *cancellable, PSPCallback callback, gpointer user_data) | |
1423 | { | ||
1424 | PSPData *data; | ||
1425 | |||
1426 | ✗ | data = g_new0 (PSPData, 1); | |
1427 | ✗ | data->printer_name = g_strdup (printer_name); | |
1428 | ✗ | data->ppd_copy = g_strdup (ppd_copy); | |
1429 | ✗ | if (cancellable) | |
1430 | ✗ | data->cancellable = g_object_ref (cancellable); | |
1431 | ✗ | data->callback = callback; | |
1432 | ✗ | data->user_data = user_data; | |
1433 | ✗ | return data; | |
1434 | } | ||
1435 | |||
1436 | static void | ||
1437 | ✗ | psp_data_free (PSPData *data) | |
1438 | { | ||
1439 | ✗ | g_free (data->printer_name); | |
1440 | ✗ | if (data->ppd_copy != NULL) | |
1441 | { | ||
1442 | ✗ | g_unlink (data->ppd_copy); | |
1443 | ✗ | g_free (data->ppd_copy); | |
1444 | } | ||
1445 | ✗ | g_clear_object (&data->cancellable); | |
1446 | ✗ | g_free (data); | |
1447 | ✗ | } | |
1448 | |||
1449 | ✗ | G_DEFINE_AUTOPTR_CLEANUP_FUNC (PSPData, psp_data_free) | |
1450 | |||
1451 | static void | ||
1452 | ✗ | printer_set_ppd_async_dbus_cb (GObject *source_object, | |
1453 | GAsyncResult *res, | ||
1454 | gpointer user_data) | ||
1455 | { | ||
1456 | ✗ | g_autoptr(GVariant) output = NULL; | |
1457 | ✗ | gboolean result = FALSE; | |
1458 | ✗ | g_autoptr(PSPData) data = user_data; | |
1459 | ✗ | g_autoptr(GError) error = NULL; | |
1460 | |||
1461 | ✗ | output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), | |
1462 | res, | ||
1463 | &error); | ||
1464 | |||
1465 | ✗ | if (output) | |
1466 | { | ||
1467 | const gchar *ret_error; | ||
1468 | |||
1469 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
1470 | ✗ | if (ret_error[0] != '\0') | |
1471 | ✗ | g_warning ("cups-pk-helper: setting of driver for printer %s failed: %s", data->printer_name, ret_error); | |
1472 | else | ||
1473 | ✗ | result = TRUE; | |
1474 | } | ||
1475 | else | ||
1476 | { | ||
1477 | ✗ | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
1478 | ✗ | g_warning ("%s", error->message); | |
1479 | } | ||
1480 | |||
1481 | /* Don't call callback if cancelled */ | ||
1482 | ✗ | if (!data->cancellable || | |
1483 | ✗ | !g_cancellable_is_cancelled (data->cancellable)) | |
1484 | ✗ | data->callback (data->printer_name, | |
1485 | result, | ||
1486 | ✗ | data->user_data); | |
1487 | ✗ | } | |
1488 | |||
1489 | /* | ||
1490 | * Set ppd for given printer. | ||
1491 | * Don't use this for classes, just for printers. | ||
1492 | */ | ||
1493 | void | ||
1494 | ✗ | printer_set_ppd_async (const gchar *printer_name, | |
1495 | const gchar *ppd_name, | ||
1496 | GCancellable *cancellable, | ||
1497 | PSPCallback callback, | ||
1498 | gpointer user_data) | ||
1499 | { | ||
1500 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
1501 | ✗ | g_autoptr(GError) error = NULL; | |
1502 | |||
1503 | ✗ | if (printer_name == NULL || | |
1504 | ✗ | printer_name[0] == '\0') | |
1505 | { | ||
1506 | ✗ | callback (printer_name, FALSE, user_data); | |
1507 | ✗ | return; | |
1508 | } | ||
1509 | |||
1510 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
1511 | ✗ | if (!bus) | |
1512 | { | ||
1513 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
1514 | ✗ | callback (printer_name, FALSE, user_data); | |
1515 | ✗ | return; | |
1516 | } | ||
1517 | |||
1518 | ✗ | g_dbus_connection_call (bus, | |
1519 | MECHANISM_BUS, | ||
1520 | "/", | ||
1521 | MECHANISM_BUS, | ||
1522 | "PrinterAdd", | ||
1523 | g_variant_new ("(sssss)", | ||
1524 | printer_name, | ||
1525 | "", | ||
1526 | ppd_name, | ||
1527 | "", | ||
1528 | ""), | ||
1529 | G_VARIANT_TYPE ("(s)"), | ||
1530 | G_DBUS_CALL_FLAGS_NONE, | ||
1531 | -1, | ||
1532 | cancellable, | ||
1533 | printer_set_ppd_async_dbus_cb, | ||
1534 | ✗ | psp_data_new (printer_name, NULL, cancellable, callback, user_data)); | |
1535 | } | ||
1536 | |||
1537 | static void | ||
1538 | ✗ | printer_set_ppd_file_async_scb (GObject *source_object, | |
1539 | GAsyncResult *res, | ||
1540 | gpointer user_data) | ||
1541 | { | ||
1542 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
1543 | gboolean success; | ||
1544 | ✗ | g_autoptr(PSPData) data = user_data; | |
1545 | ✗ | g_autoptr(GError) error = NULL; | |
1546 | |||
1547 | ✗ | success = g_file_copy_finish (G_FILE (source_object), | |
1548 | res, | ||
1549 | &error); | ||
1550 | |||
1551 | ✗ | if (!success) | |
1552 | { | ||
1553 | ✗ | g_warning ("%s", error->message); | |
1554 | ✗ | data->callback (data->printer_name, FALSE, data->user_data); | |
1555 | ✗ | return; | |
1556 | } | ||
1557 | |||
1558 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
1559 | ✗ | if (!bus) | |
1560 | { | ||
1561 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
1562 | ✗ | data->callback (data->printer_name, FALSE, data->user_data); | |
1563 | ✗ | return; | |
1564 | } | ||
1565 | |||
1566 | ✗ | g_dbus_connection_call (bus, | |
1567 | MECHANISM_BUS, | ||
1568 | "/", | ||
1569 | MECHANISM_BUS, | ||
1570 | "PrinterAddWithPpdFile", | ||
1571 | g_variant_new ("(sssss)", | ||
1572 | ✗ | data->printer_name, | |
1573 | "", | ||
1574 | ✗ | data->ppd_copy, | |
1575 | "", | ||
1576 | ""), | ||
1577 | G_VARIANT_TYPE ("(s)"), | ||
1578 | G_DBUS_CALL_FLAGS_NONE, | ||
1579 | -1, | ||
1580 | ✗ | data->cancellable, | |
1581 | printer_set_ppd_async_dbus_cb, | ||
1582 | data); | ||
1583 | ✗ | g_steal_pointer (&data); | |
1584 | } | ||
1585 | |||
1586 | /* | ||
1587 | * Set ppd for given printer. | ||
1588 | * Don't use this for classes, just for printers. | ||
1589 | */ | ||
1590 | void | ||
1591 | ✗ | printer_set_ppd_file_async (const gchar *printer_name, | |
1592 | const gchar *ppd_filename, | ||
1593 | GCancellable *cancellable, | ||
1594 | PSPCallback callback, | ||
1595 | gpointer user_data) | ||
1596 | { | ||
1597 | ✗ | g_autoptr(GFileIOStream) stream = NULL; | |
1598 | ✗ | g_autoptr(GFile) source_ppd_file = NULL; | |
1599 | ✗ | g_autoptr(GFile) destination_ppd_file = NULL; | |
1600 | |||
1601 | ✗ | if (printer_name == NULL || | |
1602 | ✗ | printer_name[0] == '\0') | |
1603 | { | ||
1604 | ✗ | callback (printer_name, FALSE, user_data); | |
1605 | ✗ | return; | |
1606 | } | ||
1607 | |||
1608 | /* | ||
1609 | * We need to copy the PPD to temp directory at first. | ||
1610 | * This is needed because of SELinux. | ||
1611 | */ | ||
1612 | ✗ | source_ppd_file = g_file_new_for_path (ppd_filename); | |
1613 | ✗ | destination_ppd_file = g_file_new_tmp ("g-c-c-XXXXXX.ppd", &stream, NULL); | |
1614 | |||
1615 | ✗ | g_file_copy_async (source_ppd_file, | |
1616 | destination_ppd_file, | ||
1617 | G_FILE_COPY_OVERWRITE, | ||
1618 | G_PRIORITY_DEFAULT, | ||
1619 | cancellable, | ||
1620 | NULL, | ||
1621 | NULL, | ||
1622 | printer_set_ppd_file_async_scb, | ||
1623 | ✗ | psp_data_new (printer_name, g_file_get_path (destination_ppd_file), cancellable, callback, user_data)); | |
1624 | } | ||
1625 | |||
1626 | typedef void (*GPACallback) (gchar **attribute_values, | ||
1627 | gpointer user_data); | ||
1628 | |||
1629 | typedef struct | ||
1630 | { | ||
1631 | gchar **ppds_names; | ||
1632 | gchar *attribute_name; | ||
1633 | gchar **result; | ||
1634 | GPACallback callback; | ||
1635 | gpointer user_data; | ||
1636 | GMainContext *context; | ||
1637 | } GPAData; | ||
1638 | |||
1639 | static GPAData * | ||
1640 | ✗ | gpa_data_new (gchar **ppds_names, gchar *attribute_name, GPACallback callback, gpointer user_data) | |
1641 | { | ||
1642 | GPAData *data; | ||
1643 | |||
1644 | ✗ | data = g_new0 (GPAData, 1); | |
1645 | ✗ | data->ppds_names = g_strdupv (ppds_names); | |
1646 | ✗ | data->attribute_name = g_strdup (attribute_name); | |
1647 | ✗ | data->callback = callback; | |
1648 | ✗ | data->user_data = user_data; | |
1649 | ✗ | data->context = g_main_context_ref_thread_default (); | |
1650 | |||
1651 | ✗ | return data; | |
1652 | } | ||
1653 | |||
1654 | static void | ||
1655 | ✗ | gpa_data_free (GPAData *data) | |
1656 | { | ||
1657 | ✗ | g_free (data->attribute_name); | |
1658 | ✗ | g_strfreev (data->ppds_names); | |
1659 | ✗ | if (data->result != NULL) | |
1660 | ✗ | g_strfreev (data->result); | |
1661 | ✗ | if (data->context) | |
1662 | ✗ | g_main_context_unref (data->context); | |
1663 | ✗ | g_free (data); | |
1664 | ✗ | } | |
1665 | |||
1666 | static gboolean | ||
1667 | ✗ | get_ppds_attribute_idle_cb (gpointer user_data) | |
1668 | { | ||
1669 | ✗ | GPAData *data = (GPAData *) user_data; | |
1670 | |||
1671 | ✗ | data->callback (data->result, data->user_data); | |
1672 | |||
1673 | ✗ | return FALSE; | |
1674 | } | ||
1675 | |||
1676 | static void | ||
1677 | ✗ | get_ppds_attribute_cb (gpointer user_data) | |
1678 | { | ||
1679 | ✗ | GPAData *data = (GPAData *) user_data; | |
1680 | ✗ | g_autoptr(GSource) idle_source = NULL; | |
1681 | |||
1682 | ✗ | idle_source = g_idle_source_new (); | |
1683 | ✗ | g_source_set_callback (idle_source, | |
1684 | get_ppds_attribute_idle_cb, | ||
1685 | data, | ||
1686 | (GDestroyNotify) gpa_data_free); | ||
1687 | ✗ | g_source_attach (idle_source, data->context); | |
1688 | ✗ | } | |
1689 | |||
1690 | static gpointer | ||
1691 | ✗ | get_ppds_attribute_func (gpointer user_data) | |
1692 | { | ||
1693 | ppd_file_t *ppd_file; | ||
1694 | ppd_attr_t *ppd_attr; | ||
1695 | ✗ | GPAData *data = user_data; | |
1696 | gint i; | ||
1697 | |||
1698 | ✗ | data->result = g_new0 (gchar *, g_strv_length (data->ppds_names) + 1); | |
1699 | ✗ | for (i = 0; data->ppds_names[i]; i++) | |
1700 | { | ||
1701 | ✗ | g_autofree gchar *ppd_filename = g_strdup (cupsGetServerPPD (CUPS_HTTP_DEFAULT, data->ppds_names[i])); | |
1702 | ✗ | if (ppd_filename) | |
1703 | { | ||
1704 | ✗ | ppd_file = ppdOpenFile (ppd_filename); | |
1705 | ✗ | if (ppd_file) | |
1706 | { | ||
1707 | ✗ | ppd_attr = ppdFindAttr (ppd_file, data->attribute_name, NULL); | |
1708 | ✗ | if (ppd_attr != NULL) | |
1709 | ✗ | data->result[i] = g_strdup (ppd_attr->value); | |
1710 | |||
1711 | ✗ | ppdClose (ppd_file); | |
1712 | } | ||
1713 | |||
1714 | ✗ | g_unlink (ppd_filename); | |
1715 | } | ||
1716 | } | ||
1717 | |||
1718 | ✗ | get_ppds_attribute_cb (data); | |
1719 | |||
1720 | ✗ | return NULL; | |
1721 | } | ||
1722 | |||
1723 | /* | ||
1724 | * Get values of requested PPD attribute for given PPDs. | ||
1725 | */ | ||
1726 | static void | ||
1727 | ✗ | get_ppds_attribute_async (gchar **ppds_names, | |
1728 | gchar *attribute_name, | ||
1729 | GPACallback callback, | ||
1730 | gpointer user_data) | ||
1731 | { | ||
1732 | GPAData *data; | ||
1733 | ✗ | g_autoptr(GThread) thread = NULL; | |
1734 | ✗ | g_autoptr(GError) error = NULL; | |
1735 | |||
1736 | ✗ | if (!ppds_names || !attribute_name) | |
1737 | { | ||
1738 | ✗ | callback (NULL, user_data); | |
1739 | ✗ | return; | |
1740 | } | ||
1741 | |||
1742 | ✗ | data = gpa_data_new (ppds_names, attribute_name, callback, user_data); | |
1743 | |||
1744 | ✗ | thread = g_thread_try_new ("get-ppds-attribute", | |
1745 | get_ppds_attribute_func, | ||
1746 | data, | ||
1747 | &error); | ||
1748 | |||
1749 | ✗ | if (!thread) | |
1750 | { | ||
1751 | ✗ | g_warning ("%s", error->message); | |
1752 | ✗ | callback (NULL, user_data); | |
1753 | |||
1754 | ✗ | gpa_data_free (data); | |
1755 | } | ||
1756 | } | ||
1757 | |||
1758 | |||
1759 | |||
1760 | typedef void (*GDACallback) (gchar *device_id, | ||
1761 | gchar *device_make_and_model, | ||
1762 | gchar *device_uri, | ||
1763 | gpointer user_data); | ||
1764 | |||
1765 | typedef struct | ||
1766 | { | ||
1767 | gchar *printer_name; | ||
1768 | gchar *device_uri; | ||
1769 | GList *backend_list; | ||
1770 | GCancellable *cancellable; | ||
1771 | GDACallback callback; | ||
1772 | gpointer user_data; | ||
1773 | } GDAData; | ||
1774 | |||
1775 | static GDAData * | ||
1776 | ✗ | gda_data_new (const gchar *printer_name, GCancellable *cancellable, GDACallback callback, gpointer user_data) | |
1777 | { | ||
1778 | GDAData *data; | ||
1779 | |||
1780 | ✗ | data = g_new0 (GDAData, 1); | |
1781 | ✗ | data->printer_name = g_strdup (printer_name); | |
1782 | ✗ | if (cancellable) | |
1783 | ✗ | data->cancellable = g_object_ref (cancellable); | |
1784 | ✗ | data->callback = callback; | |
1785 | ✗ | data->user_data = user_data; | |
1786 | |||
1787 | ✗ | return data; | |
1788 | } | ||
1789 | |||
1790 | static void | ||
1791 | ✗ | gda_data_free (GDAData *data) | |
1792 | { | ||
1793 | ✗ | g_free (data->printer_name); | |
1794 | ✗ | g_free (data->device_uri); | |
1795 | ✗ | g_list_free_full(data->backend_list, g_free); | |
1796 | ✗ | g_clear_object (&data->cancellable); | |
1797 | ✗ | g_free (data); | |
1798 | ✗ | } | |
1799 | |||
1800 | ✗ | G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDAData, gda_data_free) | |
1801 | |||
1802 | typedef struct | ||
1803 | { | ||
1804 | gchar *printer_name; | ||
1805 | gint count; | ||
1806 | PPDName **result; | ||
1807 | GCancellable *cancellable; | ||
1808 | GPNCallback callback; | ||
1809 | gpointer user_data; | ||
1810 | } GPNData; | ||
1811 | |||
1812 | static GPNData * | ||
1813 | ✗ | gpn_data_new (const gchar *printer_name, gint count, GCancellable *cancellable, GPNCallback callback, gpointer user_data) | |
1814 | { | ||
1815 | GPNData *data; | ||
1816 | |||
1817 | ✗ | data = g_new0 (GPNData, 1); | |
1818 | ✗ | data->printer_name = g_strdup (printer_name); | |
1819 | ✗ | data->count = count; | |
1820 | ✗ | if (cancellable) | |
1821 | ✗ | data->cancellable = g_object_ref (cancellable); | |
1822 | ✗ | data->callback = callback; | |
1823 | ✗ | data->user_data = user_data; | |
1824 | |||
1825 | ✗ | return data; | |
1826 | } | ||
1827 | |||
1828 | static void | ||
1829 | ✗ | gpn_data_free (GPNData *data) | |
1830 | { | ||
1831 | ✗ | g_free (data->printer_name); | |
1832 | ✗ | if (data->result != NULL) | |
1833 | { | ||
1834 | ✗ | for (int i = 0; data->result[i]; i++) | |
1835 | { | ||
1836 | ✗ | g_free (data->result[i]->ppd_name); | |
1837 | ✗ | g_free (data->result[i]->ppd_display_name); | |
1838 | ✗ | g_free (data->result[i]); | |
1839 | } | ||
1840 | ✗ | g_free (data->result); | |
1841 | } | ||
1842 | ✗ | g_clear_object (&data->cancellable); | |
1843 | ✗ | g_free (data); | |
1844 | ✗ | } | |
1845 | |||
1846 | ✗ | G_DEFINE_AUTOPTR_CLEANUP_FUNC (GPNData, gpn_data_free) | |
1847 | |||
1848 | static void | ||
1849 | ✗ | get_ppd_names_async_cb (gchar **attribute_values, | |
1850 | gpointer user_data) | ||
1851 | { | ||
1852 | ✗ | g_autoptr(GPNData) data = user_data; | |
1853 | gint i; | ||
1854 | |||
1855 | ✗ | if (g_cancellable_is_cancelled (data->cancellable)) | |
1856 | { | ||
1857 | ✗ | data->callback (NULL, | |
1858 | ✗ | data->printer_name, | |
1859 | TRUE, | ||
1860 | ✗ | data->user_data); | |
1861 | ✗ | return; | |
1862 | } | ||
1863 | |||
1864 | ✗ | if (attribute_values) | |
1865 | { | ||
1866 | ✗ | for (i = 0; attribute_values[i]; i++) | |
1867 | ✗ | data->result[i]->ppd_display_name = g_strdup (attribute_values[i]); | |
1868 | } | ||
1869 | |||
1870 | ✗ | data->callback (data->result, | |
1871 | ✗ | data->printer_name, | |
1872 | FALSE, | ||
1873 | ✗ | data->user_data); | |
1874 | } | ||
1875 | |||
1876 | static void | ||
1877 | ✗ | get_ppd_names_async_dbus_scb (GObject *source_object, | |
1878 | GAsyncResult *res, | ||
1879 | gpointer user_data) | ||
1880 | { | ||
1881 | ✗ | g_autoptr(GVariant) output = NULL; | |
1882 | PPDName *ppd_item; | ||
1883 | ✗ | PPDName **result = NULL; | |
1884 | ✗ | g_autoptr(GPNData) data = user_data; | |
1885 | ✗ | g_autoptr(GError) error = NULL; | |
1886 | ✗ | GList *driver_list = NULL; | |
1887 | GList *iter; | ||
1888 | ✗ | gint i, j, n = 0; | |
1889 | static const char * const match_levels[] = { | ||
1890 | "exact-cmd", | ||
1891 | "exact", | ||
1892 | "close", | ||
1893 | "generic", | ||
1894 | "none"}; | ||
1895 | |||
1896 | ✗ | output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), | |
1897 | res, | ||
1898 | &error); | ||
1899 | |||
1900 | ✗ | if (output) | |
1901 | { | ||
1902 | ✗ | g_autoptr(GVariant) array = NULL; | |
1903 | |||
1904 | ✗ | g_variant_get (output, "(@a(ss))", | |
1905 | &array); | ||
1906 | |||
1907 | ✗ | for (j = 0; j < G_N_ELEMENTS (match_levels) && n < data->count; j++) | |
1908 | { | ||
1909 | ✗ | g_autoptr(GVariantIter) iter = NULL; | |
1910 | const gchar *driver, *match; | ||
1911 | |||
1912 | ✗ | g_variant_get (array, | |
1913 | "a(ss)", | ||
1914 | &iter); | ||
1915 | |||
1916 | ✗ | while (g_variant_iter_next (iter, "(&s&s)", &driver, &match)) | |
1917 | { | ||
1918 | ✗ | if (g_str_equal (match, match_levels[j]) && n < data->count) | |
1919 | { | ||
1920 | ✗ | ppd_item = g_new0 (PPDName, 1); | |
1921 | ✗ | ppd_item->ppd_name = g_strdup (driver); | |
1922 | |||
1923 | ✗ | if (g_strcmp0 (match, "exact-cmd") == 0) | |
1924 | ✗ | ppd_item->ppd_match_level = PPD_EXACT_CMD_MATCH; | |
1925 | ✗ | else if (g_strcmp0 (match, "exact") == 0) | |
1926 | ✗ | ppd_item->ppd_match_level = PPD_EXACT_MATCH; | |
1927 | ✗ | else if (g_strcmp0 (match, "close") == 0) | |
1928 | ✗ | ppd_item->ppd_match_level = PPD_CLOSE_MATCH; | |
1929 | ✗ | else if (g_strcmp0 (match, "generic") == 0) | |
1930 | ✗ | ppd_item->ppd_match_level = PPD_GENERIC_MATCH; | |
1931 | ✗ | else if (g_strcmp0 (match, "none") == 0) | |
1932 | ✗ | ppd_item->ppd_match_level = PPD_NO_MATCH; | |
1933 | |||
1934 | ✗ | driver_list = g_list_append (driver_list, ppd_item); | |
1935 | |||
1936 | ✗ | n++; | |
1937 | } | ||
1938 | } | ||
1939 | } | ||
1940 | } | ||
1941 | else | ||
1942 | { | ||
1943 | ✗ | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
1944 | ✗ | g_warning ("%s", error->message); | |
1945 | } | ||
1946 | |||
1947 | ✗ | if (n > 0) | |
1948 | { | ||
1949 | ✗ | result = g_new0 (PPDName *, n + 1); | |
1950 | ✗ | i = 0; | |
1951 | ✗ | for (iter = driver_list; iter; iter = iter->next) | |
1952 | { | ||
1953 | ✗ | result[i] = iter->data; | |
1954 | ✗ | i++; | |
1955 | } | ||
1956 | } | ||
1957 | |||
1958 | ✗ | if (result) | |
1959 | { | ||
1960 | ✗ | g_auto(GStrv) ppds_names = NULL; | |
1961 | |||
1962 | ✗ | data->result = result; | |
1963 | |||
1964 | ✗ | ppds_names = g_new0 (gchar *, n + 1); | |
1965 | ✗ | for (i = 0; i < n; i++) | |
1966 | ✗ | ppds_names[i] = g_strdup (result[i]->ppd_name); | |
1967 | |||
1968 | ✗ | get_ppds_attribute_async (ppds_names, | |
1969 | "NickName", | ||
1970 | get_ppd_names_async_cb, | ||
1971 | data); | ||
1972 | ✗ | g_steal_pointer (&data); | |
1973 | } | ||
1974 | else | ||
1975 | { | ||
1976 | ✗ | data->callback (NULL, | |
1977 | ✗ | data->printer_name, | |
1978 | ✗ | g_cancellable_is_cancelled (data->cancellable), | |
1979 | ✗ | data->user_data); | |
1980 | } | ||
1981 | ✗ | } | |
1982 | |||
1983 | static void | ||
1984 | ✗ | get_device_attributes_cb (gchar *device_id, | |
1985 | gchar *device_make_and_model, | ||
1986 | gchar *device_uri, | ||
1987 | gpointer user_data) | ||
1988 | { | ||
1989 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
1990 | ✗ | g_autoptr(GError) error = NULL; | |
1991 | ✗ | g_autoptr(GPNData) data = user_data; | |
1992 | |||
1993 | ✗ | if (g_cancellable_is_cancelled (data->cancellable)) | |
1994 | { | ||
1995 | ✗ | data->callback (NULL, | |
1996 | ✗ | data->printer_name, | |
1997 | TRUE, | ||
1998 | ✗ | data->user_data); | |
1999 | ✗ | return; | |
2000 | } | ||
2001 | |||
2002 | ✗ | if (!device_id || !device_make_and_model || !device_uri) | |
2003 | { | ||
2004 | ✗ | data->callback (NULL, | |
2005 | ✗ | data->printer_name, | |
2006 | FALSE, | ||
2007 | ✗ | data->user_data); | |
2008 | ✗ | return; | |
2009 | } | ||
2010 | |||
2011 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); | |
2012 | ✗ | if (!bus) | |
2013 | { | ||
2014 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
2015 | ✗ | data->callback (NULL, | |
2016 | ✗ | data->printer_name, | |
2017 | FALSE, | ||
2018 | ✗ | data->user_data); | |
2019 | ✗ | return; | |
2020 | } | ||
2021 | |||
2022 | ✗ | g_dbus_connection_call (bus, | |
2023 | SCP_BUS, | ||
2024 | SCP_PATH, | ||
2025 | SCP_IFACE, | ||
2026 | "GetBestDrivers", | ||
2027 | g_variant_new ("(sss)", | ||
2028 | device_id, | ||
2029 | device_make_and_model, | ||
2030 | device_uri), | ||
2031 | G_VARIANT_TYPE ("(a(ss))"), | ||
2032 | G_DBUS_CALL_FLAGS_NONE, | ||
2033 | DBUS_TIMEOUT_LONG, | ||
2034 | ✗ | data->cancellable, | |
2035 | get_ppd_names_async_dbus_scb, | ||
2036 | data); | ||
2037 | ✗ | g_steal_pointer (&data); | |
2038 | } | ||
2039 | |||
2040 | /* | ||
2041 | * Special item for the list of backends. It represents | ||
2042 | * backends not present in the list itself. | ||
2043 | */ | ||
2044 | #define OTHER_BACKENDS "other-backends" | ||
2045 | |||
2046 | /* | ||
2047 | * List of CUPS backends sorted according to their speed, | ||
2048 | * the fastest is the first one. The last item represents | ||
2049 | * backends not present in the list. | ||
2050 | */ | ||
2051 | const gchar *cups_backends[] = { | ||
2052 | "usb", | ||
2053 | "socket", | ||
2054 | "serial", | ||
2055 | "parallel", | ||
2056 | "lpd", | ||
2057 | "ipp", | ||
2058 | "hp", | ||
2059 | "dnssd", | ||
2060 | "snmp", | ||
2061 | "bluetooth", | ||
2062 | "beh", | ||
2063 | "ncp", | ||
2064 | "hpfax", | ||
2065 | OTHER_BACKENDS | ||
2066 | }; | ||
2067 | |||
2068 | static GList * | ||
2069 | ✗ | create_backends_list () | |
2070 | { | ||
2071 | ✗ | GList *list = NULL; | |
2072 | gint i; | ||
2073 | |||
2074 | ✗ | for (i = 0; i < G_N_ELEMENTS (cups_backends); i++) | |
2075 | ✗ | list = g_list_prepend (list, g_strdup (cups_backends[i])); | |
2076 | ✗ | list = g_list_reverse (list); | |
2077 | |||
2078 | ✗ | return list; | |
2079 | } | ||
2080 | |||
2081 | static GVariantBuilder * | ||
2082 | ✗ | create_other_backends_array () | |
2083 | { | ||
2084 | GVariantBuilder *builder; | ||
2085 | gint i; | ||
2086 | |||
2087 | ✗ | builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); | |
2088 | ✗ | for (i = 0; i < G_N_ELEMENTS (cups_backends) - 1; i++) | |
2089 | ✗ | g_variant_builder_add (builder, "s", cups_backends[i]); | |
2090 | |||
2091 | ✗ | return builder; | |
2092 | } | ||
2093 | |||
2094 | static void | ||
2095 | ✗ | get_device_attributes_async_dbus_cb (GObject *source_object, | |
2096 | GAsyncResult *res, | ||
2097 | gpointer user_data) | ||
2098 | |||
2099 | { | ||
2100 | ✗ | g_autoptr(GVariant) output = NULL; | |
2101 | ✗ | g_autoptr(GDAData) data = user_data; | |
2102 | ✗ | g_autoptr(GError) error = NULL; | |
2103 | ✗ | gchar *device_id = NULL; | |
2104 | ✗ | gchar *device_make_and_model = NULL; | |
2105 | |||
2106 | ✗ | output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), | |
2107 | res, | ||
2108 | &error); | ||
2109 | |||
2110 | ✗ | if (output) | |
2111 | { | ||
2112 | const gchar *ret_error; | ||
2113 | ✗ | g_autoptr(GVariant) devices_variant = NULL; | |
2114 | ✗ | gint index = -1; | |
2115 | |||
2116 | ✗ | g_variant_get (output, "(&s@a{ss})", | |
2117 | &ret_error, | ||
2118 | &devices_variant); | ||
2119 | |||
2120 | ✗ | if (ret_error[0] != '\0') | |
2121 | { | ||
2122 | ✗ | g_warning ("cups-pk-helper: getting of attributes for printer %s failed: %s", data->printer_name, ret_error); | |
2123 | } | ||
2124 | |||
2125 | ✗ | if (data->device_uri) | |
2126 | { | ||
2127 | ✗ | g_autoptr(GVariantIter) iter = NULL; | |
2128 | const gchar *key, *value; | ||
2129 | ✗ | g_autofree gchar *suffix = NULL; | |
2130 | |||
2131 | ✗ | g_variant_get (devices_variant, | |
2132 | "a{ss}", | ||
2133 | &iter); | ||
2134 | |||
2135 | ✗ | while (g_variant_iter_next (iter, "{&s&s}", &key, &value)) | |
2136 | { | ||
2137 | ✗ | if (g_str_equal (value, data->device_uri)) | |
2138 | { | ||
2139 | ✗ | gchar *number = g_strrstr (key, ":"); | |
2140 | ✗ | if (number != NULL) | |
2141 | { | ||
2142 | gchar *endptr; | ||
2143 | |||
2144 | ✗ | number++; | |
2145 | ✗ | index = g_ascii_strtoll (number, &endptr, 10); | |
2146 | ✗ | if (index == 0 && endptr == (number)) | |
2147 | ✗ | index = -1; | |
2148 | } | ||
2149 | } | ||
2150 | } | ||
2151 | |||
2152 | ✗ | suffix = g_strdup_printf (":%d", index); | |
2153 | |||
2154 | ✗ | g_variant_get (devices_variant, | |
2155 | "a{ss}", | ||
2156 | &iter); | ||
2157 | |||
2158 | ✗ | while (g_variant_iter_next (iter, "{&s&s}", &key, &value)) | |
2159 | { | ||
2160 | ✗ | if (g_str_has_suffix (key, suffix)) | |
2161 | { | ||
2162 | ✗ | if (g_str_has_prefix (key, "device-id")) | |
2163 | { | ||
2164 | ✗ | device_id = g_strdup (value); | |
2165 | } | ||
2166 | |||
2167 | ✗ | if (g_str_has_prefix (key, "device-make-and-model")) | |
2168 | { | ||
2169 | ✗ | device_make_and_model = g_strdup (value); | |
2170 | } | ||
2171 | } | ||
2172 | } | ||
2173 | } | ||
2174 | } | ||
2175 | else | ||
2176 | { | ||
2177 | ✗ | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
2178 | ✗ | g_warning ("%s", error->message); | |
2179 | } | ||
2180 | |||
2181 | ✗ | if (!device_id || !device_make_and_model) | |
2182 | { | ||
2183 | ✗ | GVariantBuilder *include_scheme_builder = NULL; | |
2184 | ✗ | GVariantBuilder *exclude_scheme_builder = NULL; | |
2185 | |||
2186 | ✗ | g_free (device_id); | |
2187 | ✗ | g_free (device_make_and_model); | |
2188 | |||
2189 | ✗ | device_id = NULL; | |
2190 | ✗ | device_make_and_model = NULL; | |
2191 | |||
2192 | ✗ | if (data->backend_list && !g_cancellable_is_cancelled (data->cancellable)) | |
2193 | { | ||
2194 | const gchar *backend_name; | ||
2195 | |||
2196 | ✗ | backend_name = data->backend_list->data; | |
2197 | |||
2198 | ✗ | if (g_strcmp0 (backend_name, OTHER_BACKENDS) != 0) | |
2199 | { | ||
2200 | ✗ | include_scheme_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); | |
2201 | ✗ | g_variant_builder_add (include_scheme_builder, "s", backend_name); | |
2202 | } | ||
2203 | else | ||
2204 | { | ||
2205 | ✗ | exclude_scheme_builder = create_other_backends_array (); | |
2206 | } | ||
2207 | |||
2208 | ✗ | data->backend_list = g_list_delete_link (data->backend_list, data->backend_list); | |
2209 | |||
2210 | ✗ | g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source_object)), | |
2211 | MECHANISM_BUS, | ||
2212 | "/", | ||
2213 | MECHANISM_BUS, | ||
2214 | "DevicesGet", | ||
2215 | g_variant_new ("(iiasas)", | ||
2216 | 0, | ||
2217 | 0, | ||
2218 | include_scheme_builder, | ||
2219 | exclude_scheme_builder), | ||
2220 | G_VARIANT_TYPE ("(sa{ss})"), | ||
2221 | G_DBUS_CALL_FLAGS_NONE, | ||
2222 | DBUS_TIMEOUT, | ||
2223 | ✗ | data->cancellable, | |
2224 | get_device_attributes_async_dbus_cb, | ||
2225 | data); | ||
2226 | ✗ | g_steal_pointer (&data); | |
2227 | |||
2228 | ✗ | if (include_scheme_builder) | |
2229 | ✗ | g_variant_builder_unref (include_scheme_builder); | |
2230 | |||
2231 | ✗ | if (exclude_scheme_builder) | |
2232 | ✗ | g_variant_builder_unref (exclude_scheme_builder); | |
2233 | |||
2234 | ✗ | return; | |
2235 | } | ||
2236 | } | ||
2237 | |||
2238 | ✗ | data->callback (device_id, | |
2239 | device_make_and_model, | ||
2240 | ✗ | data->device_uri, | |
2241 | ✗ | data->user_data); | |
2242 | } | ||
2243 | |||
2244 | static void | ||
2245 | ✗ | get_device_attributes_async_scb (GHashTable *result, | |
2246 | gpointer user_data) | ||
2247 | { | ||
2248 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
2249 | GVariantBuilder include_scheme_builder; | ||
2250 | IPPAttribute *attr; | ||
2251 | ✗ | g_autoptr(GDAData) data = user_data; | |
2252 | ✗ | g_autoptr(GError) error = NULL; | |
2253 | |||
2254 | ✗ | if (result) | |
2255 | { | ||
2256 | ✗ | attr = g_hash_table_lookup (result, "device-uri"); | |
2257 | ✗ | if (attr && attr->attribute_type == IPP_ATTRIBUTE_TYPE_STRING && | |
2258 | ✗ | attr->num_of_values > 0) | |
2259 | ✗ | data->device_uri = g_strdup (attr->attribute_values[0].string_value); | |
2260 | ✗ | g_hash_table_unref (result); | |
2261 | } | ||
2262 | |||
2263 | ✗ | if (g_cancellable_is_cancelled (data->cancellable)) | |
2264 | { | ||
2265 | ✗ | data->callback (NULL, NULL, NULL, data->user_data); | |
2266 | ✗ | return; | |
2267 | } | ||
2268 | |||
2269 | ✗ | if (!data->device_uri) | |
2270 | { | ||
2271 | ✗ | data->callback (NULL, NULL, NULL, data->user_data); | |
2272 | ✗ | return; | |
2273 | } | ||
2274 | |||
2275 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
2276 | ✗ | if (!bus) | |
2277 | { | ||
2278 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
2279 | ✗ | data->callback (NULL, NULL, NULL, data->user_data); | |
2280 | ✗ | return; | |
2281 | } | ||
2282 | |||
2283 | ✗ | data->backend_list = create_backends_list (); | |
2284 | |||
2285 | ✗ | g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as")); | |
2286 | ✗ | g_variant_builder_add (&include_scheme_builder, "s", data->backend_list->data); | |
2287 | |||
2288 | ✗ | data->backend_list = g_list_delete_link (data->backend_list, data->backend_list); | |
2289 | |||
2290 | ✗ | g_dbus_connection_call (g_object_ref (bus), | |
2291 | MECHANISM_BUS, | ||
2292 | "/", | ||
2293 | MECHANISM_BUS, | ||
2294 | "DevicesGet", | ||
2295 | g_variant_new ("(iiasas)", | ||
2296 | 0, | ||
2297 | 0, | ||
2298 | &include_scheme_builder, | ||
2299 | NULL), | ||
2300 | G_VARIANT_TYPE ("(sa{ss})"), | ||
2301 | G_DBUS_CALL_FLAGS_NONE, | ||
2302 | DBUS_TIMEOUT, | ||
2303 | ✗ | data->cancellable, | |
2304 | get_device_attributes_async_dbus_cb, | ||
2305 | data); | ||
2306 | ✗ | g_steal_pointer (&data); | |
2307 | } | ||
2308 | |||
2309 | /* | ||
2310 | * Get device-id, device-make-and-model and device-uri for given printer. | ||
2311 | */ | ||
2312 | static void | ||
2313 | ✗ | get_device_attributes_async (const gchar *printer_name, | |
2314 | GCancellable *cancellable, | ||
2315 | GDACallback callback, | ||
2316 | gpointer user_data) | ||
2317 | { | ||
2318 | ✗ | g_auto(GStrv) attributes = NULL; | |
2319 | |||
2320 | ✗ | if (!printer_name) | |
2321 | { | ||
2322 | ✗ | callback (NULL, NULL, NULL, user_data); | |
2323 | ✗ | return; | |
2324 | } | ||
2325 | |||
2326 | ✗ | attributes = g_new0 (gchar *, 2); | |
2327 | ✗ | attributes[0] = g_strdup ("device-uri"); | |
2328 | |||
2329 | ✗ | get_ipp_attributes_async (printer_name, | |
2330 | attributes, | ||
2331 | get_device_attributes_async_scb, | ||
2332 | ✗ | gda_data_new (printer_name, cancellable, callback, user_data)); | |
2333 | } | ||
2334 | |||
2335 | /* | ||
2336 | * Return "count" best matching driver names for given printer. | ||
2337 | */ | ||
2338 | void | ||
2339 | ✗ | get_ppd_names_async (gchar *printer_name, | |
2340 | gint count, | ||
2341 | GCancellable *cancellable, | ||
2342 | GPNCallback callback, | ||
2343 | gpointer user_data) | ||
2344 | { | ||
2345 | ✗ | if (!printer_name) | |
2346 | { | ||
2347 | ✗ | callback (NULL, NULL, TRUE, user_data); | |
2348 | ✗ | return; | |
2349 | } | ||
2350 | |||
2351 | /* | ||
2352 | * We have to find out device-id for this printer at first. | ||
2353 | */ | ||
2354 | ✗ | get_device_attributes_async (printer_name, | |
2355 | cancellable, | ||
2356 | get_device_attributes_cb, | ||
2357 | ✗ | gpn_data_new (printer_name, count, cancellable, callback, user_data)); | |
2358 | } | ||
2359 | |||
2360 | typedef struct | ||
2361 | { | ||
2362 | PPDList *result; | ||
2363 | GCancellable *cancellable; | ||
2364 | GAPCallback callback; | ||
2365 | gpointer user_data; | ||
2366 | GMainContext *context; | ||
2367 | } GAPData; | ||
2368 | |||
2369 | static GAPData * | ||
2370 | ✗ | gap_data_new (GCancellable *cancellable, GAPCallback callback, gpointer user_data) | |
2371 | { | ||
2372 | GAPData *data; | ||
2373 | |||
2374 | ✗ | data = g_new0 (GAPData, 1); | |
2375 | ✗ | if (cancellable) | |
2376 | ✗ | data->cancellable = g_object_ref (cancellable); | |
2377 | ✗ | data->callback = callback; | |
2378 | ✗ | data->user_data = user_data; | |
2379 | ✗ | data->context = g_main_context_ref_thread_default (); | |
2380 | |||
2381 | ✗ | return data; | |
2382 | } | ||
2383 | |||
2384 | static void | ||
2385 | ✗ | gap_data_free (GAPData *data) | |
2386 | { | ||
2387 | ✗ | if (data->result != NULL) | |
2388 | ✗ | ppd_list_free (data->result); | |
2389 | ✗ | g_clear_object (&data->cancellable); | |
2390 | ✗ | if (data->context) | |
2391 | ✗ | g_main_context_unref (data->context); | |
2392 | ✗ | g_free (data); | |
2393 | ✗ | } | |
2394 | |||
2395 | static gboolean | ||
2396 | ✗ | get_all_ppds_idle_cb (gpointer user_data) | |
2397 | { | ||
2398 | ✗ | GAPData *data = user_data; | |
2399 | |||
2400 | ✗ | if (!g_cancellable_is_cancelled (data->cancellable)) | |
2401 | ✗ | data->callback (data->result, data->user_data); | |
2402 | |||
2403 | ✗ | return FALSE; | |
2404 | } | ||
2405 | |||
2406 | static void | ||
2407 | ✗ | get_all_ppds_cb (gpointer user_data) | |
2408 | { | ||
2409 | ✗ | GAPData *data = user_data; | |
2410 | ✗ | g_autoptr(GSource) idle_source = NULL; | |
2411 | |||
2412 | ✗ | idle_source = g_idle_source_new (); | |
2413 | ✗ | g_source_set_callback (idle_source, | |
2414 | get_all_ppds_idle_cb, | ||
2415 | data, | ||
2416 | (GDestroyNotify) gap_data_free); | ||
2417 | ✗ | g_source_attach (idle_source, data->context); | |
2418 | ✗ | } | |
2419 | |||
2420 | static const struct { | ||
2421 | const char *normalized_name; | ||
2422 | const char *display_name; | ||
2423 | } manufacturers_names[] = { | ||
2424 | { "alps", "Alps" }, | ||
2425 | { "anitech", "Anitech" }, | ||
2426 | { "apple", "Apple" }, | ||
2427 | { "apollo", "Apollo" }, | ||
2428 | { "brother", "Brother" }, | ||
2429 | { "canon", "Canon" }, | ||
2430 | { "citizen", "Citizen" }, | ||
2431 | { "citoh", "Citoh" }, | ||
2432 | { "compaq", "Compaq" }, | ||
2433 | { "dec", "DEC" }, | ||
2434 | { "dell", "Dell" }, | ||
2435 | { "dnp", "DNP" }, | ||
2436 | { "dymo", "Dymo" }, | ||
2437 | { "epson", "Epson" }, | ||
2438 | { "fujifilm", "Fujifilm" }, | ||
2439 | { "fujitsu", "Fujitsu" }, | ||
2440 | { "gelsprinter", "Ricoh" }, | ||
2441 | { "generic", "Generic" }, | ||
2442 | { "genicom", "Genicom" }, | ||
2443 | { "gestetner", "Gestetner" }, | ||
2444 | { "hewlett packard", "Hewlett-Packard" }, | ||
2445 | { "heidelberg", "Heidelberg" }, | ||
2446 | { "hitachi", "Hitachi" }, | ||
2447 | { "hp", "Hewlett-Packard" }, | ||
2448 | { "ibm", "IBM" }, | ||
2449 | { "imagen", "Imagen" }, | ||
2450 | { "imagistics", "Imagistics" }, | ||
2451 | { "infoprint", "InfoPrint" }, | ||
2452 | { "infotec", "Infotec" }, | ||
2453 | { "intellitech", "Intellitech" }, | ||
2454 | { "kodak", "Kodak" }, | ||
2455 | { "konica minolta", "Minolta" }, | ||
2456 | { "kyocera", "Kyocera" }, | ||
2457 | { "kyocera mita", "Kyocera" }, | ||
2458 | { "lanier", "Lanier" }, | ||
2459 | { "lexmark international", "Lexmark" }, | ||
2460 | { "lexmark", "Lexmark" }, | ||
2461 | { "minolta", "Minolta" }, | ||
2462 | { "minolta qms", "Minolta" }, | ||
2463 | { "mitsubishi", "Mitsubishi" }, | ||
2464 | { "nec", "NEC" }, | ||
2465 | { "nrg", "NRG" }, | ||
2466 | { "oce", "Oce" }, | ||
2467 | { "oki", "Oki" }, | ||
2468 | { "oki data corp", "Oki" }, | ||
2469 | { "olivetti", "Olivetti" }, | ||
2470 | { "olympus", "Olympus" }, | ||
2471 | { "panasonic", "Panasonic" }, | ||
2472 | { "pcpi", "PCPI" }, | ||
2473 | { "pentax", "Pentax" }, | ||
2474 | { "qms", "QMS" }, | ||
2475 | { "raven", "Raven" }, | ||
2476 | { "raw", "Raw" }, | ||
2477 | { "ricoh", "Ricoh" }, | ||
2478 | { "samsung", "Samsung" }, | ||
2479 | { "savin", "Savin" }, | ||
2480 | { "seiko", "Seiko" }, | ||
2481 | { "sharp", "Sharp" }, | ||
2482 | { "shinko", "Shinko" }, | ||
2483 | { "sipix", "SiPix" }, | ||
2484 | { "sony", "Sony" }, | ||
2485 | { "star", "Star" }, | ||
2486 | { "tally", "Tally" }, | ||
2487 | { "tektronix", "Tektronix" }, | ||
2488 | { "texas instruments", "Texas Instruments" }, | ||
2489 | { "toshiba", "Toshiba" }, | ||
2490 | { "toshiba tec corp.", "Toshiba" }, | ||
2491 | { "xante", "Xante" }, | ||
2492 | { "xerox", "Xerox" }, | ||
2493 | { "zebra", "Zebra" }, | ||
2494 | }; | ||
2495 | |||
2496 | static gpointer | ||
2497 | ✗ | get_all_ppds_func (gpointer user_data) | |
2498 | { | ||
2499 | ipp_attribute_t *attr; | ||
2500 | ✗ | GHashTable *ppds_hash = NULL; | |
2501 | ✗ | GHashTable *manufacturers_hash = NULL; | |
2502 | ✗ | GAPData *data = user_data; | |
2503 | PPDName *item; | ||
2504 | ipp_t *request; | ||
2505 | ipp_t *response; | ||
2506 | GList *list; | ||
2507 | gchar *manufacturer_display_name; | ||
2508 | gint i, j; | ||
2509 | |||
2510 | ✗ | request = ippNewRequest (CUPS_GET_PPDS); | |
2511 | ✗ | response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/"); | |
2512 | |||
2513 | ✗ | if (response && | |
2514 | ✗ | ippGetStatusCode (response) <= IPP_OK_CONFLICT) | |
2515 | { | ||
2516 | /* | ||
2517 | * This hash contains names of manufacturers as keys and | ||
2518 | * values are GLists of PPD names. | ||
2519 | */ | ||
2520 | ✗ | ppds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); | |
2521 | |||
2522 | /* | ||
2523 | * This hash contains all possible names of manufacturers as keys | ||
2524 | * and values are just first occurrences of their equivalents. | ||
2525 | * This is for mapping of e.g. "Hewlett Packard" and "HP" to the same name | ||
2526 | * (the one which comes first). | ||
2527 | */ | ||
2528 | ✗ | manufacturers_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); | |
2529 | |||
2530 | ✗ | for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++) | |
2531 | { | ||
2532 | ✗ | g_hash_table_insert (manufacturers_hash, | |
2533 | ✗ | g_strdup (manufacturers_names[i].normalized_name), | |
2534 | ✗ | g_strdup (manufacturers_names[i].display_name)); | |
2535 | } | ||
2536 | |||
2537 | ✗ | for (attr = ippFirstAttribute (response); attr != NULL; attr = ippNextAttribute (response)) | |
2538 | { | ||
2539 | ✗ | const gchar *ppd_device_id = NULL; | |
2540 | ✗ | const gchar *ppd_make_and_model = NULL; | |
2541 | ✗ | const gchar *ppd_name = NULL; | |
2542 | ✗ | const gchar *ppd_product = NULL; | |
2543 | ✗ | const gchar *ppd_make = NULL; | |
2544 | ✗ | g_autofree gchar *mdl = NULL; | |
2545 | ✗ | g_autofree gchar *mfg = NULL; | |
2546 | ✗ | g_autofree gchar *mfg_normalized = NULL; | |
2547 | |||
2548 | ✗ | while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER) | |
2549 | ✗ | attr = ippNextAttribute (response); | |
2550 | |||
2551 | ✗ | if (attr == NULL) | |
2552 | ✗ | break; | |
2553 | |||
2554 | ✗ | while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER) | |
2555 | { | ||
2556 | ✗ | if (g_strcmp0 (ippGetName (attr), "ppd-device-id") == 0 && | |
2557 | ✗ | ippGetValueTag (attr) == IPP_TAG_TEXT) | |
2558 | ✗ | ppd_device_id = ippGetString (attr, 0, NULL); | |
2559 | ✗ | else if (g_strcmp0 (ippGetName (attr), "ppd-make-and-model") == 0 && | |
2560 | ✗ | ippGetValueTag (attr) == IPP_TAG_TEXT) | |
2561 | ✗ | ppd_make_and_model = ippGetString (attr, 0, NULL); | |
2562 | ✗ | else if (g_strcmp0 (ippGetName (attr), "ppd-name") == 0 && | |
2563 | ✗ | ippGetValueTag (attr) == IPP_TAG_NAME) | |
2564 | ✗ | ppd_name = ippGetString (attr, 0, NULL); | |
2565 | ✗ | else if (g_strcmp0 (ippGetName (attr), "ppd-product") == 0 && | |
2566 | ✗ | ippGetValueTag (attr) == IPP_TAG_TEXT) | |
2567 | ✗ | ppd_product = ippGetString (attr, 0, NULL); | |
2568 | ✗ | else if (g_strcmp0 (ippGetName (attr), "ppd-make") == 0 && | |
2569 | ✗ | ippGetValueTag (attr) == IPP_TAG_TEXT) | |
2570 | ✗ | ppd_make = ippGetString (attr, 0, NULL); | |
2571 | |||
2572 | ✗ | attr = ippNextAttribute (response); | |
2573 | } | ||
2574 | |||
2575 | /* Get manufacturer's name */ | ||
2576 | ✗ | if (ppd_device_id && ppd_device_id[0] != '\0') | |
2577 | { | ||
2578 | ✗ | mfg = get_tag_value (ppd_device_id, "mfg"); | |
2579 | ✗ | if (!mfg) | |
2580 | ✗ | mfg = get_tag_value (ppd_device_id, "manufacturer"); | |
2581 | ✗ | mfg_normalized = normalize (mfg); | |
2582 | } | ||
2583 | |||
2584 | ✗ | if (!mfg && | |
2585 | ✗ | ppd_make && | |
2586 | ✗ | ppd_make[0] != '\0') | |
2587 | { | ||
2588 | ✗ | mfg = g_strdup (ppd_make); | |
2589 | ✗ | mfg_normalized = normalize (ppd_make); | |
2590 | } | ||
2591 | |||
2592 | /* Get model */ | ||
2593 | ✗ | if (ppd_make_and_model && | |
2594 | ✗ | ppd_make_and_model[0] != '\0') | |
2595 | { | ||
2596 | ✗ | mdl = g_strdup (ppd_make_and_model); | |
2597 | } | ||
2598 | |||
2599 | ✗ | if (!mdl && | |
2600 | ✗ | ppd_product && | |
2601 | ✗ | ppd_product[0] != '\0') | |
2602 | { | ||
2603 | ✗ | mdl = g_strdup (ppd_product); | |
2604 | } | ||
2605 | |||
2606 | ✗ | if (!mdl && | |
2607 | ✗ | ppd_device_id && | |
2608 | ✗ | ppd_device_id[0] != '\0') | |
2609 | { | ||
2610 | ✗ | mdl = get_tag_value (ppd_device_id, "mdl"); | |
2611 | ✗ | if (!mdl) | |
2612 | ✗ | mdl = get_tag_value (ppd_device_id, "model"); | |
2613 | } | ||
2614 | |||
2615 | ✗ | if (ppd_name && ppd_name[0] != '\0' && | |
2616 | ✗ | mdl && mdl[0] != '\0' && | |
2617 | ✗ | mfg && mfg[0] != '\0') | |
2618 | { | ||
2619 | ✗ | manufacturer_display_name = g_hash_table_lookup (manufacturers_hash, mfg_normalized); | |
2620 | ✗ | if (!manufacturer_display_name) | |
2621 | { | ||
2622 | ✗ | g_hash_table_insert (manufacturers_hash, g_strdup (mfg_normalized), g_strdup (mfg)); | |
2623 | } | ||
2624 | else | ||
2625 | { | ||
2626 | ✗ | g_free (mfg_normalized); | |
2627 | ✗ | mfg_normalized = normalize (manufacturer_display_name); | |
2628 | } | ||
2629 | |||
2630 | ✗ | item = g_new0 (PPDName, 1); | |
2631 | ✗ | item->ppd_name = g_strdup (ppd_name); | |
2632 | ✗ | item->ppd_display_name = g_strdup (mdl); | |
2633 | ✗ | item->ppd_match_level = -1; | |
2634 | |||
2635 | ✗ | list = g_hash_table_lookup (ppds_hash, mfg_normalized); | |
2636 | ✗ | if (list) | |
2637 | { | ||
2638 | ✗ | list = g_list_append (list, item); | |
2639 | } | ||
2640 | else | ||
2641 | { | ||
2642 | ✗ | list = g_list_append (list, item); | |
2643 | ✗ | g_hash_table_insert (ppds_hash, g_strdup (mfg_normalized), list); | |
2644 | } | ||
2645 | } | ||
2646 | |||
2647 | ✗ | if (attr == NULL) | |
2648 | ✗ | break; | |
2649 | } | ||
2650 | } | ||
2651 | |||
2652 | ✗ | if (response) | |
2653 | ✗ | ippDelete(response); | |
2654 | |||
2655 | ✗ | if (ppds_hash && | |
2656 | manufacturers_hash) | ||
2657 | { | ||
2658 | GHashTableIter iter; | ||
2659 | gpointer key; | ||
2660 | gpointer value; | ||
2661 | GList *ppd_item; | ||
2662 | ✗ | GList *sort_list = NULL; | |
2663 | GList *list_iter; | ||
2664 | gchar *name; | ||
2665 | |||
2666 | ✗ | data->result = g_new0 (PPDList, 1); | |
2667 | ✗ | data->result->num_of_manufacturers = g_hash_table_size (ppds_hash); | |
2668 | ✗ | data->result->manufacturers = g_new0 (PPDManufacturerItem *, data->result->num_of_manufacturers); | |
2669 | |||
2670 | ✗ | g_hash_table_iter_init (&iter, ppds_hash); | |
2671 | ✗ | while (g_hash_table_iter_next (&iter, &key, &value)) | |
2672 | { | ||
2673 | ✗ | sort_list = g_list_append (sort_list, g_strdup (key)); | |
2674 | } | ||
2675 | |||
2676 | /* Sort list of manufacturers */ | ||
2677 | ✗ | sort_list = g_list_sort (sort_list, (GCompareFunc) g_strcmp0); | |
2678 | |||
2679 | /* | ||
2680 | * Fill resulting list of lists (list of manufacturers where | ||
2681 | * each item contains list of PPD names) | ||
2682 | */ | ||
2683 | ✗ | i = 0; | |
2684 | ✗ | for (list_iter = sort_list; list_iter; list_iter = list_iter->next) | |
2685 | { | ||
2686 | ✗ | name = (gchar *) list_iter->data; | |
2687 | ✗ | value = g_hash_table_lookup (ppds_hash, name); | |
2688 | |||
2689 | ✗ | data->result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1); | |
2690 | ✗ | data->result->manufacturers[i]->manufacturer_name = g_strdup (name); | |
2691 | ✗ | data->result->manufacturers[i]->manufacturer_display_name = g_strdup (g_hash_table_lookup (manufacturers_hash, name)); | |
2692 | ✗ | data->result->manufacturers[i]->num_of_ppds = g_list_length ((GList *) value); | |
2693 | ✗ | data->result->manufacturers[i]->ppds = g_new0 (PPDName *, data->result->manufacturers[i]->num_of_ppds); | |
2694 | |||
2695 | ✗ | for (ppd_item = (GList *) value, j = 0; ppd_item; ppd_item = ppd_item->next, j++) | |
2696 | { | ||
2697 | ✗ | data->result->manufacturers[i]->ppds[j] = ppd_item->data; | |
2698 | } | ||
2699 | |||
2700 | ✗ | g_list_free ((GList *) value); | |
2701 | |||
2702 | ✗ | i++; | |
2703 | } | ||
2704 | |||
2705 | ✗ | g_list_free_full (sort_list, g_free); | |
2706 | ✗ | g_hash_table_destroy (ppds_hash); | |
2707 | ✗ | g_hash_table_destroy (manufacturers_hash); | |
2708 | } | ||
2709 | |||
2710 | ✗ | get_all_ppds_cb (data); | |
2711 | |||
2712 | ✗ | return NULL; | |
2713 | } | ||
2714 | |||
2715 | /* | ||
2716 | * Get names of all installed PPDs sorted by manufacturers names. | ||
2717 | */ | ||
2718 | void | ||
2719 | ✗ | get_all_ppds_async (GCancellable *cancellable, | |
2720 | GAPCallback callback, | ||
2721 | gpointer user_data) | ||
2722 | { | ||
2723 | GAPData *data; | ||
2724 | ✗ | g_autoptr(GThread) thread = NULL; | |
2725 | ✗ | g_autoptr(GError) error = NULL; | |
2726 | |||
2727 | ✗ | data = gap_data_new (cancellable, callback, user_data); | |
2728 | |||
2729 | ✗ | thread = g_thread_try_new ("get-all-ppds", | |
2730 | get_all_ppds_func, | ||
2731 | data, | ||
2732 | &error); | ||
2733 | |||
2734 | ✗ | if (!thread) | |
2735 | { | ||
2736 | ✗ | g_warning ("%s", error->message); | |
2737 | ✗ | callback (NULL, user_data); | |
2738 | |||
2739 | ✗ | gap_data_free (data); | |
2740 | } | ||
2741 | ✗ | } | |
2742 | |||
2743 | PPDList * | ||
2744 | ✗ | ppd_list_copy (PPDList *list) | |
2745 | { | ||
2746 | ✗ | PPDList *result = NULL; | |
2747 | gint i, j; | ||
2748 | |||
2749 | ✗ | if (list) | |
2750 | { | ||
2751 | ✗ | result = g_new0 (PPDList, 1); | |
2752 | ✗ | result->num_of_manufacturers = list->num_of_manufacturers; | |
2753 | ✗ | result->manufacturers = g_new0 (PPDManufacturerItem *, list->num_of_manufacturers); | |
2754 | |||
2755 | ✗ | for (i = 0; i < result->num_of_manufacturers; i++) | |
2756 | { | ||
2757 | ✗ | result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1); | |
2758 | ✗ | result->manufacturers[i]->num_of_ppds = list->manufacturers[i]->num_of_ppds; | |
2759 | ✗ | result->manufacturers[i]->ppds = g_new0 (PPDName *, result->manufacturers[i]->num_of_ppds); | |
2760 | |||
2761 | ✗ | result->manufacturers[i]->manufacturer_display_name = | |
2762 | ✗ | g_strdup (list->manufacturers[i]->manufacturer_display_name); | |
2763 | |||
2764 | ✗ | result->manufacturers[i]->manufacturer_name = | |
2765 | ✗ | g_strdup (list->manufacturers[i]->manufacturer_name); | |
2766 | |||
2767 | ✗ | for (j = 0; j < result->manufacturers[i]->num_of_ppds; j++) | |
2768 | { | ||
2769 | ✗ | result->manufacturers[i]->ppds[j] = g_new0 (PPDName, 1); | |
2770 | |||
2771 | ✗ | result->manufacturers[i]->ppds[j]->ppd_display_name = | |
2772 | ✗ | g_strdup (list->manufacturers[i]->ppds[j]->ppd_display_name); | |
2773 | |||
2774 | ✗ | result->manufacturers[i]->ppds[j]->ppd_name = | |
2775 | ✗ | g_strdup (list->manufacturers[i]->ppds[j]->ppd_name); | |
2776 | |||
2777 | ✗ | result->manufacturers[i]->ppds[j]->ppd_match_level = | |
2778 | ✗ | list->manufacturers[i]->ppds[j]->ppd_match_level; | |
2779 | } | ||
2780 | } | ||
2781 | } | ||
2782 | |||
2783 | ✗ | return result; | |
2784 | } | ||
2785 | |||
2786 | void | ||
2787 | ✗ | ppd_list_free (PPDList *list) | |
2788 | { | ||
2789 | gint i, j; | ||
2790 | |||
2791 | ✗ | if (list) | |
2792 | { | ||
2793 | ✗ | for (i = 0; i < list->num_of_manufacturers; i++) | |
2794 | { | ||
2795 | ✗ | for (j = 0; j < list->manufacturers[i]->num_of_ppds; j++) | |
2796 | { | ||
2797 | ✗ | g_free (list->manufacturers[i]->ppds[j]->ppd_name); | |
2798 | ✗ | g_free (list->manufacturers[i]->ppds[j]->ppd_display_name); | |
2799 | ✗ | g_free (list->manufacturers[i]->ppds[j]); | |
2800 | } | ||
2801 | |||
2802 | ✗ | g_free (list->manufacturers[i]->manufacturer_name); | |
2803 | ✗ | g_free (list->manufacturers[i]->manufacturer_display_name); | |
2804 | ✗ | g_free (list->manufacturers[i]->ppds); | |
2805 | ✗ | g_free (list->manufacturers[i]); | |
2806 | } | ||
2807 | |||
2808 | ✗ | g_free (list->manufacturers); | |
2809 | ✗ | g_free (list); | |
2810 | } | ||
2811 | ✗ | } | |
2812 | |||
2813 | gchar * | ||
2814 | ✗ | get_standard_manufacturers_name (const gchar *name) | |
2815 | { | ||
2816 | ✗ | g_autofree gchar *normalized_name = NULL; | |
2817 | gint i; | ||
2818 | |||
2819 | ✗ | if (name == NULL) | |
2820 | ✗ | return NULL; | |
2821 | |||
2822 | ✗ | normalized_name = normalize (name); | |
2823 | |||
2824 | ✗ | for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++) | |
2825 | { | ||
2826 | ✗ | if (g_strcmp0 (manufacturers_names[i].normalized_name, normalized_name) == 0) | |
2827 | { | ||
2828 | ✗ | return g_strdup (manufacturers_names[i].display_name); | |
2829 | } | ||
2830 | } | ||
2831 | |||
2832 | ✗ | return NULL; | |
2833 | } | ||
2834 | |||
2835 | typedef struct | ||
2836 | { | ||
2837 | gchar *printer_name; | ||
2838 | gchar *host_name; | ||
2839 | gint port; | ||
2840 | gchar *result; | ||
2841 | PGPCallback callback; | ||
2842 | gpointer user_data; | ||
2843 | GMainContext *context; | ||
2844 | } PGPData; | ||
2845 | |||
2846 | static PGPData * | ||
2847 | ✗ | pgp_data_new (const gchar *printer_name, const gchar *host_name, gint port, PGPCallback callback, gpointer user_data) | |
2848 | { | ||
2849 | PGPData *data; | ||
2850 | |||
2851 | ✗ | data = g_new0 (PGPData, 1); | |
2852 | ✗ | data->printer_name = g_strdup (printer_name); | |
2853 | ✗ | data->host_name = g_strdup (host_name); | |
2854 | ✗ | data->port = port; | |
2855 | ✗ | data->callback = callback; | |
2856 | ✗ | data->user_data = user_data; | |
2857 | ✗ | data->context = g_main_context_ref_thread_default (); | |
2858 | |||
2859 | ✗ | return data; | |
2860 | } | ||
2861 | |||
2862 | static void | ||
2863 | ✗ | pgp_data_free (PGPData *data) | |
2864 | { | ||
2865 | ✗ | g_free (data->printer_name); | |
2866 | ✗ | g_free (data->host_name); | |
2867 | ✗ | g_free (data->result); | |
2868 | ✗ | if (data->context) | |
2869 | ✗ | g_main_context_unref (data->context); | |
2870 | ✗ | g_free (data); | |
2871 | ✗ | } | |
2872 | |||
2873 | static gboolean | ||
2874 | ✗ | printer_get_ppd_idle_cb (gpointer user_data) | |
2875 | { | ||
2876 | ✗ | PGPData *data = user_data; | |
2877 | |||
2878 | ✗ | data->callback (data->result, data->user_data); | |
2879 | |||
2880 | ✗ | return FALSE; | |
2881 | } | ||
2882 | |||
2883 | static void | ||
2884 | ✗ | printer_get_ppd_cb (gpointer user_data) | |
2885 | { | ||
2886 | ✗ | PGPData *data = user_data; | |
2887 | ✗ | g_autoptr(GSource) idle_source = NULL; | |
2888 | |||
2889 | ✗ | idle_source = g_idle_source_new (); | |
2890 | ✗ | g_source_set_callback (idle_source, | |
2891 | printer_get_ppd_idle_cb, | ||
2892 | data, | ||
2893 | (GDestroyNotify) pgp_data_free); | ||
2894 | ✗ | g_source_attach (idle_source, data->context); | |
2895 | ✗ | } | |
2896 | |||
2897 | static gpointer | ||
2898 | ✗ | printer_get_ppd_func (gpointer user_data) | |
2899 | { | ||
2900 | ✗ | PGPData *data = user_data; | |
2901 | |||
2902 | ✗ | if (data->host_name) | |
2903 | { | ||
2904 | http_t *http; | ||
2905 | |||
2906 | #ifdef HAVE_CUPS_HTTPCONNECT2 | ||
2907 | ✗ | http = httpConnect2 (data->host_name, data->port, NULL, AF_UNSPEC, | |
2908 | HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL); | ||
2909 | #else | ||
2910 | http = httpConnect (data->host_name, data->port); | ||
2911 | #endif | ||
2912 | ✗ | if (http) | |
2913 | { | ||
2914 | ✗ | data->result = g_strdup (cupsGetPPD2 (http, data->printer_name)); | |
2915 | ✗ | httpClose (http); | |
2916 | } | ||
2917 | } | ||
2918 | else | ||
2919 | { | ||
2920 | ✗ | data->result = g_strdup (cupsGetPPD (data->printer_name)); | |
2921 | } | ||
2922 | |||
2923 | ✗ | printer_get_ppd_cb (data); | |
2924 | |||
2925 | ✗ | return NULL; | |
2926 | } | ||
2927 | |||
2928 | void | ||
2929 | ✗ | printer_get_ppd_async (const gchar *printer_name, | |
2930 | const gchar *host_name, | ||
2931 | gint port, | ||
2932 | PGPCallback callback, | ||
2933 | gpointer user_data) | ||
2934 | { | ||
2935 | PGPData *data; | ||
2936 | ✗ | g_autoptr(GThread) thread = NULL; | |
2937 | ✗ | g_autoptr(GError) error = NULL; | |
2938 | |||
2939 | ✗ | data = pgp_data_new (printer_name, host_name, port, callback, user_data); | |
2940 | |||
2941 | ✗ | thread = g_thread_try_new ("printer-get-ppd", | |
2942 | printer_get_ppd_func, | ||
2943 | data, | ||
2944 | &error); | ||
2945 | |||
2946 | ✗ | if (!thread) | |
2947 | { | ||
2948 | ✗ | g_warning ("%s", error->message); | |
2949 | ✗ | callback (NULL, user_data); | |
2950 | |||
2951 | ✗ | pgp_data_free (data); | |
2952 | } | ||
2953 | ✗ | } | |
2954 | |||
2955 | typedef struct | ||
2956 | { | ||
2957 | gchar *printer_name; | ||
2958 | cups_dest_t *result; | ||
2959 | GNDCallback callback; | ||
2960 | gpointer user_data; | ||
2961 | GMainContext *context; | ||
2962 | } GNDData; | ||
2963 | |||
2964 | static GNDData * | ||
2965 | ✗ | gnd_data_new (const gchar *printer_name, GNDCallback callback, gpointer user_data) | |
2966 | { | ||
2967 | GNDData *data; | ||
2968 | |||
2969 | ✗ | data = g_new0 (GNDData, 1); | |
2970 | ✗ | data->printer_name = g_strdup (printer_name); | |
2971 | ✗ | data->callback = callback; | |
2972 | ✗ | data->user_data = user_data; | |
2973 | ✗ | data->context = g_main_context_ref_thread_default (); | |
2974 | |||
2975 | ✗ | return data; | |
2976 | } | ||
2977 | |||
2978 | static void | ||
2979 | ✗ | gnd_data_free (GNDData *data) | |
2980 | { | ||
2981 | ✗ | g_free (data->printer_name); | |
2982 | ✗ | if (data->context) | |
2983 | ✗ | g_main_context_unref (data->context); | |
2984 | ✗ | g_free (data); | |
2985 | ✗ | } | |
2986 | |||
2987 | static gboolean | ||
2988 | ✗ | get_named_dest_idle_cb (gpointer user_data) | |
2989 | { | ||
2990 | ✗ | GNDData *data = user_data; | |
2991 | |||
2992 | ✗ | data->callback (data->result, data->user_data); | |
2993 | |||
2994 | ✗ | return FALSE; | |
2995 | } | ||
2996 | |||
2997 | static void | ||
2998 | ✗ | get_named_dest_cb (gpointer user_data) | |
2999 | { | ||
3000 | ✗ | GNDData *data = user_data; | |
3001 | ✗ | g_autoptr(GSource) idle_source = NULL; | |
3002 | |||
3003 | ✗ | idle_source = g_idle_source_new (); | |
3004 | ✗ | g_source_set_callback (idle_source, | |
3005 | get_named_dest_idle_cb, | ||
3006 | data, | ||
3007 | (GDestroyNotify) gnd_data_free); | ||
3008 | ✗ | g_source_attach (idle_source, data->context); | |
3009 | ✗ | } | |
3010 | |||
3011 | static gpointer | ||
3012 | ✗ | get_named_dest_func (gpointer user_data) | |
3013 | { | ||
3014 | ✗ | GNDData *data = user_data; | |
3015 | |||
3016 | ✗ | data->result = cupsGetNamedDest (CUPS_HTTP_DEFAULT, data->printer_name, NULL); | |
3017 | |||
3018 | ✗ | get_named_dest_cb (data); | |
3019 | |||
3020 | ✗ | return NULL; | |
3021 | } | ||
3022 | |||
3023 | void | ||
3024 | ✗ | get_named_dest_async (const gchar *printer_name, | |
3025 | GNDCallback callback, | ||
3026 | gpointer user_data) | ||
3027 | { | ||
3028 | GNDData *data; | ||
3029 | ✗ | g_autoptr(GThread) thread = NULL; | |
3030 | ✗ | g_autoptr(GError) error = NULL; | |
3031 | |||
3032 | ✗ | data = gnd_data_new (printer_name, callback, user_data); | |
3033 | |||
3034 | ✗ | thread = g_thread_try_new ("get-named-dest", | |
3035 | get_named_dest_func, | ||
3036 | data, | ||
3037 | &error); | ||
3038 | |||
3039 | ✗ | if (!thread) | |
3040 | { | ||
3041 | ✗ | g_warning ("%s", error->message); | |
3042 | ✗ | callback (NULL, user_data); | |
3043 | |||
3044 | ✗ | gnd_data_free (data); | |
3045 | } | ||
3046 | ✗ | } | |
3047 | |||
3048 | typedef struct | ||
3049 | { | ||
3050 | GCancellable *cancellable; | ||
3051 | PAOCallback callback; | ||
3052 | gpointer user_data; | ||
3053 | } PAOData; | ||
3054 | |||
3055 | static PAOData * | ||
3056 | ✗ | pao_data_new (GCancellable *cancellable, PAOCallback callback, gpointer user_data) | |
3057 | { | ||
3058 | PAOData *data; | ||
3059 | |||
3060 | ✗ | data = g_new0 (PAOData, 1); | |
3061 | ✗ | if (cancellable) | |
3062 | ✗ | data->cancellable = g_object_ref (cancellable); | |
3063 | ✗ | data->callback = callback; | |
3064 | ✗ | data->user_data = user_data; | |
3065 | ✗ | return data; | |
3066 | } | ||
3067 | |||
3068 | static void | ||
3069 | ✗ | pao_data_free (PAOData *data) | |
3070 | { | ||
3071 | ✗ | g_clear_object (&data->cancellable); | |
3072 | ✗ | g_free (data); | |
3073 | ✗ | } | |
3074 | |||
3075 | ✗ | G_DEFINE_AUTOPTR_CLEANUP_FUNC (PAOData, pao_data_free) | |
3076 | |||
3077 | static void | ||
3078 | ✗ | printer_add_option_async_dbus_cb (GObject *source_object, | |
3079 | GAsyncResult *res, | ||
3080 | gpointer user_data) | ||
3081 | { | ||
3082 | ✗ | g_autoptr(GVariant) output = NULL; | |
3083 | ✗ | gboolean success = FALSE; | |
3084 | ✗ | g_autoptr(PAOData) data = user_data; | |
3085 | ✗ | g_autoptr(GError) error = NULL; | |
3086 | |||
3087 | ✗ | output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), | |
3088 | res, | ||
3089 | &error); | ||
3090 | |||
3091 | ✗ | if (output) | |
3092 | { | ||
3093 | const gchar *ret_error; | ||
3094 | |||
3095 | ✗ | g_variant_get (output, "(&s)", &ret_error); | |
3096 | ✗ | if (ret_error[0] != '\0') | |
3097 | ✗ | g_warning ("cups-pk-helper: setting of an option failed: %s", ret_error); | |
3098 | else | ||
3099 | ✗ | success = TRUE; | |
3100 | } | ||
3101 | else | ||
3102 | { | ||
3103 | ✗ | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
3104 | ✗ | g_warning ("%s", error->message); | |
3105 | } | ||
3106 | |||
3107 | ✗ | if (!g_cancellable_is_cancelled (data->cancellable)) | |
3108 | ✗ | data->callback (success, data->user_data); | |
3109 | ✗ | } | |
3110 | |||
3111 | void | ||
3112 | ✗ | printer_add_option_async (const gchar *printer_name, | |
3113 | const gchar *option_name, | ||
3114 | gchar **values, | ||
3115 | gboolean set_default, | ||
3116 | GCancellable *cancellable, | ||
3117 | PAOCallback callback, | ||
3118 | gpointer user_data) | ||
3119 | { | ||
3120 | GVariantBuilder array_builder; | ||
3121 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
3122 | ✗ | g_autoptr(GError) error = NULL; | |
3123 | gint i; | ||
3124 | |||
3125 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
3126 | ✗ | if (!bus) | |
3127 | { | ||
3128 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
3129 | ✗ | callback (FALSE, user_data); | |
3130 | ✗ | return; | |
3131 | } | ||
3132 | |||
3133 | ✗ | g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); | |
3134 | ✗ | if (values) | |
3135 | { | ||
3136 | ✗ | for (i = 0; values[i]; i++) | |
3137 | ✗ | g_variant_builder_add (&array_builder, "s", values[i]); | |
3138 | } | ||
3139 | |||
3140 | ✗ | g_dbus_connection_call (bus, | |
3141 | MECHANISM_BUS, | ||
3142 | "/", | ||
3143 | MECHANISM_BUS, | ||
3144 | set_default ? "PrinterAddOptionDefault" : | ||
3145 | "PrinterAddOption", | ||
3146 | g_variant_new ("(ssas)", | ||
3147 | printer_name, | ||
3148 | option_name, | ||
3149 | &array_builder), | ||
3150 | G_VARIANT_TYPE ("(s)"), | ||
3151 | G_DBUS_CALL_FLAGS_NONE, | ||
3152 | DBUS_TIMEOUT, | ||
3153 | cancellable, | ||
3154 | printer_add_option_async_dbus_cb, | ||
3155 | ✗ | pao_data_new (cancellable, callback, user_data)); | |
3156 | } | ||
3157 | |||
3158 | typedef struct | ||
3159 | { | ||
3160 | GList *backend_list; | ||
3161 | GCancellable *cancellable; | ||
3162 | GCDCallback callback; | ||
3163 | gpointer user_data; | ||
3164 | } GCDData; | ||
3165 | |||
3166 | static GCDData * | ||
3167 | ✗ | gcd_data_new (GList *backend_list, GCancellable *cancellable, GCDCallback callback, gpointer user_data) | |
3168 | { | ||
3169 | GCDData *data; | ||
3170 | |||
3171 | ✗ | data = g_new0 (GCDData, 1); | |
3172 | ✗ | data->backend_list = backend_list; | |
3173 | ✗ | if (cancellable) | |
3174 | ✗ | data->cancellable = g_object_ref (cancellable); | |
3175 | ✗ | data->callback = callback; | |
3176 | ✗ | data->user_data = user_data; | |
3177 | |||
3178 | ✗ | return data; | |
3179 | } | ||
3180 | |||
3181 | static void | ||
3182 | ✗ | gcd_data_free (GCDData *data) | |
3183 | { | ||
3184 | ✗ | g_list_free_full (data->backend_list, g_free); | |
3185 | ✗ | g_clear_object (&data->cancellable); | |
3186 | ✗ | g_free (data); | |
3187 | ✗ | } | |
3188 | |||
3189 | ✗ | G_DEFINE_AUTOPTR_CLEANUP_FUNC (GCDData, gcd_data_free) | |
3190 | |||
3191 | static gint | ||
3192 | ✗ | get_suffix_index (const gchar *string) | |
3193 | { | ||
3194 | gchar *number; | ||
3195 | gchar *endptr; | ||
3196 | ✗ | gint index = -1; | |
3197 | |||
3198 | ✗ | number = g_strrstr (string, ":"); | |
3199 | ✗ | if (number) | |
3200 | { | ||
3201 | ✗ | number++; | |
3202 | ✗ | index = g_ascii_strtoll (number, &endptr, 10); | |
3203 | ✗ | if (index == 0 && endptr == number) | |
3204 | ✗ | index = -1; | |
3205 | } | ||
3206 | |||
3207 | ✗ | return index; | |
3208 | } | ||
3209 | |||
3210 | static void | ||
3211 | ✗ | get_cups_devices_async_dbus_cb (GObject *source_object, | |
3212 | GAsyncResult *res, | ||
3213 | gpointer user_data) | ||
3214 | |||
3215 | { | ||
3216 | ✗ | g_autoptr(GPtrArray) devices = NULL; | |
3217 | ✗ | g_autoptr(GVariant) output = NULL; | |
3218 | ✗ | g_autoptr(GCDData) data = user_data; | |
3219 | ✗ | g_autoptr(GError) error = NULL; | |
3220 | ✗ | gint num_of_devices = 0; | |
3221 | |||
3222 | ✗ | output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), | |
3223 | res, | ||
3224 | &error); | ||
3225 | |||
3226 | ✗ | if (output) | |
3227 | { | ||
3228 | const gchar *ret_error; | ||
3229 | ✗ | g_autoptr(GVariant) devices_variant = NULL; | |
3230 | gboolean is_network_device; | ||
3231 | ✗ | g_autoptr(GVariantIter) iter = NULL; | |
3232 | const gchar *key, *value; | ||
3233 | ✗ | gint index = -1, max_index = -1, i; | |
3234 | |||
3235 | ✗ | g_variant_get (output, "(&s@a{ss})", | |
3236 | &ret_error, | ||
3237 | &devices_variant); | ||
3238 | |||
3239 | ✗ | if (ret_error[0] != '\0') | |
3240 | { | ||
3241 | ✗ | g_warning ("cups-pk-helper: getting of CUPS devices failed: %s", ret_error); | |
3242 | } | ||
3243 | |||
3244 | ✗ | g_variant_get (devices_variant, "a{ss}", &iter); | |
3245 | ✗ | while (g_variant_iter_next (iter, "{&s&s}", &key, &value)) | |
3246 | { | ||
3247 | ✗ | index = get_suffix_index (key); | |
3248 | ✗ | if (index > max_index) | |
3249 | ✗ | max_index = index; | |
3250 | } | ||
3251 | |||
3252 | ✗ | if (max_index >= 0) | |
3253 | { | ||
3254 | ✗ | g_autoptr(GVariantIter) iter2 = NULL; | |
3255 | |||
3256 | ✗ | num_of_devices = max_index + 1; | |
3257 | ✗ | devices = g_ptr_array_new_with_free_func (g_object_unref); | |
3258 | ✗ | for (i = 0; i < num_of_devices; i++) | |
3259 | ✗ | g_ptr_array_add (devices, pp_print_device_new ()); | |
3260 | |||
3261 | ✗ | g_variant_get (devices_variant, "a{ss}", &iter2); | |
3262 | ✗ | while (g_variant_iter_next (iter2, "{&s&s}", &key, &value)) | |
3263 | { | ||
3264 | PpPrintDevice *device; | ||
3265 | |||
3266 | ✗ | index = get_suffix_index (key); | |
3267 | ✗ | if (index >= 0) | |
3268 | { | ||
3269 | ✗ | device = g_ptr_array_index (devices, index); | |
3270 | ✗ | if (g_str_has_prefix (key, "device-class")) | |
3271 | { | ||
3272 | ✗ | is_network_device = g_strcmp0 (value, "network") == 0; | |
3273 | ✗ | g_object_set (device, "is-network-device", is_network_device, NULL); | |
3274 | } | ||
3275 | ✗ | else if (g_str_has_prefix (key, "device-id")) | |
3276 | ✗ | g_object_set (device, "device-id", value, NULL); | |
3277 | ✗ | else if (g_str_has_prefix (key, "device-info")) | |
3278 | ✗ | g_object_set (device, "device-info", value, NULL); | |
3279 | ✗ | else if (g_str_has_prefix (key, "device-make-and-model")) | |
3280 | { | ||
3281 | ✗ | g_object_set (device, | |
3282 | "device-make-and-model", value, | ||
3283 | "device-name", value, | ||
3284 | NULL); | ||
3285 | } | ||
3286 | ✗ | else if (g_str_has_prefix (key, "device-uri")) | |
3287 | ✗ | g_object_set (device, "device-uri", value, NULL); | |
3288 | ✗ | else if (g_str_has_prefix (key, "device-location")) | |
3289 | ✗ | g_object_set (device, "device-location", value, NULL); | |
3290 | |||
3291 | ✗ | g_object_set (device, "acquisition-method", ACQUISITION_METHOD_DEFAULT_CUPS_SERVER, NULL); | |
3292 | } | ||
3293 | } | ||
3294 | } | ||
3295 | } | ||
3296 | else | ||
3297 | { | ||
3298 | ✗ | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | |
3299 | ✗ | g_warning ("%s", error->message); | |
3300 | |||
3301 | ✗ | data->callback (devices, | |
3302 | TRUE, | ||
3303 | ✗ | g_cancellable_is_cancelled (data->cancellable), | |
3304 | ✗ | data->user_data); | |
3305 | ✗ | return; | |
3306 | } | ||
3307 | |||
3308 | ✗ | if (data->backend_list) | |
3309 | { | ||
3310 | ✗ | if (!g_cancellable_is_cancelled (data->cancellable)) | |
3311 | { | ||
3312 | ✗ | GVariantBuilder *include_scheme_builder = NULL; | |
3313 | ✗ | GVariantBuilder *exclude_scheme_builder = NULL; | |
3314 | ✗ | g_autofree gchar *backend_name = NULL; | |
3315 | |||
3316 | ✗ | backend_name = data->backend_list->data; | |
3317 | |||
3318 | ✗ | data->callback (devices, | |
3319 | FALSE, | ||
3320 | FALSE, | ||
3321 | ✗ | data->user_data); | |
3322 | |||
3323 | ✗ | if (g_strcmp0 (backend_name, OTHER_BACKENDS) != 0) | |
3324 | { | ||
3325 | ✗ | include_scheme_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); | |
3326 | ✗ | g_variant_builder_add (include_scheme_builder, "s", backend_name); | |
3327 | } | ||
3328 | else | ||
3329 | { | ||
3330 | ✗ | exclude_scheme_builder = create_other_backends_array (); | |
3331 | } | ||
3332 | |||
3333 | ✗ | data->backend_list = g_list_delete_link (data->backend_list, data->backend_list); | |
3334 | |||
3335 | ✗ | g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source_object)), | |
3336 | MECHANISM_BUS, | ||
3337 | "/", | ||
3338 | MECHANISM_BUS, | ||
3339 | "DevicesGet", | ||
3340 | g_variant_new ("(iiasas)", | ||
3341 | 0, | ||
3342 | 0, | ||
3343 | include_scheme_builder, | ||
3344 | exclude_scheme_builder), | ||
3345 | G_VARIANT_TYPE ("(sa{ss})"), | ||
3346 | G_DBUS_CALL_FLAGS_NONE, | ||
3347 | DBUS_TIMEOUT, | ||
3348 | ✗ | data->cancellable, | |
3349 | get_cups_devices_async_dbus_cb, | ||
3350 | data); | ||
3351 | ✗ | g_steal_pointer (&data); | |
3352 | |||
3353 | ✗ | if (include_scheme_builder) | |
3354 | ✗ | g_variant_builder_unref (include_scheme_builder); | |
3355 | |||
3356 | ✗ | if (exclude_scheme_builder) | |
3357 | ✗ | g_variant_builder_unref (exclude_scheme_builder); | |
3358 | |||
3359 | ✗ | return; | |
3360 | } | ||
3361 | else | ||
3362 | { | ||
3363 | ✗ | data->callback (devices, | |
3364 | TRUE, | ||
3365 | TRUE, | ||
3366 | ✗ | data->user_data); | |
3367 | } | ||
3368 | } | ||
3369 | else | ||
3370 | { | ||
3371 | ✗ | data->callback (devices, | |
3372 | TRUE, | ||
3373 | ✗ | g_cancellable_is_cancelled (data->cancellable), | |
3374 | ✗ | data->user_data); | |
3375 | } | ||
3376 | } | ||
3377 | |||
3378 | void | ||
3379 | ✗ | get_cups_devices_async (GCancellable *cancellable, | |
3380 | GCDCallback callback, | ||
3381 | gpointer user_data) | ||
3382 | { | ||
3383 | ✗ | g_autoptr(GDBusConnection) bus = NULL; | |
3384 | GVariantBuilder include_scheme_builder; | ||
3385 | GList *backend_list; | ||
3386 | ✗ | g_autoptr(GError) error = NULL; | |
3387 | ✗ | g_autofree gchar *backend_name = NULL; | |
3388 | |||
3389 | ✗ | bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); | |
3390 | ✗ | if (!bus) | |
3391 | { | ||
3392 | ✗ | g_warning ("Failed to get system bus: %s", error->message); | |
3393 | ✗ | callback (NULL, TRUE, FALSE, user_data); | |
3394 | ✗ | return; | |
3395 | } | ||
3396 | |||
3397 | ✗ | backend_list = create_backends_list (); | |
3398 | |||
3399 | ✗ | backend_name = backend_list->data; | |
3400 | |||
3401 | ✗ | g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as")); | |
3402 | ✗ | g_variant_builder_add (&include_scheme_builder, "s", backend_name); | |
3403 | |||
3404 | ✗ | backend_list = g_list_delete_link (backend_list, backend_list); | |
3405 | |||
3406 | ✗ | g_dbus_connection_call (bus, | |
3407 | MECHANISM_BUS, | ||
3408 | "/", | ||
3409 | MECHANISM_BUS, | ||
3410 | "DevicesGet", | ||
3411 | g_variant_new ("(iiasas)", | ||
3412 | 0, | ||
3413 | 0, | ||
3414 | &include_scheme_builder, | ||
3415 | NULL), | ||
3416 | G_VARIANT_TYPE ("(sa{ss})"), | ||
3417 | G_DBUS_CALL_FLAGS_NONE, | ||
3418 | DBUS_TIMEOUT, | ||
3419 | cancellable, | ||
3420 | get_cups_devices_async_dbus_cb, | ||
3421 | ✗ | gcd_data_new (backend_list, cancellable, callback, user_data)); | |
3422 | } | ||
3423 | |||
3424 | gchar * | ||
3425 | ✗ | guess_device_hostname (PpPrintDevice *device) | |
3426 | { | ||
3427 | http_uri_status_t status; | ||
3428 | char scheme[HTTP_MAX_URI]; | ||
3429 | char username[HTTP_MAX_URI]; | ||
3430 | char hostname[HTTP_MAX_URI]; | ||
3431 | char resource[HTTP_MAX_URI]; | ||
3432 | int port; | ||
3433 | ✗ | gchar *result = NULL; | |
3434 | gchar *hostname_begin; | ||
3435 | ✗ | gchar *hostname_end = NULL; | |
3436 | |||
3437 | ✗ | if (device != NULL && pp_print_device_get_device_uri (device) != NULL) | |
3438 | { | ||
3439 | ✗ | if (g_str_has_prefix (pp_print_device_get_device_uri (device), "socket") || | |
3440 | ✗ | g_str_has_prefix (pp_print_device_get_device_uri (device), "lpd") || | |
3441 | ✗ | g_str_has_prefix (pp_print_device_get_device_uri (device), "ipp") || | |
3442 | ✗ | g_str_has_prefix (pp_print_device_get_device_uri (device), "smb")) | |
3443 | { | ||
3444 | ✗ | status = httpSeparateURI (HTTP_URI_CODING_ALL, | |
3445 | ✗ | pp_print_device_get_device_uri (device), | |
3446 | scheme, HTTP_MAX_URI, | ||
3447 | username, HTTP_MAX_URI, | ||
3448 | hostname, HTTP_MAX_URI, | ||
3449 | &port, | ||
3450 | resource, HTTP_MAX_URI); | ||
3451 | |||
3452 | ✗ | if (status >= HTTP_URI_STATUS_OK && | |
3453 | ✗ | hostname[0] != '\0') | |
3454 | ✗ | result = g_strdup (hostname); | |
3455 | } | ||
3456 | ✗ | else if ((g_str_has_prefix (pp_print_device_get_device_uri (device), "dnssd") || | |
3457 | ✗ | g_str_has_prefix (pp_print_device_get_device_uri (device), "mdns")) && | |
3458 | ✗ | pp_print_device_get_device_info (device) != NULL) | |
3459 | { | ||
3460 | /* | ||
3461 | * CUPS browses its printers as | ||
3462 | * "PrinterName @ ComputerName" or "PrinterInfo @ ComputerName" | ||
3463 | * through DNS-SD. | ||
3464 | */ | ||
3465 | ✗ | hostname_begin = g_strrstr (pp_print_device_get_device_info (device), " @ "); | |
3466 | ✗ | if (hostname_begin != NULL) | |
3467 | ✗ | result = g_strdup (hostname_begin + 3); | |
3468 | } | ||
3469 | ✗ | else if (g_str_has_prefix (pp_print_device_get_device_uri (device), "hp:/net/") || | |
3470 | ✗ | g_str_has_prefix (pp_print_device_get_device_uri (device), "hpfax:/net/")) | |
3471 | { | ||
3472 | /* | ||
3473 | * HPLIP printers have URI of form hp:/net/%s?ip=%s&port=%d | ||
3474 | * or hp:/net/%s?ip=%s. | ||
3475 | */ | ||
3476 | ✗ | hostname_begin = g_strrstr (pp_print_device_get_device_uri (device), "ip="); | |
3477 | ✗ | if (hostname_begin != NULL) | |
3478 | { | ||
3479 | ✗ | hostname_begin += 3; | |
3480 | ✗ | hostname_end = strstr (hostname_begin, "&"); | |
3481 | } | ||
3482 | |||
3483 | ✗ | if (hostname_end != NULL) | |
3484 | ✗ | result = g_strndup (hostname_begin, hostname_end - hostname_begin); | |
3485 | else | ||
3486 | ✗ | result = g_strdup (hostname_begin); | |
3487 | } | ||
3488 | } | ||
3489 | |||
3490 | ✗ | return result; | |
3491 | } | ||
3492 | |||
3493 | gchar * | ||
3494 | ✗ | canonicalize_device_name (GList *device_names, | |
3495 | GPtrArray *local_cups_devices, | ||
3496 | cups_dest_t *dests, | ||
3497 | gint num_of_dests, | ||
3498 | PpPrintDevice *device) | ||
3499 | { | ||
3500 | PpPrintDevice *item; | ||
3501 | gboolean already_present; | ||
3502 | GList *iter; | ||
3503 | gsize len; | ||
3504 | ✗ | g_autofree gchar *name = NULL; | |
3505 | gchar *occurrence; | ||
3506 | gint name_index, j; | ||
3507 | static const char * const residues[] = { | ||
3508 | "-foomatic", | ||
3509 | "-hpijs", | ||
3510 | "-hpcups", | ||
3511 | "-cups", | ||
3512 | "-gutenprint", | ||
3513 | "-series", | ||
3514 | "-label-printer", | ||
3515 | "-dot-matrix", | ||
3516 | "-ps3", | ||
3517 | "-ps2", | ||
3518 | "-br-script", | ||
3519 | "-kpdl", | ||
3520 | "-pcl3", | ||
3521 | "-pcl", | ||
3522 | "-zxs", | ||
3523 | "-pxl"}; | ||
3524 | |||
3525 | ✗ | if (pp_print_device_get_device_id (device) != NULL) | |
3526 | { | ||
3527 | ✗ | name = get_tag_value (pp_print_device_get_device_id (device), "mdl"); | |
3528 | ✗ | if (name == NULL) | |
3529 | ✗ | name = get_tag_value (pp_print_device_get_device_id (device), "model"); | |
3530 | } | ||
3531 | |||
3532 | ✗ | if (name == NULL && | |
3533 | ✗ | pp_print_device_get_device_make_and_model (device) != NULL && | |
3534 | ✗ | pp_print_device_get_device_make_and_model (device)[0] != '\0') | |
3535 | { | ||
3536 | ✗ | name = g_strdup (pp_print_device_get_device_make_and_model (device)); | |
3537 | } | ||
3538 | |||
3539 | ✗ | if (name == NULL && | |
3540 | ✗ | pp_print_device_get_device_original_name (device) != NULL && | |
3541 | ✗ | pp_print_device_get_device_original_name (device)[0] != '\0') | |
3542 | { | ||
3543 | ✗ | name = g_strdup (pp_print_device_get_device_original_name (device)); | |
3544 | } | ||
3545 | |||
3546 | ✗ | if (name == NULL && | |
3547 | ✗ | pp_print_device_get_device_info (device) != NULL && | |
3548 | ✗ | pp_print_device_get_device_info (device)[0] != '\0') | |
3549 | { | ||
3550 | ✗ | name = g_strdup (pp_print_device_get_device_info (device)); | |
3551 | } | ||
3552 | |||
3553 | ✗ | if (name == NULL) | |
3554 | ✗ | return NULL; | |
3555 | |||
3556 | ✗ | g_strstrip (name); | |
3557 | ✗ | g_strcanon (name, ALLOWED_CHARACTERS, '-'); | |
3558 | |||
3559 | /* Remove common strings found in driver names */ | ||
3560 | ✗ | for (j = 0; j < G_N_ELEMENTS (residues); j++) | |
3561 | { | ||
3562 | ✗ | g_autofree gchar *lower_name = g_ascii_strdown (name, -1); | |
3563 | |||
3564 | ✗ | occurrence = g_strrstr (lower_name, residues[j]); | |
3565 | ✗ | if (occurrence != NULL) | |
3566 | { | ||
3567 | ✗ | occurrence[0] = '\0'; | |
3568 | ✗ | name[strlen (lower_name)] = '\0'; | |
3569 | } | ||
3570 | } | ||
3571 | |||
3572 | /* Remove trailing "-" */ | ||
3573 | ✗ | len = strlen (name); | |
3574 | ✗ | while (len-- && name[len] == '-') | |
3575 | ✗ | name[len] = '\0'; | |
3576 | |||
3577 | /* Merge "--" to "-" */ | ||
3578 | ✗ | occurrence = g_strrstr (name, "--"); | |
3579 | ✗ | while (occurrence != NULL) | |
3580 | { | ||
3581 | ✗ | shift_string_left (occurrence); | |
3582 | ✗ | occurrence = g_strrstr (name, "--"); | |
3583 | } | ||
3584 | |||
3585 | /* Remove leading "-" */ | ||
3586 | ✗ | if (name[0] == '-') | |
3587 | ✗ | shift_string_left (name); | |
3588 | |||
3589 | ✗ | name_index = 2; | |
3590 | ✗ | already_present = FALSE; | |
3591 | while (TRUE) | ||
3592 | ✗ | { | |
3593 | ✗ | g_autofree gchar *new_name = NULL; | |
3594 | |||
3595 | ✗ | if (already_present) | |
3596 | { | ||
3597 | ✗ | new_name = g_strdup_printf ("%s-%d", name, name_index); | |
3598 | ✗ | name_index++; | |
3599 | } | ||
3600 | else | ||
3601 | { | ||
3602 | ✗ | new_name = g_strdup (name); | |
3603 | } | ||
3604 | |||
3605 | ✗ | already_present = FALSE; | |
3606 | ✗ | for (j = 0; j < num_of_dests; j++) | |
3607 | ✗ | if (g_strcmp0 (dests[j].name, new_name) == 0) | |
3608 | ✗ | already_present = TRUE; | |
3609 | |||
3610 | ✗ | for (iter = device_names; iter; iter = iter->next) | |
3611 | { | ||
3612 | ✗ | gchar *device_original_name = iter->data; | |
3613 | ✗ | if (g_strcmp0 (device_original_name, new_name) == 0) | |
3614 | ✗ | already_present = TRUE; | |
3615 | } | ||
3616 | |||
3617 | ✗ | for (guint i = 0; i < local_cups_devices->len; i++) | |
3618 | { | ||
3619 | ✗ | item = g_ptr_array_index (local_cups_devices, i); | |
3620 | ✗ | if (g_strcmp0 (pp_print_device_get_device_original_name (item), new_name) == 0) | |
3621 | ✗ | already_present = TRUE; | |
3622 | } | ||
3623 | |||
3624 | ✗ | if (!already_present) | |
3625 | ✗ | return g_steal_pointer (&new_name); | |
3626 | } | ||
3627 | } | ||
3628 | |||
3629 | void | ||
3630 | 7 | shift_string_left (gchar *str) | |
3631 | { | ||
3632 | gchar *next; | ||
3633 | |||
3634 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | if (str != NULL && str[0] != '\0') |
3635 | { | ||
3636 | 7 | next = g_utf8_find_next_char (str, NULL); | |
3637 | 7 | memmove (str, next, strlen (next) + 1); | |
3638 | } | ||
3639 | 7 | } | |
3640 | |||
3641 | gboolean | ||
3642 | ✗ | printer_name_is_valid (const gchar *str) | |
3643 | { | ||
3644 | ✗ | const gchar *invalid_chars = " \t#/"; | |
3645 | ✗ | return strlen(str) == strcspn(str, invalid_chars); | |
3646 | } | ||
3647 |