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 39static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *); 40 41static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn); 42 43static 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 47static void _stop(const ifc_status_monitor_t *this_p); 48 49static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user); 50 51static void _destroy(const ifc_status_monitor_t *this_p); 52 53static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status, 54 .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,}; 55 56typedef 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 69const 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 79static 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 115static 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 142static 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 186static 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 263static 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 282static 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}