Revert manifest to default one
[external/cups.git] / backend / socket.c
1 /*
2  * "$Id: socket.c 9793 2011-05-20 03:49:49Z mike $"
3  *
4  *   AppSocket backend for CUPS.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2007 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  *   "LICENSE" which should have been included with this file.  If this
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   main()    - Send a file to the printer or server.
20  *   wait_bc() - Wait for back-channel data...
21  */
22
23 /*
24  * Include necessary headers.
25  */
26
27 #include <cups/http-private.h>
28 #include "backend-private.h"
29 #include <stdarg.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #ifdef WIN32
34 #  include <winsock.h>
35 #else
36 #  include <unistd.h>
37 #  include <fcntl.h>
38 #  include <sys/socket.h>
39 #  include <netinet/in.h>
40 #  include <arpa/inet.h>
41 #  include <netdb.h>
42 #endif /* WIN32 */
43
44
45 /*
46  * Local functions...
47  */
48
49 static int      wait_bc(int device_fd, int secs);
50
51
52 /*
53  * 'main()' - Send a file to the printer or server.
54  *
55  * Usage:
56  *
57  *    printer-uri job-id user title copies options [file]
58  */
59
60 int                                     /* O - Exit status */
61 main(int  argc,                         /* I - Number of command-line arguments (6 or 7) */
62      char *argv[])                      /* I - Command-line arguments */
63 {
64   const char    *device_uri;            /* Device URI */
65   char          scheme[255],            /* Scheme in URI */
66                 hostname[1024],         /* Hostname */
67                 username[255],          /* Username info (not used) */
68                 resource[1024],         /* Resource info (not used) */
69                 *options,               /* Pointer to options */
70                 *name,                  /* Name of option */
71                 *value,                 /* Value of option */
72                 sep;                    /* Option separator */
73   int           print_fd;               /* Print file */
74   int           copies;                 /* Number of copies to print */
75   time_t        start_time;             /* Time of first connect */
76 #ifdef __APPLE__
77   time_t        current_time,           /* Current time */
78                 wait_time;              /* Time to wait before shutting down socket */
79 #endif /* __APPLE__ */
80   int           contimeout;             /* Connection timeout */
81   int           waiteof;                /* Wait for end-of-file? */
82   int           port;                   /* Port number */
83   char          portname[255];          /* Port name */
84   int           delay;                  /* Delay for retries... */
85   int           device_fd;              /* AppSocket */
86   int           error;                  /* Error code (if any) */
87   http_addrlist_t *addrlist,            /* Address list */
88                 *addr;                  /* Connected address */
89   char          addrname[256];          /* Address name */
90   int           snmp_fd,                /* SNMP socket */
91                 start_count,            /* Page count via SNMP at start */
92                 page_count,             /* Page count via SNMP */
93                 have_supplies;          /* Printer supports supply levels? */
94   ssize_t       bytes = 0,              /* Initial bytes read */
95                 tbytes;                 /* Total number of bytes written */
96   char          buffer[1024];           /* Initial print buffer */
97 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
98   struct sigaction action;              /* Actions for POSIX signals */
99 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
100
101
102  /*
103   * Make sure status messages are not buffered...
104   */
105
106   setbuf(stderr, NULL);
107
108  /*
109   * Ignore SIGPIPE signals...
110   */
111
112 #ifdef HAVE_SIGSET
113   sigset(SIGPIPE, SIG_IGN);
114 #elif defined(HAVE_SIGACTION)
115   memset(&action, 0, sizeof(action));
116   action.sa_handler = SIG_IGN;
117   sigaction(SIGPIPE, &action, NULL);
118 #else
119   signal(SIGPIPE, SIG_IGN);
120 #endif /* HAVE_SIGSET */
121
122  /*
123   * Check command-line...
124   */
125
126   if (argc == 1)
127   {
128     printf("network socket \"Unknown\" \"%s\"\n",
129            _cupsLangString(cupsLangDefault(), _("AppSocket/HP JetDirect")));
130     return (CUPS_BACKEND_OK);
131   }
132   else if (argc < 6 || argc > 7)
133   {
134     _cupsLangPrintf(stderr,
135                     _("Usage: %s job-id user title copies options [file]"),
136                     argv[0]);
137     return (CUPS_BACKEND_FAILED);
138   }
139
140  /*
141   * If we have 7 arguments, print the file named on the command-line.
142   * Otherwise, send stdin instead...
143   */
144
145   if (argc == 6)
146   {
147     print_fd = 0;
148     copies   = 1;
149   }
150   else
151   {
152    /*
153     * Try to open the print file...
154     */
155
156     if ((print_fd = open(argv[6], O_RDONLY)) < 0)
157     {
158       _cupsLangPrintError("ERROR", _("Unable to open print file"));
159       return (CUPS_BACKEND_FAILED);
160     }
161
162     copies = atoi(argv[4]);
163   }
164
165  /*
166   * Extract the hostname and port number from the URI...
167   */
168
169   while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
170   {
171     _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
172     sleep(10);
173
174     if (getenv("CLASS") != NULL)
175       return (CUPS_BACKEND_FAILED);
176   }
177
178   httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
179                   username, sizeof(username), hostname, sizeof(hostname), &port,
180                   resource, sizeof(resource));
181
182   if (port == 0)
183     port = 9100;        /* Default to HP JetDirect/Tektronix PhaserShare */
184
185  /*
186   * Get options, if any...
187   */
188
189   waiteof    = 1;
190   contimeout = 7 * 24 * 60 * 60;
191
192   if ((options = strchr(resource, '?')) != NULL)
193   {
194    /*
195     * Yup, terminate the device name string and move to the first
196     * character of the options...
197     */
198
199     *options++ = '\0';
200
201    /*
202     * Parse options...
203     */
204
205     while (*options)
206     {
207      /*
208       * Get the name...
209       */
210
211       name = options;
212
213       while (*options && *options != '=' && *options != '+' && *options != '&')
214         options ++;
215
216       if ((sep = *options) != '\0')
217         *options++ = '\0';
218
219       if (sep == '=')
220       {
221        /*
222         * Get the value...
223         */
224
225         value = options;
226
227         while (*options && *options != '+' && *options != '&')
228           options ++;
229
230         if (*options)
231           *options++ = '\0';
232       }
233       else
234         value = (char *)"";
235
236      /*
237       * Process the option...
238       */
239
240       if (!_cups_strcasecmp(name, "waiteof"))
241       {
242        /*
243         * Set the wait-for-eof value...
244         */
245
246         waiteof = !value[0] || !_cups_strcasecmp(value, "on") ||
247                   !_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "true");
248       }
249       else if (!_cups_strcasecmp(name, "contimeout"))
250       {
251        /*
252         * Set the connection timeout...
253         */
254
255         if (atoi(value) > 0)
256           contimeout = atoi(value);
257       }
258     }
259   }
260
261  /*
262   * Then try finding the remote host...
263   */
264
265   start_time = time(NULL);
266
267   sprintf(portname, "%d", port);
268
269   fputs("STATE: +connecting-to-device\n", stderr);
270   fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
271
272   while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
273   {
274     _cupsLangPrintFilter(stderr, "INFO",
275                          _("Unable to locate printer \"%s\"."), hostname);
276     sleep(10);
277
278     if (getenv("CLASS") != NULL)
279     {
280       fputs("STATE: -connecting-to-device\n", stderr);
281       return (CUPS_BACKEND_STOP);
282     }
283   }
284
285  /*
286   * See if the printer supports SNMP...
287   */
288
289   if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
290   {
291     have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
292                                          &start_count, NULL);
293   }
294   else
295     have_supplies = start_count = 0;
296
297  /*
298   * Wait for data from the filter...
299   */
300
301   if (print_fd == 0)
302   {
303     if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 1, backendNetworkSideCB))
304       return (CUPS_BACKEND_OK);
305     else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
306       return (CUPS_BACKEND_OK);
307   }
308
309  /*
310   * Connect to the printer...
311   */
312
313   fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
314   _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
315
316   for (delay = 5;;)
317   {
318     if ((addr = httpAddrConnect(addrlist, &device_fd)) == NULL)
319     {
320       error     = errno;
321       device_fd = -1;
322
323       if (getenv("CLASS") != NULL)
324       {
325        /*
326         * If the CLASS environment variable is set, the job was submitted
327         * to a class and not to a specific queue.  In this case, we want
328         * to abort immediately so that the job can be requeued on the next
329         * available printer in the class.
330         */
331
332         _cupsLangPrintFilter(stderr, "INFO",
333                              _("Unable to contact printer, queuing on next "
334                                "printer in class."));
335
336        /*
337         * Sleep 5 seconds to keep the job from requeuing too rapidly...
338         */
339
340         sleep(5);
341
342         return (CUPS_BACKEND_FAILED);
343       }
344
345       fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(error));
346
347       if (error == ECONNREFUSED || error == EHOSTDOWN ||
348           error == EHOSTUNREACH)
349       {
350         if (contimeout && (time(NULL) - start_time) > contimeout)
351         {
352           _cupsLangPrintFilter(stderr, "ERROR",
353                                _("The printer is not responding."));
354           return (CUPS_BACKEND_FAILED);
355         }
356
357         switch (error)
358         {
359           case EHOSTDOWN :
360               _cupsLangPrintFilter(stderr, "WARNING",
361                                    _("The printer may not exist or "
362                                      "is unavailable at this time."));
363               break;
364
365           case EHOSTUNREACH :
366               _cupsLangPrintFilter(stderr, "WARNING",
367                                    _("The printer is unreachable at this "
368                                      "time."));
369               break;
370
371           case ECONNREFUSED :
372           default :
373               _cupsLangPrintFilter(stderr, "WARNING",
374                                    _("The printer is busy."));
375               break;
376         }
377
378         sleep(delay);
379
380         if (delay < 30)
381           delay += 5;
382       }
383       else
384       {
385         _cupsLangPrintFilter(stderr, "ERROR",
386                              _("The printer is not responding."));
387         sleep(30);
388       }
389     }
390     else
391       break;
392   }
393
394   fputs("STATE: -connecting-to-device\n", stderr);
395   _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
396
397   fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
398           httpAddrString(&(addr->addr), addrname, sizeof(addrname)),
399           _httpAddrPort(&(addr->addr)));
400
401  /*
402   * Print everything...
403   */
404
405   tbytes = 0;
406
407   if (bytes > 0)
408     tbytes += write(device_fd, buffer, bytes);
409
410   while (copies > 0 && tbytes >= 0)
411   {
412     copies --;
413
414     if (print_fd != 0)
415     {
416       fputs("PAGE: 1 1\n", stderr);
417       lseek(print_fd, 0, SEEK_SET);
418     }
419
420     tbytes = backendRunLoop(print_fd, device_fd, snmp_fd, &(addrlist->addr), 1,
421                             0, backendNetworkSideCB);
422
423     if (print_fd != 0 && tbytes >= 0)
424       _cupsLangPrintFilter(stderr, "INFO", _("Print file sent."));
425   }
426
427 #ifdef __APPLE__
428  /*
429   * Wait up to 5 seconds to get any pending back-channel data...
430   */
431
432   wait_time = time(NULL) + 5;
433   while (wait_time >= time(&current_time))
434     if (wait_bc(device_fd, wait_time - current_time) <= 0)
435       break;
436 #endif /* __APPLE__ */
437
438   if (waiteof)
439   {
440    /*
441     * Shutdown the socket and wait for the other end to finish...
442     */
443
444     _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to finish."));
445
446     shutdown(device_fd, 1);
447
448     while (wait_bc(device_fd, 90) > 0);
449   }
450
451  /*
452   * Collect the final page count as needed...
453   */
454
455   if (have_supplies &&
456       !backendSNMPSupplies(snmp_fd, &(addrlist->addr), &page_count, NULL) &&
457       page_count > start_count)
458     fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
459
460  /*
461   * Close the socket connection...
462   */
463
464   close(device_fd);
465
466   httpAddrFreeList(addrlist);
467
468  /*
469   * Close the input file and return...
470   */
471
472   if (print_fd != 0)
473     close(print_fd);
474
475   _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
476
477   return (CUPS_BACKEND_OK);
478 }
479
480
481 /*
482  * 'wait_bc()' - Wait for back-channel data...
483  */
484
485 static int                              /* O - # bytes read or -1 on error */
486 wait_bc(int device_fd,                  /* I - Socket */
487         int secs)                       /* I - Seconds to wait */
488 {
489   struct timeval timeout;               /* Timeout for select() */
490   fd_set        input;                  /* Input set for select() */
491   ssize_t       bytes;                  /* Number of back-channel bytes read */
492   char          buffer[1024];           /* Back-channel buffer */
493
494
495  /*
496   * Wait up to "secs" seconds for backchannel data...
497   */
498
499   timeout.tv_sec  = secs;
500   timeout.tv_usec = 0;
501
502   FD_ZERO(&input);
503   FD_SET(device_fd, &input);
504
505   if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0)
506   {
507    /*
508     * Grab the data coming back and spit it out to stderr...
509     */
510
511     if ((bytes = read(device_fd, buffer, sizeof(buffer))) > 0)
512     {
513       fprintf(stderr, "DEBUG: Received %d bytes of back-channel data\n",
514               (int)bytes);
515       cupsBackChannelWrite(buffer, bytes, 1.0);
516     }
517
518     return (bytes);
519   }
520   else
521     return (-1);
522 }
523
524
525 /*
526  * End of "$Id: socket.c 9793 2011-05-20 03:49:49Z mike $".
527  */