/*
 *  Unix SMB/CIFS implementation.
 *  RPC Pipe client / server routines
 *  Copyright (C) Andrew Tridgell              1992-2000,
 *  Copyright (C) Jean François Micouleau      1998-2000.
 *  Copyright (C) Gerald Carter                2002-2005.
 *
 *  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
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "includes.h"
#include "../librpc/gen_ndr/spoolss.h"
#include "rpc_server/spoolss/srv_spoolss_util.h"
#include "nt_printing.h"
#include "ads.h"
#include "secrets.h"
#include "krb5_env.h"
#include "../libcli/registry/util_reg.h"
#include "auth.h"
#include "../librpc/ndr/libndr.h"
#include "rpc_client/cli_winreg_spoolss.h"

#ifdef HAVE_ADS
/*****************************************************************
 ****************************************************************/

WERROR nt_printer_guid_store(struct messaging_context *msg_ctx,
			     const char *printer, struct GUID guid)
{
	TALLOC_CTX *tmp_ctx;
	const struct auth_session_info *session_info;
	const char *guid_str;
	DATA_BLOB blob;
	WERROR result;

	tmp_ctx = talloc_new(NULL);
	if (!tmp_ctx) {
		DEBUG(0, ("Out of memory?!\n"));
		return WERR_NOMEM;
	}

	session_info = get_session_info_system();
	if (session_info == NULL) {
		DEBUG(0, ("Could not get system session_info\n"));
		result = WERR_NOMEM;
		goto done;
	}

	guid_str = GUID_string(tmp_ctx, &guid);
	if (!guid_str) {
		DEBUG(0, ("Out of memory?!\n"));
		result = WERR_NOMEM;
		goto done;
	}

	/* We used to store this as a REG_BINARY but that causes
	   Vista to whine */

	if (!push_reg_sz(tmp_ctx, &blob, guid_str)) {
		DEBUG(0, ("Could not marshall string %s for objectGUID\n",
			  guid_str));
		result = WERR_NOMEM;
		goto done;
	}

	result = winreg_set_printer_dataex_internal(tmp_ctx, session_info, msg_ctx,
					   printer,
					   SPOOL_DSSPOOLER_KEY, "objectGUID",
					   REG_SZ, blob.data, blob.length);
	if (!W_ERROR_IS_OK(result)) {
		DEBUG(0, ("Failed to store GUID for printer %s\n", printer));
		goto done;
	}

	result = WERR_OK;
done:
	talloc_free(tmp_ctx);

	return result;
}

static WERROR nt_printer_dn_lookup(TALLOC_CTX *mem_ctx,
				   ADS_STRUCT *ads,
				   const char *printer,
				   char **pprinter_dn)
{
	char *printer_dn = NULL;
	char *srv_dn = NULL;
	char *srv_cn_0 = NULL;
	char *srv_cn_escaped = NULL;
	char *sharename_escaped = NULL;
	char *srv_dn_utf8 = NULL;
	char **srv_cn_utf8 = NULL;
	size_t converted_size;
	ADS_STATUS ads_status;
	LDAPMessage *res;
	WERROR result;
	bool ok;

	ads_status = ads_find_machine_acct(ads, &res, lp_netbios_name());
	if (!ADS_ERR_OK(ads_status)) {
		DEBUG(2, ("Failed to find machine account for %s\n",
			  lp_netbios_name()));
		result = WERR_NOT_FOUND;
		goto err_out;
	}

	/*
	 * We use ldap_get_dn here as we need the answer in utf8 to call
	 * ldap_explode_dn(). JRA.
	 */
	srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
	ads_msgfree(ads, res);
	if (srv_dn_utf8 == NULL) {
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
	if (srv_cn_utf8 == NULL) {
		ldap_memfree(srv_dn_utf8);
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	/* Now convert to CH_UNIX. */
	ok = pull_utf8_talloc(mem_ctx, &srv_dn, srv_dn_utf8, &converted_size);
	ldap_memfree(srv_dn_utf8);
	if (!ok) {
		ldap_memfree(srv_cn_utf8);
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	ok = pull_utf8_talloc(mem_ctx, &srv_cn_0, srv_cn_utf8[0], &converted_size);
	ldap_memfree(srv_cn_utf8);
	if (!ok) {
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
	if (srv_cn_escaped == NULL) {
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	sharename_escaped = escape_rdn_val_string_alloc(printer);
	if (sharename_escaped == NULL) {
		result = WERR_SERVER_UNAVAILABLE;
		goto err_out;
	}

	printer_dn = talloc_asprintf(mem_ctx,
				     "cn=%s-%s,%s",
				     srv_cn_escaped,
				     sharename_escaped,
				     srv_dn);
	if (printer_dn == NULL) {
		result = WERR_NOMEM;
		goto err_out;
	}

	*pprinter_dn = printer_dn;

	result = WERR_OK;
err_out:
	SAFE_FREE(sharename_escaped);
	SAFE_FREE(srv_cn_escaped);
	TALLOC_FREE(srv_cn_0);
	TALLOC_FREE(srv_dn);
	return result;
}

static WERROR nt_printer_guid_retrieve_internal(ADS_STRUCT *ads,
						const char *printer_dn,
						struct GUID *pguid)
{
	ADS_STATUS ads_status;
	LDAPMessage *res;
	const char *attrs[] = {"objectGUID", NULL};
	struct GUID guid;
	bool ok;

	ads_status = ads_search_dn(ads, &res, printer_dn, attrs);
	if (!ADS_ERR_OK(ads_status)) {
		DEBUG(2, ("Failed to retrieve GUID from DC - %s\n",
			  ads_errstr(ads_status)));
		return WERR_BADFILE;
	}

	ZERO_STRUCT(guid);
	ok = ads_pull_guid(ads, res, &guid);
	ads_msgfree(ads, res);
	if (!ok) {
		return WERR_NOMEM;
	}

	*pguid = guid;

	return WERR_OK;
}

WERROR nt_printer_guid_retrieve(TALLOC_CTX *mem_ctx, const char *printer,
				struct GUID *pguid)
{
	ADS_STRUCT *ads = NULL;
	char *old_krb5ccname = NULL;
	char *printer_dn;
	WERROR result;
	ADS_STATUS ads_status;
	TALLOC_CTX *tmp_ctx;

	tmp_ctx = talloc_new(mem_ctx);
	if (tmp_ctx == NULL) {
		return WERR_NOMEM;
	}

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (ads == NULL) {
		result = WERR_SERVER_UNAVAILABLE;
		goto out;
	}

	old_krb5ccname = getenv(KRB5_ENV_CCNAME);
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
							    NULL, NULL);

	ads_status = ads_connect(ads);
	if (!ADS_ERR_OK(ads_status)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_status)));
		result = WERR_ACCESS_DENIED;
		goto out;
	}

	result = nt_printer_dn_lookup(tmp_ctx, ads, printer, &printer_dn);
	if (!W_ERROR_IS_OK(result)) {
		goto out;
	}

	result = nt_printer_guid_retrieve_internal(ads, printer_dn, pguid);
out:
	TALLOC_FREE(tmp_ctx);
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	unsetenv(KRB5_ENV_CCNAME);
	if (old_krb5ccname != NULL) {
		setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0);
	}

	return result;
}

WERROR nt_printer_guid_get(TALLOC_CTX *mem_ctx,
			   const struct auth_session_info *session_info,
			   struct messaging_context *msg_ctx,
			   const char *printer, struct GUID *guid)
{
	TALLOC_CTX *tmp_ctx;
	enum winreg_Type type;
	DATA_BLOB blob;
	uint32_t len;
	NTSTATUS status;
	WERROR result;

	tmp_ctx = talloc_new(mem_ctx);
	if (tmp_ctx == NULL) {
		DEBUG(0, ("out of memory?!\n"));
		return WERR_NOMEM;
	}

	result = winreg_get_printer_dataex_internal(tmp_ctx, session_info,
						    msg_ctx, printer,
						    SPOOL_DSSPOOLER_KEY,
						    "objectGUID",
						    &type,
						    &blob.data,
						    &len);
	if (!W_ERROR_IS_OK(result)) {
		DEBUG(0, ("Failed to get GUID for printer %s\n", printer));
		goto out_ctx_free;
	}
	blob.length = (size_t)len;

	/* We used to store the guid as REG_BINARY, then swapped
	   to REG_SZ for Vista compatibility so check for both */

	switch (type) {
	case REG_SZ: {
		bool ok;
		const char *guid_str;
		ok = pull_reg_sz(tmp_ctx, &blob, &guid_str);
		if (!ok) {
			DEBUG(0, ("Failed to unmarshall GUID for printer %s\n",
				  printer));
			result = WERR_REG_CORRUPT;
			goto out_ctx_free;
		}
		status = GUID_from_string(guid_str, guid);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("bad GUID for printer %s\n", printer));
			result = ntstatus_to_werror(status);
			goto out_ctx_free;
		}
		break;
	}
	case REG_BINARY:
		if (blob.length != sizeof(struct GUID)) {
			DEBUG(0, ("bad GUID for printer %s\n", printer));
			result = WERR_REG_CORRUPT;
			goto out_ctx_free;
		}
		memcpy(guid, blob.data, sizeof(struct GUID));
		break;
	default:
		DEBUG(0,("GUID value stored as invalid type (%d)\n", type));
		result = WERR_REG_CORRUPT;
		goto out_ctx_free;
		break;
	}
	result = WERR_OK;

out_ctx_free:
	talloc_free(tmp_ctx);
	return result;
}

static WERROR nt_printer_info_to_mods(TALLOC_CTX *ctx,
				      struct spoolss_PrinterInfo2 *info2,
				      ADS_MODLIST *mods)
{
	char *info_str;

	ads_mod_str(ctx, mods, SPOOL_REG_PRINTERNAME, info2->sharename);
	ads_mod_str(ctx, mods, SPOOL_REG_SHORTSERVERNAME, lp_netbios_name());
	ads_mod_str(ctx, mods, SPOOL_REG_SERVERNAME, get_mydnsfullname());

	info_str = talloc_asprintf(ctx, "\\\\%s\\%s",
				   get_mydnsfullname(), info2->sharename);
	if (info_str == NULL) {
		return WERR_NOMEM;
	}
	ads_mod_str(ctx, mods, SPOOL_REG_UNCNAME, info_str);

	info_str = talloc_asprintf(ctx, "%d", 4);
	if (info_str == NULL) {
		return WERR_NOMEM;
	}
	ads_mod_str(ctx, mods, SPOOL_REG_VERSIONNUMBER, info_str);

	/* empty strings in the mods list result in an attrubute error */
	if (strlen(info2->drivername) != 0)
		ads_mod_str(ctx, mods, SPOOL_REG_DRIVERNAME, info2->drivername);
	if (strlen(info2->location) != 0)
		ads_mod_str(ctx, mods, SPOOL_REG_LOCATION, info2->location);
	if (strlen(info2->comment) != 0)
		ads_mod_str(ctx, mods, SPOOL_REG_DESCRIPTION, info2->comment);
	if (strlen(info2->portname) != 0)
		ads_mod_str(ctx, mods, SPOOL_REG_PORTNAME, info2->portname);
	if (strlen(info2->sepfile) != 0)
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);

	info_str = talloc_asprintf(ctx, "%u", info2->starttime);
	if (info_str == NULL) {
		return WERR_NOMEM;
	}
	ads_mod_str(ctx, mods, SPOOL_REG_PRINTSTARTTIME, info_str);

	info_str = talloc_asprintf(ctx, "%u", info2->untiltime);
	if (info_str == NULL) {
		return WERR_NOMEM;
	}
	ads_mod_str(ctx, mods, SPOOL_REG_PRINTENDTIME, info_str);

	info_str = talloc_asprintf(ctx, "%u", info2->priority);
	if (info_str == NULL) {
		return WERR_NOMEM;
	}
	ads_mod_str(ctx, mods, SPOOL_REG_PRIORITY, info_str);

	if (info2->attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS) {
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTKEEPPRINTEDJOBS, "TRUE");
	} else {
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTKEEPPRINTEDJOBS, "FALSE");
	}

	switch (info2->attributes & 0x3) {
	case 0:
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
			    SPOOL_REGVAL_PRINTWHILESPOOLING);
		break;
	case 1:
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
			    SPOOL_REGVAL_PRINTAFTERSPOOLED);
		break;
	case 2:
		ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
			    SPOOL_REGVAL_PRINTDIRECT);
		break;
	default:
		DEBUG(3, ("unsupported printer attributes %x\n",
			  info2->attributes));
	}

	return WERR_OK;
}

static WERROR nt_printer_publish_ads(struct messaging_context *msg_ctx,
				     ADS_STRUCT *ads,
				     struct spoolss_PrinterInfo2 *pinfo2)
{
	ADS_STATUS ads_rc;
	TALLOC_CTX *ctx;
	ADS_MODLIST mods;
	struct GUID guid;
	WERROR win_rc = WERR_OK;
	const char *printer = pinfo2->sharename;
	char *printer_dn = NULL;

	/* build the ads mods */
	ctx = talloc_init("nt_printer_publish_ads");
	if (ctx == NULL) {
		return WERR_NOMEM;
	}

	DEBUG(5, ("publishing printer %s\n", printer));

	win_rc = nt_printer_dn_lookup(ctx, ads, printer, &printer_dn);
	if (!W_ERROR_IS_OK(win_rc)) {
		DEBUG(2, ("Failed to create printer dn\n"));
		TALLOC_FREE(ctx);
		return win_rc;
	}

	mods = ads_init_mods(ctx);

	if (mods == NULL) {
		TALLOC_FREE(ctx);
		return WERR_NOMEM;
	}

	win_rc = nt_printer_info_to_mods(ctx, pinfo2, &mods);
	if (!W_ERROR_IS_OK(win_rc)) {
		TALLOC_FREE(ctx);
		return win_rc;
	}

	/* publish it */
	ads_rc = ads_mod_printer_entry(ads, printer_dn, ctx, &mods);
	if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
		int i;
		for (i=0; mods[i] != 0; i++)
			;
		mods[i] = (LDAPMod *)-1;
		ads_rc = ads_add_printer_entry(ads, printer_dn, ctx, &mods);
	}

	if (!ADS_ERR_OK(ads_rc)) {
		DEBUG(3, ("error publishing %s: %s\n",
			  printer, ads_errstr(ads_rc)));
		/* XXX failed to publish, so no guid to retrieve */
	}

	win_rc = nt_printer_guid_retrieve_internal(ads, printer_dn, &guid);
	if (!W_ERROR_IS_OK(win_rc)) {
		TALLOC_FREE(ctx);
		return win_rc;
	}

	win_rc = nt_printer_guid_store(msg_ctx, printer, guid);
	if (!W_ERROR_IS_OK(win_rc)) {
		DEBUG(3, ("failed to store printer %s guid\n",
			  printer));
		/* not catastrophic, retrieve on next use */
		win_rc = WERR_OK;
	}

	TALLOC_FREE(ctx);

	return win_rc;
}

static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
                                       const char *printer)
{
	ADS_STATUS ads_rc;
	LDAPMessage *res = NULL;
	char *prt_dn = NULL;

	DEBUG(5, ("unpublishing printer %s\n", printer));

	/* remove the printer from the directory */
	ads_rc = ads_find_printer_on_server(ads, &res,
					    printer, lp_netbios_name());

	if (ADS_ERR_OK(ads_rc) && res && ads_count_replies(ads, res)) {
		prt_dn = ads_get_dn(ads, talloc_tos(), res);
		if (!prt_dn) {
			ads_msgfree(ads, res);
			return WERR_NOMEM;
		}
		ads_rc = ads_del_dn(ads, prt_dn);
		TALLOC_FREE(prt_dn);
	}

	if (res) {
		ads_msgfree(ads, res);
	}
	return WERR_OK;
}

/****************************************************************************
 * Publish a printer in the directory
 *
 * @param mem_ctx      memory context
 * @param session_info  session_info to access winreg pipe
 * @param pinfo2       printer information
 * @param action       publish/unpublish action
 * @return WERROR indicating status of publishing
 ***************************************************************************/

WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
			  const struct auth_session_info *session_info,
			  struct messaging_context *msg_ctx,
			  struct spoolss_PrinterInfo2 *pinfo2,
			  int action)
{
	uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
	struct spoolss_SetPrinterInfo2 *sinfo2;
	ADS_STATUS ads_rc;
	ADS_STRUCT *ads = NULL;
	WERROR win_rc;
	char *old_krb5ccname = NULL;

	sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
	if (!sinfo2) {
		return WERR_NOMEM;
	}

	switch (action) {
	case DSPRINT_PUBLISH:
	case DSPRINT_UPDATE:
		pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
		break;
	case DSPRINT_UNPUBLISH:
		pinfo2->attributes &= (~PRINTER_ATTRIBUTE_PUBLISHED);
		break;
	default:
		win_rc = WERR_NOT_SUPPORTED;
		goto done;
	}

	sinfo2->attributes = pinfo2->attributes;

	win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx,
					pinfo2->sharename, info2_mask,
					sinfo2, NULL, NULL);
	if (!W_ERROR_IS_OK(win_rc)) {
		DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
		goto done;
	}

	TALLOC_FREE(sinfo2);

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (!ads) {
		DEBUG(3, ("ads_init() failed\n"));
		win_rc = WERR_SERVER_UNAVAILABLE;
		goto done;
	}
	old_krb5ccname = getenv(KRB5_ENV_CCNAME);
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
		NULL, NULL);

	/* ads_connect() will find the DC for us */
	ads_rc = ads_connect(ads);
	if (!ADS_ERR_OK(ads_rc)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
		win_rc = WERR_ACCESS_DENIED;
		goto done;
	}

	switch (action) {
	case DSPRINT_PUBLISH:
	case DSPRINT_UPDATE:
		win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
		break;
	case DSPRINT_UNPUBLISH:
		win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
		break;
	}

done:
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	unsetenv(KRB5_ENV_CCNAME);
	if (old_krb5ccname) {
		setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0);
	}
	return win_rc;
}

WERROR check_published_printers(struct messaging_context *msg_ctx)
{
	ADS_STATUS ads_rc;
	ADS_STRUCT *ads = NULL;
	int snum;
	int n_services = lp_numservices();
	TALLOC_CTX *tmp_ctx = NULL;
	struct auth_session_info *session_info = NULL;
	struct spoolss_PrinterInfo2 *pinfo2;
	NTSTATUS status;
	WERROR result;
	char *old_krb5ccname = NULL;

	tmp_ctx = talloc_new(NULL);
	if (!tmp_ctx) return WERR_NOMEM;

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (!ads) {
		DEBUG(3, ("ads_init() failed\n"));
		return WERR_SERVER_UNAVAILABLE;
	}
	old_krb5ccname = getenv(KRB5_ENV_CCNAME);
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
		NULL, NULL);

	/* ads_connect() will find the DC for us */
	ads_rc = ads_connect(ads);
	if (!ADS_ERR_OK(ads_rc)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
		result = WERR_ACCESS_DENIED;
		goto done;
	}

	status = make_session_info_system(tmp_ctx, &session_info);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("check_published_printers: "
			  "Could not create system session_info\n"));
		result = WERR_ACCESS_DENIED;
		goto done;
	}

	for (snum = 0; snum < n_services; snum++) {
		if (!lp_snum_ok(snum) || !lp_printable(snum)) {
			continue;
		}

		result = winreg_get_printer_internal(tmp_ctx, session_info, msg_ctx,
					    lp_servicename(talloc_tos(), snum),
					    &pinfo2);
		if (!W_ERROR_IS_OK(result)) {
			continue;
		}

		if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
			nt_printer_publish_ads(msg_ctx, ads, pinfo2);
		}

		TALLOC_FREE(pinfo2);
	}

	result = WERR_OK;
done:
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	unsetenv(KRB5_ENV_CCNAME);
	if (old_krb5ccname) {
		setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0);
	}
	talloc_free(tmp_ctx);
	return result;
}

bool is_printer_published(TALLOC_CTX *mem_ctx,
			  const struct auth_session_info *session_info,
			  struct messaging_context *msg_ctx,
			  const char *servername,
			  const char *printer,
			  struct spoolss_PrinterInfo2 **info2)
{
	struct spoolss_PrinterInfo2 *pinfo2 = NULL;
	WERROR result;
	struct dcerpc_binding_handle *b;

	result = winreg_printer_binding_handle(mem_ctx,
					       session_info,
					       msg_ctx,
					       &b);
	if (!W_ERROR_IS_OK(result)) {
		return false;
	}

	result = winreg_get_printer(mem_ctx, b,
				    printer, &pinfo2);
	if (!W_ERROR_IS_OK(result)) {
		return false;
	}

	if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
		TALLOC_FREE(pinfo2);
		return false;
	}

	if (info2) {
		*info2 = talloc_move(mem_ctx, &pinfo2);
	}
	talloc_free(pinfo2);
	return true;
}
#else
WERROR nt_printer_guid_store(struct messaging_context *msg_ctx,
			   const char *printer, struct GUID guid)
{
	return WERR_NOT_SUPPORTED;
}

WERROR nt_printer_guid_retrieve(TALLOC_CTX *mem_ctx, const char *printer,
				struct GUID *pguid)
{
	return WERR_NOT_SUPPORTED;
}

WERROR nt_printer_guid_get(TALLOC_CTX *mem_ctx,
			   const struct auth_session_info *session_info,
			   struct messaging_context *msg_ctx,
			   const char *printer, struct GUID *guid)
{
	return WERR_NOT_SUPPORTED;
}

WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
			  const struct auth_session_info *session_info,
			  struct messaging_context *msg_ctx,
			  struct spoolss_PrinterInfo2 *pinfo2,
			  int action)
{
	return WERR_OK;
}

WERROR check_published_printers(struct messaging_context *msg_ctx)
{
	return WERR_OK;
}

bool is_printer_published(TALLOC_CTX *mem_ctx,
			  const struct auth_session_info *session_info,
			  struct messaging_context *msg_ctx,
			  const char *servername,
			  const char *printer,
			  struct spoolss_PrinterInfo2 **info2)
{
	return False;
}
#endif /* HAVE_ADS */
