• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27 #ifndef __USE_UNIX98
28 #define __USE_UNIX98
29 #endif
30 
31 #include <pthread.h>
32 
33 #include <semaphore.h>
34 #include <lib_wprint.h>
35 #include <printer_capabilities_types.h>
36 
37 #include "ifc_print_job.h"
38 #include "wprint_debug.h"
39 #include "plugin_db.h"
40 
41 #include "ifc_status_monitor.h"
42 
43 #include "ippstatus_monitor.h"
44 #include "ippstatus_capabilities.h"
45 #include "ipp_print.h"
46 #include "ipphelper.h"
47 
48 #include "lib_printable_area.h"
49 #include "wprint_io_plugin.h"
50 #include "../plugins/media.h"
51 
52 #define TAG "lib_wprint"
53 
54 /* As expected by target devices */
55 #define USERAGENT_PREFIX "wPrintAndroid"
56 
57 #define USE_PWG_OVER_PCLM 0
58 
59 #if (USE_PWG_OVER_PCLM != 0)
60 #define _DEFAULT_PRINT_FORMAT  PRINT_FORMAT_PWG
61 #define _DEFAULT_PCL_TYPE      PCLPWG
62 #else // (USE_PWG_OVER_PCLM != 0)
63 #define _DEFAULT_PRINT_FORMAT  PRINT_FORMAT_PCLM
64 #define _DEFAULT_PCL_TYPE      PCLm
65 #endif // (USE_PWG_OVER_PCLM != 0)
66 
67 #define _MAX_SPOOLED_JOBS     100
68 #define _MAX_MSGS             (_MAX_SPOOLED_JOBS * 5)
69 
70 #define _MAX_PAGES_PER_JOB   1000
71 
72 #define MAX_IDLE_WAIT        (5 * 60)
73 
74 #define DEFAULT_RESOLUTION   (300)
75 #define HIGH_RESOLUTION_PHOTO (600)
76 
77 // When searching for a supported resolution this is the max resolution we will consider.
78 #define MAX_SUPPORTED_RESOLUTION (720)
79 
80 #define MAX_DONE_WAIT (5 * 60)
81 #define MAX_START_WAIT (45)
82 
83 #define IO_PORT_FILE   0
84 
85 /*
86  * The following macros allow for up to 8 bits (256) for spooled job id#s and
87  * 24 bits (16 million) of a running sequence number to provide a reasonably
88  * unique job handle
89  */
90 
91 // _ENCODE_HANDLE() is only called from _get_handle()
92 #define _ENCODE_HANDLE(X) ( (((++_running_number) & 0xffffff) << 8) | ((X) & 0xff) )
93 #define _DECODE_HANDLE(X) ((X) & 0xff)
94 
95 #undef snprintf
96 #undef vsnprintf
97 
98 typedef enum {
99     JOB_STATE_FREE, // queue element free
100     JOB_STATE_QUEUED, // job queued and waiting to be run
101     JOB_STATE_RUNNING, // job running (printing)
102     JOB_STATE_BLOCKED, // print job blocked due to printer stall/error
103     JOB_STATE_CANCEL_REQUEST, // print job cancelled by user,
104     JOB_STATE_CANCELLED, // print job cancelled by user, waiting to be freed
105     JOB_STATE_COMPLETED, // print job completed successfully, waiting to be freed
106     JOB_STATE_ERROR, // job could not be run due to error
107     JOB_STATE_CORRUPTED, // job could not be run due to error
108     JOB_STATE_BAD_CERTIFICATE, // server gave an unexpected ssl certificate
109     NUM_JOB_STATES
110 } _job_state_t;
111 
112 typedef enum {
113     TOP_MARGIN = 0,
114     LEFT_MARGIN,
115     RIGHT_MARGIN,
116     BOTTOM_MARGIN,
117 
118     NUM_PAGE_MARGINS
119 } _page_margins_t;
120 
121 typedef enum {
122     MSG_RUN_JOB, MSG_QUIT,
123 } wprint_msg_t;
124 
125 typedef struct {
126     wprint_msg_t id;
127     wJob_t job_id;
128 } _msg_t;
129 
130 /*
131  * Define an entry in the job queue
132  */
133 typedef struct {
134     wJob_t job_handle;
135     _job_state_t job_state;
136     unsigned int blocked_reasons;
137     wprint_status_cb_t cb_fn;
138     char *printer_addr;
139     port_t port_num;
140     wprint_plugin_t *plugin;
141     ifc_print_job_t *print_ifc;
142     char *mime_type;
143     char *pathname;
144     bool is_dir;
145     bool last_page_seen;
146     int num_pages;
147     msg_q_id pageQ;
148     msg_q_id saveQ;
149 
150     wprint_job_params_t job_params;
151     bool cancel_ok;
152     bool use_secure_uri;
153 
154     const ifc_status_monitor_t *status_ifc;
155     char debug_path[MAX_PATHNAME_LENGTH + 1];
156     char printer_uri[1024];
157     int job_debug_fd;
158     int page_debug_fd;
159 
160     /* A buffer of bytes containing the certificate received while setting up this job, if any. */
161     uint8 *certificate;
162     int certificate_len;
163 } _job_queue_t;
164 
165 /*
166  * An entry for queued pages
167  */
168 typedef struct {
169     int page_num;
170     bool pdf_page;
171     bool last_page;
172     bool corrupted;
173     char filename[MAX_PATHNAME_LENGTH + 1];
174     unsigned int top_margin;
175     unsigned int left_margin;
176     unsigned int right_margin;
177     unsigned int bottom_margin;
178 } _page_t;
179 
180 /*
181  * Entry for a registered plugin
182  */
183 typedef struct {
184     port_t port_num;
185     const wprint_io_plugin_t *io_plugin;
186 } _io_plugin_t;
187 
188 static _job_queue_t _job_queue[_MAX_SPOOLED_JOBS];
189 static msg_q_id _msgQ;
190 
191 static pthread_t _job_status_tid;
192 static pthread_t _job_tid;
193 
194 static pthread_mutex_t _q_lock;
195 static pthread_mutexattr_t _q_lock_attr;
196 
197 static sem_t _job_end_wait_sem;
198 static sem_t _job_start_wait_sem;
199 
200 static _io_plugin_t _io_plugins[2];
201 
202 static volatile bool stop_run = false;
203 static volatile bool stop_mutex = false;
204 
205 static printer_capabilities_t g_printer_caps = {0};
206 
207 char g_osName[MAX_ID_STRING_LENGTH + 1] = {0};
208 char g_appName[MAX_ID_STRING_LENGTH + 1] = {0};
209 char g_appVersion[MAX_ID_STRING_LENGTH + 1] = {0};
210 
211 /*
212  * Convert a pcl_t type to a human-readable string
213  */
getPCLTypeString(pcl_t pclenum)214 static char *getPCLTypeString(pcl_t pclenum) {
215     switch (pclenum) {
216         case PCLNONE:
217             return "PCL_NONE";
218         case PCLm:
219             return "PCLm";
220         case PCLJPEG:
221             return "PCL_JPEG";
222         case PCLPWG:
223             return "PWG-Raster";
224         default:
225             return "unkonwn PCL Type";
226     }
227 }
228 
229 /*
230  * Return a _job_queue_t item by its job_handle or NULL if not found.
231  */
_get_job_desc(wJob_t job_handle)232 static _job_queue_t *_get_job_desc(wJob_t job_handle) {
233     unsigned long index;
234     if (job_handle == WPRINT_BAD_JOB_HANDLE) {
235         return NULL;
236     }
237     index = _DECODE_HANDLE(job_handle);
238     if ((index < _MAX_SPOOLED_JOBS) && (_job_queue[index].job_handle == job_handle) &&
239             (_job_queue[index].job_state != JOB_STATE_FREE)) {
240         return (&_job_queue[index]);
241     } else {
242         return NULL;
243     }
244 }
245 
246 /*
247  * Functions below to fill out the _debug_stream_ifc interface
248  */
249 
_stream_dbg_end_job(wJob_t job_handle)250 static void _stream_dbg_end_job(wJob_t job_handle) {
251     _job_queue_t *jq = _get_job_desc(job_handle);
252     if (jq && (jq->job_debug_fd >= 0)) {
253         close(jq->job_debug_fd);
254         jq->job_debug_fd = -1;
255     }
256 }
257 
_stream_dbg_start_job(wJob_t job_handle,const char * ext)258 static void _stream_dbg_start_job(wJob_t job_handle, const char *ext) {
259     _stream_dbg_end_job(job_handle);
260     _job_queue_t *jq = _get_job_desc(job_handle);
261     if (jq && jq->debug_path[0]) {
262         char filename[MAX_PATHNAME_LENGTH + 1];
263         snprintf(filename, MAX_PATHNAME_LENGTH, "%s/jobstream.%s", jq->debug_path, ext);
264         filename[MAX_PATHNAME_LENGTH] = 0;
265         jq->job_debug_fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
266     }
267 }
268 
_stream_dbg_job_data(wJob_t job_handle,const unsigned char * buff,unsigned long nbytes)269 static void _stream_dbg_job_data(wJob_t job_handle, const unsigned char *buff,
270         unsigned long nbytes) {
271     _job_queue_t *jq = _get_job_desc(job_handle);
272     ssize_t bytes_written;
273     if (jq && (jq->job_debug_fd >= 0)) {
274         while (nbytes > 0) {
275             bytes_written = write(jq->job_debug_fd, buff, nbytes);
276             if (bytes_written < 0) {
277                 return;
278             }
279             nbytes -= bytes_written;
280             buff += bytes_written;
281         }
282     }
283 }
284 
_stream_dbg_end_page(wJob_t job_handle)285 static void _stream_dbg_end_page(wJob_t job_handle) {
286     _job_queue_t *jq = _get_job_desc(job_handle);
287     if (jq && (jq->page_debug_fd >= 0)) {
288         close(jq->page_debug_fd);
289         jq->page_debug_fd = -1;
290     }
291 }
292 
_stream_dbg_page_data(wJob_t job_handle,const unsigned char * buff,unsigned long nbytes)293 static void _stream_dbg_page_data(wJob_t job_handle, const unsigned char *buff,
294         unsigned long nbytes) {
295     _job_queue_t *jq = _get_job_desc(job_handle);
296     ssize_t bytes_written;
297     if (jq && (jq->page_debug_fd >= 0)) {
298         while (nbytes > 0) {
299             bytes_written = write(jq->page_debug_fd, buff, nbytes);
300             if (bytes_written < 0) {
301                 return;
302             }
303             nbytes -= bytes_written;
304             buff += bytes_written;
305         }
306     }
307 }
308 
309 #define PPM_IDENTIFIER "P6\n"
310 #define PPM_HEADER_LENGTH 128
311 
_stream_dbg_start_page(wJob_t job_handle,int page_number,int width,int height)312 static void _stream_dbg_start_page(wJob_t job_handle, int page_number, int width, int height) {
313     _stream_dbg_end_page(job_handle);
314     _job_queue_t *jq = _get_job_desc(job_handle);
315     if (jq && jq->debug_path[0]) {
316         union {
317             char filename[MAX_PATHNAME_LENGTH + 1];
318             char ppm_header[PPM_HEADER_LENGTH + 1];
319         } buff;
320         snprintf(buff.filename, MAX_PATHNAME_LENGTH, "%s/page%4.4d.ppm", jq->debug_path,
321                 page_number);
322         buff.filename[MAX_PATHNAME_LENGTH] = 0;
323         jq->page_debug_fd = open(buff.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
324         int length = snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n",
325                 PPM_IDENTIFIER, 0, ' ', width, height, 255);
326         int padding = sizeof(buff.ppm_header) - length;
327         snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n",
328                 PPM_IDENTIFIER, padding, ' ', width, height, 255);
329         _stream_dbg_page_data(job_handle, (const unsigned char *) buff.ppm_header,
330                 PPM_HEADER_LENGTH);
331     }
332 }
333 
334 static const ifc_wprint_debug_stream_t _debug_stream_ifc = {
335         .debug_start_job = _stream_dbg_start_job, .debug_job_data = _stream_dbg_job_data,
336         .debug_end_job = _stream_dbg_end_job, .debug_start_page = _stream_dbg_start_page,
337         .debug_page_data = _stream_dbg_page_data, .debug_end_page = _stream_dbg_end_page
338 };
339 
340 /*
341  * Return the debug stream interface corresponding to the specified job handle
342  */
getDebugStreamIfc(wJob_t handle)343 const ifc_wprint_debug_stream_t *getDebugStreamIfc(wJob_t handle) {
344     _job_queue_t *jq = _get_job_desc(handle);
345     if (jq) {
346         return (jq->debug_path[0] == 0) ? NULL : &_debug_stream_ifc;
347     }
348     return NULL;
349 }
350 
351 const ifc_wprint_t _wprint_ifc = {
352         .msgQCreate = msgQCreate, .msgQDelete = msgQDelete,
353         .msgQSend = msgQSend, .msgQReceive = msgQReceive, .msgQNumMsgs = msgQNumMsgs,
354         .get_debug_stream_ifc = getDebugStreamIfc
355 };
356 
357 static pcl_t _default_pcl_type = _DEFAULT_PCL_TYPE;
358 
_printer_file_connect(const ifc_wprint_t * wprint_ifc)359 static const ifc_print_job_t *_printer_file_connect(const ifc_wprint_t *wprint_ifc) {
360     return printer_connect(IO_PORT_FILE);
361 }
362 
_get_caps_ifc(port_t port_num)363 static const ifc_printer_capabilities_t *_get_caps_ifc(port_t port_num) {
364     int i;
365     for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) {
366         if (_io_plugins[i].port_num == port_num) {
367             if (_io_plugins[i].io_plugin == NULL) {
368                 return NULL;
369             }
370             if (_io_plugins[i].io_plugin->getCapsIFC == NULL) {
371                 return NULL;
372             } else {
373                 return (_io_plugins[i].io_plugin->getCapsIFC(&_wprint_ifc));
374             }
375         }
376     }
377     return NULL;
378 }
379 
_get_status_ifc(port_t port_num)380 static const ifc_status_monitor_t *_get_status_ifc(port_t port_num) {
381     int i;
382     for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) {
383         if (_io_plugins[i].port_num == port_num) {
384             if (_io_plugins[i].io_plugin == NULL) {
385                 return NULL;
386             }
387             if (_io_plugins[i].io_plugin->getStatusIFC == NULL) {
388                 return NULL;
389             } else {
390                 return (_io_plugins[i].io_plugin->getStatusIFC(&_wprint_ifc));
391             }
392         }
393     }
394     return NULL;
395 }
396 
_get_print_ifc(port_t port_num)397 static const ifc_print_job_t *_get_print_ifc(port_t port_num) {
398     int i;
399     for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) {
400         if (_io_plugins[i].port_num == port_num) {
401             if (_io_plugins[i].io_plugin == NULL) {
402                 return NULL;
403             }
404             if (_io_plugins[i].io_plugin->getPrintIFC == NULL) {
405                 return NULL;
406             } else {
407                 return (_io_plugins[i].io_plugin->getPrintIFC(&_wprint_ifc));
408             }
409         }
410     }
411     return NULL;
412 }
413 
414 /*
415  * Lock the semaphore for this module
416  */
_lock(void)417 static void _lock(void) {
418     if (!stop_mutex) {
419         pthread_mutex_lock(&_q_lock);
420     }
421 }
422 
423 /*
424  * Unlock the semaphore for this module
425  */
_unlock(void)426 static void _unlock(void) {
427     if (!stop_mutex) {
428         pthread_mutex_unlock(&_q_lock);
429     }
430 }
431 
_get_handle(void)432 static wJob_t _get_handle(void) {
433     static unsigned long _running_number = 0;
434     wJob_t job_handle = WPRINT_BAD_JOB_HANDLE;
435     int i, index, size;
436     char *ptr;
437 
438     for (i = 0; i < _MAX_SPOOLED_JOBS; i++) {
439         index = (i + _running_number) % _MAX_SPOOLED_JOBS;
440 
441         if (_job_queue[index].job_state == JOB_STATE_FREE) {
442             size = MAX_MIME_LENGTH + MAX_PRINTER_ADDR_LENGTH + MAX_PATHNAME_LENGTH + 4;
443             ptr = malloc(size);
444             if (ptr) {
445                 memset(&_job_queue[index], 0, sizeof(_job_queue_t));
446                 memset(ptr, 0, size);
447 
448                 _job_queue[index].job_debug_fd = -1;
449                 _job_queue[index].page_debug_fd = -1;
450                 _job_queue[index].printer_addr = ptr;
451 
452                 ptr += (MAX_PRINTER_ADDR_LENGTH + 1);
453                 _job_queue[index].mime_type = ptr;
454                 ptr += (MAX_MIME_LENGTH + 1);
455                 _job_queue[index].pathname = ptr;
456 
457                 _job_queue[index].job_state = JOB_STATE_QUEUED;
458                 _job_queue[index].job_handle = _ENCODE_HANDLE(index);
459 
460                 job_handle = _job_queue[index].job_handle;
461             }
462             break;
463         }
464     }
465     return job_handle;
466 }
467 
_recycle_handle(wJob_t job_handle)468 static int _recycle_handle(wJob_t job_handle) {
469     _job_queue_t *jq = _get_job_desc(job_handle);
470 
471     if (jq == NULL) {
472         return ERROR;
473     } else if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) ||
474             (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) {
475         if (jq->print_ifc != NULL) {
476             jq->print_ifc->destroy(jq->print_ifc);
477         }
478 
479         jq->print_ifc = NULL;
480         if (jq->status_ifc != NULL) {
481             jq->status_ifc->destroy(jq->status_ifc);
482         }
483         jq->status_ifc = NULL;
484         if (jq->job_params.useragent != NULL) {
485             free((void *) jq->job_params.useragent);
486         }
487         if (jq->job_params.certificate != NULL) {
488             free((void *) jq->job_params.certificate);
489         }
490         free(jq->printer_addr);
491         jq->job_state = JOB_STATE_FREE;
492         if (jq->job_debug_fd != -1) {
493             close(jq->job_debug_fd);
494         }
495         jq->job_debug_fd = -1;
496         if (jq->page_debug_fd != -1) {
497             close(jq->page_debug_fd);
498         }
499         jq->page_debug_fd = -1;
500         jq->debug_path[0] = 0;
501         if (jq->certificate) {
502             free(jq->certificate);
503             jq->certificate = NULL;
504         }
505         return OK;
506     } else {
507         return ERROR;
508     }
509 }
510 
511 /*
512  * Stops the job status thread if it exists
513  */
_stop_status_thread(_job_queue_t * jq)514 static int _stop_status_thread(_job_queue_t *jq) {
515     if (!pthread_equal(_job_status_tid, pthread_self()) && (jq && jq->status_ifc)) {
516         (jq->status_ifc->stop)(jq->status_ifc);
517         _unlock();
518         pthread_join(_job_status_tid, 0);
519         _lock();
520         _job_status_tid = pthread_self();
521         return OK;
522     } else {
523         return ERROR;
524     }
525 }
526 
527 /*
528  * Helper function to send job status callbacks to wprintJNI
529  */
_send_status_callback(_job_queue_t * jq,wprint_job_callback_params_t cb_param,int state,unsigned int blocked_reasons,int job_done_result)530 static void _send_status_callback(_job_queue_t *jq, wprint_job_callback_params_t cb_param,
531                                   int state, unsigned int blocked_reasons, int job_done_result) {
532     if (jq->cb_fn) {
533         cb_param.id = WPRINT_CB_PARAM_JOB_STATE;
534         cb_param.param.state = state;
535         cb_param.blocked_reasons = blocked_reasons;
536         cb_param.job_done_result = job_done_result;
537         jq->cb_fn(jq->job_handle, (void *) &cb_param);
538     }
539 }
540 
541 /*
542  * Handles a new status message from the printer. Based on the status of wprint and the printer,
543  * this function will start/end a job, send another page, or return blocking errors.
544  */
_job_status_callback(const printer_state_dyn_t * new_status,const printer_state_dyn_t * old_status,void * param)545 static void _job_status_callback(const printer_state_dyn_t *new_status,
546         const printer_state_dyn_t *old_status, void *param) {
547     wprint_job_callback_params_t cb_param;
548     _job_queue_t *jq = (_job_queue_t *) param;
549     unsigned int i, blocked_reasons;
550     print_status_t statusnew, statusold;
551 
552     statusnew = new_status->printer_status & ~PRINTER_IDLE_BIT;
553     statusold = old_status->printer_status & ~PRINTER_IDLE_BIT;
554     cb_param.certificate = jq->certificate;
555     cb_param.certificate_len = jq->certificate_len;
556 
557     LOGD("_job_status_callback(): current printer state: %d", statusnew);
558     blocked_reasons = 0;
559     for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
560         if (new_status->printer_reasons[i] == PRINT_STATUS_MAX_STATE) {
561             break;
562         }
563         LOGD("_job_status_callback(): blocking reason %d: %d", i, new_status->printer_reasons[i]);
564         blocked_reasons |= (1 << new_status->printer_reasons[i]);
565     }
566 
567     switch (statusnew) {
568         case PRINT_STATUS_UNKNOWN:
569             if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE)
570                     || (new_status->printer_reasons[0] == PRINT_STATUS_UNKNOWN)) {
571                 sem_post(&_job_start_wait_sem);
572                 sem_post(&_job_end_wait_sem);
573                 _lock();
574                 if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE)
575                         && ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL))) {
576                     jq->print_ifc->enable_timeout(jq->print_ifc, 1);
577                 }
578                 _unlock();
579             }
580             break;
581 
582         case PRINT_STATUS_IDLE:
583             if ((statusold > PRINT_STATUS_IDLE) || (statusold == PRINT_STATUS_CANCELLED)) {
584                 // Print is over but the job wasn't ended correctly
585                 if (jq->is_dir && !jq->last_page_seen) {
586                     wprintPage(jq->job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0);
587                 }
588                 sem_post(&_job_end_wait_sem);
589             }
590             break;
591 
592         case PRINT_STATUS_PRINTING:
593             // print job is unblocked but job-id is not generated
594             if (new_status->job_id == -1) {
595                 sem_post(&_job_start_wait_sem);
596                 _lock();
597                 if ((jq->job_state != JOB_STATE_RUNNING) ||
598                     (jq->blocked_reasons != blocked_reasons)) {
599                     jq->job_state = JOB_STATE_RUNNING;
600                     jq->blocked_reasons = blocked_reasons;
601                     _send_status_callback(jq, cb_param, JOB_RUNNING, jq->blocked_reasons, OK);
602                 }
603                 _unlock();
604             }
605             break;
606 
607         /* all is well, handled in job status */
608         case PRINT_STATUS_CANCELLED:
609         /* all is well, handled in job status */
610         case PRINT_STATUS_UNABLE_TO_CONNECT:
611             break;
612 
613         default:
614             // an error has occurred, report it back to the client
615             sem_post(&_job_start_wait_sem);
616             _lock();
617 
618             if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->blocked_reasons != blocked_reasons)) {
619                 jq->job_state = JOB_STATE_BLOCKED;
620                 jq->blocked_reasons = blocked_reasons;
621                 // print job is blocked at the initial stage and job-id is not generated
622                 if (new_status->job_id == -1) {
623                     _send_status_callback(jq, cb_param, JOB_BLOCKED, blocked_reasons, OK);
624                 }
625             }
626             _unlock();
627             break;
628     }
629 }
630 
631 /*
632  * Callback after getting the print job state
633  */
_print_job_state_callback(const job_state_dyn_t * new_state,void * param)634 static void _print_job_state_callback(const job_state_dyn_t *new_state, void *param) {
635     wprint_job_callback_params_t cb_param = {};
636     _job_queue_t *jq = (_job_queue_t *) param;
637     unsigned long long blocked_reasons = 0;
638     int i;
639 
640     cb_param.certificate = jq->certificate;
641     cb_param.certificate_len = jq->certificate_len;
642 
643     LOGI("_print_job_state_callback(): new state: %d", new_state->job_state);
644     for (i = 0; i <= IPP_JOB_STATE_REASON_MAX_VALUE; i++) {
645         if (new_state->job_state_reasons[i] == IPP_JOB_STATE_REASON_MAX_VALUE)
646             break;
647         LOGD("_print_job_state_callback(): blocking reason %d: %d", i,
648              new_state->job_state_reasons[i]);
649         blocked_reasons |= (LONG_ONE << new_state->job_state_reasons[i]);
650     }
651 
652     switch (new_state->job_state) {
653         case IPP_JOB_STATE_UNABLE_TO_CONNECT:
654             sem_post(&_job_start_wait_sem);
655             _lock();
656             jq->job_state = JOB_STATE_ERROR;
657             jq->blocked_reasons = blocked_reasons;
658             _unlock();
659             sem_post(&_job_end_wait_sem);
660             break;
661 
662         case IPP_JOB_STATE_UNKNOWN:
663         case IPP_JOB_STATE_PENDING:
664         case IPP_JOB_STATE_PENDING_HELD:
665             break;
666 
667         case IPP_JOB_STATE_PROCESSING:
668             sem_post(&_job_start_wait_sem);
669             // clear errors
670             _lock();
671             if (jq->job_state != JOB_STATE_RUNNING) {
672                 jq->job_state = JOB_STATE_RUNNING;
673                 _send_status_callback(jq, cb_param, JOB_RUNNING, 0, OK);
674             }
675             _unlock();
676             break;
677 
678         case IPP_JOB_STATE_PROCESSING_STOPPED:
679             if (jq->job_state == JOB_STATE_BLOCKED) {
680                 _send_status_callback(jq, cb_param, JOB_BLOCKED, jq->blocked_reasons, OK);
681             }
682             break;
683 
684         case IPP_JOB_STATE_CANCELED:
685             sem_post(&_job_start_wait_sem);
686             sem_post(&_job_end_wait_sem);
687             if ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL)) {
688                 jq->print_ifc->enable_timeout(jq->print_ifc, 1);
689             }
690             _lock();
691             jq->job_params.cancelled = true;
692             jq->blocked_reasons = blocked_reasons;
693             _unlock();
694             break;
695 
696         case IPP_JOB_STATE_ABORTED:
697             sem_post(&_job_start_wait_sem);
698             sem_post(&_job_end_wait_sem);
699             _lock();
700             jq->job_state = JOB_STATE_ERROR;
701             jq->blocked_reasons = blocked_reasons;
702             _unlock();
703             break;
704 
705         case IPP_JOB_STATE_COMPLETED:
706             sem_post(&_job_end_wait_sem);
707             break;
708 
709         default:
710             break;
711     }
712 }
713 
_job_status_thread(void * param)714 static void *_job_status_thread(void *param) {
715     _job_queue_t *jq = (_job_queue_t *) param;
716     (jq->status_ifc->start)(jq->status_ifc, _job_status_callback, _print_job_state_callback, param);
717     return NULL;
718 }
719 
_start_status_thread(_job_queue_t * jq)720 static int _start_status_thread(_job_queue_t *jq) {
721     sigset_t allsig, oldsig;
722     int result = ERROR;
723 
724     if ((jq == NULL) || (jq->status_ifc == NULL)) {
725         return result;
726     }
727 
728     result = OK;
729     sigfillset(&allsig);
730 #if CHECK_PTHREAD_SIGMASK_STATUS
731     result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
732 #else // else CHECK_PTHREAD_SIGMASK_STATUS
733     pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
734 #endif // CHECK_PTHREAD_SIGMASK_STATUS
735     if (result == OK) {
736         result = pthread_create(&_job_status_tid, 0, _job_status_thread, jq);
737         if ((result == ERROR) && (_job_status_tid != pthread_self())) {
738 #if USE_PTHREAD_CANCEL
739             pthread_cancel(_job_status_tid);
740 #else // else USE_PTHREAD_CANCEL
741             pthread_kill(_job_status_tid, SIGKILL);
742 #endif // USE_PTHREAD_CANCEL
743             _job_status_tid = pthread_self();
744         }
745     }
746 
747     if (result == OK) {
748         sched_yield();
749 #if CHECK_PTHREAD_SIGMASK_STATUS
750         result = pthread_sigmask(SIG_SETMASK, &oldsig, 0);
751 #else // else CHECK_PTHREAD_SIGMASK_STATUS
752         pthread_sigmask(SIG_SETMASK, &oldsig, 0);
753 #endif // CHECK_PTHREAD_SIGMASK_STATUS
754     }
755     return result;
756 }
757 
758 /*
759  * Return true unless the server gave an unexpected certificate
760  */
_is_certificate_allowed(_job_queue_t * jq)761 static bool _is_certificate_allowed(_job_queue_t *jq) {
762     int result = true;
763 
764     // Compare certificates if both are known
765     if (jq->job_params.certificate && jq->certificate) {
766         if (jq->job_params.certificate_len != jq->certificate_len) {
767             LOGD("_is_certificate_allowed: certificate length mismatch allowed=%d, received=%d",
768                 jq->job_params.certificate_len, jq->certificate_len);
769             result = false;
770         } else if (0 != memcmp(jq->job_params.certificate, jq->certificate, jq->certificate_len)) {
771             LOGD("_is_certificate_allowed: certificate content mismatch");
772             result = false;
773         } else {
774             LOGD("_is_certificate_allowed: certificate match, len=%d",
775                 jq->job_params.certificate_len);
776         }
777     }
778 
779     return result;
780 }
781 
782 /*
783  * Callback from lower layers containing certificate data, if any.
784  */
_validate_certificate(wprint_connect_info_t * connect_info,uint8 * data,int data_len)785 static int _validate_certificate(wprint_connect_info_t *connect_info, uint8 *data, int data_len) {
786     _job_queue_t *jq = connect_info->user;
787     LOGD("_validate_certificate: %s://%s:%d%s handling server cert len=%d for job %ld",
788         connect_info->uri_scheme, connect_info->printer_addr, connect_info->port_num,
789         connect_info->uri_path, data_len, jq->job_handle);
790 
791     // Free any old certificate we have and save new certificate data
792     if (jq->certificate) {
793         free(jq->certificate);
794         jq->certificate = NULL;
795     }
796     jq->certificate = (uint8 *)malloc(data_len);
797     int error = 0;
798     if (jq->certificate == NULL) {
799         LOGD("_validate_certificate: malloc failed");
800         error = -1;
801     } else {
802         memcpy(jq->certificate, data, data_len);
803         jq->certificate_len = data_len;
804         if (!_is_certificate_allowed(jq)) {
805             LOGD("_validate_certificate: received certificate disallowed.");
806             error = -1;
807         }
808     }
809     return error;
810 }
811 
812 /*
813  * Initialize the status interface (so we can use it to query for printer status.
814  */
_initialize_status_ifc(_job_queue_t * jq)815 static void _initialize_status_ifc(_job_queue_t *jq) {
816     wprint_connect_info_t connect_info;
817     connect_info.printer_addr = jq->printer_addr;
818     connect_info.uri_path = jq->printer_uri;
819     connect_info.port_num = jq->port_num;
820     if (jq->use_secure_uri) {
821         connect_info.uri_scheme = IPPS_PREFIX;
822         connect_info.user = jq;
823         connect_info.validate_certificate = _validate_certificate;
824     } else {
825         connect_info.uri_scheme = IPP_PREFIX;
826         connect_info.validate_certificate = NULL;
827     }
828     connect_info.timeout = DEFAULT_IPP_TIMEOUT;
829     connect_info.requesting_user_name = jq->job_params.job_originating_user_name;
830 
831     // Initialize the status interface with this connection info
832     jq->status_ifc->init(jq->status_ifc, &connect_info);
833 }
834 
835 /*
836  * Runs a print job. Contains logic for what to do given different printer statuses.
837  */
_job_thread(void * param)838 static void *_job_thread(void *param) {
839     wprint_job_callback_params_t cb_param = {};
840     _msg_t msg;
841     wJob_t job_handle;
842     _job_queue_t *jq;
843     _page_t page;
844     int i;
845     status_t job_result;
846     int corrupted = 0;
847     printer_capabilities_t printer_caps;
848 
849     while (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), WAIT_FOREVER)) {
850         if (msg.id == MSG_RUN_JOB) {
851             LOGI("_job_thread(): Received message: MSG_RUN_JOB");
852         } else {
853             LOGI("_job_thread(): Received message: MSG_QUIT");
854         }
855 
856         if (msg.id == MSG_QUIT) {
857             break;
858         }
859 
860         job_handle = msg.job_id;
861 
862         //  check if this is a valid job_handle that is still active
863         _lock();
864 
865         jq = _get_job_desc(job_handle);
866 
867         //  set state to running and invoke the plugin, there is one
868         if (jq) {
869             if (jq->job_state != JOB_STATE_QUEUED) {
870                 _unlock();
871                 continue;
872             }
873             corrupted = 0;
874             job_result = OK;
875             jq->job_params.plugin_data = NULL;
876 
877             // clear out the semaphore just in case
878             while (sem_trywait(&_job_start_wait_sem) == OK) {
879             }
880             while (sem_trywait(&_job_end_wait_sem) == OK) {
881             }
882 
883             // initialize the status ifc
884             if (jq->status_ifc != NULL) {
885                 _initialize_status_ifc(jq);
886             }
887             // wait for the printer to be idle
888             if ((jq->status_ifc != NULL) && (jq->status_ifc->get_status != NULL)) {
889                 int retry = 0;
890                 bool idle = false;
891                 bool bad_certificate = false;
892                 printer_state_dyn_t printer_state;
893                 while (!idle && !stop_run) {
894                     print_status_t status;
895                     jq->status_ifc->get_status(jq->status_ifc, &printer_state);
896                     status = printer_state.printer_status & ~PRINTER_IDLE_BIT;
897 
898                     // Pass along any certificate received in future callbacks
899                     cb_param.certificate = jq->certificate;
900                     cb_param.certificate_len = jq->certificate_len;
901 
902                     // Presume we found an idle state
903                     idle = true;
904                     if (status == PRINT_STATUS_IDLE) {
905                         printer_state.printer_status = PRINT_STATUS_IDLE;
906                         jq->blocked_reasons = 0;
907                     } else if (status == PRINT_STATUS_UNKNOWN
908                             && printer_state.printer_reasons[0] == PRINT_STATUS_UNKNOWN) {
909                         // no status available, break out and hope for the best
910                         printer_state.printer_status = PRINT_STATUS_IDLE;
911                     } else if ((status == PRINT_STATUS_UNKNOWN || status == PRINT_STATUS_SVC_REQUEST)
912                             && ((printer_state.printer_reasons[0] == PRINT_STATUS_UNABLE_TO_CONNECT)
913                                 || (printer_state.printer_reasons[0] == PRINT_STATUS_OFFLINE))) {
914                         if (_is_certificate_allowed(jq)) {
915                             LOGD("%s: Received an Unable to Connect message", __func__);
916                             jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT;
917                         } else {
918                             LOGD("%s: Bad certificate", __func__);
919                             bad_certificate = true;
920                         }
921                     } else if (printer_state.printer_status & PRINTER_IDLE_BIT) {
922                         LOGD("%s: printer blocked but appears to be in an idle state. "
923                                 "Allowing job to proceed", __func__);
924                         printer_state.printer_status = PRINT_STATUS_IDLE;
925                     } else if (retry >= MAX_IDLE_WAIT) {
926                         jq->blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY;
927                     } else if (!jq->job_params.cancelled) {
928                         // Printer still appears busy, so stay in loop, notify, and poll again.
929                         idle = false;
930                         int blocked_reasons = 0;
931                         for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
932                             if (printer_state.printer_reasons[i] == PRINT_STATUS_MAX_STATE) {
933                                 break;
934                             }
935                             blocked_reasons |= (LONG_ONE << printer_state.printer_reasons[i]);
936                         }
937                         if (blocked_reasons == 0) {
938                             blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY;
939                         }
940 
941                         if ((jq->job_state != JOB_STATE_BLOCKED)
942                                 || (jq->blocked_reasons != blocked_reasons)) {
943                             jq->job_state = JOB_STATE_BLOCKED;
944                             jq->blocked_reasons = blocked_reasons;
945                             _send_status_callback(jq, cb_param, JOB_BLOCKED, blocked_reasons, OK);
946                         }
947                         _unlock();
948                         sleep(1);
949                         _lock();
950                         retry++;
951                     }
952                 }
953 
954                 if (jq->job_params.cancelled) {
955                     job_result = CANCELLED;
956                 } else if (bad_certificate) {
957                     job_result = BAD_CERTIFICATE;
958                 } else {
959                     if (stop_run) {
960                         jq->job_state = JOB_STATE_ERROR;
961                         job_result = ERROR;
962                     } else {
963                         job_result = (((printer_state.printer_status & ~PRINTER_IDLE_BIT) ==
964                                        PRINT_STATUS_IDLE) ? OK : ERROR);
965                     }
966                 }
967             }
968 
969             if (job_result == OK) {
970                 if (jq->print_ifc) {
971                     job_result = jq->print_ifc->init(jq->print_ifc, jq->printer_addr,
972                             jq->port_num, jq->printer_uri, jq->use_secure_uri);
973                     if (job_result == ERROR) {
974                         jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT;
975                     }
976                 }
977             }
978 
979             /*  call the plugin's start_job method, if no other job is running
980              use callback to notify the client */
981 
982             if (job_result == OK) {
983                 _send_status_callback(jq, cb_param, JOB_RUNNING, 0, OK);
984             }
985 
986             memcpy(&printer_caps, &g_printer_caps, sizeof(printer_capabilities_t));
987 
988             jq->job_params.page_num = -1;
989             if (job_result == OK) {
990                 if (jq->print_ifc != NULL) {
991                     LOGD("_job_thread: Calling validate_job");
992                     if (jq->print_ifc->validate_job != NULL) {
993                         job_result = jq->print_ifc->validate_job(jq->print_ifc, &jq->job_params,
994                                 &printer_caps);
995                     }
996                     if (!_is_certificate_allowed(jq)) {
997                         LOGD("_job_thread: bad certificate found at validate job");
998                         job_result = BAD_CERTIFICATE;
999                     }
1000                     /* PDF format plugin's start_job and end_job are to be called for each copy,
1001                      * inside the for-loop for num_copies.
1002                      */
1003                     if (job_result == OK) {
1004                         _job_status_tid = pthread_self();
1005                         _start_status_thread(jq);
1006                     }
1007 
1008                     // Do not call start_job unless validate_job returned OK
1009                     if ((job_result == OK) && (jq->print_ifc->start_job != NULL) &&
1010                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) {
1011                         jq->print_ifc->start_job(jq->print_ifc, &jq->job_params, &printer_caps);
1012                     }
1013                 }
1014 
1015                 // Do not call start_job unless validate_job returned OK
1016                 if (job_result == OK && jq->plugin->start_job != NULL) {
1017                     job_result = jq->plugin->start_job(job_handle, (void *) &_wprint_ifc,
1018                             (void *) jq->print_ifc, &(jq->job_params));
1019                 }
1020             }
1021 
1022             if (job_result == OK) {
1023                 jq->job_params.page_num = 0;
1024             }
1025 
1026             // multi-page print job
1027             if (jq->is_dir && (job_result == OK)) {
1028                 int per_copy_page_num;
1029                 for (i = 0; (i < jq->job_params.num_copies) &&
1030                         ((job_result == OK) || (job_result == CORRUPT)) &&
1031                         (!jq->job_params.cancelled); i++) {
1032                     if ((i > 0) &&
1033                             jq->job_params.copies_supported &&
1034                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) {
1035                         LOGD("_job_thread multi_page: breaking out copies supported");
1036                         break;
1037                     }
1038                     bool pdf_printed = false;
1039                     if (jq->print_ifc->start_job != NULL &&
1040                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) {
1041                         jq->print_ifc->start_job(jq->print_ifc, &jq->job_params, &printer_caps);
1042                     }
1043 
1044                     per_copy_page_num = 0;
1045                     jq->job_state = JOB_STATE_RUNNING;
1046 
1047                     // while there is a page to print
1048                     _unlock();
1049 
1050                     while (OK == msgQReceive(jq->pageQ, (char *) &page, sizeof(page),
1051                             WAIT_FOREVER)) {
1052                         _lock();
1053 
1054                         // check for any printing problems so far
1055                         if (jq->print_ifc->check_status) {
1056                             if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) {
1057                                 job_result = ERROR;
1058                                 break;
1059                             }
1060                         }
1061 
1062                         /* take empty filename as cue to break out of the loop
1063                          * but we have to do last_page processing
1064                          */
1065 
1066                         // all copies are clubbed together as a single print job
1067                         if (page.last_page && ((i == jq->job_params.num_copies - 1) ||
1068                                 (jq->job_params.copies_supported &&
1069                                         strcmp(jq->job_params.print_format,
1070                                                 PRINT_FORMAT_PDF) == 0))) {
1071                             jq->job_params.last_page = page.last_page;
1072                         } else {
1073                             jq->job_params.last_page = false;
1074                         }
1075 
1076                         bool printBlankPage = (strcmp(jq->job_params.print_format,
1077                                 PRINT_FORMAT_PCLM) == 0) ? wprintBlankPageForPclm(
1078                                 &jq->job_params, &printer_caps) : wprintBlankPageForPwg(
1079                                 &jq->job_params, &printer_caps);
1080 
1081                         printBlankPage &= (jq->plugin->print_blank_page != NULL);
1082 
1083                         if (strlen(page.filename) > 0) {
1084                             per_copy_page_num++;
1085                             {
1086                                 jq->job_params.page_num++;
1087                             }
1088                             if (page.pdf_page) {
1089                                 jq->job_params.page_num = page.page_num;
1090                             } else {
1091                                 jq->job_params.page_num = per_copy_page_num;
1092                             }
1093 
1094                             // setup page margin information
1095                             jq->job_params.print_top_margin += page.top_margin;
1096                             jq->job_params.print_left_margin += page.left_margin;
1097                             jq->job_params.print_right_margin += page.right_margin;
1098                             jq->job_params.print_bottom_margin += page.bottom_margin;
1099 
1100                             jq->job_params.copy_num = (i + 1);
1101                             jq->job_params.copy_page_num = page.page_num;
1102                             jq->job_params.page_backside = !(page.page_num & 0x1);
1103                             jq->job_params.page_corrupted = (page.corrupted ? 1 : 0);
1104                             jq->job_params.page_printing = true;
1105                             _unlock();
1106 
1107                             if (!page.corrupted) {
1108                                 LOGD("_job_thread(): page not corrupt, calling plugin's print_page"
1109                                         " function for page #%d", page.page_num);
1110 
1111                                 // make sure we always print an even number of pages in duplex jobs
1112                                 if ((page.page_num == jq->job_params.job_pages_per_set) &&
1113                                         !(jq->job_params.face_down_tray) && printBlankPage) {
1114                                     jq->plugin->print_blank_page(job_handle, &(jq->job_params),
1115                                             jq->mime_type, page.filename);
1116                                 }
1117 
1118                                 if (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0) {
1119                                     job_result = jq->plugin->print_page(&(jq->job_params),
1120                                             jq->mime_type,
1121                                             page.filename);
1122                                 } else if (!pdf_printed) {
1123                                     // for PDF plugin, print_page prints entire document,
1124                                     // so need to be called only once
1125                                     job_result = jq->plugin->print_page(&(jq->job_params),
1126                                             jq->mime_type,
1127                                             page.filename);
1128                                     pdf_printed = true;
1129                                 }
1130                             } else {
1131                                 LOGD("_job_thread(): page IS corrupt, printing blank page for "
1132                                         "page #%d", page.page_num);
1133                                 job_result = CORRUPT;
1134                                 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) &&
1135                                         (jq->plugin->print_blank_page != NULL)) {
1136                                     jq->plugin->print_blank_page(job_handle, &(jq->job_params),
1137                                             jq->mime_type, page.filename);
1138                                 }
1139                             }
1140                             _lock();
1141 
1142                             jq->job_params.print_top_margin -= page.top_margin;
1143                             jq->job_params.print_left_margin -= page.left_margin;
1144                             jq->job_params.print_right_margin -= page.right_margin;
1145                             jq->job_params.print_bottom_margin -= page.bottom_margin;
1146                             jq->job_params.page_printing = false;
1147 
1148                             // make sure we only count corrupted pages once
1149                             if (page.corrupted == false) {
1150                                 page.corrupted = ((job_result == CORRUPT) ? true : false);
1151                                 corrupted += (job_result == CORRUPT);
1152                             }
1153                         }
1154 
1155                         // make sure we always print an even number of pages in duplex jobs
1156                         if (page.last_page && (jq->job_params.face_down_tray) &&
1157                                 !(jq->job_params.page_backside) && printBlankPage) {
1158                             _unlock();
1159                             jq->plugin->print_blank_page(job_handle, &(jq->job_params),
1160                                     jq->mime_type, page.filename);
1161                             _lock();
1162                         }
1163 
1164                         // if multiple copies are requested, save the contents of the pageQ message
1165                         if (jq->saveQ && !jq->job_params.cancelled && (job_result != ERROR)) {
1166                             job_result = msgQSend(jq->saveQ, (char *) &page,
1167                                     sizeof(page), NO_WAIT, MSG_Q_FIFO);
1168 
1169                             // swap pageQ and saveQ
1170                             if (page.last_page && !jq->job_params.last_page) {
1171                                 msg_q_id tmpQ = jq->pageQ;
1172                                 jq->pageQ = jq->saveQ;
1173                                 jq->saveQ = tmpQ;
1174 
1175                                 // defensive programming
1176                                 while (msgQNumMsgs(tmpQ) > 0) {
1177                                     msgQReceive(tmpQ, (char *) &page, sizeof(page), NO_WAIT);
1178                                     LOGE("pageQ inconsistencies, discarding page #%d, file %s",
1179                                             page.page_num, page.filename);
1180                                 }
1181                             }
1182                         }
1183 
1184                         if (page.last_page || jq->job_params.cancelled) {
1185                             // Leave the sempahore locked
1186                             break;
1187                         }
1188 
1189                         // unlock to go back to the top of the while loop
1190                         _unlock();
1191                     } // while there is another page
1192 
1193                     if ((strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0) &&
1194                             (jq->print_ifc->end_job)) {
1195                         int end_job_result = jq->print_ifc->end_job(jq->print_ifc);
1196                         if (job_result == OK) {
1197                             if (end_job_result == ERROR) {
1198                                 job_result = ERROR;
1199                             } else if (end_job_result == CANCELLED) {
1200                                 job_result = CANCELLED;
1201                             }
1202                         }
1203                     }
1204                 } // for each copy of the job
1205             } else if (job_result == OK) {
1206                 // single page job
1207                 for (i = 0; ((i < jq->job_params.num_copies) && (job_result == OK)); i++) {
1208                     if ((i > 0) && jq->job_params.copies_supported &&
1209                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) {
1210                         LOGD("_job_thread single_page: breaking out copies supported");
1211                         break;
1212                     }
1213 
1214                     // check for any printing problems so far
1215                     if ((jq->print_ifc != NULL) && (jq->print_ifc->check_status)) {
1216                         if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) {
1217                             job_result = ERROR;
1218                             break;
1219                         }
1220                     }
1221 
1222                     jq->job_state = JOB_STATE_RUNNING;
1223                     jq->job_params.page_num++;
1224                     jq->job_params.last_page = (i == (jq->job_params.num_copies - 1));
1225                     jq->job_params.copy_num = (i + 1);
1226                     jq->job_params.copy_page_num = 1;
1227                     jq->job_params.page_corrupted = (job_result == CORRUPT);
1228                     jq->job_params.page_printing = true;
1229 
1230                     _unlock();
1231                     job_result = jq->plugin->print_page(&(jq->job_params), jq->mime_type,
1232                             jq->pathname);
1233 
1234                     if ((jq->job_params.duplex != DUPLEX_MODE_NONE)
1235                             && (jq->plugin->print_blank_page != NULL)) {
1236                         jq->plugin->print_blank_page(job_handle,
1237                                 &(jq->job_params), jq->mime_type, page.filename);
1238                     }
1239 
1240                     _lock();
1241                     jq->job_params.page_printing = false;
1242 
1243                     corrupted += (job_result == CORRUPT);
1244                 } // for each copy
1245             }
1246 
1247             // if we started the job end it
1248             if (jq->job_params.page_num >= 0) {
1249                 // if the job was cancelled without sending anything through, print a blank sheet
1250                 if ((jq->job_params.page_num == 0) && (jq->plugin->print_blank_page != NULL)) {
1251                     jq->plugin->print_blank_page(job_handle, &(jq->job_params), jq->mime_type,
1252                             page.filename);
1253                 }
1254                 if (jq->plugin->end_job != NULL) {
1255                     jq->plugin->end_job(&(jq->job_params));
1256                 }
1257                 if ((jq->print_ifc != NULL) && (jq->print_ifc->end_job) &&
1258                         (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) {
1259                     _unlock();
1260                     int end_job_result = jq->print_ifc->end_job(jq->print_ifc);
1261                     if (job_result == OK) {
1262                         if (end_job_result == ERROR) {
1263                             job_result = ERROR;
1264                         } else if (end_job_result == CANCELLED) {
1265                             job_result = CANCELLED;
1266                         }
1267                     }
1268                     _lock();
1269                 }
1270             }
1271 
1272             // if we started to print, wait for idle
1273             if ((jq->job_params.page_num > 0) && (jq->status_ifc != NULL)) {
1274                 int retry, result;
1275                 _unlock();
1276 
1277                 for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_START_WAIT));
1278                         retry++) {
1279                     if (retry != 0) {
1280                         sleep(1);
1281                     }
1282                     result = sem_trywait(&_job_start_wait_sem);
1283                 }
1284 
1285                 if (result == OK) {
1286                     for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_DONE_WAIT));
1287                             retry++) {
1288                         if (retry != 0) {
1289                             _lock();
1290                             if (jq->job_params.cancelled && !jq->cancel_ok) {
1291                                 /* The user tried to cancel and it either didn't go through
1292                                  * or the printer doesn't support cancel through an OID.
1293                                  * Either way it's pointless to sit here waiting for idle when
1294                                  * may never come, so we'll bail out early
1295                                  */
1296                                 retry = (MAX_DONE_WAIT + 1);
1297                             }
1298                             _unlock();
1299                             sleep(1);
1300                             if (retry == MAX_DONE_WAIT) {
1301                                 _lock();
1302                                 if (!jq->job_params.cancelled &&
1303                                         (jq->blocked_reasons
1304                                                 & (BLOCKED_REASON_OUT_OF_PAPER
1305                                                         | BLOCKED_REASON_JAMMED
1306                                                         | BLOCKED_REASON_DOOR_OPEN))) {
1307                                     retry = (MAX_DONE_WAIT - 1);
1308                                 }
1309                                 _unlock();
1310                             }
1311                         }
1312                         result = sem_trywait(&_job_end_wait_sem);
1313                     }
1314                 } else {
1315                     LOGD("_job_thread(): the job never started");
1316                 }
1317                 _lock();
1318             }
1319 
1320             // make sure page_num doesn't stay as a negative number
1321             jq->job_params.page_num = MAX(0, jq->job_params.page_num);
1322             _stop_status_thread(jq);
1323 
1324             if (corrupted != 0) {
1325                 job_result = CORRUPT;
1326             }
1327 
1328             LOGI("job_thread(): with job_state value: %d ", jq->job_state);
1329             if (jq->job_state == JOB_STATE_ERROR) {
1330                 job_result = ERROR;
1331                 LOGI("_job_thread(): job finished early: do not send callback again");
1332             } else {
1333                 switch (job_result) {
1334                     case OK:
1335                         if (!jq->job_params.cancelled) {
1336                             jq->job_state = JOB_STATE_COMPLETED;
1337                             jq->blocked_reasons = 0;
1338                             break;
1339                         } else {
1340                             job_result = CANCELLED;
1341                         }
1342                     case CANCELLED:
1343                         jq->job_state = JOB_STATE_CANCELLED;
1344                         jq->blocked_reasons = BLOCKED_REASONS_CANCELLED;
1345                         if (!jq->cancel_ok) {
1346                             jq->blocked_reasons |= BLOCKED_REASON_PARTIAL_CANCEL;
1347                         }
1348                         break;
1349                     case CORRUPT:
1350                         LOGE("_job_thread(): %d file(s) in the job were corrupted", corrupted);
1351                         jq->job_state = JOB_STATE_CORRUPTED;
1352                         jq->blocked_reasons = 0;
1353                         break;
1354                     case BAD_CERTIFICATE:
1355                         LOGD("_job_thread(): BAD_CERTIFICATE");
1356                         jq->job_state = JOB_STATE_BAD_CERTIFICATE;
1357                         jq->blocked_reasons = 0;
1358                         break;
1359                     case ERROR:
1360                     default:
1361                         LOGE("_job_thread(): ERROR plugin->start_job(%ld): %s => %s", job_handle,
1362                                 jq->mime_type, jq->job_params.print_format);
1363                         job_result = ERROR;
1364                         jq->job_state = JOB_STATE_ERROR;
1365                         break;
1366                 } // job_result
1367             }
1368 
1369             // end of job callback
1370             _send_status_callback(jq, cb_param, JOB_DONE, jq->blocked_reasons, job_result);
1371 
1372             if (jq->print_ifc != NULL) {
1373                 jq->print_ifc->destroy(jq->print_ifc);
1374                 jq->print_ifc = NULL;
1375             }
1376 
1377             if (jq->status_ifc != NULL) {
1378                 jq->status_ifc->destroy(jq->status_ifc);
1379                 jq->status_ifc = NULL;
1380             }
1381         } else {
1382             LOGI("_job_thread(): job %ld not in queue .. maybe cancelled", job_handle);
1383         }
1384 
1385         _unlock();
1386         LOGI("_job_thread(): job finished: %ld", job_handle);
1387     }
1388 
1389     sem_post(&_job_end_wait_sem);
1390     return NULL;
1391 }
1392 
1393 /*
1394  * Starts the wprint background job thread
1395  */
_start_thread(void)1396 static int _start_thread(void) {
1397     sigset_t allsig, oldsig;
1398     int result;
1399 
1400     _job_tid = pthread_self();
1401 
1402     result = OK;
1403     stop_run = false;
1404     sigfillset(&allsig);
1405 #if CHECK_PTHREAD_SIGMASK_STATUS
1406     result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
1407 #else // else CHECK_PTHREAD_SIGMASK_STATUS
1408     pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
1409 #endif // CHECK_PTHREAD_SIGMASK_STATUS
1410     if (result == OK) {
1411         result = pthread_create(&_job_tid, 0, _job_thread, NULL);
1412         if ((result == ERROR) && (_job_tid != pthread_self())) {
1413 #if USE_PTHREAD_CANCEL
1414             pthread_cancel(_job_tid);
1415 #else // else USE_PTHREAD_CANCEL
1416             pthread_kill(_job_tid, SIGKILL);
1417 #endif // USE_PTHREAD_CANCEL
1418             _job_tid = pthread_self();
1419         }
1420     }
1421 
1422     if (result == OK) {
1423         sched_yield();
1424 #if CHECK_PTHREAD_SIGMASK_STATUS
1425         result = pthread_sigmask(SIG_SETMASK, &oldsig, 0);
1426 #else // else CHECK_PTHREAD_SIGMASK_STATUS
1427         pthread_sigmask(SIG_SETMASK, &oldsig, 0);
1428 #endif // CHECK_PTHREAD_SIGMASK_STATUS
1429     }
1430 
1431     return result;
1432 }
1433 
1434 /*
1435  * Waits for the job thread to reach a stopped state
1436  */
_stop_thread(void)1437 static int _stop_thread(void) {
1438     stop_run = true;
1439     if (!pthread_equal(_job_tid, pthread_self())) {
1440         pthread_join(_job_tid, 0);
1441         _job_tid = pthread_self();
1442         return OK;
1443     } else {
1444         return ERROR;
1445     }
1446 }
1447 
1448 static const wprint_io_plugin_t _file_io_plugin = {
1449         .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION),
1450         .port_num = PORT_FILE, .getCapsIFC = NULL, .getStatusIFC = NULL,
1451         .getPrintIFC = _printer_file_connect,};
1452 
1453 static const wprint_io_plugin_t _ipp_io_plugin = {
1454         .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION),
1455         .port_num = PORT_IPP, .getCapsIFC = ipp_status_get_capabilities_ifc,
1456         .getStatusIFC = ipp_status_get_monitor_ifc, .getPrintIFC = ipp_get_print_ifc,};
1457 
_setup_io_plugins()1458 static void _setup_io_plugins() {
1459     _io_plugins[0].port_num = PORT_FILE;
1460     _io_plugins[0].io_plugin = &_file_io_plugin;
1461 
1462     _io_plugins[1].port_num = PORT_IPP;
1463     _io_plugins[1].io_plugin = &_ipp_io_plugin;
1464 }
1465 
1466 extern wprint_plugin_t *libwprintplugin_pcl_reg(void);
1467 
1468 extern wprint_plugin_t *libwprintplugin_pdf_reg(void);
1469 
_setup_print_plugins()1470 static void _setup_print_plugins() {
1471     plugin_reset();
1472     plugin_add(libwprintplugin_pcl_reg());
1473     plugin_add(libwprintplugin_pdf_reg());
1474 }
1475 
wprintIsRunning()1476 bool wprintIsRunning() {
1477     return _msgQ != 0;
1478 }
1479 
wprintInit(void)1480 int wprintInit(void) {
1481     int count = 0;
1482 
1483     _setup_print_plugins();
1484     _setup_io_plugins();
1485 
1486     _msgQ = msgQCreate(_MAX_MSGS, sizeof(_msg_t));
1487 
1488     if (!_msgQ) {
1489         LOGE("ERROR: cannot create msgQ");
1490         return ERROR;
1491     }
1492 
1493     sem_init(&_job_end_wait_sem, 0, 0);
1494     sem_init(&_job_start_wait_sem, 0, 0);
1495 
1496     signal(SIGPIPE, SIG_IGN); // avoid broken pipe process shutdowns
1497     pthread_mutexattr_settype(&_q_lock_attr, PTHREAD_MUTEX_RECURSIVE_NP);
1498     pthread_mutex_init(&_q_lock, &_q_lock_attr);
1499     stop_mutex = false;
1500 
1501     if (_start_thread() != OK) {
1502         LOGE("could not start job thread");
1503         return ERROR;
1504     }
1505     return count;
1506 }
1507 
1508 static const printer_capabilities_t _default_cap = {.color = true, .borderless = true,
1509         .numSupportedMediaSizes = 0, .numSupportedMediaTrays = 0,
1510         .numSupportedMediaTypes = 0,};
1511 
1512 /*
1513  * Check if a media size is supported
1514  */
is_supported(media_size_t media_size)1515 static bool is_supported(media_size_t media_size) {
1516     int i;
1517     for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
1518         if (SupportedMediaSizes[i].media_size == media_size) return true;
1519     }
1520     return false;
1521 }
1522 
1523 /*
1524  * Return true if the specified int array of the supplied length contains a value.
1525  */
int_array_contains(const int * array,int length,int value)1526 static bool int_array_contains(const int *array, int length, int value) {
1527     for (int i = 0; i < length; i++) {
1528         if (array[i] == value) return true;
1529     }
1530     return false;
1531 }
1532 
1533 /*
1534  * Checks printers reported media sizes and validates that wprint supports them
1535  */
_validate_supported_media_sizes(printer_capabilities_t * printer_cap)1536 static void _validate_supported_media_sizes(printer_capabilities_t *printer_cap) {
1537     if (printer_cap == NULL) return;
1538 
1539     if (printer_cap->numSupportedMediaSizes == 0) {
1540         unsigned int i = 0;
1541         printer_cap->supportedMediaSizes[i++] = ISO_A4;
1542         printer_cap->supportedMediaSizes[i++] = US_LETTER;
1543         printer_cap->supportedMediaSizes[i++] = INDEX_CARD_4X6;
1544         printer_cap->supportedMediaSizes[i++] = INDEX_CARD_5X7;
1545         printer_cap->numSupportedMediaSizes = i;
1546     } else {
1547         unsigned int read, write;
1548         for (read = write = 0; read < printer_cap->numSupportedMediaSizes; read++) {
1549             if (is_supported(printer_cap->supportedMediaSizes[read])) {
1550                 printer_cap->supportedMediaSizes[write++] =
1551                         printer_cap->supportedMediaSizes[read];
1552             }
1553         }
1554         printer_cap->numSupportedMediaSizes = write;
1555     }
1556 }
1557 
1558 /*
1559  * Checks printers numSupportedMediaTrays. If none, then add Auto.
1560  */
_validate_supported_media_trays(printer_capabilities_t * printer_cap)1561 static void _validate_supported_media_trays(printer_capabilities_t *printer_cap) {
1562     if (printer_cap == NULL) return;
1563 
1564     if (printer_cap->numSupportedMediaTrays == 0) {
1565         printer_cap->supportedMediaTrays[0] = TRAY_SRC_AUTO_SELECT;
1566         printer_cap->numSupportedMediaTrays = 1;
1567     }
1568 }
1569 
1570 /*
1571  * Add a printer's supported input formats to the capabilities struct
1572  */
_collect_supported_input_formats(printer_capabilities_t * printer_caps)1573 static void _collect_supported_input_formats(printer_capabilities_t *printer_caps) {
1574     unsigned long long input_formats = 0;
1575     plugin_get_passthru_input_formats(&input_formats);
1576 
1577     // remove things the printer can't support
1578     if (!printer_caps->canPrintPDF) {
1579         input_formats &= ~(1 << INPUT_MIME_TYPE_PDF);
1580     }
1581     if (!printer_caps->canPrintPCLm) {
1582         input_formats &= ~(1 << INPUT_MIME_TYPE_PCLM);
1583     }
1584     if (!printer_caps->canPrintPWG) {
1585         input_formats &= ~(1 << INPUT_MIME_TYPE_PWG);
1586     }
1587     printer_caps->supportedInputMimeTypes = input_formats;
1588 }
1589 
1590 /*
1591  * Check the print resolutions supported by the printer and verify that wprint supports them.
1592  * If nothing is found, the desired resolution is selected.
1593  */
_findCloseResolutionSupported(int desiredResolution,int maxResolution,const printer_capabilities_t * printer_cap)1594 static unsigned int _findCloseResolutionSupported(int desiredResolution, int maxResolution,
1595         const printer_capabilities_t *printer_cap) {
1596     int closeResolution = 0;
1597     int closeDifference = 0;
1598     unsigned int index = 0;
1599     for (index = 0; index < printer_cap->numSupportedResolutions; index++) {
1600         int resolution = printer_cap->supportedResolutions[index];
1601         if (resolution == desiredResolution) {
1602             // An exact match wins.. stop looking.
1603             return resolution;
1604         } else {
1605             int difference = abs(desiredResolution - resolution);
1606             if ((closeResolution == 0) || (difference < closeDifference)) {
1607                 if (resolution <= maxResolution) {
1608                     // We found a better match now.. record it but keep looking.
1609                     closeResolution = resolution;
1610                     closeDifference = difference;
1611                 }
1612             }
1613         }
1614     }
1615 
1616     // If we get here we did not find an exact match.
1617     if (closeResolution == 0) {
1618         // We did not find anything.. just pick the desired value.
1619         closeResolution = desiredResolution;
1620     }
1621     return closeResolution;
1622 }
1623 
wprintGetCapabilities(const wprint_connect_info_t * connect_info,printer_capabilities_t * printer_cap)1624 status_t wprintGetCapabilities(const wprint_connect_info_t *connect_info,
1625         printer_capabilities_t *printer_cap) {
1626     LOGD("wprintGetCapabilities: Enter");
1627     status_t result = ERROR;
1628     int index;
1629     int port_num = connect_info->port_num;
1630     const ifc_printer_capabilities_t *caps_ifc = NULL;
1631 
1632     memcpy(printer_cap, &_default_cap, sizeof(printer_capabilities_t));
1633 
1634     caps_ifc = _get_caps_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP));
1635     LOGD("wprintGetCapabilities: after getting caps ifc: %p", caps_ifc);
1636     switch (port_num) {
1637         case PORT_FILE:
1638             printer_cap->duplex = 1;
1639             printer_cap->borderless = 1;
1640             printer_cap->canPrintPCLm = (_default_pcl_type == PCLm);
1641             printer_cap->canPrintPWG = (_default_pcl_type == PCLPWG);
1642             printer_cap->stripHeight = STRIPE_HEIGHT;
1643             result = OK;
1644             break;
1645         default:
1646             break;
1647     }
1648 
1649     if (caps_ifc != NULL) {
1650         caps_ifc->init(caps_ifc, connect_info);
1651         result = caps_ifc->get_capabilities(caps_ifc, printer_cap);
1652         caps_ifc->destroy(caps_ifc);
1653     }
1654 
1655     _validate_supported_media_sizes(printer_cap);
1656     _collect_supported_input_formats(printer_cap);
1657     _validate_supported_media_trays(printer_cap);
1658 
1659     printer_cap->isSupported = (printer_cap->canPrintPCLm || printer_cap->canPrintPDF ||
1660             printer_cap->canPrintPWG);
1661 
1662     if (result == OK) {
1663         LOGD("\tmake: %s", printer_cap->make);
1664         LOGD("\thas color: %d", printer_cap->color);
1665         LOGD("\tcan duplex: %d", printer_cap->duplex);
1666         LOGD("\tcan rotate back page: %d", printer_cap->canRotateDuplexBackPage);
1667         LOGD("\tcan print borderless: %d", printer_cap->borderless);
1668         LOGD("\tcan print pdf: %d", printer_cap->canPrintPDF);
1669         LOGD("\tcan print pclm: %d", printer_cap->canPrintPCLm);
1670         LOGD("\tcan print pwg: %d", printer_cap->canPrintPWG);
1671         LOGD("\tsource application name supported: %d", printer_cap->docSourceAppName);
1672         LOGD("\tsource application version supported: %d", printer_cap->docSourceAppVersion);
1673         LOGD("\tsource os name supported: %d", printer_cap->docSourceOsName);
1674         LOGD("\tsource os version supported: %d", printer_cap->docSourceOsVersion);
1675         LOGD("\tprinter supported: %d", printer_cap->isSupported);
1676         LOGD("\tstrip height: %d", printer_cap->stripHeight);
1677         LOGD("\tinkjet: %d", printer_cap->inkjet);
1678         LOGD("\tresolutions supported:");
1679         for (index = 0; index < printer_cap->numSupportedResolutions; index++) {
1680             LOGD("\t (%d dpi)", printer_cap->supportedResolutions[index]);
1681         }
1682     }
1683     LOGD("wprintGetCapabilities: Exit");
1684     return result;
1685 }
1686 
1687 typedef struct _status_handle_st {
1688     const ifc_status_monitor_t *status_ifc;
1689     pthread_t status_tid;
1690 
1691     void (*status_callback)(const printer_state_dyn_t *new_status,
1692                             const printer_state_dyn_t *old_status,
1693                             void *param);
1694 
1695     void *param;
1696 } _status_handle_t;
1697 
wprintStatusMonitorSetup(const wprint_connect_info_t * connect_info)1698 wStatus_t wprintStatusMonitorSetup(const wprint_connect_info_t *connect_info) {
1699     const ifc_status_monitor_t *status_ifc;
1700     int port_num = connect_info->port_num;
1701     _status_handle_t *handle = NULL;
1702     if (connect_info->printer_addr != NULL) {
1703         status_ifc = _get_status_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP));
1704         if (status_ifc != NULL) {
1705             handle = malloc(sizeof(_status_handle_t));
1706             if (handle != NULL) {
1707                 handle->status_ifc = status_ifc;
1708                 handle->status_ifc->init(handle->status_ifc, connect_info);
1709                 handle->status_tid = pthread_self();
1710                 handle->param = NULL;
1711             } else {
1712                 LOGE("wprintStatusMonitorSetup(): failed to allocate memory for status handle");
1713                 status_ifc->destroy(status_ifc);
1714             }
1715         }
1716     }
1717 
1718     return (wStatus_t) handle;
1719 }
1720 
_printer_status_thread(void * param)1721 static void *_printer_status_thread(void *param) {
1722     _status_handle_t *handle = (_status_handle_t *) param;
1723     handle->status_ifc->start(handle->status_ifc, handle->status_callback, NULL, handle->param);
1724     return (NULL);
1725 }
1726 
_start_printer_status_thread(_status_handle_t * handle)1727 static int _start_printer_status_thread(_status_handle_t *handle) {
1728     sigset_t allsig, oldsig;
1729     int result = ERROR;
1730 
1731     if (handle == NULL)
1732         return (result);
1733 
1734     result = OK;
1735     sigfillset(&allsig);
1736 #if CHECK_PTHREAD_SIGMASK_STATUS
1737     result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
1738 #else /* else CHECK_PTHREAD_SIGMASK_STATUS */
1739     pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
1740 #endif /* CHECK_PTHREAD_SIGMASK_STATUS */
1741     if (result == OK) {
1742         result = pthread_create(&handle->status_tid, 0, _printer_status_thread, handle);
1743         if ((result == ERROR) && (handle->status_tid != pthread_self())) {
1744 #if USE_PTHREAD_CANCEL
1745             pthread_cancel(handle->status_tid);
1746 #else /* else USE_PTHREAD_CANCEL */
1747             pthread_kill(handle->status_tid, SIGKILL);
1748 #endif /* USE_PTHREAD_CANCEL */
1749             handle->status_tid = pthread_self();
1750         }
1751     }
1752 
1753     if (result == OK) {
1754         sched_yield();
1755 #if CHECK_PTHREAD_SIGMASK_STATUS
1756         result = pthread_sigmask(SIG_SETMASK, &oldsig, 0);
1757 #else /* else CHECK_PTHREAD_SIGMASK_STATUS */
1758         pthread_sigmask(SIG_SETMASK, &oldsig, 0);
1759 #endif /* CHECK_PTHREAD_SIGMASK_STATUS */
1760     }
1761 
1762     return (result);
1763 } /*  _start_status_thread  */
1764 
wprintStatusMonitorStart(wStatus_t status_handle,void (* status_callback)(const printer_state_dyn_t * new_status,const printer_state_dyn_t * old_status,void * param),void * param)1765 int wprintStatusMonitorStart(wStatus_t status_handle,
1766                              void (*status_callback)(const printer_state_dyn_t *new_status,
1767                                                      const printer_state_dyn_t *old_status,
1768                                                      void *param),
1769                              void *param) {
1770     int result = ERROR;
1771     _status_handle_t *handle = (_status_handle_t *) status_handle;
1772     if (handle != NULL) {
1773         handle->status_callback = status_callback;
1774         handle->param = param;
1775         result = _start_printer_status_thread(handle);
1776     } else {
1777         LOGE("wprintStatusMonitorStart(): status handle is NULL");
1778     }
1779     return result;
1780 }
1781 
wprintStatusMonitorStop(wStatus_t status_handle)1782 void wprintStatusMonitorStop(wStatus_t status_handle) {
1783     _status_handle_t *handle = (_status_handle_t *) status_handle;
1784     if (handle != NULL) {
1785         handle->status_ifc->stop(handle->status_ifc);
1786         pthread_join(handle->status_tid, 0);
1787         handle->status_ifc->destroy(handle->status_ifc);
1788         free(handle);
1789     }
1790 }
1791 
1792 /*
1793  * Returns a preferred print format supported by the printer
1794  */
_get_print_format(const char * mime_type,const wprint_job_params_t * job_params,const printer_capabilities_t * cap)1795 char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params,
1796         const printer_capabilities_t *cap) {
1797     char *print_format = NULL;
1798 
1799     errno = OK;
1800 
1801     if (((strcmp(mime_type, MIME_TYPE_PDF) == 0) && cap->canPrintPDF)) {
1802         // For content type=photo and a printer that supports both PCLm and PDF,
1803         // prefer PCLm over PDF.
1804         if (job_params && (strcasecmp(job_params->docCategory, "photo") == 0) &&
1805                 cap->canPrintPCLm) {
1806             print_format = PRINT_FORMAT_PCLM;
1807             LOGI("_get_print_format(): print_format switched from PDF to PCLm");
1808         } else {
1809             print_format = PRINT_FORMAT_PDF;
1810         }
1811     } else if (cap->canPrintPCLm || cap->canPrintPDF) {
1812         // PCLm is a subset of PDF
1813         print_format = PRINT_FORMAT_PCLM;
1814 #if (USE_PWG_OVER_PCLM != 0)
1815         if (cap->canPrintPWG) {
1816             print_format = PRINT_FORMAT_PWG;
1817         }
1818 #endif // (USE_PWG_OVER_PCLM != 0)
1819     } else if (cap->canPrintPWG) {
1820         print_format = PRINT_FORMAT_PWG;
1821     } else {
1822         errno = EBADRQC;
1823     }
1824 
1825     if (print_format != NULL) {
1826         LOGI("\t_get_print_format(): print_format: %s", print_format);
1827     }
1828 
1829     return print_format;
1830 }
1831 
wprintGetDefaultJobParams(wprint_job_params_t * job_params)1832 status_t wprintGetDefaultJobParams(wprint_job_params_t *job_params) {
1833     status_t result = ERROR;
1834     static const wprint_job_params_t _default_job_params = {.print_format = _DEFAULT_PRINT_FORMAT,
1835             .pcl_type = _DEFAULT_PCL_TYPE, .media_size = US_LETTER, .media_type = MEDIA_PLAIN,
1836             .duplex = DUPLEX_MODE_NONE, .dry_time = DUPLEX_DRY_TIME_NORMAL,
1837             .color_space = COLOR_SPACE_COLOR, .media_tray = TRAY_SRC_AUTO_SELECT,
1838             .pixel_units = DEFAULT_RESOLUTION, .render_flags = 0, .num_copies =1,
1839             .borderless = false, .cancelled = false, .face_down_tray = false,
1840             .ipp_1_0_supported = false, .ipp_2_0_supported = false, .epcl_ipp_supported = false,
1841             .strip_height = STRIPE_HEIGHT, .docCategory = {0},
1842             .copies_supported = false, .print_at_scale = false};
1843 
1844     if (job_params == NULL) return result;
1845 
1846     memcpy(job_params, &_default_job_params, sizeof(_default_job_params));
1847 
1848     return OK;
1849 }
1850 
wprintGetFinalJobParams(wprint_job_params_t * job_params,const printer_capabilities_t * printer_cap)1851 status_t wprintGetFinalJobParams(wprint_job_params_t *job_params,
1852         const printer_capabilities_t *printer_cap) {
1853     int i;
1854     status_t result = ERROR;
1855     float margins[NUM_PAGE_MARGINS];
1856 
1857     if (job_params == NULL) {
1858         return result;
1859     }
1860     result = OK;
1861 
1862     job_params->accepts_pclm = printer_cap->canPrintPCLm;
1863     job_params->accepts_pdf = printer_cap->canPrintPDF;
1864     job_params->media_default = printer_cap->mediaDefault;
1865 
1866     if (printer_cap->ePclIppVersion == 1) {
1867         job_params->epcl_ipp_supported = true;
1868     }
1869 
1870     if (printer_cap->canCopy) {
1871         job_params->copies_supported = true;
1872     }
1873 
1874     if (printer_cap->ippVersionMajor == 2) {
1875         job_params->ipp_1_0_supported = true;
1876         job_params->ipp_2_0_supported = true;
1877     } else if (printer_cap->ippVersionMajor == 1) {
1878         job_params->ipp_1_0_supported = true;
1879         job_params->ipp_2_0_supported = false;
1880     }
1881 
1882     if (!printer_cap->color) {
1883         job_params->color_space = COLOR_SPACE_MONO;
1884     }
1885 
1886     if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) {
1887         job_params->pcl_type = PCLm;
1888 #if (USE_PWG_OVER_PCLM != 0)
1889         if ( printer_cap->canPrintPWG) {
1890             job_params->pcl_type = PCLPWG;
1891         }
1892 #endif // (USE_PWG_OVER_PCLM != 0)
1893     } else if (printer_cap->canPrintPWG) {
1894         job_params->pcl_type = PCLPWG;
1895     }
1896 
1897     LOGD("wprintGetFinalJobParams: Using PCL Type %s", getPCLTypeString(job_params->pcl_type));
1898 
1899     // set strip height
1900     job_params->strip_height = printer_cap->stripHeight;
1901 
1902     // make sure the number of copies is valid
1903     if (job_params->num_copies <= 0) {
1904         job_params->num_copies = 1;
1905     }
1906 
1907     // If printing photo and HIGH quality is supported, specify it.
1908     if (strcasecmp(job_params->docCategory, "photo") == 0 && int_array_contains(
1909             printer_cap->supportedQuality, printer_cap->numSupportedQuality, IPP_QUALITY_HIGH)) {
1910         job_params->print_quality = IPP_QUALITY_HIGH;
1911     } else if (int_array_contains(printer_cap->supportedQuality, printer_cap->numSupportedQuality,
1912             IPP_QUALITY_NORMAL)) {
1913         job_params->print_quality = IPP_QUALITY_NORMAL;
1914     } else {
1915         job_params->print_quality = IPP_QUALITY_DRAFT;
1916     }
1917 
1918     // confirm that the media size is supported
1919     for (i = 0; i < printer_cap->numSupportedMediaSizes; i++) {
1920         if (job_params->media_size == printer_cap->supportedMediaSizes[i]) {
1921             break;
1922         }
1923     }
1924 
1925     if (i >= printer_cap->numSupportedMediaSizes) {
1926         job_params->media_size = ISO_A4;
1927         job_params->media_tray = TRAY_SRC_AUTO_SELECT;
1928     }
1929 
1930     // check that we support the media tray
1931     for (i = 0; i < printer_cap->numSupportedMediaTrays; i++) {
1932         if (job_params->media_tray == printer_cap->supportedMediaTrays[i]) {
1933             break;
1934         }
1935     }
1936 
1937     // media tray not supported, default to automatic
1938     if (i >= printer_cap->numSupportedMediaTrays) {
1939         job_params->media_tray = TRAY_SRC_AUTO_SELECT;
1940     }
1941 
1942     if (printer_cap->isMediaSizeNameSupported == true) {
1943         job_params->media_size_name = true;
1944     } else {
1945         job_params->media_size_name = false;
1946     }
1947 
1948     // verify borderless setting
1949     if ((job_params->borderless == true) && !printer_cap->borderless) {
1950         job_params->borderless = false;
1951     }
1952 
1953     // borderless and margins don't get along
1954     if (job_params->borderless &&
1955             ((job_params->job_top_margin > 0.0f) || (job_params->job_left_margin > 0.0f) ||
1956                     (job_params->job_right_margin > 0.0f)
1957                     || (job_params->job_bottom_margin > 0.0f))) {
1958         job_params->borderless = false;
1959     }
1960 
1961     // verify duplex setting
1962     if ((job_params->duplex != DUPLEX_MODE_NONE) && !printer_cap->duplex) {
1963         job_params->duplex = DUPLEX_MODE_NONE;
1964     }
1965 
1966     // borderless and duplex don't get along either
1967     if (job_params->borderless && (job_params->duplex != DUPLEX_MODE_NONE)) {
1968         job_params->duplex = DUPLEX_MODE_NONE;
1969     }
1970 
1971     if ((job_params->duplex == DUPLEX_MODE_BOOK)
1972             && !printer_cap->canRotateDuplexBackPage) {
1973         job_params->render_flags |= RENDER_FLAG_ROTATE_BACK_PAGE;
1974     }
1975 
1976     if (job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) {
1977         LOGD("wprintGetFinalJobParams: Duplex is on and device needs back page rotated.");
1978     }
1979 
1980     job_params->face_down_tray = printer_cap->faceDownTray;
1981 
1982     if (job_params->render_flags & RENDER_FLAG_AUTO_SCALE) {
1983         job_params->render_flags |= AUTO_SCALE_RENDER_FLAGS;
1984     } else if (job_params->render_flags & RENDER_FLAG_AUTO_FIT) {
1985         job_params->render_flags |= AUTO_FIT_RENDER_FLAGS;
1986     }
1987 
1988     job_params->pixel_units = _findCloseResolutionSupported(
1989             job_params->print_quality == IPP_QUALITY_HIGH ? HIGH_RESOLUTION_PHOTO
1990                                                           : DEFAULT_RESOLUTION,
1991             MAX_SUPPORTED_RESOLUTION, printer_cap);
1992 
1993     printable_area_get_default_margins(job_params, printer_cap, &margins[TOP_MARGIN],
1994             &margins[LEFT_MARGIN], &margins[RIGHT_MARGIN], &margins[BOTTOM_MARGIN]);
1995     printable_area_get(job_params, margins[TOP_MARGIN], margins[LEFT_MARGIN],
1996             margins[RIGHT_MARGIN], margins[BOTTOM_MARGIN]);
1997 
1998     job_params->accepts_app_name = printer_cap->docSourceAppName;
1999     job_params->accepts_app_version = printer_cap->docSourceAppVersion;
2000     job_params->accepts_os_name = printer_cap->docSourceOsName;
2001     job_params->accepts_os_version = printer_cap->docSourceOsVersion;
2002 
2003     return result;
2004 }
2005 
wprintStartJob(const char * printer_addr,port_t port_num,const wprint_job_params_t * job_params,const printer_capabilities_t * printer_cap,const char * mime_type,const char * pathname,wprint_status_cb_t cb_fn,const char * debugDir,const char * scheme)2006 wJob_t wprintStartJob(const char *printer_addr, port_t port_num,
2007         const wprint_job_params_t *job_params, const printer_capabilities_t *printer_cap,
2008         const char *mime_type, const char *pathname, wprint_status_cb_t cb_fn,
2009         const char *debugDir, const char *scheme) {
2010     wJob_t job_handle = WPRINT_BAD_JOB_HANDLE;
2011     _msg_t msg;
2012     struct stat stat_buf;
2013     bool is_dir = false;
2014     _job_queue_t *jq;
2015     wprint_plugin_t *plugin = NULL;
2016     char *print_format;
2017     ifc_print_job_t *print_ifc;
2018 
2019     if (mime_type == NULL) {
2020         errno = EINVAL;
2021         return job_handle;
2022     }
2023 
2024     print_format = _get_print_format(mime_type, job_params, printer_cap);
2025     if (print_format == NULL) return job_handle;
2026 
2027     // check to see if we have an appropriate plugin
2028     if (OK == stat(pathname, &stat_buf)) {
2029         if (S_ISDIR(stat_buf.st_mode)) {
2030             is_dir = true;
2031         } else if (stat_buf.st_size == 0) {
2032             errno = EBADF;
2033             return job_handle;
2034         }
2035     } else {
2036         errno = ENOENT;
2037         return job_handle;
2038     }
2039 
2040     // Make sure we have job_params
2041     if (job_params == NULL) {
2042         errno = ECOMM;
2043         return job_handle;
2044     }
2045 
2046     plugin = plugin_search(mime_type, print_format);
2047     _lock();
2048 
2049     if (plugin) {
2050         job_handle = _get_handle();
2051         if (job_handle == WPRINT_BAD_JOB_HANDLE) {
2052             errno = EAGAIN;
2053         }
2054     } else {
2055         errno = ENOSYS;
2056         LOGE("wprintStartJob(): ERROR: no plugin found for %s => %s", mime_type, print_format);
2057     }
2058 
2059     if (job_handle != WPRINT_BAD_JOB_HANDLE) {
2060         print_ifc = (ifc_print_job_t *) _get_print_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP));
2061 
2062         // fill out the job queue record
2063         jq = _get_job_desc(job_handle);
2064         if (jq == NULL) {
2065             _recycle_handle(job_handle);
2066             job_handle = WPRINT_BAD_JOB_HANDLE;
2067             _unlock();
2068             return job_handle;
2069         }
2070 
2071         if (debugDir != NULL) {
2072             strncpy(jq->debug_path, debugDir, MAX_PATHNAME_LENGTH);
2073             jq->debug_path[MAX_PATHNAME_LENGTH] = 0;
2074         }
2075 
2076         strncpy(jq->printer_addr, printer_addr, MAX_PRINTER_ADDR_LENGTH);
2077         strncpy(jq->mime_type, mime_type, MAX_MIME_LENGTH);
2078         strncpy(jq->pathname, pathname, MAX_PATHNAME_LENGTH);
2079 
2080         jq->port_num = port_num;
2081         jq->cb_fn = cb_fn;
2082         jq->print_ifc = print_ifc;
2083         jq->cancel_ok = true; // assume cancel is ok
2084         jq->plugin = plugin;
2085         memcpy(jq->printer_uri, printer_cap->httpResource,
2086                 MIN(ARRAY_SIZE(printer_cap->httpResource), ARRAY_SIZE(jq->printer_uri)));
2087 
2088         jq->status_ifc = _get_status_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP));
2089 
2090         memcpy((char *) &(jq->job_params), job_params, sizeof(wprint_job_params_t));
2091 
2092         jq->use_secure_uri = (strstr(scheme, IPPS_PREFIX) != NULL);
2093 
2094         size_t useragent_len = strlen(USERAGENT_PREFIX) + strlen(jq->job_params.docCategory) + 1;
2095         char *useragent = (char *) malloc(useragent_len);
2096         if (useragent != NULL) {
2097             snprintf(useragent, useragent_len, USERAGENT_PREFIX "%s", jq->job_params.docCategory);
2098             jq->job_params.useragent = useragent;
2099         }
2100 
2101         // Make a copy of the job_params certificate if it is present
2102         if (job_params->certificate) {
2103             jq->job_params.certificate = malloc(job_params->certificate_len);
2104             if (jq->job_params.certificate) {
2105                 memcpy(jq->job_params.certificate, job_params->certificate,
2106                         job_params->certificate_len);
2107             }
2108         }
2109 
2110         jq->job_params.page_num = 0;
2111         jq->job_params.print_format = print_format;
2112         if (strcmp(print_format, PRINT_FORMAT_PCLM) == 0) {
2113             if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) {
2114                 jq->job_params.pcl_type = PCLm;
2115             } else {
2116                 jq->job_params.pcl_type = PCLNONE;
2117             }
2118         }
2119 
2120         if (strcmp(print_format, PRINT_FORMAT_PWG) == 0) {
2121             if (printer_cap->canPrintPWG) {
2122                 jq->job_params.pcl_type = PCLPWG;
2123             } else {
2124                 jq->job_params.pcl_type = PCLNONE;
2125             }
2126         }
2127 
2128         // if the pathname is a directory, then this is a multi-page job with individual pages
2129         if (is_dir) {
2130             jq->is_dir = true;
2131             jq->num_pages = 0;
2132 
2133             // create a pageQ for queuing page information
2134             jq->pageQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t));
2135 
2136             // create a secondary page Q for subsequently saving page data for copies #2 to n
2137             if (jq->job_params.num_copies > 1) {
2138                 jq->saveQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t));
2139             }
2140         } else {
2141             jq->num_pages = 1;
2142         }
2143 
2144         // post a message with job_handle to the msgQ that is serviced by a thread
2145         msg.id = MSG_RUN_JOB;
2146         msg.job_id = job_handle;
2147 
2148         memcpy(&g_printer_caps, printer_cap, sizeof(printer_capabilities_t));
2149         if (print_ifc && plugin && plugin->print_page &&
2150                 (msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO) == OK)) {
2151             errno = OK;
2152             LOGD("wprintStartJob(): print job %ld queued (%s => %s)", job_handle,
2153                     mime_type, print_format);
2154         } else {
2155             if (print_ifc == NULL) {
2156                 errno = EAFNOSUPPORT;
2157             } else if ((plugin == NULL) || (plugin->print_page == NULL)) {
2158                 errno = ELIBACC;
2159             } else {
2160                 errno = EBADMSG;
2161             }
2162 
2163             LOGE("wprintStartJob(): ERROR plugin->start_job(%ld) : %s => %s", job_handle,
2164                     mime_type, print_format);
2165             jq->job_state = JOB_STATE_ERROR;
2166             _recycle_handle(job_handle);
2167             job_handle = WPRINT_BAD_JOB_HANDLE;
2168         }
2169     }
2170     _unlock();
2171     return job_handle;
2172 }
2173 
wprintEndJob(wJob_t job_handle)2174 status_t wprintEndJob(wJob_t job_handle) {
2175     _page_t page;
2176     _job_queue_t *jq;
2177     status_t result = ERROR;
2178 
2179     _lock();
2180     jq = _get_job_desc(job_handle);
2181 
2182     if (jq) {
2183         // if the job is done and is to be freed, do it
2184         if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) ||
2185                 (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) {
2186             result = OK;
2187             if (jq->pageQ) {
2188                 while ((msgQNumMsgs(jq->pageQ) > 0)
2189                         && (msgQReceive(jq->pageQ, (char *) &page, sizeof(page),
2190                                 WAIT_FOREVER) == OK)) {
2191                 }
2192                 result |= msgQDelete(jq->pageQ);
2193                 jq->pageQ = NULL;
2194             }
2195 
2196             if (jq->saveQ) {
2197                 while ((msgQNumMsgs(jq->saveQ) > 0)
2198                         && (msgQReceive(jq->saveQ, (char *) &page, sizeof(page),
2199                                 WAIT_FOREVER) == OK)) {
2200                 }
2201                 result |= msgQDelete(jq->saveQ);
2202                 jq->saveQ = NULL;
2203             }
2204             _recycle_handle(job_handle);
2205         } else {
2206             LOGE("job %ld cannot be ended from state %d", job_handle, jq->job_state);
2207         }
2208     } else {
2209         LOGE("ERROR: wprintEndJob(%ld), job not found", job_handle);
2210     }
2211 
2212     _unlock();
2213     return result;
2214 }
2215 
wprintPage(wJob_t job_handle,int page_num,const char * filename,bool last_page,bool pdf_page,unsigned int top_margin,unsigned int left_margin,unsigned int right_margin,unsigned int bottom_margin)2216 status_t wprintPage(wJob_t job_handle, int page_num, const char *filename, bool last_page,
2217         bool pdf_page, unsigned int top_margin, unsigned int left_margin, unsigned int right_margin,
2218         unsigned int bottom_margin) {
2219     _job_queue_t *jq;
2220     _page_t page;
2221     status_t result = ERROR;
2222     struct stat stat_buf;
2223 
2224     _lock();
2225     jq = _get_job_desc(job_handle);
2226 
2227     // use empty string to indicate EOJ for an empty job
2228     if (!filename) {
2229         filename = "";
2230         last_page = true;
2231     } else if (OK == stat(filename, &stat_buf)) {
2232         if (!S_ISREG(stat_buf.st_mode) || (stat_buf.st_size == 0)) {
2233             _unlock();
2234             return result;
2235         }
2236     } else {
2237         _unlock();
2238         return result;
2239     }
2240 
2241     // must be setup as a multi-page job, page_num must be valid, and filename must fit
2242     if (jq && jq->is_dir && !(jq->last_page_seen) && (((strlen(filename) < MAX_PATHNAME_LENGTH)) ||
2243             (jq && (strcmp(filename, "") == 0) && last_page))) {
2244         memset(&page, 0, sizeof(page));
2245         page.page_num = page_num;
2246         page.corrupted = false;
2247         page.pdf_page = pdf_page;
2248         page.last_page = last_page;
2249         page.top_margin = top_margin;
2250         page.left_margin = left_margin;
2251         page.right_margin = right_margin;
2252         page.bottom_margin = bottom_margin;
2253 
2254         if ((strlen(filename) == 0) || strchr(filename, '/')) {
2255             // assume empty or complete pathname and use it as it is
2256             strncpy(page.filename, filename, MAX_PATHNAME_LENGTH);
2257         } else {
2258             // generate a complete pathname
2259             snprintf(page.filename, MAX_PATHNAME_LENGTH, "%s/%s", jq->pathname, filename);
2260         }
2261 
2262         if (last_page) {
2263             jq->last_page_seen = true;
2264         }
2265 
2266         result = msgQSend(jq->pageQ, (char *) &page, sizeof(page), NO_WAIT, MSG_Q_FIFO);
2267     }
2268 
2269     if (result == OK) {
2270         LOGD("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page);
2271         if (!(last_page && (strcmp(filename, "") == 0))) {
2272             jq->num_pages++;
2273         }
2274     } else {
2275         LOGE("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page);
2276     }
2277 
2278     _unlock();
2279     return result;
2280 }
2281 
wprintCancelJob(wJob_t job_handle)2282 status_t wprintCancelJob(wJob_t job_handle) {
2283     _job_queue_t *jq;
2284     status_t result;
2285 
2286     _lock();
2287 
2288     jq = _get_job_desc(job_handle);
2289 
2290     if (jq) {
2291         LOGI("received cancel request");
2292         // send an empty page in case we're waiting on the msgQ page receive
2293         if ((jq->job_state == JOB_STATE_RUNNING) || (jq->job_state == JOB_STATE_BLOCKED)) {
2294             bool enableTimeout = true;
2295             jq->cancel_ok = true;
2296             jq->job_params.cancelled = true;
2297             wprintPage(job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0);
2298             if (jq->status_ifc) {
2299                 // are we blocked waiting for the job to start
2300                 if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->job_params.page_num != 0)) {
2301                     errno = OK;
2302                     jq->cancel_ok = ((jq->status_ifc->cancel)(jq->status_ifc,
2303                             jq->job_params.job_originating_user_name) == 0);
2304                     if ((jq->cancel_ok == true) && (errno != OK)) {
2305                         enableTimeout = false;
2306                     }
2307                 }
2308             }
2309             if (!jq->cancel_ok) {
2310                 LOGE("CANCEL did not go through or is not supported for this device");
2311                 enableTimeout = true;
2312             }
2313             if (enableTimeout && (jq->print_ifc != NULL) &&
2314                     (jq->print_ifc->enable_timeout != NULL)) {
2315                 jq->print_ifc->enable_timeout(jq->print_ifc, 1);
2316             }
2317 
2318             errno = (jq->cancel_ok ? OK : ENOTSUP);
2319             jq->job_state = JOB_STATE_CANCEL_REQUEST;
2320             result = OK;
2321         } else if ((jq->job_state == JOB_STATE_CANCEL_REQUEST) ||
2322                 (jq->job_state == JOB_STATE_CANCELLED)) {
2323             result = OK;
2324             errno = (jq->cancel_ok ? OK : ENOTSUP);
2325         } else if (jq->job_state == JOB_STATE_QUEUED) {
2326             jq->job_params.cancelled = true;
2327             jq->job_state = JOB_STATE_CANCELLED;
2328 
2329             if (jq->cb_fn) {
2330                 wprint_job_callback_params_t cb_param;
2331                 cb_param.param.state = JOB_DONE;
2332                 cb_param.blocked_reasons = BLOCKED_REASONS_CANCELLED;
2333                 cb_param.job_done_result = CANCELLED;
2334                 cb_param.certificate = jq->certificate;
2335                 cb_param.certificate_len = jq->certificate_len;
2336 
2337                 jq->cb_fn(job_handle, (void *) &cb_param);
2338             }
2339 
2340             errno = OK;
2341             result = OK;
2342         } else {
2343             LOGE("job in other state");
2344             result = ERROR;
2345             errno = EBADRQC;
2346         }
2347     } else {
2348         LOGE("could not find job");
2349         result = ERROR;
2350         errno = EBADR;
2351     }
2352 
2353     _unlock();
2354 
2355     return result;
2356 }
2357 
wprintExit(void)2358 status_t wprintExit(void) {
2359     _msg_t msg;
2360 
2361     if (_msgQ) {
2362         //  toss the remaining messages in the msgQ
2363         while ((msgQNumMsgs(_msgQ) > 0) &&
2364                 (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {}
2365 
2366         // send a quit message
2367         msg.id = MSG_QUIT;
2368         msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO);
2369 
2370         // stop the job thread
2371         _stop_thread();
2372 
2373         // empty out the semaphore
2374         while (sem_trywait(&_job_end_wait_sem) == OK);
2375         while (sem_trywait(&_job_start_wait_sem) == OK);
2376 
2377         // receive any messages just in case
2378         while ((msgQNumMsgs(_msgQ) > 0)
2379                 && (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {}
2380 
2381         // delete the msgQ
2382         msgQDelete(_msgQ);
2383         _msgQ = NULL;
2384 
2385         sem_destroy(&_job_end_wait_sem);
2386         sem_destroy(&_job_start_wait_sem);
2387 
2388         pthread_mutex_destroy(&_q_lock);
2389         stop_mutex = true;
2390     }
2391 
2392     return OK;
2393 }
2394 
wprintSetSourceInfo(const char * appName,const char * appVersion,const char * osName)2395 void wprintSetSourceInfo(const char *appName, const char *appVersion, const char *osName) {
2396     if (appName) {
2397         strncpy(g_appName, appName, (sizeof(g_appName) - 1));
2398     }
2399 
2400     if (appVersion) {
2401         strncpy(g_appVersion, appVersion, (sizeof(g_appVersion) - 1));
2402     }
2403 
2404     if (osName) {
2405         strncpy(g_osName, osName, (sizeof(g_osName) - 1));
2406     }
2407 
2408     LOGI("App Name: '%s', Version: '%s', OS: '%s'", g_appName, g_appVersion, g_osName);
2409 }
2410 
wprintBlankPageForPclm(const wprint_job_params_t * job_params,const printer_capabilities_t * printer_cap)2411 bool wprintBlankPageForPclm(const wprint_job_params_t *job_params,
2412         const printer_capabilities_t *printer_cap) {
2413     return ((job_params->job_pages_per_set % 2) &&
2414             ((job_params->num_copies > 1 && printer_cap->duplex) ||
2415                     (job_params->num_copies == 1)) && (job_params->duplex != DUPLEX_MODE_NONE));
2416 }
2417 
wprintBlankPageForPwg(const wprint_job_params_t * job_params,const printer_capabilities_t * printer_cap)2418 bool wprintBlankPageForPwg(const wprint_job_params_t *job_params,
2419         const printer_capabilities_t *printer_cap) {
2420     return ((job_params->job_pages_per_set % 2) && (job_params->duplex != DUPLEX_MODE_NONE) &&
2421             !(printer_cap->jobPagesPerSetSupported &&
2422                     strcmp(job_params->print_format, PRINT_FORMAT_PWG) == 0));
2423 }