1 /*
2 * Copyright (C) 2016 Red Hat, Inc
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Martin Hatina <mhatina@redhat.com>
18 * Marek Kasik <mkasik@redhat.com>
19 */
20
21 #include "pp-printer.h"
22
23 #include "pp-job.h"
24
25 #if (CUPS_VERSION_MAJOR == 1) && (CUPS_VERSION_MINOR <= 6)
26 #define IPP_STATE_IDLE IPP_IDLE
27 #endif
28
29 struct _PpPrinter
30 {
31 GObject parent_instance;
32 gchar *printer_name;
33 };
34
G_DEFINE_TYPE(PpPrinter,pp_printer,G_TYPE_OBJECT)35 G_DEFINE_TYPE (PpPrinter, pp_printer, G_TYPE_OBJECT)
36
37 static void
38 pp_printer_dispose (GObject *object)
39 {
40 PpPrinter *self = PP_PRINTER (object);
41
42 g_clear_pointer (&self->printer_name, g_free);
43
44 G_OBJECT_CLASS (pp_printer_parent_class)->dispose (object);
45 }
46
47 static void
pp_printer_class_init(PpPrinterClass * klass)48 pp_printer_class_init (PpPrinterClass *klass)
49 {
50 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
51
52 gobject_class->dispose = pp_printer_dispose;
53 }
54
55 static void
pp_printer_init(PpPrinter * self)56 pp_printer_init (PpPrinter *self)
57 {
58 }
59
60 PpPrinter *
pp_printer_new(const gchar * name)61 pp_printer_new (const gchar *name)
62 {
63 PpPrinter *self = g_object_new (PP_TYPE_PRINTER, NULL);
64
65 self->printer_name = g_strdup (name);
66
67 return self;
68 }
69
70 const gchar *
pp_printer_get_name(PpPrinter * self)71 pp_printer_get_name (PpPrinter *self)
72 {
73 g_return_val_if_fail (PP_IS_PRINTER (self), NULL);
74 return self->printer_name;
75 }
76
77 static void
printer_rename_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)78 printer_rename_thread (GTask *task,
79 gpointer source_object,
80 gpointer task_data,
81 GCancellable *cancellable)
82 {
83 PpPrinter *self = PP_PRINTER (source_object);
84 gboolean result;
85 const gchar *new_printer_name = task_data;
86
87 result = printer_rename (self->printer_name, new_printer_name);
88
89 if (result)
90 {
91 g_free (self->printer_name);
92 self->printer_name = g_strdup (new_printer_name);
93 }
94
95 g_task_return_boolean (task, result);
96 }
97
98 static void
printer_rename_dbus_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)99 printer_rename_dbus_cb (GObject *source_object,
100 GAsyncResult *res,
101 gpointer user_data)
102 {
103 PpPrinter *self;
104 g_autoptr(GVariant) output = NULL;
105 gboolean result = FALSE;
106 g_autoptr(GError) error = NULL;
107 g_autoptr(GTask) task = user_data;
108
109 output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
110 res,
111 &error);
112
113 if (output != NULL)
114 {
115 const gchar *ret_error;
116
117 self = g_task_get_source_object (task);
118
119 g_variant_get (output, "(&s)", &ret_error);
120 if (ret_error[0] != '\0')
121 {
122 g_warning ("cups-pk-helper: renaming of printer %s failed: %s", self->printer_name, ret_error);
123 }
124 else
125 {
126 result = TRUE;
127 g_free (self->printer_name);
128 self->printer_name = g_strdup (g_task_get_task_data (task));
129 }
130
131 g_task_return_boolean (task, result);
132 }
133 else
134 {
135 if (error->domain == G_DBUS_ERROR &&
136 (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN ||
137 error->code == G_DBUS_ERROR_UNKNOWN_METHOD))
138 {
139 g_warning ("Update cups-pk-helper to at least 0.2.6 please to be able to use PrinterRename method.");
140
141 g_task_run_in_thread (task, printer_rename_thread);
142 }
143 else
144 {
145 g_task_return_boolean (task, FALSE);
146 }
147 }
148 }
149
150 static void
get_bus_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)151 get_bus_cb (GObject *source_object,
152 GAsyncResult *res,
153 gpointer user_data)
154 {
155 PpPrinter *self;
156 GDBusConnection *bus;
157 g_autoptr(GError) error = NULL;
158 g_autoptr(GTask) task = user_data;
159
160 bus = g_bus_get_finish (res, &error);
161 if (bus != NULL)
162 {
163 self = g_task_get_source_object (task);
164 g_dbus_connection_call (bus,
165 MECHANISM_BUS,
166 "/",
167 MECHANISM_BUS,
168 "PrinterRename",
169 g_variant_new ("(ss)",
170 self->printer_name,
171 g_task_get_task_data (task)),
172 G_VARIANT_TYPE ("(s)"),
173 G_DBUS_CALL_FLAGS_NONE,
174 -1,
175 g_task_get_cancellable (task),
176 printer_rename_dbus_cb,
177 task);
178 g_steal_pointer (&task);
179 }
180 else
181 {
182 g_warning ("Failed to get system bus: %s", error->message);
183 g_task_return_boolean (task, FALSE);
184 }
185 }
186
187 void
pp_printer_rename_async(PpPrinter * self,const gchar * new_printer_name,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)188 pp_printer_rename_async (PpPrinter *self,
189 const gchar *new_printer_name,
190 GCancellable *cancellable,
191 GAsyncReadyCallback callback,
192 gpointer user_data)
193 {
194 GTask *task;
195
196 g_return_if_fail (new_printer_name != NULL);
197
198 task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
199 g_task_set_task_data (task, g_strdup (new_printer_name), g_free);
200
201 g_bus_get (G_BUS_TYPE_SYSTEM,
202 cancellable,
203 get_bus_cb,
204 task);
205 }
206
207 gboolean
pp_printer_rename_finish(PpPrinter * self,GAsyncResult * res,GError ** error)208 pp_printer_rename_finish (PpPrinter *self,
209 GAsyncResult *res,
210 GError **error)
211 {
212 g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
213
214 return g_task_propagate_boolean (G_TASK (res), error);
215 }
216
217 typedef struct
218 {
219 gboolean myjobs;
220 gint which_jobs;
221 } GetJobsData;
222
223 static void
get_jobs_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)224 get_jobs_thread (GTask *task,
225 gpointer source_object,
226 gpointer task_data,
227 GCancellable *cancellable)
228 {
229 ipp_attribute_t *attr = NULL;
230 static gchar *printer_attributes[] = { "auth-info-required" };
231 GetJobsData *get_jobs_data = task_data;
232 cups_job_t *jobs = NULL;
233 PpPrinter *self = PP_PRINTER (source_object);
234 gboolean auth_info_is_required;
235 PpJob *job;
236 ipp_t *job_request;
237 ipp_t *job_response;
238 ipp_t *printer_request;
239 ipp_t *printer_response;
240 gchar **auth_info_required = NULL;
241 g_autofree gchar *printer_name = NULL;
242 g_autoptr(GPtrArray) array = NULL;
243 gint num_jobs;
244 gint i, j;
245
246 num_jobs = cupsGetJobs (&jobs,
247 self->printer_name,
248 get_jobs_data->myjobs ? 1 : 0,
249 get_jobs_data->which_jobs);
250
251 array = g_ptr_array_new_with_free_func (g_object_unref);
252 for (i = 0; i < num_jobs; i++)
253 {
254 auth_info_is_required = FALSE;
255 if (jobs[i].state == IPP_JOB_HELD)
256 {
257 g_autofree gchar *job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", jobs[i].id);
258
259 job_request = ippNewRequest (IPP_GET_JOB_ATTRIBUTES);
260 ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_URI,
261 "job-uri", NULL, job_uri);
262 ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_NAME,
263 "requesting-user-name", NULL, cupsUser ());
264 ippAddString (job_request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
265 "requested-attributes", NULL, "job-hold-until");
266 job_response = cupsDoRequest (CUPS_HTTP_DEFAULT, job_request, "/");
267
268 if (job_response != NULL)
269 {
270 attr = ippFindAttribute (job_response, "job-hold-until", IPP_TAG_ZERO);
271 if (attr != NULL && g_strcmp0 (ippGetString (attr, 0, NULL), "auth-info-required") == 0)
272 {
273 auth_info_is_required = TRUE;
274
275 if (auth_info_required == NULL)
276 {
277 g_autofree gchar *printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", self->printer_name);
278
279 printer_request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
280 ippAddString (printer_request, IPP_TAG_OPERATION, IPP_TAG_URI,
281 "printer-uri", NULL, printer_uri);
282 ippAddString (printer_request, IPP_TAG_OPERATION, IPP_TAG_NAME,
283 "requesting-user-name", NULL, cupsUser ());
284 ippAddStrings (printer_request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
285 "requested-attributes", 1, NULL, (const char **) printer_attributes);
286 printer_response = cupsDoRequest (CUPS_HTTP_DEFAULT, printer_request, "/");
287
288 if (printer_response != NULL)
289 {
290 attr = ippFindAttribute (printer_response, "auth-info-required", IPP_TAG_ZERO);
291 if (attr != NULL)
292 {
293 auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
294 for (j = 0; j < ippGetCount (attr); j++)
295 auth_info_required[j] = g_strdup (ippGetString (attr, j, NULL));
296 }
297
298 ippDelete (printer_response);
299 }
300 }
301 }
302
303 ippDelete (job_response);
304 }
305 }
306
307 job = pp_job_new (jobs[i].id, jobs[i].title, jobs[i].state, auth_info_is_required ? auth_info_required : NULL);
308
309 g_ptr_array_add (array, job);
310 }
311
312 g_strfreev (auth_info_required);
313 cupsFreeJobs (num_jobs, jobs);
314
315 if (g_task_set_return_on_cancel (task, FALSE))
316 {
317 g_task_return_pointer (task, g_steal_pointer (&array), (GDestroyNotify) g_ptr_array_unref);
318 }
319 }
320
321 void
pp_printer_get_jobs_async(PpPrinter * self,gboolean myjobs,gint which_jobs,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)322 pp_printer_get_jobs_async (PpPrinter *self,
323 gboolean myjobs,
324 gint which_jobs,
325 GCancellable *cancellable,
326 GAsyncReadyCallback callback,
327 gpointer user_data)
328 {
329 GetJobsData *get_jobs_data;
330 g_autoptr(GTask) task = NULL;
331
332 get_jobs_data = g_new (GetJobsData, 1);
333 get_jobs_data->myjobs = myjobs;
334 get_jobs_data->which_jobs = which_jobs;
335
336 task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
337 g_task_set_task_data (task, get_jobs_data, g_free);
338 g_task_set_return_on_cancel (task, TRUE);
339 g_task_run_in_thread (task, get_jobs_thread);
340 }
341
342 GPtrArray *
pp_printer_get_jobs_finish(PpPrinter * self,GAsyncResult * res,GError ** error)343 pp_printer_get_jobs_finish (PpPrinter *self,
344 GAsyncResult *res,
345 GError **error)
346 {
347 g_return_val_if_fail (g_task_is_valid (res, self), NULL);
348
349 return g_task_propagate_pointer (G_TASK (res), error);
350 }
351
352 static void
pp_printer_delete_dbus_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)353 pp_printer_delete_dbus_cb (GObject *source_object,
354 GAsyncResult *res,
355 gpointer user_data)
356 {
357 PpPrinter *self;
358 g_autoptr(GVariant) output = NULL;
359 gboolean result = FALSE;
360 g_autoptr(GError) error = NULL;
361 GTask *task = user_data;
362
363 output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
364 res,
365 &error);
366
367 if (output != NULL)
368 {
369 const gchar *ret_error;
370
371 self = g_task_get_source_object (task);
372
373 g_variant_get (output, "(&s)", &ret_error);
374 if (ret_error[0] != '\0')
375 g_warning ("cups-pk-helper: removing of printer %s failed: %s", self->printer_name, ret_error);
376 else
377 result = TRUE;
378
379 g_task_return_boolean (task, result);
380 }
381 else
382 {
383 g_warning ("%s", error->message);
384
385 g_task_return_boolean (task, FALSE);
386 }
387 }
388
389 static void
pp_printer_delete_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)390 pp_printer_delete_cb (GObject *source_object,
391 GAsyncResult *res,
392 gpointer user_data)
393 {
394 PpPrinter *self;
395 GDBusConnection *bus;
396 g_autoptr(GError) error = NULL;
397 GTask *task = user_data;
398
399 bus = g_bus_get_finish (res, &error);
400 if (bus != NULL)
401 {
402 self = g_task_get_source_object (task);
403
404 g_dbus_connection_call (bus,
405 MECHANISM_BUS,
406 "/",
407 MECHANISM_BUS,
408 "PrinterDelete",
409 g_variant_new ("(s)", self->printer_name),
410 G_VARIANT_TYPE ("(s)"),
411 G_DBUS_CALL_FLAGS_NONE,
412 -1,
413 g_task_get_cancellable (task),
414 pp_printer_delete_dbus_cb,
415 task);
416 }
417 else
418 {
419 g_warning ("Failed to get system bus: %s", error->message);
420 g_task_return_boolean (task, FALSE);
421 }
422 }
423
424 void
pp_printer_delete_async(PpPrinter * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)425 pp_printer_delete_async (PpPrinter *self,
426 GCancellable *cancellable,
427 GAsyncReadyCallback callback,
428 gpointer user_data)
429 {
430 GTask *task;
431
432 task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
433
434 g_bus_get (G_BUS_TYPE_SYSTEM,
435 cancellable,
436 pp_printer_delete_cb,
437 task);
438 }
439
440 gboolean
pp_printer_delete_finish(PpPrinter * self,GAsyncResult * res,GError ** error)441 pp_printer_delete_finish (PpPrinter *self,
442 GAsyncResult *res,
443 GError **error)
444 {
445 g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
446
447 return g_task_propagate_boolean (G_TASK (res), error);
448 }
449
450 typedef struct
451 {
452 gchar *filename;
453 gchar *job_name;
454 } PrintFileData;
455
456 static void
print_file_data_free(PrintFileData * print_file_data)457 print_file_data_free (PrintFileData *print_file_data)
458 {
459 g_free (print_file_data->filename);
460 g_free (print_file_data->job_name);
461
462 g_slice_free (PrintFileData, print_file_data);
463 }
464
465 static void
print_file_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)466 print_file_thread (GTask *task,
467 gpointer source_object,
468 gpointer task_data,
469 GCancellable *cancellable)
470 {
471 PpPrinter *self = PP_PRINTER (source_object);
472 PrintFileData *print_file_data;
473 cups_ptype_t type = 0;
474 cups_dest_t *dest = NULL;
475 const gchar *printer_type = NULL;
476 gboolean ret = FALSE;
477 g_autofree gchar *printer_uri = NULL;
478 g_autofree gchar *resource = NULL;
479 ipp_t *response = NULL;
480 ipp_t *request;
481
482 dest = cupsGetNamedDest (CUPS_HTTP_DEFAULT, self->printer_name, NULL);
483 if (dest != NULL)
484 {
485 printer_type = cupsGetOption ("printer-type",
486 dest->num_options,
487 dest->options);
488 cupsFreeDests (1, dest);
489
490 if (printer_type)
491 type = atoi (printer_type);
492 }
493
494 if (type & CUPS_PRINTER_CLASS)
495 {
496 printer_uri = g_strdup_printf ("ipp://localhost/classes/%s", self->printer_name);
497 resource = g_strdup_printf ("/classes/%s", self->printer_name);
498 }
499 else
500 {
501 printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", self->printer_name);
502 resource = g_strdup_printf ("/printers/%s", self->printer_name);
503 }
504
505 print_file_data = g_task_get_task_data (task);
506
507 request = ippNewRequest (IPP_PRINT_JOB);
508 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
509 "printer-uri", NULL, printer_uri);
510 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
511 "requesting-user-name", NULL, cupsUser ());
512 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
513 "job-name", NULL, print_file_data->job_name);
514 response = cupsDoFileRequest (CUPS_HTTP_DEFAULT, request, resource, print_file_data->filename);
515
516 if (response != NULL)
517 {
518 if (ippGetState (response) == IPP_ERROR)
519 g_warning ("An error has occurred during printing of test page.");
520 if (ippGetState (response) == IPP_STATE_IDLE)
521 ret = TRUE;
522
523 ippDelete (response);
524 }
525
526 if (g_task_set_return_on_cancel (task, FALSE))
527 {
528 g_task_return_boolean (task, ret);
529 }
530 }
531
532 void
pp_printer_print_file_async(PpPrinter * self,const gchar * filename,const gchar * job_name,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)533 pp_printer_print_file_async (PpPrinter *self,
534 const gchar *filename,
535 const gchar *job_name,
536 GCancellable *cancellable,
537 GAsyncReadyCallback callback,
538 gpointer user_data)
539 {
540 PrintFileData *print_file_data;
541 g_autoptr(GTask) task = NULL;
542
543 print_file_data = g_new (PrintFileData, 1);
544 print_file_data->filename = g_strdup (filename);
545 print_file_data->job_name = g_strdup (job_name);
546
547 task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
548
549 g_task_set_return_on_cancel (task, TRUE);
550 g_task_set_task_data (task, print_file_data, (GDestroyNotify) print_file_data_free);
551
552 g_task_run_in_thread (task, print_file_thread);
553 }
554
555 gboolean
pp_printer_print_file_finish(PpPrinter * self,GAsyncResult * res,GError ** error)556 pp_printer_print_file_finish (PpPrinter *self,
557 GAsyncResult *res,
558 GError **error)
559 {
560 g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
561
562 return g_task_propagate_boolean (G_TASK (res), error);
563 }
564