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