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), void *param);
46
47 static void _stop(const ifc_status_monitor_t *this_p);
48
49 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user);
50
51 static void _destroy(const ifc_status_monitor_t *this_p);
52
53 static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status,
54 .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,};
55
56 typedef struct {
57 unsigned char initialized;
58 http_t *http;
59 char printer_uri[1024];
60 char http_resource[1024];
61 unsigned char stop_monitor;
62 unsigned char monitor_running;
63 sem_t monitor_sem;
64 pthread_mutex_t mutex;
65 pthread_mutexattr_t mutexattr;
66 ifc_status_monitor_t ifc;
67 } ipp_monitor_t;
68
ipp_status_get_monitor_ifc(const ifc_wprint_t * wprint_ifc)69 const ifc_status_monitor_t *ipp_status_get_monitor_ifc(const ifc_wprint_t *wprint_ifc) {
70 ipp_monitor_t *monitor = (ipp_monitor_t *) malloc(sizeof(ipp_monitor_t));
71
72 // setup the interface
73 monitor->initialized = 0;
74 monitor->http = NULL;
75 memcpy(&monitor->ifc, &_status_ifc, sizeof(ifc_status_monitor_t));
76 return &monitor->ifc;
77 }
78
_init(const ifc_status_monitor_t * this_p,const wprint_connect_info_t * connect_info)79 static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *connect_info) {
80 ipp_monitor_t *monitor;
81 LOGD("_init(): enter");
82 do {
83 if (this_p == NULL) {
84 continue;
85 }
86 monitor = IMPL(ipp_monitor_t, ifc, this_p);
87
88 if (monitor->initialized != 0) {
89 sem_post(&monitor->monitor_sem);
90 sem_destroy(&monitor->monitor_sem);
91
92 pthread_mutex_unlock(&monitor->mutex);
93 pthread_mutex_destroy(&monitor->mutex);
94 }
95
96 if (monitor->http != NULL) {
97 httpClose(monitor->http);
98 }
99
100 monitor->http = ipp_cups_connect(connect_info, monitor->printer_uri,
101 sizeof(monitor->printer_uri));
102 getResourceFromURI(monitor->printer_uri, monitor->http_resource, 1024);
103
104 monitor->monitor_running = 0;
105 monitor->stop_monitor = 0;
106
107 pthread_mutexattr_init(&monitor->mutexattr);
108 pthread_mutexattr_settype(&(monitor->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP);
109 pthread_mutex_init(&monitor->mutex, &monitor->mutexattr);
110 sem_init(&monitor->monitor_sem, 0, 0);
111 monitor->initialized = 1;
112 } while (0);
113 }
114
_destroy(const ifc_status_monitor_t * this_p)115 static void _destroy(const ifc_status_monitor_t *this_p) {
116 ipp_monitor_t *monitor;
117 LOGD("_destroy(): enter");
118 do {
119 if (this_p == NULL) {
120 continue;
121 }
122
123 monitor = IMPL(ipp_monitor_t, ifc, this_p);
124 if (monitor->initialized) {
125 pthread_mutex_lock(&monitor->mutex);
126
127 sem_post(&monitor->monitor_sem);
128 sem_destroy(&monitor->monitor_sem);
129
130 pthread_mutex_unlock(&monitor->mutex);
131 pthread_mutex_destroy(&monitor->mutex);
132 }
133
134 if (monitor->http != NULL) {
135 httpClose(monitor->http);
136 }
137
138 free(monitor);
139 } while (0);
140 }
141
_get_status(const ifc_status_monitor_t * this_p,printer_state_dyn_t * printer_state_dyn)142 static void _get_status(const ifc_status_monitor_t *this_p,
143 printer_state_dyn_t *printer_state_dyn) {
144 int i;
145 ipp_monitor_t *monitor;
146 ipp_pstate_t printer_state;
147 ipp_status_t ipp_status;
148 LOGD("_get_status(): enter");
149 do {
150 if (printer_state_dyn == NULL) {
151 LOGD("_get_status(): printer_state_dyn is null!");
152 continue;
153 }
154
155 printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN;
156 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN;
157 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
158 printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE;
159 }
160
161 if (this_p == NULL) {
162 LOGE("_get_status(): this_p is null!");
163 continue;
164 }
165
166 monitor = IMPL(ipp_monitor_t, ifc, this_p);
167 if (!monitor->initialized) {
168 LOGE("_get_status(): Monitor is uninitialized");
169 continue;
170 }
171
172 if (monitor->http == NULL) {
173 LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect");
174 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
175 continue;
176 }
177
178 printer_state_dyn->printer_status = PRINT_STATUS_IDLE;
179 ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn,
180 &printer_state);
181 LOGD("_get_status(): ipp_status=%d", ipp_status);
182 debuglist_printerStatus(printer_state_dyn);
183 } while (0);
184 }
185
_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 * param)186 static void _start(const ifc_status_monitor_t *this_p,
187 void (*status_cb)(const printer_state_dyn_t *new_status,
188 const printer_state_dyn_t *old_status, void *status_param),
189 void *param) {
190 int i;
191 printer_state_dyn_t last_status, curr_status;
192 ipp_monitor_t *monitor = NULL;
193
194 LOGD("_start(): enter");
195
196 // initialize our status structures
197 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
198 curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
199 last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
200 }
201
202 last_status.printer_status = PRINT_STATUS_UNKNOWN;
203 last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
204
205 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
206 curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
207
208 // send out the first callback
209 if (status_cb != NULL) {
210 (*status_cb)(&curr_status, &last_status, param);
211 }
212 do {
213 curr_status.printer_status = PRINT_STATUS_SVC_REQUEST;
214 curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
215
216 if (this_p == NULL) {
217 continue;
218 }
219
220 monitor = IMPL(ipp_monitor_t, ifc, this_p);
221 if (!monitor->initialized) {
222 continue;
223 }
224
225 if (monitor->monitor_running) {
226 continue;
227 }
228
229 monitor->stop_monitor = 0;
230 monitor->monitor_running = 1;
231 if (monitor->http == NULL) {
232 if (status_cb != NULL) {
233 (*status_cb)(&curr_status, &last_status, param);
234 }
235 sem_wait(&monitor->monitor_sem);
236
237 last_status.printer_status = PRINT_STATUS_UNKNOWN;
238 last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
239
240 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
241 curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
242 } else {
243 while (!monitor->stop_monitor) {
244 pthread_mutex_lock(&monitor->mutex);
245 _get_status(this_p, &curr_status);
246 pthread_mutex_unlock(&monitor->mutex);
247 if ((status_cb != NULL) &&
248 (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) {
249 (*status_cb)(&curr_status, &last_status, param);
250 memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t));
251 }
252 sleep(1);
253 }
254 }
255 monitor->monitor_running = 0;
256 } while (0);
257
258 if (status_cb != NULL) {
259 (*status_cb)(&curr_status, &last_status, param);
260 }
261 }
262
_stop(const ifc_status_monitor_t * this_p)263 static void _stop(const ifc_status_monitor_t *this_p) {
264 // request a stop and release the semaphore
265 ipp_monitor_t *monitor;
266 LOGD("_stop(): enter");
267 do {
268 if (this_p == NULL) {
269 continue;
270 }
271
272 monitor = IMPL(ipp_monitor_t, ifc, this_p);
273 if (!monitor->initialized) {
274 continue;
275 }
276
277 sem_post(&monitor->monitor_sem);
278 monitor->stop_monitor = 1;
279 } while (0);
280 }
281
_cancel(const ifc_status_monitor_t * this_p,const char * requesting_user)282 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) {
283 status_t return_value = ERROR;
284 int job_id = -1;
285 ipp_monitor_t *monitor = NULL;
286 ipp_t *request = NULL;
287 ipp_t *response = NULL;
288 ipp_attribute_t *attr;
289
290 LOGD("_cancel(): enter");
291
292 monitor = IMPL(ipp_monitor_t, ifc, this_p);
293 if (this_p != NULL && monitor != NULL && monitor->initialized) {
294 pthread_mutex_lock(&monitor->mutex);
295 do {
296 if (monitor->stop_monitor) {
297 break;
298 }
299
300 request = ippNewRequest(IPP_GET_JOBS);
301 if (request == NULL) {
302 break;
303 }
304
305 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
306 monitor->printer_uri);
307 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
308 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
309 NULL, requesting_user);
310
311 // Requested printer attributes
312 static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"};
313
314 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes",
315 sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs);
316
317 response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
318 monitor->printer_uri);
319 if (response == NULL) {
320 ipp_status_t ipp_status = cupsLastError();
321 LOGD("_cancel get job attributes: response is null, ipp_status %d: %s",
322 ipp_status, ippErrorString(ipp_status));
323 return_value = ERROR;
324 } else {
325 attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
326 if (attr != NULL) {
327 job_id = ippGetInteger(attr, 0);
328 LOGD("_cancel got job-id: %d", job_id);
329 } else { // We need the job id to attempt a cancel
330 break;
331 }
332
333 attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM);
334 if (attr != NULL) {
335 ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0);
336 LOGD("_cancel got job-state: %d", jobState);
337 }
338
339 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
340 if (attr != NULL) {
341 int idx;
342 for (idx = 0; idx < ippGetCount(attr); idx++) {
343 LOGD("before job-state-reason (%d): %s", idx,
344 ippGetString(attr, idx, NULL));
345 }
346 }
347 }
348 } while (0);
349
350 ippDelete(request);
351 request = NULL;
352 ippDelete(response);
353 response = NULL;
354
355 do {
356 if (job_id == -1) {
357 break;
358 }
359
360 request = ippNewRequest(IPP_CANCEL_JOB);
361 if (request == NULL) {
362 break;
363 }
364
365 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
366 monitor->printer_uri);
367 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
368 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
369 "requesting-user-name", NULL, requesting_user);
370
371 if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
372 monitor->printer_uri)) == NULL) {
373 ipp_status_t ipp_status = cupsLastError();
374 LOGD("cancel: response is null: ipp_status %d %s", ipp_status,
375 ippErrorString(ipp_status));
376 return_value = ERROR;
377 } else {
378 ipp_status_t ipp_status = cupsLastError();
379 LOGE("IPP_Status for cancel request was %d %s", ipp_status,
380 ippErrorString(ipp_status));
381 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
382 if (attr != NULL) {
383 int idx;
384 for (idx = 0; ippGetCount(attr); idx++) {
385 LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL));
386 }
387 }
388 return_value = OK;
389 }
390 } while (0);
391
392 ippDelete(request);
393 ippDelete(response);
394
395 if (monitor->initialized) {
396 pthread_mutex_unlock(&monitor->mutex);
397 }
398 }
399 return return_value;
400 }