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