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