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