Home | History | Annotate | Download | only in ipphelper
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 }