1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
|
/*
* implicitclass backend for implementing an implicit-class-like behavior
* of redundant print servers managed by cups-browsed.
*
* Copyright 2015-2019 by Till Kamppeter
* Copyright 2018-2019 by Deepak Patankar
*
* This is based on dnssd.c of CUPS
* dnssd.c copyright notice is follows:
*
* Copyright 2008-2015 by Apple Inc.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* law. Distribution and use rights are outlined in the file "COPYING"
* which should have been included with this file.
*/
/*
* Include necessary headers.
*/
#include "backend-private.h"
#include <cups/array.h>
#include <ctype.h>
#include <cups/array.h>
#include <ctype.h>
#include <cups/cups.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cupsfilters/pdftoippprinter.h>
/*
* Local globals...
*/
/* IPP Attribute which cups-browsed uses to tell us the destination queue for
the current job */
#define CUPS_BROWSED_DEST_PRINTER "cups-browsed-dest-printer"
static int job_canceled = 0; /* Set to 1 on SIGTERM */
/*
* Local functions... */
static void sigterm_handler(int sig);
#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
#define HAVE_CUPS_1_6 1
#endif
#ifndef HAVE_CUPS_1_6
int
ippGetInteger(ipp_attribute_t *attr,
int element)
{
return (attr->values[element].integer);
}
#endif
int /* O - Next delay value */
next_delay(int current, /* I - Current delay value or 0 */
int *previous) /* IO - Previous delay value */
{
int next; /* Next delay value */
if (current > 0) {
next = (current + *previous) % 12;
*previous = next < current ? 0 : current;
} else {
next = 1;
*previous = 0;
}
return (next);
}
/*
* 'main()' - Browse for printers.
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line args */
char *argv[]) /* I - Command-line arguments */
{
const char *device_uri; /* URI with which we were called */
char scheme[64], username[32], queue_name[1024], resource[32],
printer_uri[1024],document_format[256],resolution[16];
int port, status;
const char *ptr1 = NULL;
char *ptr2,*ptr3,*ptr4;
const char *job_id;
char *filename, /* PDF file to convert */
tempfile[1024],
tempfile_filter[1024]; /* Temporary file */
int i;
char dest_host[1024]; /* Destination host */
ipp_t *request, *response;
ipp_attribute_t *attr;
int bytes; /* Bytes copied */
char uri[HTTP_MAX_URI];
char *argv_nt[8];
int outbuflen, filefd, savestdout, exit_status, dup_status;
char buf[1024];
const char *serverbin;
static const char *pattrs[] =
{
"printer-defaults"
};
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action; /* Actions for POSIX signals */
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
/*
* Don't buffer stderr, and catch SIGTERM...
*/
setbuf(stderr, NULL);
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
sigset(SIGTERM, sigterm_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = sigterm_handler;
sigaction(SIGTERM, &action, NULL);
#else
signal(SIGTERM, sigterm_handler);
#endif /* HAVE_SIGSET */
/*
* Check command-line...
*/
if (argc >= 6) {
if ((device_uri = getenv("DEVICE_URI")) == NULL) {
if (!argv || !argv[0] || !strchr(argv[0], ':'))
return (-1);
device_uri = argv[0];
}
status = httpSeparateURI(HTTP_URI_CODING_ALL, device_uri,
scheme, sizeof(scheme),
username, sizeof(username),
queue_name, sizeof(queue_name),
&port,
resource, sizeof(resource));
if (status != HTTP_URI_STATUS_OK &&
status != HTTP_URI_STATUS_UNKNOWN_SCHEME) {
fprintf(stderr, "ERROR: Incorrect device URI syntax: %s\n",
device_uri);
return (CUPS_BACKEND_STOP);
}
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
"localhost", ippPort(), "/printers/%s", queue_name);
job_id = argv[1];
for (i = 0; i < 120; i++) {
/* Wait up to 60 sec for cups-browsed to supply the destination host */
/* Try reading the option in which cups-browsed has deposited the
destination host */
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
uri);
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes",
sizeof(pattrs) / sizeof(pattrs[0]),
NULL, pattrs);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name",
NULL, cupsUser());
if ((response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) ==
NULL)
goto failed;
for (attr = ippFirstAttribute(response); attr != NULL;
attr = ippNextAttribute(response)) {
while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
attr = ippNextAttribute(response);
if (attr == NULL)
break;
ptr1 = NULL;
while (attr != NULL && ippGetGroupTag(attr) ==
IPP_TAG_PRINTER) {
if (!strcmp(ippGetName(attr),
CUPS_BROWSED_DEST_PRINTER "-default"))
ptr1 = ippGetString(attr, 0, NULL);
if (ptr1 != NULL)
break;
attr = ippNextAttribute(response);
}
if (ptr1 != NULL)
break;
}
fprintf(stderr, "DEBUG: Read " CUPS_BROWSED_DEST_PRINTER " option: %s\n",
(ptr1 ? ptr1 : "Option not found"));
if (ptr1 == NULL)
goto failed;
/* Destination host is between double quotes, as double quotes are
illegal in host names one easily recognizes whether the option is
complete and avoids accepting a partially written host name */
if (*ptr1 != '"')
goto failed;
ptr1 ++;
/* Check whether option was set for this job, if not, keep waiting */
if (strncmp(ptr1, job_id, strlen(job_id)) != 0)
goto failed;
ptr1 += strlen(job_id);
if (*ptr1 != ' ')
goto failed;
ptr1 ++;
/* Read destination host name (or message) and check whether it is
complete (second double quote) */
if ((ptr2 = strchr(ptr1, '"')) != NULL) {
*ptr2 = '\0';
break;
}
failed:
/* Pause half a second before next attempt */
usleep(500000);
}
if (i >= 120) {
/* Timeout, no useful data from cups-browsed received */
fprintf(stderr, "ERROR: No destination host name supplied by cups-browsed for printer \"%s\", is cups-browsed running?\n",
queue_name);
return (CUPS_BACKEND_STOP);
}
strncpy(dest_host,ptr1,sizeof(dest_host) - 1);
if (!strcmp(dest_host, "NO_DEST_FOUND")) {
/* All remote queues are either disabled or not accepting jobs, let
CUPS retry after the usual interval */
fprintf(stderr, "ERROR: No suitable destination host found by cups-browsed.\n");
return (CUPS_BACKEND_RETRY);
} else if (!strcmp(dest_host, "ALL_DESTS_BUSY")) {
/* We queue on the client and all remote queues are busy, so we wait
5 sec and check again then */
fprintf(stderr, "DEBUG: No free destination host found by cups-browsed, retrying in 5 sec.\n");
sleep(5);
return (CUPS_BACKEND_RETRY_CURRENT);
} else {
/* We have the destination host name now, do the job */
const char *title;
int num_options = 0;
cups_option_t *options = NULL;
int fd;
char buffer[8192];
fprintf(stderr, "DEBUG: Received destination host name from cups-browsed: printer-uri %s\n",
ptr1);
/* Parse the command line options and prepare them for the new print
job */
cupsSetUser(argv[2]);
title = argv[3];
if (title == NULL) {
if (argc == 7) {
if ((title = strrchr(argv[6], '/')) != NULL)
title ++;
else
title = argv[6];
} else
title = "(stdin)";
}
num_options = cupsAddOption("copies", argv[4], num_options, &options);
num_options = cupsParseOptions(argv[5], num_options, &options);
if (argc == 7)
fd = open(argv[6], O_RDONLY);
else
fd = 0; /* stdin */
/* Finding the document format in which the pdftoippprinter will
convert the pdf file */
if ((ptr3 = strchr(ptr1, ' ')) != NULL) {
*ptr3='\0';
ptr3++;
}
/* Finding the resolution requested for the job*/
if ((ptr4 = strchr(ptr3, ' ')) != NULL) {
*ptr4='\0';
ptr4++;
}
strncpy(printer_uri, ptr1, sizeof(printer_uri) - 1);
strncpy(document_format, ptr3, sizeof(document_format) - 1);
strncpy(resolution, ptr4, sizeof(resolution) - 1);
fprintf(stderr,"DEBUG: Received job for the printer with the destination uri - %s, Final-document format for the printer - %s and requested resolution - %s\n",
printer_uri, document_format, resolution);
/* We need to send modified arguments to the IPP backend */
if (argc == 6) {
/* Copy stdin to a temp file...*/
if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0){
fprintf(stderr,"Debug: Can't Read PDF file.\n");
return CUPS_BACKEND_FAILED;
}
fprintf(stderr, "Debug: implicitclass - copying to temp print file \"%s\"\n",
tempfile);
while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
bytes = write(fd, buffer, bytes);
close(fd);
filename = tempfile;
} else {
/* Use the filename on the command-line... */
filename = argv[6];
tempfile[0] = '\0';
}
/* Copying the argument to a new char** which will be sent to the filter
and the ipp backend */
argv_nt[0] = calloc(strlen(printer_uri) + 8, sizeof(char));
strcpy(argv_nt[0], printer_uri);
for (i = 1; i < 5; i++)
argv_nt[i] = argv[i];
/* Few new options will be added to the argv[5]*/
outbuflen = strlen(argv[5]) + 256;
argv_nt[5] = calloc(outbuflen, sizeof(char));
strcpy(argv_nt[5], (const char*)argv[5]);
/* Filter pdftoippprinter.c will read the input from this file*/
argv_nt[6] = filename;
argv_nt[7] = NULL;
set_option_in_str(argv_nt[5], outbuflen, "output-format",
document_format);
set_option_in_str(argv_nt[5], outbuflen, "Resolution",resolution);
set_option_in_str(argv_nt[5], outbuflen, "cups-browsed-dest-printer",NULL);
set_option_in_str(argv_nt[5], outbuflen, "cups-browsed",NULL);
setenv("DEVICE_URI",printer_uri, 1);
fprintf(stderr, "Setting the device uri to %s\n",printer_uri);
fprintf(stderr, "Changed the argv[5] to %s\n",argv_nt[5]);
filefd = cupsTempFd(tempfile_filter, sizeof(tempfile_filter));
/* The output of the last filter in pdftoippprinter will be
written to this file. We could have sent the output directly
to the backend, but having this temperory file will help us
find whether the filter worked correctly and what was the
document-format of the filtered output.*/
savestdout = dup(1);
dup_status = dup2(filefd, 1);
if(dup_status < 0) {
fprintf(stderr, "Could not write the output of pdftoippprinter printer to tmp file\n");
return CUPS_BACKEND_FAILED;
}
close(filefd);
/* Calling pdftoippprinter.c filter*/
apply_filters(7,argv_nt);
/* Reset stdout to standard */
dup2(savestdout, 1);
close(savestdout);
/* We will send the filtered output of the pdftoippprinter.c to
the IPP Backend*/
argv_nt[6] = tempfile_filter;
fprintf(stderr, "DEBUG: The filtered output of pdftoippprinter is written to file %s\n",
tempfile_filter);
/* Setting the final content type to the best pdl supported by
the printer.*/
if(!strcmp(document_format,"pdf"))
setenv("FINAL_CONTENT_TYPE", "application/pdf", 1);
else if(!strcmp(document_format,"raster"))
setenv("FINAL_CONTENT_TYPE", "image/pwg-raster", 1);
else if(!strcmp(document_format,"apple-raster"))
setenv("FINAL_CONTENT_TYPE", "image/urf", 1);
else if(!strcmp(document_format,"pclm"))
setenv("FINAL_CONTENT_TYPE", "application/PCLm", 1);
else if(!strcmp(document_format,"pclxl"))
setenv("FINAL_CONTENT_TYPE", "application/vnd.hp-pclxl", 1);
else if(!strcmp(document_format,"postscript"))
setenv("FINAL_CONTENT_TYPE", "application/postscript", 1);
else if(!strcmp(document_format,"pcl"))
setenv("FINAL_CONTENT_TYPE", "application/pcl", 1);
ippDelete(response);
fprintf(stderr, "Passing the following arguments to the ipp backend\n");
/* Arguments sent to the ipp backend */
for (i = 0; i < 7; i ++) {
fprintf(stderr, "argv[%d]: %s\n", i, argv_nt[i]);
}
/* The implicitclass backend will send the job directly to the
ipp backend*/
pid_t pid = fork();
if (pid == 0) {
serverbin = getenv("CUPS_SERVERBIN");
if (serverbin == NULL)
serverbin = CUPS_SERVERBIN;
snprintf(buf, sizeof(buf) - 1, "%s/backend/ipp", serverbin);
fprintf(stderr, "DEBUG: Started IPP Backend (%s) with pid: %d\n",
buf, getpid());
execv(buf, argv_nt);
fprintf(stderr, "ERROR: Could not start IPP Backend (%s): %d %s\n",
buf, errno, strerror(errno));
return CUPS_BACKEND_FAILED;
} else {
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
fprintf(stderr, "DEBUG: The IPP Backend exited with the status %d\n",
exit_status);
}
return exit_status;
}
}
} else if (argc != 1) {
fprintf(stderr,
"Usage: %s job-id user title copies options [file]\n",
argv[0]);
return (CUPS_BACKEND_FAILED);
}
/*
* No discovery mode at all for this backend
*/
return (CUPS_BACKEND_OK);
}
/*
* 'sigterm_handler()' - Handle termination signals.
*/
static void
sigterm_handler(int sig) /* I - Signal number (unused) */
{
(void)sig;
if (job_canceled)
_exit(CUPS_BACKEND_OK);
else
job_canceled = 1;
}
|