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