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