]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/lpd.c
Import cups.org releases
[thirdparty/cups.git] / backend / lpd.c
1 /*
2 * "$Id$"
3 *
4 * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2001 by Easy Software Products, all rights reserved.
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
17 * 44141 Airport View Drive, Suite 204
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 *
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.
29 * lpd_write() - Write a buffer of data to an LPD server.
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>
40 #include <ctype.h>
41 #include <cups/string.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <signal.h>
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
56
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
65 /*
66 * It appears that rresvport() is never declared on most systems...
67 */
68
69 extern int rresvport(int *port);
70
71
72 /*
73 * Local functions...
74 */
75
76 static int lpd_command(int lpd_fd, char *format, ...);
77 static int lpd_queue(char *hostname, char *printer, char *filename,
78 int fromstdin, char *user, char *title, int copies,
79 int banner, int format, int order);
80 static int lpd_write(int lpd_fd, char *buffer, int length);
81
82
83 /*
84 * 'main()' - Send a file to the printer or server.
85 *
86 * Usage:
87 *
88 * printer-uri job-id user title copies options [file]
89 */
90
91 int /* O - Exit status */
92 main(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) */
99 *options, /* Pointer to options */
100 name[255], /* Name of option */
101 value[255], /* Value of option */
102 *ptr, /* Pointer into name or value */
103 filename[1024], /* File to print */
104 title[256]; /* Title string */
105 int port; /* Port number (not used) */
106 int status; /* Status of LPD job */
107 int banner; /* Print banner page? */
108 int format; /* Print format */
109 int order; /* Order of control/data files */
110
111
112 /*
113 * Make sure status messages are not buffered...
114 */
115
116 setbuf(stderr, NULL);
117
118 /*
119 * Check command-line...
120 */
121
122 if (argc == 1)
123 {
124 puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\"");
125 return (0);
126 }
127 else if (argc < 6 || argc > 7)
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
146 int fd; /* Temporary file */
147 char buffer[8192]; /* Buffer for copying */
148 int bytes; /* Number of bytes read */
149
150
151 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
152 {
153 perror("ERROR: unable to create temporary file");
154 return (1);
155 }
156
157 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
158 if (write(fd, buffer, bytes) < bytes)
159 {
160 perror("ERROR: unable to write to temporary file");
161 close(fd);
162 unlink(filename);
163 return (1);
164 }
165
166 close(fd);
167 }
168 else
169 {
170 strncpy(filename, argv[6], sizeof(filename) - 1);
171 filename[sizeof(filename) - 1] = '\0';
172 }
173
174 /*
175 * Extract the hostname and printer name from the URI...
176 */
177
178 httpSeparate(argv[0], method, username, hostname, &port, resource);
179
180 /*
181 * See if there are any options...
182 */
183
184 banner = 0;
185 format = 'l';
186 order = ORDER_CONTROL_DATA;
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 }
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 }
268 }
269 }
270
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
282 /*
283 * Queue the job...
284 */
285
286 if (argc > 6)
287 {
288 status = lpd_queue(hostname, resource + 1, filename, 0,
289 argv[2] /* user */, title, atoi(argv[4]) /* copies */,
290 banner, format, order);
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, 1,
297 argv[2] /* user */, title, 1, banner, format, order);
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
314 /*
315 * 'lpd_command()' - Send an LPR command sequence and wait for a reply.
316 */
317
318 static int /* O - Status of command */
319 lpd_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);
334 bytes = vsnprintf(buf, sizeof(buf), format, ap);
335 va_end(ap);
336
337 fprintf(stderr, "DEBUG: lpd_command %2.2x %s", buf[0], buf + 1);
338
339 /*
340 * Send the command...
341 */
342
343 fprintf(stderr, "DEBUG: Sending command string (%d bytes)...\n", bytes);
344
345 if (lpd_write(fd, buf, bytes) < bytes)
346 return (-1);
347
348 /*
349 * Read back the status from the command and return it...
350 */
351
352 fprintf(stderr, "DEBUG: Reading command status...\n");
353
354 if (recv(fd, &status, 1, 0) < 1)
355 return (-1);
356
357 fprintf(stderr, "DEBUG: lpd_command returning %d\n", status);
358
359 return (status);
360 }
361
362
363 /*
364 * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol.
365 */
366
367 static int /* O - Zero on success, non-zero on failure */
368 lpd_queue(char *hostname, /* I - Host to connect to */
369 char *printer, /* I - Printer/queue name */
370 char *filename, /* I - File to print */
371 int fromstdin, /* I - Printing from stdin? */
372 char *user, /* I - Requesting user */
373 char *title, /* I - Job title */
374 int copies, /* I - Number of copies */
375 int banner, /* I - Print LPD banner? */
376 int format, /* I - Format specifier */
377 int order) /* I - Order of data/control files */
378 {
379 FILE *fp; /* Job file */
380 char localhost[255]; /* Local host name */
381 int error; /* Error number */
382 struct stat filestats; /* File statistics */
383 int port; /* LPD connection port */
384 int fd; /* LPD socket */
385 char control[10240], /* LPD control 'file' */
386 *cptr; /* Pointer into control file string */
387 char status; /* Status byte from command */
388 struct sockaddr_in addr; /* Socket address */
389 struct hostent *hostaddr; /* Host address */
390 size_t nbytes, /* Number of bytes written */
391 tbytes; /* Total bytes written */
392 char buffer[8192]; /* Output buffer */
393 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
394 struct sigaction action; /* Actions for POSIX signals */
395 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
396
397
398 /*
399 * First try to reserve a port for this connection...
400 */
401
402 if ((hostaddr = gethostbyname(hostname)) == NULL)
403 {
404 fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s",
405 hostname, strerror(errno));
406 return (1);
407 }
408
409 fprintf(stderr, "INFO: Attempting to connect to host %s for printer %s\n",
410 hostname, printer);
411
412 memset(&addr, 0, sizeof(addr));
413 memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
414 addr.sin_family = hostaddr->h_addrtype;
415 addr.sin_port = htons(515); /* LPD/printer service */
416
417 for (port = 732;;)
418 {
419 if (getuid())
420 {
421 /*
422 * We're running as a normal user, so just create a regular socket...
423 */
424
425 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
426 {
427 perror("ERROR: Unable to create socket");
428 return (1);
429 }
430 }
431 else
432 {
433 /*
434 * We're running as root, so comply with RFC 1179 and reserve a
435 * priviledged port between 721 and 732...
436 */
437
438 if ((fd = rresvport(&port)) < 0)
439 {
440 perror("ERROR: Unable to reserve port");
441 sleep(30);
442 continue;
443 }
444 }
445
446 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
447 {
448 error = errno;
449 close(fd);
450 fd = -1;
451
452 if (error == ECONNREFUSED)
453 {
454 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
455 hostname);
456 sleep(30);
457 }
458 else if (error == EADDRINUSE)
459 {
460 port --;
461 if (port < 721)
462 port = 732;
463 }
464 else
465 {
466 perror("ERROR: Unable to connect to printer");
467 sleep(30);
468 }
469 }
470 else
471 break;
472 }
473
474 fprintf(stderr, "INFO: Connected from port %d...\n", port);
475
476 /*
477 * Now that we are "connected" to the port, ignore SIGTERM so that we
478 * can finish out any page data the driver sends (e.g. to eject the
479 * current page... Only ignore SIGTERM if we are printing data from
480 * stdin (otherwise you can't cancel raw jobs...)
481 */
482
483 if (fromstdin)
484 {
485 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
486 sigset(SIGTERM, SIG_IGN);
487 #elif defined(HAVE_SIGACTION)
488 memset(&action, 0, sizeof(action));
489
490 sigemptyset(&action.sa_mask);
491 action.sa_handler = SIG_IGN;
492 sigaction(SIGTERM, &action, NULL);
493 #else
494 signal(SIGTERM, SIG_IGN);
495 #endif /* HAVE_SIGSET */
496 }
497
498 /*
499 * Next, open the print file and figure out its size...
500 */
501
502 if (stat(filename, &filestats))
503 {
504 perror("ERROR: unable to stat print file");
505 return (1);
506 }
507
508 if ((fp = fopen(filename, "rb")) == NULL)
509 {
510 perror("ERROR: unable to open print file for reading");
511 return (1);
512 }
513
514 /*
515 * Send a job header to the printer, specifying no banner page and
516 * literal output...
517 */
518
519 lpd_command(fd, "\002%s\n", printer); /* Receive print job(s) */
520
521 gethostname(localhost, sizeof(localhost));
522 localhost[31] = '\0'; /* RFC 1179, Section 7.2 - host name < 32 chars */
523
524 snprintf(control, sizeof(control), "H%s\nP%s\nJ%s\n", localhost, user, title);
525 cptr = control + strlen(control);
526
527 if (banner)
528 {
529 snprintf(cptr, sizeof(control) - (cptr - control), "L%s\n", user);
530 cptr += strlen(cptr);
531 }
532
533 while (copies > 0)
534 {
535 snprintf(cptr, sizeof(control) - (cptr - control), "%cdfA%03d%s\n", format,
536 getpid() % 1000, localhost);
537 cptr += strlen(cptr);
538 copies --;
539 }
540
541 snprintf(cptr, sizeof(control) - (cptr - control),
542 "UdfA%03d%s\nNdfA%03d%s\n",
543 getpid() % 1000, localhost,
544 getpid() % 1000, localhost);
545
546 fprintf(stderr, "DEBUG: Control file is:\n%s", control);
547
548 if (order == ORDER_CONTROL_DATA)
549 {
550 lpd_command(fd, "\002%d cfA%03.3d%s\n", strlen(control), getpid() % 1000,
551 localhost);
552
553 fprintf(stderr, "INFO: Sending control file (%d bytes)\n", strlen(control));
554
555 if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
556 {
557 status = errno;
558 perror("ERROR: Unable to write control file");
559 }
560 else if (read(fd, &status, 1) < 1)
561 status = errno;
562
563 if (status != 0)
564 fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
565 status);
566 else
567 fputs("INFO: Control file sent successfully\n", stderr);
568 }
569 else
570 status = 0;
571
572 if (status == 0)
573 {
574 /*
575 * Send the print file...
576 */
577
578 lpd_command(fd, "\003%u dfA%03.3d%s\n", (unsigned)filestats.st_size,
579 getpid() % 1000, localhost);
580
581 fprintf(stderr, "INFO: Sending data file (%u bytes)\n",
582 (unsigned)filestats.st_size);
583
584 tbytes = 0;
585 while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
586 {
587 fprintf(stderr, "INFO: Spooling LPR job, %u%% complete...\n",
588 (unsigned)(100.0f * tbytes / filestats.st_size));
589
590 if (lpd_write(fd, buffer, nbytes) < nbytes)
591 {
592 perror("ERROR: Unable to send print file to printer");
593 break;
594 }
595 else
596 tbytes += nbytes;
597 }
598
599 if (tbytes < filestats.st_size)
600 status = errno;
601 else if (lpd_write(fd, "", 1) < 1)
602 status = errno;
603 else if (recv(fd, &status, 1, 0) < 1)
604 status = errno;
605
606 if (status != 0)
607 fprintf(stderr, "ERROR: Remote host did not accept data file (%d)\n",
608 status);
609 else
610 fputs("INFO: Data file sent successfully\n", stderr);
611 }
612
613 if (status == 0 && order == ORDER_DATA_CONTROL)
614 {
615 lpd_command(fd, "\002%d cfA%03.3d%s\n", strlen(control), getpid() % 1000,
616 localhost);
617
618 fprintf(stderr, "INFO: Sending control file (%d bytes)\n", strlen(control));
619
620 if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
621 {
622 status = errno;
623 perror("ERROR: Unable to write control file");
624 }
625 else if (read(fd, &status, 1) < 1)
626 status = errno;
627
628 if (status != 0)
629 fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
630 status);
631 else
632 fputs("INFO: Control file sent successfully\n", stderr);
633 }
634
635 /*
636 * Close the socket connection and input file and return...
637 */
638
639 close(fd);
640 fclose(fp);
641
642 return (status);
643 }
644
645
646 /*
647 * 'lpd_write()' - Write a buffer of data to an LPD server.
648 */
649
650 static int /* O - Number of bytes written or -1 on error */
651 lpd_write(int lpd_fd, /* I - LPD socket */
652 char *buffer, /* I - Buffer to write */
653 int length) /* I - Number of bytes to write */
654 {
655 int bytes, /* Number of bytes written */
656 total; /* Total number of bytes written */
657
658
659 total = 0;
660 while ((bytes = send(lpd_fd, buffer, length - total, 0)) >= 0)
661 {
662 total += bytes;
663 buffer += bytes;
664
665 if (total == length)
666 break;
667 }
668
669 if (bytes < 0)
670 return (-1);
671 else
672 return (length);
673 }
674
675
676 /*
677 * End of "$Id$".
678 */