/* * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2016 Mopria Alliance, Inc. * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdlib.h> #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include "lib_wprint.h" #include "ippstatus_monitor.h" #include "ipphelper.h" #include "cups.h" #include "http-private.h" #include <pthread.h> #include "wprint_debug.h" #define TAG "ippstatus_monitor" static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *); static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn); static void _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); static void _stop(const ifc_status_monitor_t *this_p); static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user); static void _destroy(const ifc_status_monitor_t *this_p); static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status, .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,}; typedef struct { unsigned char initialized; http_t *http; char printer_uri[1024]; char http_resource[1024]; unsigned char stop_monitor; unsigned char monitor_running; sem_t monitor_sem; pthread_mutex_t mutex; pthread_mutexattr_t mutexattr; ifc_status_monitor_t ifc; } ipp_monitor_t; const ifc_status_monitor_t *ipp_status_get_monitor_ifc(const ifc_wprint_t *wprint_ifc) { ipp_monitor_t *monitor = (ipp_monitor_t *) malloc(sizeof(ipp_monitor_t)); // setup the interface monitor->initialized = 0; monitor->http = NULL; memcpy(&monitor->ifc, &_status_ifc, sizeof(ifc_status_monitor_t)); return &monitor->ifc; } static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *connect_info) { ipp_monitor_t *monitor; LOGD("_init(): enter"); do { if (this_p == NULL) { continue; } monitor = IMPL(ipp_monitor_t, ifc, this_p); if (monitor->initialized != 0) { sem_post(&monitor->monitor_sem); sem_destroy(&monitor->monitor_sem); pthread_mutex_unlock(&monitor->mutex); pthread_mutex_destroy(&monitor->mutex); } if (monitor->http != NULL) { httpClose(monitor->http); } monitor->http = ipp_cups_connect(connect_info, monitor->printer_uri, sizeof(monitor->printer_uri)); getResourceFromURI(monitor->printer_uri, monitor->http_resource, 1024); monitor->monitor_running = 0; monitor->stop_monitor = 0; pthread_mutexattr_init(&monitor->mutexattr); pthread_mutexattr_settype(&(monitor->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP); pthread_mutex_init(&monitor->mutex, &monitor->mutexattr); sem_init(&monitor->monitor_sem, 0, 0); monitor->initialized = 1; } while (0); } static void _destroy(const ifc_status_monitor_t *this_p) { ipp_monitor_t *monitor; LOGD("_destroy(): enter"); do { if (this_p == NULL) { continue; } monitor = IMPL(ipp_monitor_t, ifc, this_p); if (monitor->initialized) { pthread_mutex_lock(&monitor->mutex); sem_post(&monitor->monitor_sem); sem_destroy(&monitor->monitor_sem); pthread_mutex_unlock(&monitor->mutex); pthread_mutex_destroy(&monitor->mutex); } if (monitor->http != NULL) { httpClose(monitor->http); } free(monitor); } while (0); } static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn) { int i; ipp_monitor_t *monitor; ipp_pstate_t printer_state; ipp_status_t ipp_status; LOGD("_get_status(): enter"); do { if (printer_state_dyn == NULL) { LOGD("_get_status(): printer_state_dyn is null!"); continue; } printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN; printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN; for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE; } if (this_p == NULL) { LOGE("_get_status(): this_p is null!"); continue; } monitor = IMPL(ipp_monitor_t, ifc, this_p); if (!monitor->initialized) { LOGE("_get_status(): Monitor is uninitialized"); continue; } if (monitor->http == NULL) { LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect"); printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; continue; } printer_state_dyn->printer_status = PRINT_STATUS_IDLE; ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn, &printer_state); LOGD("_get_status(): ipp_status=%d", ipp_status); debuglist_printerStatus(printer_state_dyn); } while (0); } static void _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) { int i; printer_state_dyn_t last_status, curr_status; ipp_monitor_t *monitor = NULL; LOGD("_start(): enter"); // initialize our status structures for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE; last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE; } last_status.printer_status = PRINT_STATUS_UNKNOWN; last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING; curr_status.printer_status = PRINT_STATUS_UNKNOWN; curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING; // send out the first callback if (status_cb != NULL) { (*status_cb)(&curr_status, &last_status, param); } do { curr_status.printer_status = PRINT_STATUS_SVC_REQUEST; curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; if (this_p == NULL) { continue; } monitor = IMPL(ipp_monitor_t, ifc, this_p); if (!monitor->initialized) { continue; } if (monitor->monitor_running) { continue; } monitor->stop_monitor = 0; monitor->monitor_running = 1; if (monitor->http == NULL) { if (status_cb != NULL) { (*status_cb)(&curr_status, &last_status, param); } sem_wait(&monitor->monitor_sem); last_status.printer_status = PRINT_STATUS_UNKNOWN; last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN; curr_status.printer_status = PRINT_STATUS_UNKNOWN; curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN; } else { while (!monitor->stop_monitor) { pthread_mutex_lock(&monitor->mutex); _get_status(this_p, &curr_status); pthread_mutex_unlock(&monitor->mutex); if ((status_cb != NULL) && (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) { (*status_cb)(&curr_status, &last_status, param); memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t)); } sleep(1); } } monitor->monitor_running = 0; } while (0); if (status_cb != NULL) { (*status_cb)(&curr_status, &last_status, param); } } static void _stop(const ifc_status_monitor_t *this_p) { // request a stop and release the semaphore ipp_monitor_t *monitor; LOGD("_stop(): enter"); do { if (this_p == NULL) { continue; } monitor = IMPL(ipp_monitor_t, ifc, this_p); if (!monitor->initialized) { continue; } sem_post(&monitor->monitor_sem); monitor->stop_monitor = 1; } while (0); } static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) { status_t return_value = ERROR; int job_id = -1; ipp_monitor_t *monitor = NULL; ipp_t *request = NULL; ipp_t *response = NULL; ipp_attribute_t *attr; LOGD("_cancel(): enter"); monitor = IMPL(ipp_monitor_t, ifc, this_p); if (this_p != NULL && monitor != NULL && monitor->initialized) { pthread_mutex_lock(&monitor->mutex); do { if (monitor->stop_monitor) { break; } request = ippNewRequest(IPP_GET_JOBS); if (request == NULL) { break; } ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->printer_uri); ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, requesting_user); // Requested printer attributes static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"}; ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs); response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource, monitor->printer_uri); if (response == NULL) { ipp_status_t ipp_status = cupsLastError(); LOGD("_cancel get job attributes: response is null, ipp_status %d: %s", ipp_status, ippErrorString(ipp_status)); return_value = ERROR; } else { attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER); if (attr != NULL) { job_id = ippGetInteger(attr, 0); LOGD("_cancel got job-id: %d", job_id); } else { // We need the job id to attempt a cancel break; } attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM); if (attr != NULL) { ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0); LOGD("_cancel got job-state: %d", jobState); } attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD); if (attr != NULL) { int idx; for (idx = 0; idx < ippGetCount(attr); idx++) { LOGD("before job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL)); } } } } while (0); ippDelete(request); request = NULL; ippDelete(response); response = NULL; do { if (job_id == -1) { break; } request = ippNewRequest(IPP_CANCEL_JOB); if (request == NULL) { break; } ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->printer_uri); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, requesting_user); if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource, monitor->printer_uri)) == NULL) { ipp_status_t ipp_status = cupsLastError(); LOGD("cancel: response is null: ipp_status %d %s", ipp_status, ippErrorString(ipp_status)); return_value = ERROR; } else { ipp_status_t ipp_status = cupsLastError(); LOGE("IPP_Status for cancel request was %d %s", ipp_status, ippErrorString(ipp_status)); attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD); if (attr != NULL) { int idx; for (idx = 0; ippGetCount(attr); idx++) { LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL)); } } return_value = OK; } } while (0); ippDelete(request); ippDelete(response); if (monitor->initialized) { pthread_mutex_unlock(&monitor->mutex); } } return return_value; }