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 }