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