1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright 2015  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  * Author: Felipe Borges <feborges@redhat.com>
19  */
20 
21 #include "pp-job.h"
22 
23 #include <gio/gio.h>
24 #include <cups/cups.h>
25 
26 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
27 #define HAVE_CUPS_1_6 1
28 #endif
29 
30 #ifndef HAVE_CUPS_1_6
31 #define ippGetBoolean(attr, element) attr->values[element].boolean
32 #define ippGetCount(attr)     attr->num_values
33 #define ippGetInteger(attr, element) attr->values[element].integer
34 #define ippGetString(attr, element, language) attr->values[element].string.text
35 #define ippGetValueTag(attr)  attr->value_tag
36 static int
ippGetRange(ipp_attribute_t * attr,int element,int * upper)37 ippGetRange (ipp_attribute_t *attr,
38              int element,
39              int *upper)
40 {
41   *upper = attr->values[element].range.upper;
42   return (attr->values[element].range.lower);
43 }
44 #endif
45 
46 struct _PpJob
47 {
48   GObject parent_instance;
49 
50   gint    id;
51   gchar  *title;
52   gint    state;
53   GStrv   auth_info_required;
54 };
55 
G_DEFINE_TYPE(PpJob,pp_job,G_TYPE_OBJECT)56 G_DEFINE_TYPE (PpJob, pp_job, G_TYPE_OBJECT)
57 
58 static void
59 pp_job_cancel_purge_async_dbus_cb (GObject      *source_object,
60                                    GAsyncResult *res,
61                                    gpointer      user_data)
62 {
63   g_autoptr(GVariant) output = NULL;
64 
65   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
66                                           res,
67                                           NULL);
68 }
69 
70 PpJob *
pp_job_new(gint id,const gchar * title,gint state,GStrv auth_info_required)71 pp_job_new (gint id, const gchar *title, gint state, GStrv auth_info_required)
72 {
73    PpJob *job = g_object_new (pp_job_get_type (), NULL);
74 
75    job->id = id;
76    job->title = g_strdup (title);
77    job->state = state;
78    job->auth_info_required = g_strdupv (auth_info_required);
79 
80    return job;
81 }
82 
83 const gchar *
pp_job_get_title(PpJob * self)84 pp_job_get_title (PpJob *self)
85 {
86    g_return_val_if_fail (PP_IS_JOB(self), NULL);
87    return self->title;
88 }
89 
90 gint
pp_job_get_state(PpJob * self)91 pp_job_get_state (PpJob *self)
92 {
93    g_return_val_if_fail (PP_IS_JOB(self), -1);
94    return self->state;
95 }
96 
97 GStrv
pp_job_get_auth_info_required(PpJob * self)98 pp_job_get_auth_info_required (PpJob *self)
99 {
100    g_return_val_if_fail (PP_IS_JOB(self), NULL);
101    return self->auth_info_required;
102 }
103 
104 void
pp_job_cancel_purge_async(PpJob * self,gboolean job_purge)105 pp_job_cancel_purge_async (PpJob        *self,
106                            gboolean      job_purge)
107 {
108   g_autoptr(GDBusConnection) bus = NULL;
109   g_autoptr(GError) error = NULL;
110 
111   bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
112   if (!bus)
113     {
114       g_warning ("Failed to get session bus: %s", error->message);
115       return;
116     }
117 
118   g_dbus_connection_call (bus,
119                           MECHANISM_BUS,
120                           "/",
121                           MECHANISM_BUS,
122                           "JobCancelPurge",
123                           g_variant_new ("(ib)",
124                                          self->id,
125                                          job_purge),
126                           G_VARIANT_TYPE ("(s)"),
127                           G_DBUS_CALL_FLAGS_NONE,
128                           -1,
129                           NULL,
130                           pp_job_cancel_purge_async_dbus_cb,
131                           NULL);
132 }
133 
134 static void
pp_job_set_hold_until_async_dbus_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)135 pp_job_set_hold_until_async_dbus_cb (GObject      *source_object,
136                                      GAsyncResult *res,
137                                      gpointer      user_data)
138 {
139   g_autoptr(GVariant) output = NULL;
140 
141   output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
142                                           res,
143                                           NULL);
144 }
145 
146 void
pp_job_set_hold_until_async(PpJob * self,const gchar * job_hold_until)147 pp_job_set_hold_until_async (PpJob        *self,
148                              const gchar  *job_hold_until)
149 {
150   g_autoptr(GDBusConnection) bus = NULL;
151   g_autoptr(GError) error = NULL;
152 
153   bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
154   if (!bus)
155     {
156       g_warning ("Failed to get session bus: %s", error->message);
157       return;
158     }
159 
160   g_dbus_connection_call (bus,
161                           MECHANISM_BUS,
162                           "/",
163                           MECHANISM_BUS,
164                           "JobSetHoldUntil",
165                           g_variant_new ("(is)",
166                                          self->id,
167                                          job_hold_until),
168                           G_VARIANT_TYPE ("(s)"),
169                           G_DBUS_CALL_FLAGS_NONE,
170                           -1,
171                           NULL,
172                           pp_job_set_hold_until_async_dbus_cb,
173                           NULL);
174 }
175 
176 static void
pp_job_init(PpJob * obj)177 pp_job_init (PpJob *obj)
178 {
179 }
180 
181 static void
pp_job_finalize(GObject * object)182 pp_job_finalize (GObject *object)
183 {
184   PpJob *self = PP_JOB (object);
185 
186   g_clear_pointer (&self->title, g_free);
187   g_clear_pointer (&self->auth_info_required, g_strfreev);
188 
189   G_OBJECT_CLASS (pp_job_parent_class)->finalize (object);
190 }
191 
192 static void
pp_job_class_init(PpJobClass * class)193 pp_job_class_init (PpJobClass *class)
194 {
195   GObjectClass *object_class = G_OBJECT_CLASS (class);
196 
197   object_class->finalize = pp_job_finalize;
198 }
199 
200 static void
_pp_job_get_attributes_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)201 _pp_job_get_attributes_thread (GTask        *task,
202                                gpointer      source_object,
203                                gpointer      task_data,
204                                GCancellable *cancellable)
205 {
206   PpJob *self = PP_JOB (source_object);
207   ipp_attribute_t  *attr = NULL;
208   GVariantBuilder   builder;
209   GVariant         *attributes = NULL;
210   gchar           **attributes_names = task_data;
211   ipp_t            *request;
212   ipp_t            *response = NULL;
213   g_autofree gchar *job_uri = NULL;
214   gint              i, j, length = 0, n_attrs = 0;
215 
216   job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", self->id);
217 
218   if (attributes_names != NULL)
219     {
220       length = g_strv_length (attributes_names);
221 
222       request = ippNewRequest (IPP_GET_JOB_ATTRIBUTES);
223       ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
224                     "job-uri", NULL, job_uri);
225       ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
226                     "requesting-user-name", NULL, cupsUser ());
227       ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
228                      "requested-attributes", length, NULL, (const char **) attributes_names);
229       response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
230     }
231 
232   if (response != NULL)
233     {
234       g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
235 
236       for (j = 0; j < length; j++)
237         {
238           attr = ippFindAttribute (response, attributes_names[j], IPP_TAG_ZERO);
239           n_attrs = ippGetCount (attr);
240           if (attr != NULL && n_attrs > 0 && ippGetValueTag (attr) != IPP_TAG_NOVALUE)
241             {
242               const GVariantType  *type = NULL;
243               GVariant           **values;
244               GVariant            *range[2];
245               gint                 range_uppervalue;
246 
247               values = g_new (GVariant*, n_attrs);
248 
249               switch (ippGetValueTag (attr))
250                 {
251                   case IPP_TAG_INTEGER:
252                   case IPP_TAG_ENUM:
253                     type = G_VARIANT_TYPE_INT32;
254 
255                     for (i = 0; i < n_attrs; i++)
256                       values[i] = g_variant_new_int32 (ippGetInteger (attr, i));
257                     break;
258 
259                   case IPP_TAG_NAME:
260                   case IPP_TAG_STRING:
261                   case IPP_TAG_TEXT:
262                   case IPP_TAG_URI:
263                   case IPP_TAG_KEYWORD:
264                   case IPP_TAG_URISCHEME:
265                     type = G_VARIANT_TYPE_STRING;
266 
267                     for (i = 0; i < n_attrs; i++)
268                       values[i] = g_variant_new_string (ippGetString (attr, i, NULL));
269                     break;
270 
271                   case IPP_TAG_RANGE:
272                     type = G_VARIANT_TYPE_TUPLE;
273 
274                     for (i = 0; i < n_attrs; i++)
275                       {
276                         range[0] = g_variant_new_int32 (ippGetRange (attr, i, &(range_uppervalue)));
277                         range[1] = g_variant_new_int32 (range_uppervalue);
278 
279                         values[i] = g_variant_new_tuple (range, 2);
280                       }
281                     break;
282 
283                   case IPP_TAG_BOOLEAN:
284                     type = G_VARIANT_TYPE_BOOLEAN;
285 
286                     for (i = 0; i < n_attrs; i++)
287                       values[i] = g_variant_new_boolean (ippGetBoolean (attr, i));
288                     break;
289 
290                   default:
291                     /* do nothing (switch w/ enumeration type) */
292                     break;
293                 }
294 
295               if (type != NULL)
296                 {
297                   g_variant_builder_add (&builder, "{sv}",
298                                          attributes_names[j],
299                                          g_variant_new_array (type, values, n_attrs));
300                 }
301 
302               g_free (values);
303             }
304         }
305 
306       attributes = g_variant_builder_end (&builder);
307     }
308 
309   g_task_return_pointer (task, attributes, (GDestroyNotify) g_variant_unref);
310 }
311 
312 void
pp_job_get_attributes_async(PpJob * self,gchar ** attributes_names,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)313 pp_job_get_attributes_async (PpJob                *self,
314                              gchar               **attributes_names,
315                              GCancellable         *cancellable,
316                              GAsyncReadyCallback   callback,
317                              gpointer              user_data)
318 {
319   g_autoptr(GTask) task = NULL;
320 
321   task = g_task_new (self, cancellable, callback, user_data);
322   g_task_set_task_data (task, g_strdupv (attributes_names), (GDestroyNotify) g_strfreev);
323   g_task_run_in_thread (task, _pp_job_get_attributes_thread);
324 }
325 
326 GVariant *
pp_job_get_attributes_finish(PpJob * self,GAsyncResult * result,GError ** error)327 pp_job_get_attributes_finish (PpJob         *self,
328                               GAsyncResult  *result,
329                               GError       **error)
330 {
331   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
332 
333   return g_task_propagate_pointer (G_TASK (result), error);
334 }
335 
336 static void
_pp_job_authenticate_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)337 _pp_job_authenticate_thread (GTask        *task,
338                              gpointer      source_object,
339                              gpointer      task_data,
340                              GCancellable *cancellable)
341 {
342   PpJob         *self = source_object;
343   gboolean       result = FALSE;
344   gchar        **auth_info = task_data;
345   ipp_t         *request;
346   ipp_t         *response = NULL;
347   gint           length;
348 
349   if (auth_info != NULL)
350     {
351       g_autofree gchar *job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", self->id);
352 
353       length = g_strv_length (auth_info);
354 
355       request = ippNewRequest (IPP_OP_CUPS_AUTHENTICATE_JOB);
356       ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
357                     "job-uri", NULL, job_uri);
358       ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
359                     "requesting-user-name", NULL, cupsUser ());
360       ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
361                      "auth-info", length, NULL, (const char **) auth_info);
362       response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
363 
364       result = response != NULL && ippGetStatusCode (response) <= IPP_OK;
365 
366       if (response != NULL)
367         ippDelete (response);
368     }
369 
370   g_task_return_boolean (task, result);
371 }
372 
373 void
pp_job_authenticate_async(PpJob * self,gchar ** auth_info,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)374 pp_job_authenticate_async (PpJob                *self,
375                            gchar               **auth_info,
376                            GCancellable         *cancellable,
377                            GAsyncReadyCallback   callback,
378                            gpointer              user_data)
379 {
380   g_autoptr(GTask) task = NULL;
381 
382   task = g_task_new (self, cancellable, callback, user_data);
383   g_task_set_task_data (task, g_strdupv (auth_info), (GDestroyNotify) g_strfreev);
384   g_task_run_in_thread (task, _pp_job_authenticate_thread);
385 }
386 
387 gboolean
pp_job_authenticate_finish(PpJob * self,GAsyncResult * result,GError ** error)388 pp_job_authenticate_finish (PpJob         *self,
389                             GAsyncResult  *result,
390                             GError       **error)
391 {
392   g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
393 
394   return g_task_propagate_boolean (G_TASK (result), error);
395 }
396