Revert manifest to default one
[external/cups.git] / scheduler / cups-lpd.c
1 /*
2  * "$Id: cups-lpd.c 10379 2012-03-23 22:16:22Z mike $"
3  *
4  *   Line Printer Daemon interface for CUPS.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  * Contents:
16  *
17  *   main()           - Process an incoming LPD request...
18  *   create_job()     - Create a new print job.
19  *   get_printer()    - Get the named printer and its options.
20  *   print_file()     - Add a file to the current job.
21  *   recv_print_job() - Receive a print job from the client.
22  *   remove_jobs()    - Cancel one or more jobs.
23  *   send_state()     - Send the queue state.
24  *   smart_gets()     - Get a line of text, removing the trailing CR and/or LF.
25  */
26
27 /*
28  * Include necessary headers...
29  */
30
31 #include <cups/cups-private.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <netdb.h>
39
40 #ifdef HAVE_INTTYPES_H
41 #  include <inttypes.h>
42 #endif /* HAVE_INTTYPES_H */
43
44
45 /*
46  * LPD "mini-daemon" for CUPS.  This program must be used in conjunction
47  * with inetd or another similar program that monitors ports and starts
48  * daemons for each client connection.  A typical configuration is:
49  *
50  *    printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
51  *
52  * This daemon implements most of RFC 1179 (the unofficial LPD specification)
53  * except for:
54  *
55  *     - This daemon does not check to make sure that the source port is
56  *       between 721 and 731, since it isn't necessary for proper
57  *       functioning and port-based security is no security at all!
58  *
59  *     - The "Print any waiting jobs" command is a no-op.
60  *
61  * The LPD-to-IPP mapping is as defined in RFC 2569.  The report formats
62  * currently match the Solaris LPD mini-daemon.
63  */
64
65 /*
66  * Prototypes...
67  */
68
69 static int      create_job(http_t *http, const char *dest, const char *title,
70                            const char *docname, const char *user,
71                            int num_options, cups_option_t *options);
72 static int      get_printer(http_t *http, const char *name, char *dest,
73                             int destsize, cups_option_t **options,
74                             int *accepting, int *shared, ipp_pstate_t *state);
75 static int      print_file(http_t *http, int id, const char *filename,
76                            const char *docname, const char *user,
77                            const char *format, int last);
78 static int      recv_print_job(const char *name, int num_defaults,
79                                cups_option_t *defaults);
80 static int      remove_jobs(const char *name, const char *agent,
81                             const char *list);
82 static int      send_state(const char *name, const char *list,
83                            int longstatus);
84 static char     *smart_gets(char *s, int len, FILE *fp);
85
86
87 /*
88  * 'main()' - Process an incoming LPD request...
89  */
90
91 int                                     /* O - Exit status */
92 main(int  argc,                         /* I - Number of command-line arguments */
93      char *argv[])                      /* I - Command-line arguments */
94 {
95   int           i;                      /* Looping var */
96   int           num_defaults;           /* Number of default options */
97   cups_option_t *defaults;              /* Default options */
98   char          line[256],              /* Command string */
99                 command,                /* Command code */
100                 *dest,                  /* Pointer to destination */
101                 *list,                  /* Pointer to list */
102                 *agent,                 /* Pointer to user */
103                 status;                 /* Status for client */
104   socklen_t     hostlen;                /* Size of client address */
105   http_addr_t   hostaddr;               /* Address of client */
106   char          hostname[256],          /* Name of client */
107                 hostip[256],            /* IP address */
108                 *hostfamily;            /* Address family */
109   int           hostlookups;            /* Do hostname lookups? */
110
111
112  /*
113   * Don't buffer the output...
114   */
115
116   setbuf(stdout, NULL);
117
118  /*
119   * Log things using the "cups-lpd" name...
120   */
121
122   openlog("cups-lpd", LOG_PID, LOG_LPR);
123
124  /*
125   * Scan the command-line for options...
126   */
127
128   num_defaults = 0;
129   defaults     = NULL;
130   hostlookups  = 1;
131
132   for (i = 1; i < argc; i ++)
133     if (argv[i][0] == '-')
134     {
135       switch (argv[i][1])
136       {
137         case 'h' : /* -h hostname[:port] */
138             if (argv[i][2])
139               cupsSetServer(argv[i] + 2);
140             else
141             {
142               i ++;
143               if (i < argc)
144                 cupsSetServer(argv[i]);
145               else
146                 syslog(LOG_WARNING, "Expected hostname string after -h option!");
147             }
148             break;
149
150         case 'o' : /* Option */
151             if (argv[i][2])
152               num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
153                                               &defaults);
154             else
155             {
156               i ++;
157               if (i < argc)
158                 num_defaults = cupsParseOptions(argv[i], num_defaults,
159                                                 &defaults);
160               else
161                 syslog(LOG_WARNING, "Expected option string after -o option!");
162             }
163             break;
164
165         case 'n' : /* Don't do hostname lookups */
166             hostlookups = 0;
167             break;
168
169         default :
170             syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
171             break;
172       }
173     }
174     else
175       syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
176              argv[i]);
177
178  /*
179   * Get the address of the client...
180   */
181
182   hostlen = sizeof(hostaddr);
183
184   if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
185   {
186     syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
187     strcpy(hostname, "unknown");
188   }
189   else
190   {
191     httpAddrString(&hostaddr, hostip, sizeof(hostip));
192
193     if (hostlookups)
194       httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
195     else
196       strlcpy(hostname, hostip, sizeof(hostname));
197
198 #ifdef AF_INET6
199     if (hostaddr.addr.sa_family == AF_INET6)
200       hostfamily = "IPv6";
201     else
202 #endif /* AF_INET6 */
203     hostfamily = "IPv4";
204
205     syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
206            hostip);
207   }
208
209   num_defaults = cupsAddOption("job-originating-host-name", hostname,
210                                num_defaults, &defaults);
211
212  /*
213   * RFC1179 specifies that only 1 daemon command can be received for
214   * every connection.
215   */
216
217   if (smart_gets(line, sizeof(line), stdin) == NULL)
218   {
219    /*
220     * Unable to get command from client!  Send an error status and return.
221     */
222
223     syslog(LOG_ERR, "Unable to get command line from client!");
224     putchar(1);
225     return (1);
226   }
227
228  /*
229   * The first byte is the command byte.  After that will be the queue name,
230   * resource list, and/or user name.
231   */
232
233   command = line[0];
234   dest    = line + 1;
235
236   if (command == 0x02)
237     list = NULL;
238   else
239   {
240     for (list = dest; *list && !isspace(*list & 255); list ++);
241
242     while (isspace(*list & 255))
243       *list++ = '\0';
244   }
245
246  /*
247   * Do the command...
248   */
249
250   switch (command)
251   {
252     default : /* Unknown command */
253         syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
254         syslog(LOG_ERR, "Command line = %s", line + 1);
255         putchar(1);
256
257         status = 1;
258         break;
259
260     case 0x01 : /* Print any waiting jobs */
261         syslog(LOG_INFO, "Print waiting jobs (no-op)");
262         putchar(0);
263
264         status = 0;
265         break;
266
267     case 0x02 : /* Receive a printer job */
268         syslog(LOG_INFO, "Receive print job for %s", dest);
269         /* recv_print_job() sends initial status byte */
270
271         status = recv_print_job(dest, num_defaults, defaults);
272         break;
273
274     case 0x03 : /* Send queue state (short) */
275         syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
276         /* no status byte for this command */
277
278         status = send_state(dest, list, 0);
279         break;
280
281     case 0x04 : /* Send queue state (long) */
282         syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
283         /* no status byte for this command */
284
285         status = send_state(dest, list, 1);
286         break;
287
288     case 0x05 : /* Remove jobs */
289         if (list)
290         {
291          /*
292           * Grab the agent and skip to the list of users and/or jobs.
293           */
294
295           agent = list;
296
297           for (; *list && !isspace(*list & 255); list ++);
298           while (isspace(*list & 255))
299             *list++ = '\0';
300
301           syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
302
303           status = remove_jobs(dest, agent, list);
304         }
305         else
306           status = 1;
307
308         putchar(status);
309         break;
310   }
311
312   syslog(LOG_INFO, "Closing connection");
313   closelog();
314
315   return (status);
316 }
317
318
319 /*
320  * 'create_job()' - Create a new print job.
321  */
322
323 static int                              /* O - Job ID or -1 on error */
324 create_job(http_t        *http,         /* I - HTTP connection */
325            const char    *dest,         /* I - Destination name */
326            const char    *title,        /* I - job-name */
327            const char    *docname,      /* I - Name of job file */
328            const char    *user,         /* I - requesting-user-name */
329            int           num_options,   /* I - Number of options for job */
330            cups_option_t *options)      /* I - Options for job */
331 {
332   ipp_t         *request;               /* IPP request */
333   ipp_t         *response;              /* IPP response */
334   ipp_attribute_t *attr;                /* IPP attribute */
335   char          uri[HTTP_MAX_URI];      /* Printer URI */
336   int           id;                     /* Job ID */
337
338
339  /*
340   * Setup the Create-Job request...
341   */
342
343   request = ippNewRequest(IPP_CREATE_JOB);
344
345   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
346                    "localhost", 0, "/printers/%s", dest);
347
348   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
349                NULL, uri);
350
351   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
352                "requesting-user-name", NULL, user);
353
354   if (title[0])
355     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
356                  NULL, title);
357
358   if (docname[0])
359     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
360                  NULL, docname);
361
362   cupsEncodeOptions(request, num_options, options);
363
364  /*
365   * Do the request...
366   */
367
368   snprintf(uri, sizeof(uri), "/printers/%s", dest);
369
370   response = cupsDoRequest(http, request, uri);
371
372   if (!response || cupsLastError() > IPP_OK_CONFLICT)
373   {
374     syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
375
376     ippDelete(response);
377
378     return (-1);
379   }
380
381  /*
382   * Get the job-id value from the response and return it...
383   */
384
385   if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
386   {
387     id = -1;
388
389     syslog(LOG_ERR, "No job-id attribute found in response from server!");
390   }
391   else
392   {
393     id = attr->values[0].integer;
394
395     syslog(LOG_INFO, "Print file - job ID = %d", id);
396   }
397
398   ippDelete(response);
399
400   return (id);
401 }
402
403
404 /*
405  * 'get_printer()' - Get the named printer and its options.
406  */
407
408 static int                              /* O - Number of options or -1 on error */
409 get_printer(http_t        *http,        /* I - HTTP connection */
410             const char    *name,        /* I - Printer name from request */
411             char          *dest,        /* I - Destination buffer */
412             int           destsize,     /* I - Size of destination buffer */
413             cups_option_t **options,    /* O - Printer options */
414             int           *accepting,   /* O - printer-is-accepting-jobs value */
415             int           *shared,      /* O - printer-is-shared value */
416             ipp_pstate_t  *state)       /* O - printer-state value */
417 {
418   int           num_options;            /* Number of options */
419   cups_file_t   *fp;                    /* lpoptions file */
420   char          line[1024],             /* Line from lpoptions file */
421                 *value,                 /* Pointer to value on line */
422                 *optptr;                /* Pointer to options on line */
423   int           linenum;                /* Line number in file */
424   const char    *cups_serverroot;       /* CUPS_SERVERROOT env var */
425   ipp_t         *request;               /* IPP request */
426   ipp_t         *response;              /* IPP response */
427   ipp_attribute_t *attr;                /* IPP attribute */
428   char          uri[HTTP_MAX_URI];      /* Printer URI */
429   static const char * const requested[] =
430                 {                       /* Requested attributes */
431                   "printer-info",
432                   "printer-is-accepting-jobs",
433                   "printer-is-shared",
434                   "printer-name",
435                   "printer-state"
436                 };
437
438
439  /*
440   * Initialize everything...
441   */
442
443   if (accepting)
444     *accepting = 0;
445   if (shared)
446     *shared = 0;
447   if (state)
448     *state = IPP_PRINTER_STOPPED;
449   if (options)
450     *options = NULL;
451
452  /*
453   * See if the name is a queue name optionally with an instance name.
454   */
455
456   strlcpy(dest, name, destsize);
457   if ((value = strchr(dest, '/')) != NULL)
458     *value = '\0';
459
460  /*
461   * Setup the Get-Printer-Attributes request...
462   */
463
464   request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
465
466   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
467                    "localhost", 0, "/printers/%s", dest);
468
469   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
470                NULL, uri);
471
472   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
473                 "requested-attributes",
474                 (int)(sizeof(requested) / sizeof(requested[0])),
475                 NULL, requested);
476
477  /*
478   * Do the request...
479   */
480
481   response = cupsDoRequest(http, request, "/");
482
483   if (!response || cupsLastError() > IPP_OK_CONFLICT)
484   {
485    /*
486     * If we can't find the printer by name, look up the printer-name
487     * using the printer-info values...
488     */
489
490     ipp_attribute_t     *accepting_attr,/* printer-is-accepting-jobs */
491                         *info_attr,     /* printer-info */
492                         *name_attr,     /* printer-name */
493                         *shared_attr,   /* printer-is-shared */
494                         *state_attr;    /* printer-state */
495
496
497     ippDelete(response);
498
499    /*
500     * Setup the CUPS-Get-Printers request...
501     */
502
503     request = ippNewRequest(CUPS_GET_PRINTERS);
504
505     ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
506                   "requested-attributes",
507                   (int)(sizeof(requested) / sizeof(requested[0])),
508                   NULL, requested);
509
510    /*
511     * Do the request...
512     */
513
514     response = cupsDoRequest(http, request, "/");
515
516     if (!response || cupsLastError() > IPP_OK_CONFLICT)
517     {
518       syslog(LOG_ERR, "Unable to get list of printers - %s",
519              cupsLastErrorString());
520
521       ippDelete(response);
522
523       return (-1);
524     }
525
526    /*
527     * Scan the response for printers...
528     */
529
530     *dest = '\0';
531     attr  = response->attrs;
532
533     while (attr)
534     {
535      /*
536       * Skip to the next printer...
537       */
538
539       while (attr && attr->group_tag != IPP_TAG_PRINTER)
540         attr = attr->next;
541
542       if (!attr)
543         break;
544
545      /*
546       * Get all of the attributes for the current printer...
547       */
548
549       accepting_attr = NULL;
550       info_attr      = NULL;
551       name_attr      = NULL;
552       shared_attr    = NULL;
553       state_attr     = NULL;
554
555       while (attr && attr->group_tag == IPP_TAG_PRINTER)
556       {
557         if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
558             attr->value_tag == IPP_TAG_BOOLEAN)
559           accepting_attr = attr;
560         else if (!strcmp(attr->name, "printer-info") &&
561                  attr->value_tag == IPP_TAG_TEXT)
562           info_attr = attr;
563         else if (!strcmp(attr->name, "printer-name") &&
564                  attr->value_tag == IPP_TAG_NAME)
565           name_attr = attr;
566         else if (!strcmp(attr->name, "printer-is-shared") &&
567                  attr->value_tag == IPP_TAG_BOOLEAN)
568           shared_attr = attr;
569         else if (!strcmp(attr->name, "printer-state") &&
570                  attr->value_tag == IPP_TAG_ENUM)
571           state_attr = attr;
572
573         attr = attr->next;
574       }
575
576       if (info_attr && name_attr &&
577           !_cups_strcasecmp(name, info_attr->values[0].string.text))
578       {
579        /*
580         * Found a match, use this one!
581         */
582
583         strlcpy(dest, name_attr->values[0].string.text, destsize);
584
585         if (accepting && accepting_attr)
586           *accepting = accepting_attr->values[0].boolean;
587
588         if (shared && shared_attr)
589           *shared = shared_attr->values[0].boolean;
590
591         if (state && state_attr)
592           *state = (ipp_pstate_t)state_attr->values[0].integer;
593
594         break;
595       }
596     }
597
598     ippDelete(response);
599
600     if (!*dest)
601     {
602       syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
603
604       return (-1);
605     }
606
607     name = dest;
608   }
609   else
610   {
611    /*
612     * Get values from the response...
613     */
614
615     if (accepting)
616     {
617       if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
618                                    IPP_TAG_BOOLEAN)) == NULL)
619         syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
620                         "response from server!");
621       else
622         *accepting = attr->values[0].boolean;
623     }
624
625     if (shared)
626     {
627       if ((attr = ippFindAttribute(response, "printer-is-shared",
628                                    IPP_TAG_BOOLEAN)) == NULL)
629       {
630         syslog(LOG_ERR, "No printer-is-shared attribute found in "
631                         "response from server!");
632         *shared = 1;
633       }
634       else
635         *shared = attr->values[0].boolean;
636     }
637
638     if (state)
639     {
640       if ((attr = ippFindAttribute(response, "printer-state",
641                                    IPP_TAG_ENUM)) == NULL)
642         syslog(LOG_ERR, "No printer-state attribute found in "
643                         "response from server!");
644       else
645         *state = (ipp_pstate_t)attr->values[0].integer;
646     }
647
648     ippDelete(response);
649   }
650
651  /*
652   * Next look for the printer in the lpoptions file...
653   */
654
655   num_options = 0;
656
657   if (options && shared && accepting)
658   {
659     if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
660       cups_serverroot = CUPS_SERVERROOT;
661
662     snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
663     if ((fp = cupsFileOpen(line, "r")) != NULL)
664     {
665       linenum = 0;
666       while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
667       {
668        /*
669         * Make sure we have "Dest name options" or "Default name options"...
670         */
671
672         if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
673           continue;
674
675        /*
676         * Separate destination name from options...
677         */
678
679         for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
680
681         while (*optptr == ' ')
682           *optptr++ = '\0';
683
684        /*
685         * If this is our destination, parse the options and break out of
686         * the loop - we're done!
687         */
688
689         if (!_cups_strcasecmp(value, name))
690         {
691           num_options = cupsParseOptions(optptr, num_options, options);
692           break;
693         }
694       }
695
696       cupsFileClose(fp);
697     }
698   }
699   else if (options)
700     *options = NULL;
701
702  /*
703   * Return the number of options for this destination...
704   */
705
706   return (num_options);
707 }
708
709
710 /*
711  * 'print_file()' - Add a file to the current job.
712  */
713
714 static int                              /* O - 0 on success, -1 on failure */
715 print_file(http_t     *http,            /* I - HTTP connection */
716            int        id,               /* I - Job ID */
717            const char *filename,        /* I - File to print */
718            const char *docname,         /* I - document-name */
719            const char *user,            /* I - requesting-user-name */
720            const char *format,          /* I - document-format */
721            int        last)             /* I - 1 = last file in job */
722 {
723   ipp_t         *request;               /* IPP request */
724   char          uri[HTTP_MAX_URI];      /* Printer URI */
725
726
727  /*
728   * Setup the Send-Document request...
729   */
730
731   request = ippNewRequest(IPP_SEND_DOCUMENT);
732
733   snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
734   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
735
736   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
737                "requesting-user-name", NULL, user);
738
739   if (docname)
740     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
741                  "document-name", NULL, docname);
742
743   if (format)
744     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
745                  "document-format", NULL, format);
746
747   if (last)
748     ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
749
750  /*
751   * Do the request...
752   */
753
754   snprintf(uri, sizeof(uri), "/jobs/%d", id);
755
756   ippDelete(cupsDoFileRequest(http, request, uri, filename));
757
758   if (cupsLastError() > IPP_OK_CONFLICT)
759   {
760     syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
761
762     return (-1);
763   }
764
765   return (0);
766 }
767
768
769 /*
770  * 'recv_print_job()' - Receive a print job from the client.
771  */
772
773 static int                              /* O - Command status */
774 recv_print_job(
775     const char    *queue,               /* I - Printer name */
776     int           num_defaults,         /* I - Number of default options */
777     cups_option_t *defaults)            /* I - Default options */
778 {
779   http_t        *http;                  /* HTTP connection */
780   int           i;                      /* Looping var */
781   int           status;                 /* Command status */
782   int           fd;                     /* Temporary file */
783   FILE          *fp;                    /* File pointer */
784   char          filename[1024];         /* Temporary filename */
785   int           bytes;                  /* Bytes received */
786   char          line[256],              /* Line from file/stdin */
787                 command,                /* Command from line */
788                 *count,                 /* Number of bytes */
789                 *name;                  /* Name of file */
790   const char    *job_sheets;            /* Job sheets */
791   int           num_data;               /* Number of data files */
792   char          control[1024],          /* Control filename */
793                 data[100][256],         /* Data files */
794                 temp[100][1024];        /* Temporary files */
795   char          user[1024],             /* User name */
796                 title[1024],            /* Job title */
797                 docname[1024],          /* Document name */
798                 dest[256];              /* Printer/class queue */
799   int           accepting,              /* printer-is-accepting */
800                 shared,                 /* printer-is-shared */
801                 num_options;            /* Number of options */
802   cups_option_t *options;               /* Options */
803   int           id;                     /* Job ID */
804   int           docnumber,              /* Current document number */
805                 doccount;               /* Count of documents */
806
807
808  /*
809   * Connect to the server...
810   */
811
812   http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
813   if (!http)
814   {
815     syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
816
817     putchar(1);
818
819     return (1);
820   }
821
822  /*
823   * See if the printer is available...
824   */
825
826   num_options = get_printer(http, queue, dest, sizeof(dest), &options,
827                             &accepting, &shared, NULL);
828
829   if (num_options < 0 || !accepting || !shared)
830   {
831     if (dest[0])
832       syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
833              !accepting ? "accepting jobs" : "shared");
834     else
835       syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
836
837     httpClose(http);
838
839     putchar(1);
840
841     return (1);
842   }
843
844   putchar(0);                           /* OK so far... */
845
846  /*
847   * Read the request...
848   */
849
850   status   = 0;
851   num_data = 0;
852   fd       = -1;
853
854   control[0] = '\0';
855
856   while (smart_gets(line, sizeof(line), stdin) != NULL)
857   {
858     if (strlen(line) < 2)
859     {
860       status = 1;
861       break;
862     }
863
864     command = line[0];
865     count   = line + 1;
866
867     for (name = count + 1; *name && !isspace(*name & 255); name ++);
868     while (isspace(*name & 255))
869       *name++ = '\0';
870
871     switch (command)
872     {
873       default :
874       case 0x01 : /* Abort */
875           status = 1;
876           break;
877
878       case 0x02 : /* Receive control file */
879           if (strlen(name) < 2)
880           {
881             syslog(LOG_ERR, "Bad control file name \"%s\"", name);
882             putchar(1);
883             status = 1;
884             break;
885           }
886
887           if (control[0])
888           {
889            /*
890             * Append to the existing control file - the LPD spec is
891             * not entirely clear, but at least the OS/2 LPD code sends
892             * multiple control files per connection...
893             */
894
895             if ((fd = open(control, O_WRONLY)) < 0)
896             {
897               syslog(LOG_ERR,
898                      "Unable to append to temporary control file \"%s\" - %s",
899                      control, strerror(errno));
900               putchar(1);
901               status = 1;
902               break;
903             }
904
905             lseek(fd, 0, SEEK_END);
906           }
907           else
908           {
909             if ((fd = cupsTempFd(control, sizeof(control))) < 0)
910             {
911               syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
912                      control, strerror(errno));
913               putchar(1);
914               status = 1;
915               break;
916             }
917
918             strcpy(filename, control);
919           }
920           break;
921
922       case 0x03 : /* Receive data file */
923           if (strlen(name) < 2)
924           {
925             syslog(LOG_ERR, "Bad data file name \"%s\"", name);
926             putchar(1);
927             status = 1;
928             break;
929           }
930
931           if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
932           {
933            /*
934             * Too many data files...
935             */
936
937             syslog(LOG_ERR, "Too many data files (%d)", num_data);
938             putchar(1);
939             status = 1;
940             break;
941           }
942
943           strlcpy(data[num_data], name, sizeof(data[0]));
944
945           if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
946           {
947             syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
948                    temp[num_data], strerror(errno));
949             putchar(1);
950             status = 1;
951             break;
952           }
953
954           strcpy(filename, temp[num_data]);
955
956           num_data ++;
957           break;
958     }
959
960     putchar(status);
961
962     if (status)
963       break;
964
965    /*
966     * Copy the data or control file from the client...
967     */
968
969     for (i = atoi(count); i > 0; i -= bytes)
970     {
971       if (i > sizeof(line))
972         bytes = sizeof(line);
973       else
974         bytes = i;
975
976       if ((bytes = fread(line, 1, bytes, stdin)) > 0)
977         bytes = write(fd, line, bytes);
978
979       if (bytes < 1)
980       {
981         syslog(LOG_ERR, "Error while reading file - %s",
982                strerror(errno));
983         status = 1;
984         break;
985       }
986     }
987
988    /*
989     * Read trailing nul...
990     */
991
992     if (!status)
993     {
994       if (fread(line, 1, 1, stdin) < 1)
995       {
996         status = 1;
997         syslog(LOG_ERR, "Error while reading trailing nul - %s",
998                strerror(errno));
999       }
1000       else if (line[0])
1001       {
1002         status = 1;
1003         syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
1004                line[0]);
1005       }
1006     }
1007
1008    /*
1009     * Close the file and send an acknowledgement...
1010     */
1011
1012     close(fd);
1013
1014     putchar(status);
1015
1016     if (status)
1017       break;
1018   }
1019
1020   if (!status)
1021   {
1022    /*
1023     * Process the control file and print stuff...
1024     */
1025
1026     if ((fp = fopen(control, "rb")) == NULL)
1027       status = 1;
1028     else
1029     {
1030      /*
1031       * Copy the default options...
1032       */
1033
1034       for (i = 0; i < num_defaults; i ++)
1035         num_options = cupsAddOption(defaults[i].name,
1036                                     defaults[i].value,
1037                                     num_options, &options);
1038
1039      /*
1040       * Grab the job information...
1041       */
1042
1043       title[0]   = '\0';
1044       user[0]    = '\0';
1045       docname[0] = '\0';
1046       doccount   = 0;
1047
1048       while (smart_gets(line, sizeof(line), fp) != NULL)
1049       {
1050        /*
1051         * Process control lines...
1052         */
1053
1054         switch (line[0])
1055         {
1056           case 'J' : /* Job name */
1057               strlcpy(title, line + 1, sizeof(title));
1058               break;
1059
1060           case 'N' : /* Document name */
1061               strlcpy(docname, line + 1, sizeof(docname));
1062               break;
1063
1064           case 'P' : /* User identification */
1065               strlcpy(user, line + 1, sizeof(user));
1066               break;
1067
1068           case 'L' : /* Print banner page */
1069              /*
1070               * If a banner was requested and it's not overridden by a
1071               * command line option and the destination's default is none
1072               * then add the standard banner...
1073               */
1074
1075               if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
1076                   ((job_sheets = cupsGetOption("job-sheets", num_options,
1077                                                options)) == NULL ||
1078                    !strcmp(job_sheets, "none,none")))
1079               {
1080                 num_options = cupsAddOption("job-sheets", "standard",
1081                                             num_options, &options);
1082               }
1083               break;
1084
1085           case 'c' : /* Plot CIF file */
1086           case 'd' : /* Print DVI file */
1087           case 'f' : /* Print formatted file */
1088           case 'g' : /* Plot file */
1089           case 'l' : /* Print file leaving control characters (raw) */
1090           case 'n' : /* Print ditroff output file */
1091           case 'o' : /* Print PostScript output file */
1092           case 'p' : /* Print file with 'pr' format (prettyprint) */
1093           case 'r' : /* File to print with FORTRAN carriage control */
1094           case 't' : /* Print troff output file */
1095           case 'v' : /* Print raster file */
1096               doccount ++;
1097
1098               if (line[0] == 'l' &&
1099                   !cupsGetOption("document-format", num_options, options))
1100                 num_options = cupsAddOption("raw", "", num_options, &options);
1101
1102               if (line[0] == 'p')
1103                 num_options = cupsAddOption("prettyprint", "", num_options,
1104                                             &options);
1105               break;
1106         }
1107
1108         if (status)
1109           break;
1110       }
1111
1112      /*
1113       * Check that we have a username...
1114       */
1115
1116       if (!user[0])
1117       {
1118         syslog(LOG_WARNING, "No username specified by client! "
1119                             "Using \"anonymous\"...");
1120         strcpy(user, "anonymous");
1121       }
1122
1123      /*
1124       * Create the job...
1125       */
1126
1127       if ((id = create_job(http, dest, title, docname, user, num_options,
1128                            options)) < 0)
1129         status = 1;
1130       else
1131       {
1132        /*
1133         * Then print the job files...
1134         */
1135
1136         rewind(fp);
1137
1138         docname[0] = '\0';
1139         docnumber  = 0;
1140
1141         while (smart_gets(line, sizeof(line), fp) != NULL)
1142         {
1143          /*
1144           * Process control lines...
1145           */
1146
1147           switch (line[0])
1148           {
1149             case 'N' : /* Document name */
1150                 strlcpy(docname, line + 1, sizeof(docname));
1151                 break;
1152
1153             case 'c' : /* Plot CIF file */
1154             case 'd' : /* Print DVI file */
1155             case 'f' : /* Print formatted file */
1156             case 'g' : /* Plot file */
1157             case 'l' : /* Print file leaving control characters (raw) */
1158             case 'n' : /* Print ditroff output file */
1159             case 'o' : /* Print PostScript output file */
1160             case 'p' : /* Print file with 'pr' format (prettyprint) */
1161             case 'r' : /* File to print with FORTRAN carriage control */
1162             case 't' : /* Print troff output file */
1163             case 'v' : /* Print raster file */
1164                /*
1165                 * Figure out which file we are printing...
1166                 */
1167
1168                 for (i = 0; i < num_data; i ++)
1169                   if (!strcmp(data[i], line + 1))
1170                     break;
1171
1172                 if (i >= num_data)
1173                 {
1174                   status = 1;
1175                   break;
1176                 }
1177
1178                /*
1179                 * Send the print file...
1180                 */
1181
1182                 docnumber ++;
1183
1184                 if (print_file(http, id, temp[i], docname, user,
1185                                cupsGetOption("document-format", num_options,
1186                                              options),
1187                                docnumber == doccount))
1188                   status = 1;
1189                 else
1190                   status = 0;
1191
1192                 break;
1193           }
1194
1195           if (status)
1196             break;
1197         }
1198       }
1199
1200       fclose(fp);
1201     }
1202   }
1203
1204   cupsFreeOptions(num_options, options);
1205
1206   httpClose(http);
1207
1208  /*
1209   * Clean up all temporary files and return...
1210   */
1211
1212   unlink(control);
1213
1214   for (i = 0; i < num_data; i ++)
1215     unlink(temp[i]);
1216
1217   return (status);
1218 }
1219
1220
1221 /*
1222  * 'remove_jobs()' - Cancel one or more jobs.
1223  */
1224
1225 static int                              /* O - Command status */
1226 remove_jobs(const char *dest,           /* I - Destination */
1227             const char *agent,          /* I - User agent */
1228             const char *list)           /* I - List of jobs or users */
1229 {
1230   int           id;                     /* Job ID */
1231   http_t        *http;                  /* HTTP server connection */
1232   ipp_t         *request;               /* IPP Request */
1233   char          uri[HTTP_MAX_URI];      /* Job URI */
1234
1235
1236   (void)dest;   /* Suppress compiler warnings... */
1237
1238  /*
1239   * Try connecting to the local server...
1240   */
1241
1242   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1243                                  cupsEncryption())) == NULL)
1244   {
1245     syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1246            strerror(errno));
1247     return (1);
1248   }
1249
1250  /*
1251   * Loop for each job...
1252   */
1253
1254   while ((id = atoi(list)) > 0)
1255   {
1256    /*
1257     * Skip job ID in list...
1258     */
1259
1260     while (isdigit(*list & 255))
1261       list ++;
1262     while (isspace(*list & 255))
1263       list ++;
1264
1265    /*
1266     * Build an IPP_CANCEL_JOB request, which requires the following
1267     * attributes:
1268     *
1269     *    attributes-charset
1270     *    attributes-natural-language
1271     *    job-uri
1272     *    requesting-user-name
1273     */
1274
1275     request = ippNewRequest(IPP_CANCEL_JOB);
1276
1277     sprintf(uri, "ipp://localhost/jobs/%d", id);
1278     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1279
1280     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1281                  "requesting-user-name", NULL, agent);
1282
1283    /*
1284     * Do the request and get back a response...
1285     */
1286
1287     ippDelete(cupsDoRequest(http, request, "/jobs"));
1288
1289     if (cupsLastError() > IPP_OK_CONFLICT)
1290     {
1291       syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
1292              cupsLastErrorString());
1293       httpClose(http);
1294       return (1);
1295     }
1296     else
1297       syslog(LOG_INFO, "Job ID %d canceled", id);
1298   }
1299
1300   httpClose(http);
1301
1302   return (0);
1303 }
1304
1305
1306 /*
1307  * 'send_state()' - Send the queue state.
1308  */
1309
1310 static int                              /* O - Command status */
1311 send_state(const char *queue,           /* I - Destination */
1312            const char *list,            /* I - Job or user */
1313            int        longstatus)       /* I - List of jobs or users */
1314 {
1315   int           id;                     /* Job ID from list */
1316   http_t        *http;                  /* HTTP server connection */
1317   ipp_t         *request,               /* IPP Request */
1318                 *response;              /* IPP Response */
1319   ipp_attribute_t *attr;                /* Current attribute */
1320   ipp_pstate_t  state;                  /* Printer state */
1321   const char    *jobdest,               /* Pointer into job-printer-uri */
1322                 *jobuser,               /* Pointer to job-originating-user-name */
1323                 *jobname;               /* Pointer to job-name */
1324   ipp_jstate_t  jobstate;               /* job-state */
1325   int           jobid,                  /* job-id */
1326                 jobsize,                /* job-k-octets */
1327                 jobcount,               /* Number of jobs */
1328                 jobcopies,              /* Number of copies */
1329                 rank;                   /* Rank of job */
1330   char          rankstr[255];           /* Rank string */
1331   char          namestr[1024];          /* Job name string */
1332   char          uri[HTTP_MAX_URI];      /* Printer URI */
1333   char          dest[256];              /* Printer/class queue */
1334   static const char * const ranks[10] = /* Ranking strings */
1335                 {
1336                   "th",
1337                   "st",
1338                   "nd",
1339                   "rd",
1340                   "th",
1341                   "th",
1342                   "th",
1343                   "th",
1344                   "th",
1345                   "th"
1346                 };
1347   static const char * const requested[] =
1348                 {                       /* Requested attributes */
1349                   "job-id",
1350                   "job-k-octets",
1351                   "job-state",
1352                   "job-printer-uri",
1353                   "job-originating-user-name",
1354                   "job-name",
1355                   "copies"
1356                 };
1357
1358
1359  /*
1360   * Try connecting to the local server...
1361   */
1362
1363   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1364                                  cupsEncryption())) == NULL)
1365   {
1366     syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1367            strerror(errno));
1368     printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1369     return (1);
1370   }
1371
1372  /*
1373   * Get the actual destination name and printer state...
1374   */
1375
1376   if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1377   {
1378     syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1379            cupsLastErrorString());
1380     printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1381     return (1);
1382   }
1383
1384  /*
1385   * Show the queue state...
1386   */
1387
1388   switch (state)
1389   {
1390     case IPP_PRINTER_IDLE :
1391         printf("%s is ready\n", dest);
1392         break;
1393     case IPP_PRINTER_PROCESSING :
1394         printf("%s is ready and printing\n", dest);
1395         break;
1396     case IPP_PRINTER_STOPPED :
1397         printf("%s is not ready\n", dest);
1398         break;
1399   }
1400
1401  /*
1402   * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1403   * the following attributes:
1404   *
1405   *    attributes-charset
1406   *    attributes-natural-language
1407   *    job-uri or printer-uri
1408   */
1409
1410   id = atoi(list);
1411
1412   request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
1413
1414   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1415                    "localhost", 0, "/printers/%s", dest);
1416
1417   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1418                NULL, uri);
1419
1420   if (id)
1421     ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1422   else
1423   {
1424     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1425                  "requesting-user-name", NULL, list);
1426     ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1427   }
1428
1429   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1430                 "requested-attributes",
1431                 sizeof(requested) / sizeof(requested[0]),
1432                 NULL, requested);
1433
1434  /*
1435   * Do the request and get back a response...
1436   */
1437
1438   jobcount = 0;
1439   response = cupsDoRequest(http, request, "/");
1440
1441   if (cupsLastError() > IPP_OK_CONFLICT)
1442   {
1443     printf("get-jobs failed: %s\n", cupsLastErrorString());
1444     ippDelete(response);
1445     return (1);
1446   }
1447
1448  /*
1449   * Loop through the job list and display them...
1450   */
1451
1452   for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1453   {
1454    /*
1455     * Skip leading attributes until we hit a job...
1456     */
1457
1458     while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1459       attr = attr->next;
1460
1461     if (!attr)
1462       break;
1463
1464    /*
1465     * Pull the needed attributes from this job...
1466     */
1467
1468     jobid     = 0;
1469     jobsize   = 0;
1470     jobstate  = IPP_JOB_PENDING;
1471     jobname   = "untitled";
1472     jobuser   = NULL;
1473     jobdest   = NULL;
1474     jobcopies = 1;
1475
1476     while (attr && attr->group_tag == IPP_TAG_JOB)
1477     {
1478       if (!strcmp(attr->name, "job-id") &&
1479           attr->value_tag == IPP_TAG_INTEGER)
1480         jobid = attr->values[0].integer;
1481
1482       if (!strcmp(attr->name, "job-k-octets") &&
1483           attr->value_tag == IPP_TAG_INTEGER)
1484         jobsize = attr->values[0].integer;
1485
1486       if (!strcmp(attr->name, "job-state") &&
1487           attr->value_tag == IPP_TAG_ENUM)
1488         jobstate = (ipp_jstate_t)attr->values[0].integer;
1489
1490       if (!strcmp(attr->name, "job-printer-uri") &&
1491           attr->value_tag == IPP_TAG_URI)
1492         if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1493           jobdest ++;
1494
1495       if (!strcmp(attr->name, "job-originating-user-name") &&
1496           attr->value_tag == IPP_TAG_NAME)
1497         jobuser = attr->values[0].string.text;
1498
1499       if (!strcmp(attr->name, "job-name") &&
1500           attr->value_tag == IPP_TAG_NAME)
1501         jobname = attr->values[0].string.text;
1502
1503       if (!strcmp(attr->name, "copies") &&
1504           attr->value_tag == IPP_TAG_INTEGER)
1505         jobcopies = attr->values[0].integer;
1506
1507       attr = attr->next;
1508     }
1509
1510    /*
1511     * See if we have everything needed...
1512     */
1513
1514     if (!jobdest || !jobid)
1515     {
1516       if (!attr)
1517         break;
1518       else
1519         continue;
1520     }
1521
1522     if (!longstatus && jobcount == 0)
1523       puts("Rank    Owner   Job     File(s)                         Total Size");
1524
1525     jobcount ++;
1526
1527    /*
1528     * Display the job...
1529     */
1530
1531     if (jobstate == IPP_JOB_PROCESSING)
1532       strcpy(rankstr, "active");
1533     else
1534     {
1535       snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1536       rank ++;
1537     }
1538
1539     if (longstatus)
1540     {
1541       puts("");
1542
1543       if (jobcopies > 1)
1544         snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1545                  jobname);
1546       else
1547         strlcpy(namestr, jobname, sizeof(namestr));
1548
1549       printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1550       printf("        %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
1551     }
1552     else
1553       printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1554              jobid, jobname, 1024.0 * jobsize);
1555
1556     if (!attr)
1557       break;
1558   }
1559
1560   ippDelete(response);
1561
1562   if (jobcount == 0)
1563     puts("no entries");
1564
1565   httpClose(http);
1566
1567   return (0);
1568 }
1569
1570
1571 /*
1572  * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1573  */
1574
1575 static char *                           /* O - Line read or NULL */
1576 smart_gets(char *s,                     /* I - Pointer to line buffer */
1577            int  len,                    /* I - Size of line buffer */
1578            FILE *fp)                    /* I - File to read from */
1579 {
1580   char  *ptr,                           /* Pointer into line */
1581         *end;                           /* End of line */
1582   int   ch;                             /* Character from file */
1583
1584
1585  /*
1586   * Read the line; unlike fgets(), we read the entire line but dump
1587   * characters that go past the end of the buffer.  Also, we accept
1588   * CR, LF, or CR LF for the line endings to be "safe", although
1589   * RFC 1179 specifically says "just use LF".
1590   */
1591
1592   ptr = s;
1593   end = s + len - 1;
1594
1595   while ((ch = getc(fp)) != EOF)
1596   {
1597     if (ch == '\n')
1598       break;
1599     else if (ch == '\r')
1600     {
1601      /*
1602       * See if a LF follows...
1603       */
1604
1605       ch = getc(fp);
1606
1607       if (ch != '\n')
1608         ungetc(ch, fp);
1609
1610       break;
1611     }
1612     else if (ptr < end)
1613       *ptr++ = ch;
1614   }
1615
1616   *ptr = '\0';
1617
1618   if (ch == EOF && ptr == s)
1619     return (NULL);
1620   else
1621     return (s);
1622 }
1623
1624
1625 /*
1626  * End of "$Id: cups-lpd.c 10379 2012-03-23 22:16:22Z mike $".
1627  */