113-cups-remote-printing.diff   [plain text]


Index: samba/source/printing/print_cups.c
===================================================================
--- samba/source/printing/print_cups.c.orig
+++ samba/source/printing/print_cups.c
@@ -2,6 +2,7 @@
  * Support code for the Common UNIX Printing System ("CUPS")
  *
  * Copyright 1999-2003 by Michael R Sweet.
+ * Copyright (C) 2003-2007 Apple Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,9 +25,32 @@
 #ifdef HAVE_CUPS
 #include <cups/cups.h>
 #include <cups/language.h>
+#include <cups/adminutil.h>
 
 extern userdom_struct current_user_info;
 
+#ifdef HAVE_COREFOUNDATION_COREFOUNDATION_H
+/* tdb.h #defines u32 which kills OSByteOrder.h on PPC. */
+#undef u32
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+static const char PrintServicePlist[] =
+		"/Library/Preferences/com.apple.printservice.plist";
+
+struct printer_description
+{
+    const char * printer_name;
+    const char * printer_info;
+    const char * printer_location;
+    BOOL	 is_shared;
+    BOOL	 is_remote;
+};
+
+static const char *cups_map_printer_name(http_t *http_p, const char *name);
+static BOOL cups_next_printer(ipp_attribute_t ** attrlist,
+		    struct printer_description * desc);
+
 /*
  * 'cups_passwd_cb()' - The CUPS password callback...
  */
@@ -75,6 +99,156 @@ static http_t *cups_connect(void)
 	return http;
 }
 
+static BOOL cups_sharing_enabled(http_t *http)
+{
+	cups_option_t   *options;
+	int		noptions;
+
+	/* If the PrintServices preferences file is there, we must be running
+	 * on OSX server. In this case, the preferences file overrides and
+	 * possible CUPS settings.
+	 */
+	if (access(PrintServicePlist, R_OK) == 0) {
+		return True;
+	}
+
+	/* XXX cupsAdminGetServerSettings is available since CUPS 1.3. We
+	 * should have a configure test for this.
+	 */
+	if (cupsAdminGetServerSettings(http, &noptions, &options)) {
+		BOOL result;
+		const char * val;
+
+		val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS,
+				    noptions, options);
+		result = (val && atoi(val)) ? True : False;
+
+		cupsFreeOptions(noptions, options);
+		return result;
+	}
+
+	/* Maybe sharing is on, but CUPS is AWOL. */
+	return False;
+}
+
+/* Retrieve PrintService's list of queue names that have sharing enabled. */
+static CFArrayRef printservice_get_queue_names(void)
+{
+	CFArrayRef	smbQarray = NULL;
+	CFDataRef	xmlData;
+	CFURLRef	prefsurl;
+	CFPropertyListRef plist;
+
+	smbQarray	= NULL;
+
+	prefsurl =
+	    CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+		    (const UInt8*)PrintServicePlist,
+		    (CFIndex)strlen(PrintServicePlist), false);
+	if (!prefsurl) {
+		return NULL;
+	}
+
+	if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
+		    prefsurl, &xmlData, NULL, NULL, NULL)) {
+		CFRelease(prefsurl);
+		return NULL;
+	}
+
+	plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
+		    xmlData, kCFPropertyListImmutable, NULL);
+	if (plist) {
+		smbQarray =
+		    (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)plist,
+			    CFSTR("smbSharedQueues"));
+		if (smbQarray) {
+			CFRetain(smbQarray);
+		}
+
+		CFRelease(plist);
+	}
+
+	CFRelease(xmlData);
+	CFRelease(prefsurl);
+
+	return smbQarray;
+}
+
+/* Is PrintService telling us that we should hide this printer? */
+static BOOL printservice_hide_printer(const char * name, CFArrayRef smbQarray)
+{
+	if (smbQarray) {
+		CFStringRef printername;
+		Boolean displayPrinter = True;
+
+		printername = CFStringCreateWithCString(kCFAllocatorDefault,
+			name, kCFStringEncodingUTF8 );
+
+		if (printername) {
+			displayPrinter = CFArrayContainsValue(smbQarray,
+				CFRangeMake(0, CFArrayGetCount(smbQarray)),
+				printername);
+
+			CFRelease(printername);
+		}
+
+		return displayPrinter ? False : True;
+	}
+
+	/* The PrintService plist is not present. Most likely we are a
+	 * Desktop system.
+	 */
+	return False;
+}
+
+static BOOL cups_pcap_cache_add(const struct printer_description *desc,
+				CFArrayRef smbQarray)
+{
+	const char * share_name = NULL;
+	const char * share_comment = NULL;
+
+	/* Prefer printer_info, since that's the "sharing" name. */
+	if (share_name == NULL)
+		share_name = desc->printer_info;
+	if (share_name == NULL)
+		share_name = desc->printer_name;
+
+	/* Prefer printer_location, since that's actually useful, otherwise
+	 * use whatever we can.
+	 */
+	if (share_comment == NULL)
+		share_comment = desc->printer_location;
+	if (share_comment == NULL)
+		share_comment = desc->printer_info;
+	if (share_comment == NULL)
+		share_comment = desc->printer_name;
+	if (share_comment == NULL)
+		share_comment = "";
+
+	if (smbQarray) {
+		/* We are OSX Server and respect PrintServices' list
+		 * of which printers should be shared by SMB.
+		 */
+		if (!printservice_hide_printer(desc->printer_name,
+			    smbQarray)) {
+			if (!pcap_cache_add(share_name, share_comment)) {
+				return False;
+			}
+		}
+	} else {
+		/* We are OSX Desktop and respect CUPS' view of which
+		 * printers should be shared by anything.
+		 */
+		if (desc->is_shared && !desc->is_remote) {
+			if (!pcap_cache_add(share_name, share_comment)) {
+				return False;
+			}
+		}
+	}
+
+	return True;
+}
+
 BOOL cups_cache_reload(void)
 {
 	http_t		*http = NULL;		/* HTTP connection to server */
@@ -82,13 +256,16 @@ BOOL cups_cache_reload(void)
 			*response = NULL;	/* IPP Response */
 	ipp_attribute_t	*attr;		/* Current attribute */
 	cups_lang_t	*language = NULL;	/* Default language */
-	char		*name,		/* printer-name attribute */
-			*info;		/* printer-info attribute */
-	static const char *requested[] =/* Requested attributes */
+	static const char * const requested[] =/* Requested attributes */
 			{
 			  "printer-name",
-			  "printer-info"
+			  "printer-info",
+			  "printer-location",
+			  "printer-type",
+			  "printer-is-shared"
 			};       
+	struct printer_description desc;
+	CFArrayRef smbQarray = NULL;
 	BOOL ret = False;
 
 	DEBUG(5, ("reloading cups printcap cache\n"));
@@ -107,6 +284,17 @@ BOOL cups_cache_reload(void)
 		goto out;
 	}
 
+	if (!cups_sharing_enabled(http)) {
+		DEBUG(5, ("CUPS printer sharing globally disabled\n"));
+		ret = True;
+		goto out;
+	}
+
+	/* Retrieve PrintService's list of queue names that have
+	 * sharing enabled...
+	 */
+	smbQarray = printservice_get_queue_names();
+
        /*
 	* Build a CUPS_GET_PRINTERS request, which requires the following
 	* attributes:
@@ -145,43 +333,12 @@ BOOL cups_cache_reload(void)
 	}
 
 	for (attr = response->attrs; attr != NULL;) {
-	       /*
-		* Skip leading attributes until we hit a printer...
-		*/
-
-		while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
-			attr = attr->next;
-
-		if (attr == NULL)
-        		break;
-
-	       /*
-		* Pull the needed attributes from this printer...
-		*/
-
-		name       = NULL;
-		info       = NULL;
-
-		while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) {
-        		if (strcmp(attr->name, "printer-name") == 0 &&
-			    attr->value_tag == IPP_TAG_NAME)
-				name = attr->values[0].string.text;
-
-        		if (strcmp(attr->name, "printer-info") == 0 &&
-			    attr->value_tag == IPP_TAG_TEXT)
-				info = attr->values[0].string.text;
 
-        		attr = attr->next;
-		}
-
-	       /*
-		* See if we have everything needed...
-		*/
-
-		if (name == NULL)
+		if (!cups_next_printer(&attr, &desc)) {
 			break;
+		}
 
-		if (!pcap_cache_add(name, info)) {
+		if (!cups_pcap_cache_add(&desc, smbQarray)) {
 			goto out;
 		}
 	}
@@ -225,43 +382,12 @@ BOOL cups_cache_reload(void)
 	}
 
 	for (attr = response->attrs; attr != NULL;) {
-	       /*
-		* Skip leading attributes until we hit a printer...
-		*/
-
-		while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
-			attr = attr->next;
-
-		if (attr == NULL)
-        		break;
-
-	       /*
-		* Pull the needed attributes from this printer...
-		*/
-
-		name       = NULL;
-		info       = NULL;
-
-		while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) {
-        		if (strcmp(attr->name, "printer-name") == 0 &&
-			    attr->value_tag == IPP_TAG_NAME)
-				name = attr->values[0].string.text;
-
-        		if (strcmp(attr->name, "printer-info") == 0 &&
-			    attr->value_tag == IPP_TAG_TEXT)
-				info = attr->values[0].string.text;
-
-        		attr = attr->next;
-		}
-
-	       /*
-		* See if we have everything needed...
-		*/
 
-		if (name == NULL)
+		if (!cups_next_printer(&attr, &desc)) {
 			break;
+		}
 
-		if (!pcap_cache_add(name, info)) {
+		if (!cups_pcap_cache_add(&desc, smbQarray)) {
 			goto out;
 		}
 	}
@@ -269,6 +395,9 @@ BOOL cups_cache_reload(void)
 	ret = True;
 
  out:
+	if (smbQarray)
+		CFRelease(smbQarray);
+
 	if (response)
 		ippDelete(response);
 
@@ -568,6 +697,7 @@ static int cups_job_submit(int snum, str
 	pstring		new_jobname;
 	int		num_options = 0; 
 	cups_option_t 	*options = NULL;
+	const char	*mapped_printer = NULL;
 
 	DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
 
@@ -586,6 +716,15 @@ static int cups_job_submit(int snum, str
 	}
 
        /*
+	 * Map from "printer-info" queue names to the real "printer-name" queue id.
+	 */
+
+	mapped_printer = cups_map_printer_name(http, PRINTERNAME(snum));
+	if (!mapped_printer) {
+		goto out;
+	}
+
+       /*
 	* Build an IPP_PRINT_JOB request, which requires the following
 	* attributes:
 	*
@@ -610,7 +749,7 @@ static int cups_job_submit(int snum, str
         	     "attributes-natural-language", NULL, language->language);
 
 	slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
-	         PRINTERNAME(snum));
+	         mapped_printer);
 
 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
         	     "printer-uri", NULL, uri);
@@ -648,7 +787,7 @@ static int cups_job_submit(int snum, str
 	* Do the request and get back a response...
 	*/
 
-	slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
+	slprintf(uri, sizeof(uri) - 1, "/printers/%s", mapped_printer);
 
 	if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) {
 		if (response->request.status.status_code >= IPP_OK_CONFLICT) {
@@ -690,6 +829,7 @@ static int cups_queue_get(const char *sh
                print_status_struct *status)
 {
 	fstring		printername;
+	const char	*mapped_printer = NULL;
 	http_t		*http = NULL;		/* HTTP connection to server */
 	ipp_t		*request = NULL,	/* IPP Request */
 			*response = NULL;	/* IPP Response */
@@ -750,10 +890,19 @@ static int cups_queue_get(const char *sh
 	}
 
        /*
+	 * Map from "printer-info" queue names to the real "printer-name" queue id.
+	 */
+
+	mapped_printer = cups_map_printer_name(http, printername);
+	if (!mapped_printer) {
+	    goto out;
+	}
+
+       /*
         * Generate the printer URI...
 	*/
 
-	slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", printername);
+	slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", mapped_printer);
 
        /*
 	* Build an IPP_GET_JOBS request, which requires the following
@@ -953,14 +1102,14 @@ static int cups_queue_get(const char *sh
 	*/
 
 	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
-		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
+		DEBUG(0,("Unable to get printer status for %s - %s\n", mapped_printer,
 			 ippErrorString(cupsLastError())));
 		*q = queue;
 		goto out;
 	}
 
 	if (response->request.status.status_code >= IPP_OK_CONFLICT) {
-		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
+		DEBUG(0,("Unable to get printer status for %s - %s\n", mapped_printer,
 			 ippErrorString(response->request.status.status_code)));
 		*q = queue;
 		goto out;
@@ -1013,6 +1162,7 @@ static int cups_queue_pause(int snum)
 			*response = NULL;	/* IPP Response */
 	cups_lang_t	*language = NULL;	/* Default language */
 	char		uri[HTTP_MAX_URI]; /* printer-uri attribute */
+	const char	*mapped_printer = NULL;
 
 
 	DEBUG(5,("cups_queue_pause(%d)\n", snum));
@@ -1032,6 +1182,15 @@ static int cups_queue_pause(int snum)
 	}
 
 	/*
+	 * Map from "printer-info" queue names to the real "printer-name" queue id.
+	 */
+
+	mapped_printer = cups_map_printer_name(http, PRINTERNAME(snum));
+	if (!mapped_printer) {
+		goto out;
+	}
+
+	/*
 	 * Build an IPP_PAUSE_PRINTER request, which requires the following
 	 * attributes:
 	 *
@@ -1055,7 +1214,7 @@ static int cups_queue_pause(int snum)
         	     "attributes-natural-language", NULL, language->language);
 
 	slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
-	         PRINTERNAME(snum));
+	         mapped_printer);
 
 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
 
@@ -1104,7 +1263,7 @@ static int cups_queue_resume(int snum)
 			*response = NULL;	/* IPP Response */
 	cups_lang_t	*language = NULL;	/* Default language */
 	char		uri[HTTP_MAX_URI]; /* printer-uri attribute */
-
+	const char 	*mapped_printer = NULL; /* Printer name */
 
 	DEBUG(5,("cups_queue_resume(%d)\n", snum));
 
@@ -1123,6 +1282,15 @@ static int cups_queue_resume(int snum)
 	}
 
        /*
+	 * Map from "printer-info" queue names to the real "printer-name" queue id.
+	 */
+
+	mapped_printer = cups_map_printer_name(http, PRINTERNAME(snum));
+	if (mapped_printer == NULL) {
+		goto out;
+	}
+
+       /*
 	* Build an IPP_RESUME_PRINTER request, which requires the following
 	* attributes:
 	*
@@ -1146,7 +1314,7 @@ static int cups_queue_resume(int snum)
         	     "attributes-natural-language", NULL, language->language);
 
 	slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
-	         PRINTERNAME(snum));
+	         mapped_printer);
 
 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
 
@@ -1182,6 +1350,236 @@ static int cups_queue_resume(int snum)
 	return ret;
 }
 
+
+static BOOL cups_next_printer(ipp_attribute_t ** attrlist,
+		    struct printer_description * desc)
+{
+	ipp_attribute_t * attr;
+
+	attr = *attrlist;
+	ZERO_STRUCTP(desc);
+
+	/* Skip leading attributes until we hit a printer. */
+	while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) {
+		attr = attr->next;
+	}
+
+	/* No printers in this response. */
+	if (attr == NULL) {
+		*attrlist = attr;
+		return False;
+	}
+
+	while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) {
+		if (attr->value_tag == IPP_TAG_NAME &&
+		    strcmp(attr->name, "printer-name") == 0) {
+			desc->printer_name = attr->values[0].string.text;
+		}
+
+		if (attr->value_tag == IPP_TAG_TEXT &&
+		    strcmp(attr->name, "printer-info") == 0) {
+			desc->printer_info = attr->values[0].string.text;
+		}
+
+		if (attr->value_tag == IPP_TAG_TEXT &&
+		    strcmp(attr->name, "printer-location") == 0) {
+			desc->printer_location = attr->values[0].string.text;
+		}
+
+		if (attr->value_tag == IPP_TAG_ENUM &&
+		    strcmp(attr->name, "printer-type") == 0) {
+			desc->is_remote =
+			    attr->values[0].integer & CUPS_PRINTER_REMOTE;
+		}
+
+		if (attr->value_tag == IPP_TAG_BOOLEAN &&
+		    strcmp(attr->name, "printer-is-shared") == 0) {
+			desc->is_shared = attr->values[0].boolean;
+		}
+
+		attr = attr->next;
+	}
+
+	*attrlist = attr;
+	return desc->printer_name ? True : False;
+}
+
+static BOOL printer_status(ipp_t * response,
+	const char * name, const char ** mapped, BOOL * shared)
+{
+	ipp_attribute_t * attr;
+	struct printer_description desc;
+
+	for (attr = response->attrs; attr != NULL;) {
+
+		if (!cups_next_printer(&attr, &desc)) {
+			return False;
+		}
+
+		/* If either the name or the info matches, we have
+		 * found our printer.
+		 */
+		if (strcmp(name, desc.printer_name) == 0 ||
+		    strcmp(name, desc.printer_info) == 0) {
+			*mapped = desc.printer_name;
+			*shared = (!desc.is_remote && desc.is_shared);
+			return True;
+		}
+	}
+
+	return False;
+}
+
+
+/*
+ * 'cups_map_printer_name()' -	Map from the "printer-info" values OSX uses
+ *		as queue names to the real "printer-name" queue id.
+ */
+static const char *				/* O - mapped name or NULL */
+cups_map_printer_name(http_t *http, 		/* I - HTTP connection */
+		      const char *name)		/* I - name to map */
+{
+	ipp_t		*request,		/* IPP Request */
+			*response;		/* IPP Response */
+	cups_lang_t	*language;		/* Default language */
+
+
+	const char *	mapped = NULL;
+	BOOL		shared = False;
+
+	static char	*mapped_name = NULL;	/* Returned printer name */
+	static const char * const requested[] =	/* Requested attributes */
+			{
+			  "printer-name",
+			  "printer-info",
+			  "printer-type",
+			  "printer-is-shared"
+			};
+
+	DEBUG(5,("cups_map_printer_name(%s)\n", name));
+
+	/* Free the old mapped queue name. */
+	SAFE_FREE(mapped_name);
+
+       /*
+	* Build a CUPS_GET_PRINTERS request, which requires the following
+	* attributes:
+	*
+	*    attributes-charset
+	*    attributes-natural-language
+	*    requested-attributes
+	*/
+
+	request = ippNew();
+
+	request->request.op.operation_id = CUPS_GET_PRINTERS;
+	request->request.op.request_id   = 1;
+
+	language = cupsLangDefault();
+
+	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+	              "requested-attributes",
+		      (sizeof(requested) / sizeof(requested[0])),
+		      NULL, requested);
+
+       /* Do the request and get back a response. */
+
+	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
+		DEBUG(0,("Unable to get printer list - %s\n",
+			 ippErrorString(cupsLastError())));
+		return NULL;
+	}
+
+	if (printer_status(response, name, &mapped, &shared)) {
+		mapped_name = SMB_STRDUP(mapped);
+	}
+
+	ippDelete(response);
+
+
+	/* If we did not match the name in the printer list then look at
+	 * the classes list.
+	 */
+	if (!mapped_name) {
+	       /*
+		* Build a CUPS_GET_CLASSES request, which requires the
+		* following attributes:
+		*
+		*    attributes-charset
+		*    attributes-natural-language
+		*    requested-attributes
+		*/
+
+		request = ippNew();
+
+		request->request.op.operation_id = CUPS_GET_CLASSES;
+		request->request.op.request_id   = 1;
+
+		language = cupsLangDefault();
+
+		ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+			     "attributes-charset", NULL, cupsLangEncoding(language));
+
+		ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+			     "attributes-natural-language", NULL, language->language);
+
+		ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+			      "requested-attributes",
+			      (sizeof(requested) / sizeof(requested[0])),
+			      NULL, requested);
+
+	       /* Do the request and get back a response. */
+
+		if ((response = cupsDoRequest(http, request, "/")) == NULL) {
+			DEBUG(0,("Unable to get printer list - %s\n",
+				 ippErrorString(cupsLastError())));
+			return NULL;
+		}
+
+		if (printer_status(response, name, &mapped, &shared)) {
+			mapped_name = SMB_STRDUP(mapped);
+		}
+
+		ippDelete(response);
+	}
+
+	/* If we've matched the name make sure it's configured as
+	 * shareable.
+	 */
+	if (mapped_name) {
+
+		CFArrayRef smbQarray;
+
+		smbQarray = printservice_get_queue_names();
+		if (!smbQarray) {
+			/* No PrintServices. Abide by CUPS' view of whether
+			 * this printer should be shared.
+			 */
+			if (shared) {
+				return mapped_name;
+			} else {
+				SAFE_FREE(mapped_name);
+				return NULL;
+			}
+		}
+
+		if (printservice_hide_printer(mapped_name, smbQarray)) {
+			SAFE_FREE(mapped_name);
+		}
+
+		CFRelease(smbQarray);
+		return mapped_name;
+	}
+
+	return NULL;
+}
+
 /*******************************************************************
  * CUPS printing interface definitions...
  ******************************************************************/