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