1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 * Copyright (C) 2016 Mopria Alliance, Inc.
4 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <semaphore.h>
26 #include <fcntl.h>
27
28 #include "lib_wprint.h"
29 #include "ippstatus_monitor.h"
30 #include "ipphelper.h"
31
32 #include "cups.h"
33 #include "http-private.h"
34 #include <pthread.h>
35 #include "wprint_debug.h"
36
37 #define TAG "ippstatus_monitor"
38
39 static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *);
40
41 static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn);
42
43 static void _start(const ifc_status_monitor_t *this_p, void (*status_cb)(
44 const printer_state_dyn_t *new_status, const printer_state_dyn_t *old_status,
45 void *status_param),
46 void (*job_state_cb)(const job_state_dyn_t *new_state, void *param), void *param);
47
48 static void _stop(const ifc_status_monitor_t *this_p);
49
50 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user);
51
52 static void _destroy(const ifc_status_monitor_t *this_p);
53
54 static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn,
55 int job_id);
56
57 static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status,
58 .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,};
59
60 typedef struct {
61 unsigned char initialized;
62 http_t *http;
63 char printer_uri[1024];
64 char http_resource[1024];
65 unsigned char stop_monitor;
66 unsigned char monitor_running;
67 sem_t monitor_sem;
68 pthread_mutex_t mutex;
69 pthread_mutexattr_t mutexattr;
70 ifc_status_monitor_t ifc;
71 char requesting_user[1024];
72 } ipp_monitor_t;
73
ipp_status_get_monitor_ifc(const ifc_wprint_t * wprint_ifc)74 const ifc_status_monitor_t *ipp_status_get_monitor_ifc(const ifc_wprint_t *wprint_ifc) {
75 ipp_monitor_t *monitor = (ipp_monitor_t *) malloc(sizeof(ipp_monitor_t));
76
77 // setup the interface
78 monitor->initialized = 0;
79 monitor->http = NULL;
80 memcpy(&monitor->ifc, &_status_ifc, sizeof(ifc_status_monitor_t));
81 return &monitor->ifc;
82 }
83
_init(const ifc_status_monitor_t * this_p,const wprint_connect_info_t * connect_info)84 static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *connect_info) {
85 ipp_monitor_t *monitor;
86 LOGD("_init(): enter");
87 do {
88 if (this_p == NULL) {
89 continue;
90 }
91 monitor = IMPL(ipp_monitor_t, ifc, this_p);
92
93 if (monitor->initialized != 0) {
94 sem_post(&monitor->monitor_sem);
95 sem_destroy(&monitor->monitor_sem);
96
97 pthread_mutex_unlock(&monitor->mutex);
98 pthread_mutex_destroy(&monitor->mutex);
99 }
100
101 if (monitor->http != NULL) {
102 httpClose(monitor->http);
103 }
104
105 monitor->http = ipp_cups_connect(connect_info, monitor->printer_uri,
106 sizeof(monitor->printer_uri));
107 getResourceFromURI(monitor->printer_uri, monitor->http_resource, 1024);
108
109 monitor->monitor_running = 0;
110 monitor->stop_monitor = 0;
111
112 pthread_mutexattr_init(&monitor->mutexattr);
113 pthread_mutexattr_settype(&(monitor->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP);
114 pthread_mutex_init(&monitor->mutex, &monitor->mutexattr);
115 sem_init(&monitor->monitor_sem, 0, 0);
116 monitor->initialized = 1;
117 } while (0);
118 }
119
_destroy(const ifc_status_monitor_t * this_p)120 static void _destroy(const ifc_status_monitor_t *this_p) {
121 ipp_monitor_t *monitor;
122 LOGD("_destroy(): enter");
123 do {
124 if (this_p == NULL) {
125 continue;
126 }
127
128 monitor = IMPL(ipp_monitor_t, ifc, this_p);
129 if (monitor->initialized) {
130 pthread_mutex_lock(&monitor->mutex);
131
132 sem_post(&monitor->monitor_sem);
133 sem_destroy(&monitor->monitor_sem);
134
135 pthread_mutex_unlock(&monitor->mutex);
136 pthread_mutex_destroy(&monitor->mutex);
137 }
138
139 if (monitor->http != NULL) {
140 httpClose(monitor->http);
141 }
142
143 free(monitor);
144 } while (0);
145 }
146
_get_status(const ifc_status_monitor_t * this_p,printer_state_dyn_t * printer_state_dyn)147 static void _get_status(const ifc_status_monitor_t *this_p,
148 printer_state_dyn_t *printer_state_dyn) {
149 int i;
150 ipp_monitor_t *monitor;
151 ipp_pstate_t printer_state;
152 ipp_status_t ipp_status;
153 LOGD("_get_status(): enter");
154 do {
155 if (printer_state_dyn == NULL) {
156 LOGD("_get_status(): printer_state_dyn is null!");
157 continue;
158 }
159
160 printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN;
161 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN;
162 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
163 printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE;
164 }
165
166 if (this_p == NULL) {
167 LOGE("_get_status(): this_p is null!");
168 continue;
169 }
170
171 monitor = IMPL(ipp_monitor_t, ifc, this_p);
172 if (!monitor->initialized) {
173 LOGE("_get_status(): Monitor is uninitialized");
174 continue;
175 }
176
177 if (monitor->http == NULL) {
178 LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect");
179 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
180 continue;
181 }
182
183 printer_state_dyn->printer_status = PRINT_STATUS_IDLE;
184 ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn,
185 &printer_state);
186 LOGD("_get_status(): ipp_status=%d", ipp_status);
187 debuglist_printerStatus(printer_state_dyn);
188 } while (0);
189 }
190
_start(const ifc_status_monitor_t * this_p,void (* status_cb)(const printer_state_dyn_t * new_status,const printer_state_dyn_t * old_status,void * status_param),void (* job_state_cb)(const job_state_dyn_t * new_state,void * param),void * param)191 static void _start(const ifc_status_monitor_t *this_p,
192 void (*status_cb)(const printer_state_dyn_t *new_status,
193 const printer_state_dyn_t *old_status, void *status_param),
194 void (*job_state_cb)(const job_state_dyn_t *new_state, void *param),
195 void *param) {
196 int i, job_id = -1;
197 printer_state_dyn_t last_status, curr_status;
198 job_state_dyn_t old_state, new_state;
199 ipp_monitor_t *monitor = NULL;
200
201 LOGD("_start(): enter");
202
203 // initialize our status structures
204 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
205 curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
206 last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
207 }
208
209 last_status.printer_status = PRINT_STATUS_UNKNOWN;
210 last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
211
212 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
213 curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
214
215 // send out the first callback
216 if (status_cb != NULL) {
217 (*status_cb)(&curr_status, &last_status, param);
218 }
219
220 /* initialize job status structures */
221 for (i = 0; i <= IPP_JOB_STATE_REASON_MAX_VALUE; i++) {
222 new_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
223 old_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
224 }
225
226 old_state.job_state = IPP_JOB_STATE_UNKNOWN;
227 old_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
228
229 new_state.job_state = IPP_JOB_STATE_UNKNOWN;
230 new_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
231
232 do {
233 curr_status.printer_status = PRINT_STATUS_SVC_REQUEST;
234 curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
235
236 if (this_p == NULL) {
237 continue;
238 }
239
240 monitor = IMPL(ipp_monitor_t, ifc, this_p);
241 if (!monitor->initialized) {
242 continue;
243 }
244
245 if (monitor->monitor_running) {
246 continue;
247 }
248
249 monitor->stop_monitor = 0;
250 monitor->monitor_running = 1;
251 if (monitor->http == NULL) {
252 if (status_cb != NULL) {
253 (*status_cb)(&curr_status, &last_status, param);
254 }
255 sem_wait(&monitor->monitor_sem);
256
257 last_status.printer_status = PRINT_STATUS_UNKNOWN;
258 last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
259
260 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
261 curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
262 } else {
263 while (!monitor->stop_monitor) {
264 pthread_mutex_lock(&monitor->mutex);
265 _get_status(this_p, &curr_status);
266 pthread_mutex_unlock(&monitor->mutex);
267 if ((status_cb != NULL) &&
268 (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) {
269 (*status_cb)(&curr_status, &last_status, param);
270 memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t));
271 }
272
273 // Do not call for job state if thread has been stopped
274 if (job_state_cb != NULL && !monitor->stop_monitor) {
275 pthread_mutex_lock(&monitor->mutex);
276 if (job_id == -1) {
277 job_id = getJobId(monitor->http, monitor->http_resource,
278 monitor->printer_uri, &new_state,
279 monitor->requesting_user);
280 }
281 _get_job_state(this_p, &new_state, job_id);
282 pthread_mutex_unlock(&monitor->mutex);
283
284 if (memcmp(&new_state, &old_state, sizeof(job_state_dyn_t)) != 0) {
285 (*job_state_cb)(&new_state, param);
286 memcpy(&old_state, &new_state, sizeof(job_state_dyn_t));
287 }
288 }
289 sleep(1);
290 }
291 }
292 monitor->monitor_running = 0;
293 } while (0);
294
295 if (status_cb != NULL) {
296 (*status_cb)(&curr_status, &last_status, param);
297 }
298 }
299
_stop(const ifc_status_monitor_t * this_p)300 static void _stop(const ifc_status_monitor_t *this_p) {
301 // request a stop and release the semaphore
302 ipp_monitor_t *monitor;
303 LOGD("_stop(): enter");
304 do {
305 if (this_p == NULL) {
306 continue;
307 }
308
309 monitor = IMPL(ipp_monitor_t, ifc, this_p);
310 if (!monitor->initialized) {
311 continue;
312 }
313
314 sem_post(&monitor->monitor_sem);
315 monitor->stop_monitor = 1;
316 } while (0);
317 }
318
_cancel(const ifc_status_monitor_t * this_p,const char * requesting_user)319 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) {
320 status_t return_value = ERROR;
321 int job_id = -1;
322 ipp_monitor_t *monitor = NULL;
323 ipp_t *request = NULL;
324 ipp_t *response = NULL;
325 ipp_attribute_t *attr;
326
327 LOGD("_cancel(): enter");
328
329 monitor = IMPL(ipp_monitor_t, ifc, this_p);
330 if (this_p != NULL && monitor != NULL && monitor->initialized) {
331 pthread_mutex_lock(&monitor->mutex);
332 do {
333 if (monitor->stop_monitor) {
334 break;
335 }
336
337 request = ippNewRequest(IPP_GET_JOBS);
338 if (request == NULL) {
339 break;
340 }
341
342 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
343 monitor->printer_uri);
344 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
345 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
346 NULL, requesting_user);
347
348 // Requested printer attributes
349 static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"};
350
351 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes",
352 sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs);
353
354 response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
355 monitor->printer_uri);
356 if (response == NULL) {
357 ipp_status_t ipp_status = cupsLastError();
358 LOGD("_cancel get job attributes: response is null, ipp_status %d: %s",
359 ipp_status, ippErrorString(ipp_status));
360 return_value = ERROR;
361 } else {
362 attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
363 if (attr != NULL) {
364 job_id = ippGetInteger(attr, 0);
365 LOGD("_cancel got job-id: %d", job_id);
366 } else { // We need the job id to attempt a cancel
367 break;
368 }
369
370 attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM);
371 if (attr != NULL) {
372 ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0);
373 LOGD("_cancel got job-state: %d", jobState);
374 }
375
376 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
377 if (attr != NULL) {
378 int idx;
379 for (idx = 0; idx < ippGetCount(attr); idx++) {
380 LOGD("before job-state-reason (%d): %s", idx,
381 ippGetString(attr, idx, NULL));
382 }
383 }
384 }
385 } while (0);
386
387 ippDelete(request);
388 request = NULL;
389 ippDelete(response);
390 response = NULL;
391
392 do {
393 if (job_id == -1) {
394 break;
395 }
396
397 request = ippNewRequest(IPP_CANCEL_JOB);
398 if (request == NULL) {
399 break;
400 }
401
402 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
403 monitor->printer_uri);
404 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
405 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
406 "requesting-user-name", NULL, requesting_user);
407
408 if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
409 monitor->printer_uri)) == NULL) {
410 ipp_status_t ipp_status = cupsLastError();
411 LOGD("cancel: response is null: ipp_status %d %s", ipp_status,
412 ippErrorString(ipp_status));
413 return_value = ERROR;
414 } else {
415 ipp_status_t ipp_status = cupsLastError();
416 LOGE("IPP_Status for cancel request was %d %s", ipp_status,
417 ippErrorString(ipp_status));
418 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
419 if (attr != NULL) {
420 int idx;
421 for (idx = 0; ippGetCount(attr); idx++) {
422 LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL));
423 }
424 }
425 return_value = OK;
426 }
427 } while (0);
428
429 ippDelete(request);
430 ippDelete(response);
431
432 if (monitor->initialized) {
433 pthread_mutex_unlock(&monitor->mutex);
434 }
435 }
436 return return_value;
437 }
438
439 /*
440 * Get job state for the given job_id
441 */
_get_job_state(const ifc_status_monitor_t * this_p,job_state_dyn_t * job_state_dyn,int job_id)442 static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn,
443 int job_id) {
444 if (job_id == -1) return;
445
446 LOGD("_get_job_state(): enter");
447
448 ipp_monitor_t *monitor = NULL;
449 monitor = IMPL(ipp_monitor_t, ifc, this_p);
450
451 if (this_p != NULL && monitor != NULL && monitor->initialized) {
452 pthread_mutex_lock(&monitor->mutex);
453
454 do {
455 if (monitor->stop_monitor)
456 break;
457
458 ipp_monitor_t *ipp_job;
459 ipp_job = IMPL(ipp_monitor_t, ifc, this_p);
460
461 if (ipp_job->http == NULL)
462 break;
463
464 ipp_jstate_t job_ippstate;
465 ipp_status_t ipp_status = get_JobStatus(ipp_job->http, ipp_job->printer_uri, job_id,
466 job_state_dyn, &job_ippstate,
467 monitor->requesting_user);
468 LOGD("_get_job_state(): Print job State is %d", ipp_status);
469 } while (0);
470 pthread_mutex_unlock(&monitor->mutex);
471 }
472 }