]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/lpd.c
Merge changes from 1.1.x into 1.2 devel.
[thirdparty/cups.git] / backend / lpd.c
CommitLineData
43e2fc22 1/*
753453e4 2 * "$Id: lpd.c,v 1.28.2.1 2001/12/26 16:52:06 mike Exp $"
43e2fc22 3 *
e73c6c0a 4 * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
43e2fc22 5 *
d2935a0f 6 * Copyright 1997-2001 by Easy Software Products, all rights reserved.
43e2fc22 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
43e2fc22 18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
decc1f36 26 * main() - Send a file to the printer or server.
27 * lpd_command() - Send an LPR command sequence and wait for a reply.
28 * lpd_queue() - Queue a file using the Line Printer Daemon protocol.
f8529e1a 29 * lpd_write() - Write a buffer of data to an LPD server.
e73c6c0a 30 */
31
32/*
33 * Include necessary headers.
34 */
35
36#include <cups/cups.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdarg.h>
8056639c 40#include <ctype.h>
e73c6c0a 41#include <cups/string.h>
42#include <errno.h>
43#include <sys/types.h>
44#include <sys/stat.h>
4ff40357 45#include <signal.h>
e73c6c0a 46
47#if defined(WIN32) || defined(__EMX__)
48# include <winsock.h>
49#else
50# include <sys/socket.h>
51# include <netinet/in.h>
52# include <arpa/inet.h>
53# include <netdb.h>
54#endif /* WIN32 || __EMX__ */
55
55d0f521 56
f8529e1a 57/*
58 * The order for control and data files in LPD requests...
59 */
60
61#define ORDER_CONTROL_DATA 0
62#define ORDER_DATA_CONTROL 1
63
64
55d0f521 65/*
66 * It appears that rresvport() is never declared on most systems...
67 */
68
69extern int rresvport(int *port);
e73c6c0a 70
71
72/*
73 * Local functions...
74 */
75
76static int lpd_command(int lpd_fd, char *format, ...);
77static int lpd_queue(char *hostname, char *printer, char *filename,
72a039d0 78 char *user, char *title, int copies, int banner,
f8529e1a 79 int format, int order);
80static int lpd_write(int lpd_fd, char *buffer, int length);
e73c6c0a 81
82
83/*
84 * 'main()' - Send a file to the printer or server.
43e2fc22 85 *
e73c6c0a 86 * Usage:
43e2fc22 87 *
e73c6c0a 88 * printer-uri job-id user title copies options [file]
43e2fc22 89 */
90
e73c6c0a 91int /* O - Exit status */
92main(int argc, /* I - Number of command-line arguments (6 or 7) */
93 char *argv[]) /* I - Command-line arguments */
94{
95 char method[255], /* Method in URI */
96 hostname[1024], /* Hostname */
97 username[255], /* Username info (not used) */
98 resource[1024], /* Resource info (printer name) */
72a039d0 99 *options, /* Pointer to options */
100 name[255], /* Name of option */
101 value[255], /* Value of option */
102 *ptr, /* Pointer into name or value */
8056639c 103 filename[1024], /* File to print */
104 title[256]; /* Title string */
e73c6c0a 105 int port; /* Port number (not used) */
106 int status; /* Status of LPD job */
72a039d0 107 int banner; /* Print banner page? */
108 int format; /* Print format */
f8529e1a 109 int order; /* Order of control/data files */
e73c6c0a 110
111
4b23f3b3 112 /*
113 * Make sure status messages are not buffered...
114 */
115
116 setbuf(stderr, NULL);
117
118 /*
119 * Check command-line...
120 */
121
68edc300 122 if (argc == 1)
123 {
d4c438d4 124 puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\"");
68edc300 125 return (0);
126 }
127 else if (argc < 6 || argc > 7)
e73c6c0a 128 {
129 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
130 argv[0]);
131 return (1);
132 }
133
134 /*
135 * If we have 7 arguments, print the file named on the command-line.
136 * Otherwise, copy stdin to a temporary file and print the temporary
137 * file.
138 */
139
140 if (argc == 6)
141 {
142 /*
143 * Copy stdin to a temporary file...
144 */
145
1b5bf964 146 int fd; /* Temporary file */
e73c6c0a 147 char buffer[8192]; /* Buffer for copying */
148 int bytes; /* Number of bytes read */
149
150
1b5bf964 151 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
e73c6c0a 152 {
c8f9565c 153 perror("ERROR: unable to create temporary file");
e73c6c0a 154 return (1);
155 }
156
157 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
1b5bf964 158 if (write(fd, buffer, bytes) < bytes)
e73c6c0a 159 {
c8f9565c 160 perror("ERROR: unable to write to temporary file");
1b5bf964 161 close(fd);
e73c6c0a 162 unlink(filename);
163 return (1);
164 }
165
1b5bf964 166 close(fd);
e73c6c0a 167 }
168 else
970017a4 169 {
170 strncpy(filename, argv[6], sizeof(filename) - 1);
171 filename[sizeof(filename) - 1] = '\0';
172 }
e73c6c0a 173
174 /*
175 * Extract the hostname and printer name from the URI...
176 */
177
178 httpSeparate(argv[0], method, username, hostname, &port, resource);
179
72a039d0 180 /*
181 * See if there are any options...
182 */
183
184 banner = 0;
185 format = 'l';
f8529e1a 186 order = ORDER_CONTROL_DATA;
72a039d0 187
188 if ((options = strchr(resource, '?')) != NULL)
189 {
190 /*
191 * Yup, terminate the device name string and move to the first
192 * character of the options...
193 */
194
195 *options++ = '\0';
196
197 /*
198 * Parse options...
199 */
200
201 while (*options)
202 {
203 /*
204 * Get the name...
205 */
206
207 for (ptr = name; *options && *options != '=';)
208 *ptr++ = *options++;
209 *ptr = '\0';
210
211 if (*options == '=')
212 {
213 /*
214 * Get the value...
215 */
216
217 options ++;
218
219 for (ptr = value; *options && *options != '+';)
220 *ptr++ = *options++;
221 *ptr = '\0';
222
223 if (*options == '+')
224 options ++;
225 }
226 else
227 value[0] = '\0';
228
229 /*
230 * Process the option...
231 */
232
233 if (strcasecmp(name, "banner") == 0)
234 {
235 /*
236 * Set the banner...
237 */
238
239 banner = !value[0] ||
240 strcasecmp(value, "on") == 0 ||
241 strcasecmp(value, "yes") == 0 ||
242 strcasecmp(value, "true") == 0;
243 }
244 else if (strcasecmp(name, "format") == 0 && value[0])
245 {
246 /*
247 * Set output format...
248 */
249
250 if (strchr("cdfglnoprtv", value[0]) != NULL)
251 format = value[0];
252 else
253 fprintf(stderr, "ERROR: Unknown format character \"%c\"\n", value[0]);
254 }
f8529e1a 255 else if (strcasecmp(name, "order") == 0 && value[0])
256 {
257 /*
258 * Set control/data order...
259 */
260
261 if (strcasecmp(value, "control,data") == 0)
262 order = ORDER_CONTROL_DATA;
263 else if (strcasecmp(value, "data,control") == 0)
264 order = ORDER_DATA_CONTROL;
265 else
266 fprintf(stderr, "ERROR: Unknown file order \"%s\"\n", value);
267 }
72a039d0 268 }
269 }
270
8056639c 271 /*
272 * Sanitize the document title...
273 */
274
275 strncpy(title, argv[3], sizeof(title) - 1);
276 title[sizeof(title) - 1] = '\0';
277
278 for (ptr = title; *ptr; ptr ++)
279 if (!isalnum(*ptr) && !isspace(*ptr))
280 *ptr = '_';
281
e73c6c0a 282 /*
283 * Queue the job...
284 */
285
3f9cb6c6 286 if (argc > 6)
287 {
288 status = lpd_queue(hostname, resource + 1, filename,
8056639c 289 argv[2] /* user */, title, atoi(argv[4]) /* copies */,
290 banner, format, order);
3f9cb6c6 291
292 if (!status)
293 fprintf(stderr, "PAGE: 1 %d\n", atoi(argv[4]));
294 }
295 else
296 status = lpd_queue(hostname, resource + 1, filename,
8056639c 297 argv[2] /* user */, title, 1, banner, format, order);
e73c6c0a 298
299 /*
300 * Remove the temporary file if necessary...
301 */
302
303 if (argc < 7)
304 unlink(filename);
305
306 /*
307 * Return the queue status...
308 */
309
310 return (status);
311}
312
313
43e2fc22 314/*
e73c6c0a 315 * 'lpd_command()' - Send an LPR command sequence and wait for a reply.
43e2fc22 316 */
317
e73c6c0a 318static int /* O - Status of command */
319lpd_command(int fd, /* I - Socket connection to LPD host */
320 char *format, /* I - printf()-style format string */
321 ...) /* I - Additional args as necessary */
322{
323 va_list ap; /* Argument pointer */
324 char buf[1024]; /* Output buffer */
325 int bytes; /* Number of bytes to output */
326 char status; /* Status from command */
327
328
329 /*
330 * Format the string...
331 */
332
333 va_start(ap, format);
6a536282 334 bytes = vsnprintf(buf, sizeof(buf), format, ap);
e73c6c0a 335 va_end(ap);
336
d21a7597 337 fprintf(stderr, "DEBUG: lpd_command %2.2x %s", buf[0], buf + 1);
e73c6c0a 338
339 /*
340 * Send the command...
341 */
342
85e96ec0 343 fprintf(stderr, "DEBUG: Sending command string (%d bytes)...\n", bytes);
344
f8529e1a 345 if (lpd_write(fd, buf, bytes) < bytes)
e73c6c0a 346 return (-1);
347
348 /*
349 * Read back the status from the command and return it...
350 */
351
85e96ec0 352 fprintf(stderr, "DEBUG: Reading command status...\n");
353
e73c6c0a 354 if (recv(fd, &status, 1, 0) < 1)
355 return (-1);
356
c8f9565c 357 fprintf(stderr, "DEBUG: lpd_command returning %d\n", status);
e73c6c0a 358
359 return (status);
360}
361
362
363/*
364 * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol.
365 */
366
367static int /* O - Zero on success, non-zero on failure */
368lpd_queue(char *hostname, /* I - Host to connect to */
369 char *printer, /* I - Printer/queue name */
370 char *filename, /* I - File to print */
371 char *user, /* I - Requesting user */
72a039d0 372 char *title, /* I - Job title */
373 int copies, /* I - Number of copies */
374 int banner, /* I - Print LPD banner? */
f8529e1a 375 int format, /* I - Format specifier */
376 int order) /* I - Order of data/control files */
e73c6c0a 377{
378 FILE *fp; /* Job file */
379 char localhost[255]; /* Local host name */
380 int error; /* Error number */
381 struct stat filestats; /* File statistics */
382 int port; /* LPD connection port */
383 int fd; /* LPD socket */
384 char control[10240], /* LPD control 'file' */
385 *cptr; /* Pointer into control file string */
386 char status; /* Status byte from command */
387 struct sockaddr_in addr; /* Socket address */
388 struct hostent *hostaddr; /* Host address */
389 size_t nbytes, /* Number of bytes written */
390 tbytes; /* Total bytes written */
391 char buffer[8192]; /* Output buffer */
4ff40357 392#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
393 struct sigaction action; /* Actions for POSIX signals */
394#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
e73c6c0a 395
396
397 /*
398 * First try to reserve a port for this connection...
399 */
400
753453e4 401 if ((hostaddr = httpGetHostByName(hostname)) == NULL)
e73c6c0a 402 {
403 fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s",
404 hostname, strerror(errno));
405 return (1);
406 }
407
408 fprintf(stderr, "INFO: Attempting to connect to host %s for printer %s\n",
409 hostname, printer);
410
411 memset(&addr, 0, sizeof(addr));
412 memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
413 addr.sin_family = hostaddr->h_addrtype;
414 addr.sin_port = htons(515); /* LPD/printer service */
415
416 for (port = 732;;)
417 {
55d0f521 418 if (getuid())
419 {
420 /*
421 * We're running as a normal user, so just create a regular socket...
422 */
423
424 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
425 {
426 perror("ERROR: Unable to create socket");
427 return (1);
428 }
429 }
430 else
e73c6c0a 431 {
55d0f521 432 /*
433 * We're running as root, so comply with RFC 1179 and reserve a
434 * priviledged port between 721 and 732...
435 */
436
437 if ((fd = rresvport(&port)) < 0)
438 {
439 perror("ERROR: Unable to reserve port");
440 sleep(30);
441 continue;
442 }
e73c6c0a 443 }
444
445 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
446 {
447 error = errno;
448 close(fd);
449 fd = -1;
450
451 if (error == ECONNREFUSED)
452 {
453 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
454 hostname);
455 sleep(30);
456 }
457 else if (error == EADDRINUSE)
458 {
459 port --;
460 if (port < 721)
461 port = 732;
462 }
463 else
464 {
decc1f36 465 perror("ERROR: Unable to connect to printer");
2cc18dd2 466 sleep(30);
e73c6c0a 467 }
468 }
469 else
470 break;
471 }
472
55d0f521 473 fprintf(stderr, "INFO: Connected from port %d...\n", port);
85e96ec0 474
4ff40357 475 /*
476 * Now that we are "connected" to the port, ignore SIGTERM so that we
477 * can finish out any page data the driver sends (e.g. to eject the
478 * current page...
479 */
480
481#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
482 sigset(SIGTERM, SIG_IGN);
483#elif defined(HAVE_SIGACTION)
484 memset(&action, 0, sizeof(action));
485
486 sigemptyset(&action.sa_mask);
487 action.sa_handler = SIG_IGN;
488 sigaction(SIGTERM, &action, NULL);
489#else
490 signal(SIGTERM, SIG_IGN);
491#endif /* HAVE_SIGSET */
492
e73c6c0a 493 /*
494 * Next, open the print file and figure out its size...
495 */
496
497 if (stat(filename, &filestats))
498 {
c8f9565c 499 perror("ERROR: unable to stat print file");
e73c6c0a 500 return (1);
501 }
502
503 if ((fp = fopen(filename, "rb")) == NULL)
504 {
c8f9565c 505 perror("ERROR: unable to open print file for reading");
e73c6c0a 506 return (1);
507 }
508
509 /*
510 * Send a job header to the printer, specifying no banner page and
511 * literal output...
512 */
513
514 lpd_command(fd, "\002%s\n", printer); /* Receive print job(s) */
515
516 gethostname(localhost, sizeof(localhost));
c8f9565c 517 localhost[31] = '\0'; /* RFC 1179, Section 7.2 - host name < 32 chars */
e73c6c0a 518
72a039d0 519 snprintf(control, sizeof(control), "H%s\nP%s\nJ%s\n", localhost, user, title);
e73c6c0a 520 cptr = control + strlen(control);
521
72a039d0 522 if (banner)
523 {
524 snprintf(cptr, sizeof(control) - (cptr - control), "L%s\n", user);
525 cptr += strlen(cptr);
526 }
527
e73c6c0a 528 while (copies > 0)
529 {
72a039d0 530 snprintf(cptr, sizeof(control) - (cptr - control), "%cdfA%03d%s\n", format,
970017a4 531 getpid() % 1000, localhost);
e73c6c0a 532 cptr += strlen(cptr);
533 copies --;
534 }
535
6a536282 536 snprintf(cptr, sizeof(control) - (cptr - control),
d21a7597 537 "UdfA%03d%s\nNdfA%03d%s\n",
970017a4 538 getpid() % 1000, localhost,
539 getpid() % 1000, localhost);
e73c6c0a 540
c8f9565c 541 fprintf(stderr, "DEBUG: Control file is:\n%s", control);
e73c6c0a 542
f8529e1a 543 if (order == ORDER_CONTROL_DATA)
544 {
545 lpd_command(fd, "\002%d cfA%03.3d%s\n", strlen(control), getpid() % 1000,
546 localhost);
547
548 fprintf(stderr, "INFO: Sending control file (%d bytes)\n", strlen(control));
e73c6c0a 549
f8529e1a 550 if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
551 {
552 status = errno;
553 perror("ERROR: Unable to write control file");
554 }
555 else if (read(fd, &status, 1) < 1)
556 status = errno;
e73c6c0a 557
f8529e1a 558 if (status != 0)
559 fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
560 status);
561 else
562 fputs("INFO: Control file sent successfully\n", stderr);
e73c6c0a 563 }
e73c6c0a 564 else
f8529e1a 565 status = 0;
566
567 if (status == 0)
e73c6c0a 568 {
569 /*
570 * Send the print file...
571 */
572
03ccea6f 573 lpd_command(fd, "\003%u dfA%03.3d%s\n", (unsigned)filestats.st_size,
e73c6c0a 574 getpid() % 1000, localhost);
575
c8f9565c 576 fprintf(stderr, "INFO: Sending data file (%u bytes)\n",
decc1f36 577 (unsigned)filestats.st_size);
e73c6c0a 578
579 tbytes = 0;
580 while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
581 {
582 fprintf(stderr, "INFO: Spooling LPR job, %u%% complete...\n",
5718c6e5 583 (unsigned)(100.0f * tbytes / filestats.st_size));
e73c6c0a 584
f8529e1a 585 if (lpd_write(fd, buffer, nbytes) < nbytes)
e73c6c0a 586 {
decc1f36 587 perror("ERROR: Unable to send print file to printer");
e73c6c0a 588 break;
589 }
590 else
591 tbytes += nbytes;
592 }
593
e73c6c0a 594 if (tbytes < filestats.st_size)
f8529e1a 595 status = errno;
596 else if (lpd_write(fd, "", 1) < 1)
597 status = errno;
598 else if (recv(fd, &status, 1, 0) < 1)
599 status = errno;
600
601 if (status != 0)
e73c6c0a 602 fprintf(stderr, "ERROR: Remote host did not accept data file (%d)\n",
603 status);
604 else
c8f9565c 605 fputs("INFO: Data file sent successfully\n", stderr);
e73c6c0a 606 }
607
f8529e1a 608 if (status == 0 && order == ORDER_DATA_CONTROL)
609 {
610 lpd_command(fd, "\002%d cfA%03.3d%s\n", strlen(control), getpid() % 1000,
611 localhost);
612
613 fprintf(stderr, "INFO: Sending control file (%d bytes)\n", strlen(control));
614
615 if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
616 {
617 status = errno;
618 perror("ERROR: Unable to write control file");
619 }
620 else if (read(fd, &status, 1) < 1)
621 status = errno;
622
623 if (status != 0)
624 fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
625 status);
626 else
627 fputs("INFO: Control file sent successfully\n", stderr);
628 }
629
e73c6c0a 630 /*
631 * Close the socket connection and input file and return...
632 */
633
634 close(fd);
635 fclose(fp);
636
637 return (status);
638}
43e2fc22 639
640
641/*
f8529e1a 642 * 'lpd_write()' - Write a buffer of data to an LPD server.
643 */
644
645static int /* O - Number of bytes written or -1 on error */
646lpd_write(int lpd_fd, /* I - LPD socket */
647 char *buffer, /* I - Buffer to write */
648 int length) /* I - Number of bytes to write */
649{
650 int bytes, /* Number of bytes written */
651 total; /* Total number of bytes written */
652
653
654 total = 0;
655 while ((bytes = send(lpd_fd, buffer, length - total, 0)) >= 0)
656 {
657 total += bytes;
658 buffer += bytes;
659
660 if (total == length)
661 break;
662 }
663
664 if (bytes < 0)
665 return (-1);
666 else
667 return (length);
668}
669
670
671/*
753453e4 672 * End of "$Id: lpd.c,v 1.28.2.1 2001/12/26 16:52:06 mike Exp $".
43e2fc22 673 */