Home | History | Annotate | Download | only in cups
      1 /*
      2  * PPD utilities for CUPS.
      3  *
      4  * Copyright 2007-2015 by Apple Inc.
      5  * Copyright 1997-2006 by Easy Software Products.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * which should have been included with this file.  If this file is
     11  * missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * This file is subject to the Apple OS-Developed Software exception.
     14  */
     15 
     16 /*
     17  * Include necessary headers...
     18  */
     19 
     20 #include "cups-private.h"
     21 #include "ppd-private.h"
     22 #include <fcntl.h>
     23 #include <sys/stat.h>
     24 #if defined(WIN32) || defined(__EMX__)
     25 #  include <io.h>
     26 #else
     27 #  include <unistd.h>
     28 #endif /* WIN32 || __EMX__ */
     29 
     30 
     31 /*
     32  * Local functions...
     33  */
     34 
     35 static int	cups_get_printer_uri(http_t *http, const char *name,
     36 		                     char *host, int hostsize, int *port,
     37 				     char *resource, int resourcesize,
     38 				     int depth);
     39 
     40 
     41 /*
     42  * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
     43  *
     44  * For classes, @code cupsGetPPD@ returns the PPD file for the first printer
     45  * in the class.
     46  *
     47  * The returned filename is stored in a static buffer and is overwritten with
     48  * each call to @code cupsGetPPD@ or @link cupsGetPPD2@.  The caller "owns" the
     49  * file that is created and must @code unlink@ the returned filename.
     50  */
     51 
     52 const char *				/* O - Filename for PPD file */
     53 cupsGetPPD(const char *name)		/* I - Destination name */
     54 {
     55   _ppd_globals_t *pg = _ppdGlobals();	/* Pointer to library globals */
     56   time_t	modtime = 0;		/* Modification time */
     57 
     58 
     59  /*
     60   * Return the PPD file...
     61   */
     62 
     63   pg->ppd_filename[0] = '\0';
     64 
     65   if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, pg->ppd_filename,
     66                   sizeof(pg->ppd_filename)) == HTTP_STATUS_OK)
     67     return (pg->ppd_filename);
     68   else
     69     return (NULL);
     70 }
     71 
     72 
     73 /*
     74  * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
     75  *
     76  * For classes, @code cupsGetPPD2@ returns the PPD file for the first printer
     77  * in the class.
     78  *
     79  * The returned filename is stored in a static buffer and is overwritten with
     80  * each call to @link cupsGetPPD@ or @code cupsGetPPD2@.  The caller "owns" the
     81  * file that is created and must @code unlink@ the returned filename.
     82  *
     83  * @since CUPS 1.1.21/macOS 10.4@
     84  */
     85 
     86 const char *				/* O - Filename for PPD file */
     87 cupsGetPPD2(http_t     *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
     88             const char *name)		/* I - Destination name */
     89 {
     90   _ppd_globals_t *pg = _ppdGlobals();	/* Pointer to library globals */
     91   time_t	modtime = 0;		/* Modification time */
     92 
     93 
     94   pg->ppd_filename[0] = '\0';
     95 
     96   if (cupsGetPPD3(http, name, &modtime, pg->ppd_filename,
     97                   sizeof(pg->ppd_filename)) == HTTP_STATUS_OK)
     98     return (pg->ppd_filename);
     99   else
    100     return (NULL);
    101 }
    102 
    103 
    104 /*
    105  * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
    106  *                   server if it has changed.
    107  *
    108  * The "modtime" parameter contains the modification time of any
    109  * locally-cached content and is updated with the time from the PPD file on
    110  * the server.
    111  *
    112  * The "buffer" parameter contains the local PPD filename.  If it contains
    113  * the empty string, a new temporary file is created, otherwise the existing
    114  * file will be overwritten as needed.  The caller "owns" the file that is
    115  * created and must @code unlink@ the returned filename.
    116  *
    117  * On success, @code HTTP_STATUS_OK@ is returned for a new PPD file and
    118  * @code HTTP_STATUS_NOT_MODIFIED@ if the existing PPD file is up-to-date.  Any other
    119  * status is an error.
    120  *
    121  * For classes, @code cupsGetPPD3@ returns the PPD file for the first printer
    122  * in the class.
    123  *
    124  * @since CUPS 1.4/macOS 10.6@
    125  */
    126 
    127 http_status_t				/* O  - HTTP status */
    128 cupsGetPPD3(http_t     *http,		/* I  - HTTP connection or @code CUPS_HTTP_DEFAULT@ */
    129             const char *name,		/* I  - Destination name */
    130 	    time_t     *modtime,	/* IO - Modification time */
    131 	    char       *buffer,		/* I  - Filename buffer */
    132 	    size_t     bufsize)		/* I  - Size of filename buffer */
    133 {
    134   int		http_port;		/* Port number */
    135   char		http_hostname[HTTP_MAX_HOST];
    136 					/* Hostname associated with connection */
    137   http_t	*http2;			/* Alternate HTTP connection */
    138   int		fd;			/* PPD file */
    139   char		localhost[HTTP_MAX_URI],/* Local hostname */
    140 		hostname[HTTP_MAX_URI],	/* Hostname */
    141 		resource[HTTP_MAX_URI];	/* Resource name */
    142   int		port;			/* Port number */
    143   http_status_t	status;			/* HTTP status from server */
    144   char		tempfile[1024] = "";	/* Temporary filename */
    145   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    146 
    147 
    148  /*
    149   * Range check input...
    150   */
    151 
    152   DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, "
    153                 "bufsize=%d)", http, name, modtime,
    154 		modtime ? (int)*modtime : 0, buffer, (int)bufsize));
    155 
    156   if (!name)
    157   {
    158     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer name"), 1);
    159     return (HTTP_STATUS_NOT_ACCEPTABLE);
    160   }
    161 
    162   if (!modtime)
    163   {
    164     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No modification time"), 1);
    165     return (HTTP_STATUS_NOT_ACCEPTABLE);
    166   }
    167 
    168   if (!buffer || bufsize <= 1)
    169   {
    170     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad filename buffer"), 1);
    171     return (HTTP_STATUS_NOT_ACCEPTABLE);
    172   }
    173 
    174 #ifndef WIN32
    175  /*
    176   * See if the PPD file is available locally...
    177   */
    178 
    179   if (http)
    180     httpGetHostname(http, hostname, sizeof(hostname));
    181   else
    182   {
    183     strlcpy(hostname, cupsServer(), sizeof(hostname));
    184     if (hostname[0] == '/')
    185       strlcpy(hostname, "localhost", sizeof(hostname));
    186   }
    187 
    188   if (!_cups_strcasecmp(hostname, "localhost"))
    189   {
    190     char	ppdname[1024];		/* PPD filename */
    191     struct stat	ppdinfo;		/* PPD file information */
    192 
    193 
    194     snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot,
    195              name);
    196     if (!stat(ppdname, &ppdinfo) && !access(ppdname, R_OK))
    197     {
    198      /*
    199       * OK, the file exists and is readable, use it!
    200       */
    201 
    202       if (buffer[0])
    203       {
    204         unlink(buffer);
    205 
    206 	if (symlink(ppdname, buffer) && errno != EEXIST)
    207         {
    208           _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
    209 
    210 	  return (HTTP_STATUS_SERVER_ERROR);
    211 	}
    212       }
    213       else
    214       {
    215         int		tries;		/* Number of tries */
    216         const char	*tmpdir;	/* TMPDIR environment variable */
    217 	struct timeval	curtime;	/* Current time */
    218 
    219        /*
    220 	* Previously we put root temporary files in the default CUPS temporary
    221 	* directory under /var/spool/cups.  However, since the scheduler cleans
    222 	* out temporary files there and runs independently of the user apps, we
    223 	* don't want to use it unless specifically told to by cupsd.
    224 	*/
    225 
    226 	if ((tmpdir = getenv("TMPDIR")) == NULL)
    227 #  ifdef __APPLE__
    228 	  tmpdir = "/private/tmp";	/* /tmp is a symlink to /private/tmp */
    229 #  else
    230           tmpdir = "/tmp";
    231 #  endif /* __APPLE__ */
    232 
    233        /*
    234 	* Make the temporary name using the specified directory...
    235 	*/
    236 
    237 	tries = 0;
    238 
    239 	do
    240 	{
    241 	 /*
    242 	  * Get the current time of day...
    243 	  */
    244 
    245 	  gettimeofday(&curtime, NULL);
    246 
    247 	 /*
    248 	  * Format a string using the hex time values...
    249 	  */
    250 
    251 	  snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir,
    252 		   (unsigned long)curtime.tv_sec,
    253 		   (unsigned long)curtime.tv_usec);
    254 
    255 	 /*
    256 	  * Try to make a symlink...
    257 	  */
    258 
    259 	  if (!symlink(ppdname, buffer))
    260 	    break;
    261 
    262 	  tries ++;
    263 	}
    264 	while (tries < 1000);
    265 
    266         if (tries >= 1000)
    267 	{
    268           _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
    269 
    270 	  return (HTTP_STATUS_SERVER_ERROR);
    271 	}
    272       }
    273 
    274       if (*modtime >= ppdinfo.st_mtime)
    275         return (HTTP_STATUS_NOT_MODIFIED);
    276       else
    277       {
    278         *modtime = ppdinfo.st_mtime;
    279 	return (HTTP_STATUS_OK);
    280       }
    281     }
    282   }
    283 #endif /* !WIN32 */
    284 
    285  /*
    286   * Try finding a printer URI for this printer...
    287   */
    288 
    289   if (!http)
    290     if ((http = _cupsConnect()) == NULL)
    291       return (HTTP_STATUS_SERVICE_UNAVAILABLE);
    292 
    293   if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port,
    294                             resource, sizeof(resource), 0))
    295     return (HTTP_STATUS_NOT_FOUND);
    296 
    297   DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname,
    298                 port));
    299 
    300   if (cupsServer()[0] == '/' && !_cups_strcasecmp(hostname, "localhost") && port == ippPort())
    301   {
    302    /*
    303     * Redirect localhost to domain socket...
    304     */
    305 
    306     strlcpy(hostname, cupsServer(), sizeof(hostname));
    307     port = 0;
    308 
    309     DEBUG_printf(("2cupsGetPPD3: Redirecting to \"%s\".", hostname));
    310   }
    311 
    312  /*
    313   * Remap local hostname to localhost...
    314   */
    315 
    316   httpGetHostname(NULL, localhost, sizeof(localhost));
    317 
    318   DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost));
    319 
    320   if (!_cups_strcasecmp(localhost, hostname))
    321     strlcpy(hostname, "localhost", sizeof(hostname));
    322 
    323  /*
    324   * Get the hostname and port number we are connected to...
    325   */
    326 
    327   httpGetHostname(http, http_hostname, sizeof(http_hostname));
    328   http_port = httpAddrPort(http->hostaddr);
    329 
    330   DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d",
    331                 http_hostname, http_port));
    332 
    333  /*
    334   * Reconnect to the correct server as needed...
    335   */
    336 
    337   if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port)
    338     http2 = http;
    339   else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC,
    340 				 cupsEncryption(), 1, 30000, NULL)) == NULL)
    341   {
    342     DEBUG_puts("1cupsGetPPD3: Unable to connect to server");
    343 
    344     return (HTTP_STATUS_SERVICE_UNAVAILABLE);
    345   }
    346 
    347  /*
    348   * Get a temp file...
    349   */
    350 
    351   if (buffer[0])
    352     fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
    353   else
    354     fd = cupsTempFd(tempfile, sizeof(tempfile));
    355 
    356   if (fd < 0)
    357   {
    358    /*
    359     * Can't open file; close the server connection and return NULL...
    360     */
    361 
    362     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
    363 
    364     if (http2 != http)
    365       httpClose(http2);
    366 
    367     return (HTTP_STATUS_SERVER_ERROR);
    368   }
    369 
    370  /*
    371   * And send a request to the HTTP server...
    372   */
    373 
    374   strlcat(resource, ".ppd", sizeof(resource));
    375 
    376   if (*modtime > 0)
    377     httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
    378                  httpGetDateString(*modtime));
    379 
    380   status = cupsGetFd(http2, resource, fd);
    381 
    382   close(fd);
    383 
    384  /*
    385   * See if we actually got the file or an error...
    386   */
    387 
    388   if (status == HTTP_STATUS_OK)
    389   {
    390     *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
    391 
    392     if (tempfile[0])
    393       strlcpy(buffer, tempfile, bufsize);
    394   }
    395   else if (status != HTTP_STATUS_NOT_MODIFIED)
    396   {
    397     _cupsSetHTTPError(status);
    398 
    399     if (buffer[0])
    400       unlink(buffer);
    401     else if (tempfile[0])
    402       unlink(tempfile);
    403   }
    404   else if (tempfile[0])
    405     unlink(tempfile);
    406 
    407   if (http2 != http)
    408     httpClose(http2);
    409 
    410  /*
    411   * Return the PPD file...
    412   */
    413 
    414   DEBUG_printf(("1cupsGetPPD3: Returning status %d", status));
    415 
    416   return (status);
    417 }
    418 
    419 
    420 /*
    421  * 'cupsGetServerPPD()' - Get an available PPD file from the server.
    422  *
    423  * This function returns the named PPD file from the server.  The
    424  * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@
    425  * operation.
    426  *
    427  * You must remove (unlink) the PPD file when you are finished with
    428  * it. The PPD filename is stored in a static location that will be
    429  * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@,
    430  * or @link cupsGetServerPPD@.
    431  *
    432  * @since CUPS 1.3/macOS 10.5@
    433  */
    434 
    435 char *					/* O - Name of PPD file or @code NULL@ on error */
    436 cupsGetServerPPD(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
    437                  const char *name)	/* I - Name of PPD file ("ppd-name") */
    438 {
    439   int			fd;		/* PPD file descriptor */
    440   ipp_t			*request;	/* IPP request */
    441   _ppd_globals_t	*pg = _ppdGlobals();
    442 					/* Pointer to library globals */
    443 
    444 
    445  /*
    446   * Range check input...
    447   */
    448 
    449   if (!name)
    450   {
    451     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1);
    452 
    453     return (NULL);
    454   }
    455 
    456   if (!http)
    457     if ((http = _cupsConnect()) == NULL)
    458       return (NULL);
    459 
    460  /*
    461   * Get a temp file...
    462   */
    463 
    464   if ((fd = cupsTempFd(pg->ppd_filename, sizeof(pg->ppd_filename))) < 0)
    465   {
    466    /*
    467     * Can't open file; close the server connection and return NULL...
    468     */
    469 
    470     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
    471 
    472     return (NULL);
    473   }
    474 
    475  /*
    476   * Get the PPD file...
    477   */
    478 
    479   request = ippNewRequest(IPP_OP_CUPS_GET_PPD);
    480   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
    481                name);
    482 
    483   ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
    484 
    485   close(fd);
    486 
    487   if (cupsLastError() != IPP_STATUS_OK)
    488   {
    489     unlink(pg->ppd_filename);
    490     return (NULL);
    491   }
    492   else
    493     return (pg->ppd_filename);
    494 }
    495 
    496 
    497 /*
    498  * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the
    499  *                            first printer in a class.
    500  */
    501 
    502 static int				/* O - 1 on success, 0 on failure */
    503 cups_get_printer_uri(
    504     http_t     *http,			/* I - Connection to server */
    505     const char *name,			/* I - Name of printer or class */
    506     char       *host,			/* I - Hostname buffer */
    507     int        hostsize,		/* I - Size of hostname buffer */
    508     int        *port,			/* O - Port number */
    509     char       *resource,		/* I - Resource buffer */
    510     int        resourcesize,		/* I - Size of resource buffer */
    511     int        depth)			/* I - Depth of query */
    512 {
    513   int		i;			/* Looping var */
    514   int		http_port;		/* Port number */
    515   http_t	*http2;			/* Alternate HTTP connection */
    516   ipp_t		*request,		/* IPP request */
    517 		*response;		/* IPP response */
    518   ipp_attribute_t *attr;		/* Current attribute */
    519   char		uri[HTTP_MAX_URI],	/* printer-uri attribute */
    520 		scheme[HTTP_MAX_URI],	/* Scheme name */
    521 		username[HTTP_MAX_URI],	/* Username:password */
    522 		classname[255],		/* Temporary class name */
    523 		http_hostname[HTTP_MAX_HOST];
    524 					/* Hostname associated with connection */
    525   static const char * const requested_attrs[] =
    526 		{			/* Requested attributes */
    527 		  "device-uri",
    528 		  "member-uris",
    529 		  "printer-uri-supported",
    530 		  "printer-type"
    531 		};
    532 
    533 
    534   DEBUG_printf(("4cups_get_printer_uri(http=%p, name=\"%s\", host=%p, hostsize=%d, resource=%p, resourcesize=%d, depth=%d)", http, name, host, hostsize, resource, resourcesize, depth));
    535 
    536  /*
    537   * Setup the printer URI...
    538   */
    539 
    540   if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 0, "/printers/%s", name) < HTTP_URI_STATUS_OK)
    541   {
    542     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"), 1);
    543 
    544     *host     = '\0';
    545     *resource = '\0';
    546 
    547     return (0);
    548   }
    549 
    550   DEBUG_printf(("5cups_get_printer_uri: printer-uri=\"%s\"", uri));
    551 
    552  /*
    553   * Get the hostname and port number we are connected to...
    554   */
    555 
    556   httpGetHostname(http, http_hostname, sizeof(http_hostname));
    557   http_port = httpAddrPort(http->hostaddr);
    558 
    559   DEBUG_printf(("5cups_get_printer_uri: http_hostname=\"%s\"", http_hostname));
    560 
    561  /*
    562   * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
    563   * attributes:
    564   *
    565   *    attributes-charset
    566   *    attributes-natural-language
    567   *    printer-uri
    568   *    requested-attributes
    569   */
    570 
    571   request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
    572 
    573   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
    574 
    575   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(requested_attrs) / sizeof(requested_attrs[0]), NULL, requested_attrs);
    576 
    577  /*
    578   * Do the request and get back a response...
    579   */
    580 
    581   snprintf(resource, (size_t)resourcesize, "/printers/%s", name);
    582 
    583   if ((response = cupsDoRequest(http, request, resource)) != NULL)
    584   {
    585     const char *device_uri = NULL;	/* device-uri value */
    586 
    587     if ((attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI)) != NULL)
    588     {
    589       device_uri = attr->values[0].string.text;
    590       DEBUG_printf(("5cups_get_printer_uri: device-uri=\"%s\"", device_uri));
    591     }
    592 
    593     if (device_uri &&
    594         (((!strncmp(device_uri, "ipp://", 6) || !strncmp(device_uri, "ipps://", 7)) &&
    595 	  (strstr(device_uri, "/printers/") != NULL || strstr(device_uri, "/classes/") != NULL)) ||
    596          ((strstr(device_uri, "._ipp.") != NULL || strstr(device_uri, "._ipps.") != NULL) &&
    597           !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
    598     {
    599      /*
    600       * Statically-configured shared printer.
    601       */
    602 
    603       httpSeparateURI(HTTP_URI_CODING_ALL, _httpResolveURI(device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL), scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
    604       ippDelete(response);
    605 
    606       DEBUG_printf(("5cups_get_printer_uri: Resolved to host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
    607       return (1);
    608     }
    609     else if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
    610     {
    611      /*
    612       * Get the first actual printer name in the class...
    613       */
    614 
    615       DEBUG_printf(("5cups_get_printer_uri: Got member-uris with %d values.", ippGetCount(attr)));
    616 
    617       for (i = 0; i < attr->num_values; i ++)
    618       {
    619         DEBUG_printf(("5cups_get_printer_uri: member-uris[%d]=\"%s\"", i, ippGetString(attr, i, NULL)));
    620 
    621 	httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
    622 	if (!strncmp(resource, "/printers/", 10))
    623 	{
    624 	 /*
    625 	  * Found a printer!
    626 	  */
    627 
    628           ippDelete(response);
    629 
    630 	  DEBUG_printf(("5cups_get_printer_uri: Found printer member with host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
    631 	  return (1);
    632 	}
    633       }
    634 
    635      /*
    636       * No printers in this class - try recursively looking for a printer,
    637       * but not more than 3 levels deep...
    638       */
    639 
    640       if (depth < 3)
    641       {
    642 	for (i = 0; i < attr->num_values; i ++)
    643 	{
    644 	  httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
    645 	                  scheme, sizeof(scheme), username, sizeof(username),
    646 			  host, hostsize, port, resource, resourcesize);
    647 	  if (!strncmp(resource, "/classes/", 9))
    648 	  {
    649 	   /*
    650 	    * Found a class!  Connect to the right server...
    651 	    */
    652 
    653 	    if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
    654 	      http2 = http;
    655 	    else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
    656 	    {
    657 	      DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
    658 
    659 	      continue;
    660 	    }
    661 
    662            /*
    663 	    * Look up printers on that server...
    664 	    */
    665 
    666             strlcpy(classname, resource + 9, sizeof(classname));
    667 
    668             cups_get_printer_uri(http2, classname, host, hostsize, port,
    669 	                         resource, resourcesize, depth + 1);
    670 
    671            /*
    672 	    * Close the connection as needed...
    673 	    */
    674 
    675 	    if (http2 != http)
    676 	      httpClose(http2);
    677 
    678             if (*host)
    679 	      return (1);
    680 	  }
    681 	}
    682       }
    683     }
    684     else if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
    685     {
    686       httpSeparateURI(HTTP_URI_CODING_ALL, _httpResolveURI(attr->values[0].string.text, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL), scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
    687       ippDelete(response);
    688 
    689       DEBUG_printf(("5cups_get_printer_uri: Resolved to host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
    690 
    691       if (!strncmp(resource, "/classes/", 9))
    692       {
    693         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found for class"), 1);
    694 
    695 	*host     = '\0';
    696 	*resource = '\0';
    697 
    698         DEBUG_puts("5cups_get_printer_uri: Not returning class.");
    699 	return (0);
    700       }
    701 
    702       return (1);
    703     }
    704 
    705     ippDelete(response);
    706   }
    707 
    708   if (cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND)
    709     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found"), 1);
    710 
    711   *host     = '\0';
    712   *resource = '\0';
    713 
    714   DEBUG_puts("5cups_get_printer_uri: Printer URI not found.");
    715   return (0);
    716 }
    717