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