summaryrefslogtreecommitdiffstats
path: root/source4/torture/rpc/spoolss.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/torture/rpc/spoolss.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/torture/rpc/spoolss.c')
-rw-r--r--source4/torture/rpc/spoolss.c11705
1 files changed, 11705 insertions, 0 deletions
diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c
new file mode 100644
index 0000000..05a0aef
--- /dev/null
+++ b/source4/torture/rpc/spoolss.c
@@ -0,0 +1,11705 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Stefan Metzmacher 2005
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Guenther Deschner 2009-2011,2013
+
+ 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 "torture/torture.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/cmdline/cmdline.h"
+#include "system/filesys.h"
+#include "torture/ndr/ndr.h"
+#include "torture/smb2/proto.h"
+
+#define TORTURE_WELLKNOWN_PRINTER "torture_wkn_printer"
+#define TORTURE_PRINTER "torture_printer"
+#define TORTURE_WELLKNOWN_PRINTER_EX "torture_wkn_printer_ex"
+#define TORTURE_PRINTER_EX "torture_printer_ex"
+#define TORTURE_DRIVER "torture_driver"
+#define TORTURE_DRIVER_ADD "torture_driver_add"
+#define TORTURE_DRIVER_EX "torture_driver_ex"
+#define TORTURE_DRIVER_ADOBE "torture_driver_adobe"
+#define TORTURE_DRIVER_EX_ADOBE "torture_driver_ex_adobe"
+#define TORTURE_DRIVER_ADOBE_CUPSADDSMB "torture_driver_adobe_cupsaddsmb"
+#define TORTURE_DRIVER_TIMESTAMPS "torture_driver_timestamps"
+#define TORTURE_DRIVER_DELETER "torture_driver_deleter"
+#define TORTURE_DRIVER_COPY_DIR "torture_driver_copy_from_directory"
+#define TORTURE_DRIVER_DELETERIN "torture_driver_deleterin"
+#define TORTURE_PRINTER_STATIC1 "print1"
+
+#define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
+#define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
+#define TOP_LEVEL_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print"
+#define TOP_LEVEL_CONTROL_FORMS_KEY TOP_LEVEL_CONTROL_KEY "\\Forms"
+#define TOP_LEVEL_CONTROL_PRINTERS_KEY TOP_LEVEL_CONTROL_KEY "\\Printers"
+#define TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY TOP_LEVEL_CONTROL_KEY "\\Environments"
+
+struct test_spoolss_context {
+ struct dcerpc_pipe *spoolss_pipe;
+
+ /* server environment */
+ const char *environment;
+
+ /* print server handle */
+ struct policy_handle server_handle;
+
+ /* for EnumPorts */
+ uint32_t port_count[3];
+ union spoolss_PortInfo *ports[3];
+
+ /* for EnumPrinterDrivers */
+ uint32_t driver_count[9];
+ union spoolss_DriverInfo *drivers[9];
+
+ /* for EnumMonitors */
+ uint32_t monitor_count[3];
+ union spoolss_MonitorInfo *monitors[3];
+
+ /* for EnumPrintProcessors */
+ uint32_t print_processor_count[2];
+ union spoolss_PrintProcessorInfo *print_processors[2];
+
+ /* for EnumPrinters */
+ uint32_t printer_count[6];
+ union spoolss_PrinterInfo *printers[6];
+};
+
+struct torture_driver_context {
+ struct {
+ const char *driver_directory;
+ const char *environment;
+ } local;
+ struct {
+ const char *driver_directory;
+ const char *driver_upload_directory;
+ const char *environment;
+ } remote;
+ struct spoolss_AddDriverInfo8 info8;
+ bool ex;
+};
+
+struct torture_printer_context {
+ struct dcerpc_pipe *spoolss_pipe;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct torture_driver_context driver;
+ bool ex;
+ bool wellknown;
+ bool added_driver;
+ bool have_driver;
+ struct spoolss_DeviceMode *devmode;
+ struct policy_handle handle;
+};
+
+static bool upload_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d);
+static bool remove_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d);
+static bool fillup_printserver_info(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d);
+static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir);
+
+#define COMPARE_STRING(tctx, c,r,e) \
+ torture_assert_str_equal(tctx, c.e, r.e, "invalid value")
+
+/* not every compiler supports __typeof__() */
+#if (__GNUC__ >= 3)
+#define _CHECK_FIELD_SIZE(c,r,e,type) do {\
+ if (sizeof(__typeof__(c.e)) != sizeof(type)) { \
+ torture_fail(tctx, #c "." #e "field is not " #type "\n"); \
+ }\
+ if (sizeof(__typeof__(r.e)) != sizeof(type)) { \
+ torture_fail(tctx, #r "." #e "field is not " #type "\n"); \
+ }\
+} while(0)
+#else
+#define _CHECK_FIELD_SIZE(c,r,e,type) do {} while(0)
+#endif
+
+#define COMPARE_UINT32(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, uint32_t); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+#define COMPARE_UINT64(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, uint64_t); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+
+#define COMPARE_NTTIME(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, NTTIME); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+#define COMPARE_STRING_ARRAY(tctx, c,r,e) do {\
+ int __i; \
+ if (!c.e && !r.e) { \
+ break; \
+ } \
+ if (c.e && !r.e) { \
+ torture_fail(tctx, #r "." #e " field is NULL and " #c "." #e " is not\n"); \
+ } \
+ if (!c.e && r.e) { \
+ torture_fail(tctx, #c "." #e " field is NULL and " #r "." #e " is not\n"); \
+ } \
+ for (__i=0;c.e[__i] != NULL; __i++) { \
+ torture_assert_str_equal(tctx, c.e[__i], r.e[__i], "invalid value"); \
+ } \
+} while(0)
+
+#define CHECK_ALIGN(size, n) do {\
+ if (size % n) {\
+ torture_warning(tctx, "%d is *NOT* %d byte aligned, should be %d",\
+ size, n, size + n - (size % n));\
+ }\
+} while(0)
+
+#define DO_ROUND(size, n) (((size)+((n)-1)) & ~((n)-1))
+
+#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn##_info(tctx, level, count, info);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" level %d (count: %d) got unexpected needed size: %d, we calculated: %d", level, count, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn##_info(tctx, count, info);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" (count: %d) got unexpected needed size: %d, we calculated: %d", count, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn(info, level, 0);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" level %d got unexpected needed size: %d, we calculated: %d", level, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+static bool PrinterInfo_to_SetPrinterInfo(struct torture_context *tctx,
+ const union spoolss_PrinterInfo *i,
+ uint32_t level,
+ union spoolss_SetPrinterInfo *s)
+{
+ switch (level) {
+ case 0:
+ s->info0 = talloc(tctx, struct spoolss_SetPrinterInfo0);
+ break;
+ case 2:
+ s->info2 = talloc(tctx, struct spoolss_SetPrinterInfo2);
+ s->info2->servername = i->info2.servername;
+ s->info2->printername = i->info2.printername;
+ s->info2->sharename = i->info2.sharename;
+ s->info2->portname = i->info2.portname;
+ s->info2->drivername = i->info2.drivername;
+ s->info2->comment = i->info2.comment;
+ s->info2->location = i->info2.location;
+ s->info2->devmode_ptr = 0;
+ s->info2->sepfile = i->info2.sepfile;
+ s->info2->printprocessor = i->info2.printprocessor;
+ s->info2->datatype = i->info2.datatype;
+ s->info2->parameters = i->info2.parameters;
+ s->info2->secdesc_ptr = 0;
+ s->info2->attributes = i->info2.attributes;
+ s->info2->priority = i->info2.priority;
+ s->info2->defaultpriority = i->info2.defaultpriority;
+ s->info2->starttime = i->info2.starttime;
+ s->info2->untiltime = i->info2.untiltime;
+ s->info2->status = i->info2.status;
+ s->info2->cjobs = i->info2.cjobs;
+ s->info2->averageppm = i->info2.averageppm;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OpenPrinter_server(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *server_handle)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ op.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ op.out.handle = server_handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_OpenPrinter failed");
+ torture_assert_werr_ok(tctx, op.out.result, "dcerpc_spoolss_OpenPrinter failed");
+
+ return true;
+}
+
+static bool test_EnumPorts(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPorts level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPorts_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPorts unexpected return code");
+
+ blob = data_blob_talloc_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ torture_assert(tctx, info, "EnumPorts returned no info");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, needed, 4);
+
+ ctx->port_count[level] = count;
+ ctx->ports[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->port_count[level], ctx->port_count[old_level],
+ "EnumPorts invalid value");
+ }
+ /* if the array sizes are not the same we would maybe segfault in the following code */
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->port_count[level];j++) {
+ union spoolss_PortInfo *cur = &ctx->ports[level][j];
+ union spoolss_PortInfo *ref = &ctx->ports[2][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info2, port_name);
+ break;
+ case 2:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_GetPrintProcessorDirectory r;
+ struct {
+ uint16_t level;
+ const char *server;
+ } levels[] = {{
+ .level = 1,
+ .server = NULL
+ },{
+ .level = 1,
+ .server = ""
+ },{
+ .level = 78,
+ .server = ""
+ },{
+ .level = 1,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i].level;
+ DATA_BLOB blob;
+
+ r.in.server = levels[i].server;
+ r.in.environment = ctx->environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrintProcessorDirectory level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_GetPrintProcessorDirectory failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "GetPrintProcessorDirectory unexpected return code");
+
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrintProcessorDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, needed, 2);
+ }
+
+ return true;
+}
+
+
+static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_GetPrinterDriverDirectory r;
+ struct {
+ uint16_t level;
+ const char *server;
+ } levels[] = {{
+ .level = 1,
+ .server = NULL
+ },{
+ .level = 1,
+ .server = ""
+ },{
+ .level = 78,
+ .server = ""
+ },{
+ .level = 1,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i].level;
+ DATA_BLOB blob;
+
+ r.in.server = levels[i].server;
+ r.in.environment = ctx->environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "GetPrinterDriverDirectory unexpected return code");
+
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, needed, 2);
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers_buffers(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count_p,
+ union spoolss_DriverInfo **info_p)
+{
+ struct spoolss_EnumPrinterDrivers r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+ DATA_BLOB buffer;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(tctx, offered);
+ }
+
+ r.in.server = server_name;
+ r.in.environment = environment;
+ r.in.level = level;
+ r.in.buffer = offered ? &buffer : NULL;
+ r.in.offered = offered;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDrivers(%s) level %u, offered: %u\n",
+ r.in.environment, r.in.level, r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r),
+ "EnumPrinterDrivers failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r),
+ "EnumPrinterDrivers failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "EnumPrinterDrivers failed");
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, needed, 4);
+
+ return true;
+
+}
+
+
+static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_DriverInfo **info_p)
+{
+ return test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+ environment, level, 0,
+ count_p, info_p);
+}
+
+static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ const char *driver_name,
+ union spoolss_DriverInfo *info_p)
+{
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+ int i;
+ const char *environment_ret = NULL;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, environment, level, &count, &info),
+ "failed to enumerate printer drivers");
+
+ for (i=0; i < count; i++) {
+ const char *driver_name_ret = "";
+ switch (level) {
+ case 1:
+ driver_name_ret = info[i].info1.driver_name;
+ break;
+ case 2:
+ driver_name_ret = info[i].info2.driver_name;
+ environment_ret = info[i].info2.architecture;
+ break;
+ case 3:
+ driver_name_ret = info[i].info3.driver_name;
+ environment_ret = info[i].info3.architecture;
+ break;
+ case 4:
+ driver_name_ret = info[i].info4.driver_name;
+ environment_ret = info[i].info4.architecture;
+ break;
+ case 5:
+ driver_name_ret = info[i].info5.driver_name;
+ environment_ret = info[i].info5.architecture;
+ break;
+ case 6:
+ driver_name_ret = info[i].info6.driver_name;
+ environment_ret = info[i].info6.architecture;
+ break;
+ case 7:
+ driver_name_ret = info[i].info7.driver_name;
+ break;
+ case 8:
+ driver_name_ret = info[i].info8.driver_name;
+ environment_ret = info[i].info8.architecture;
+ break;
+ default:
+ break;
+ }
+ if (environment_ret) {
+ torture_assert_str_equal(tctx, environment, environment_ret, "architecture mismatch");
+ }
+ if (strequal(driver_name, driver_name_ret)) {
+ if (info_p) {
+ *info_p = info[i];
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool test_EnumPrinterDrivers(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint16_t levels[] = { 1, 2, 3, 4, 5, 6, 8 };
+ uint16_t buffer_sizes[] = { 0, 1024, 6040, 0xffff };
+ int i, j, a;
+
+ /* FIXME: gd, come back and fix "" as server, and handle
+ * priority of returned error codes in torture test and samba 3
+ * server */
+ const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ const char *environments[2];
+
+ environments[0] = SPOOLSS_ARCHITECTURE_ALL;
+ environments[1] = ctx->environment;
+
+ for (a=0;a<ARRAY_SIZE(environments);a++) {
+
+ for (i=0;i<ARRAY_SIZE(buffer_sizes);i++) {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+ environments[a], 3,
+ buffer_sizes[i],
+ NULL, NULL),
+ "failed to enumerate drivers");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, environments[a], level, &count, &info),
+ "failed to enumerate drivers");
+
+ ctx->driver_count[level] = count;
+ ctx->drivers[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+
+ torture_assert_int_equal(tctx, ctx->driver_count[level], ctx->driver_count[old_level],
+ "EnumPrinterDrivers invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+
+ for (j=0;j<ctx->driver_count[level - 1];j++) {
+ union spoolss_DriverInfo *cur = &ctx->drivers[level - 1][j];
+ union spoolss_DriverInfo *ref = &ctx->drivers[8][j];
+
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info8, driver_name);
+ break;
+ case 2:
+ COMPARE_UINT32(tctx, cur->info2, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, config_file);
+ break;
+ case 3:
+ COMPARE_UINT32(tctx, cur->info3, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, default_datatype);
+ break;
+ case 4:
+ COMPARE_UINT32(tctx, cur->info4, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, previous_names);
+ break;
+ case 5:
+ COMPARE_UINT32(tctx, cur->info5, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, config_file);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_attributes);*/
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info8, config_version);*/
+ /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_version); */
+ break;
+ case 6:
+ COMPARE_UINT32(tctx, cur->info6, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, previous_names);
+ COMPARE_NTTIME(tctx, cur->info6, ref->info8, driver_date);
+ COMPARE_UINT64(tctx, cur->info6, ref->info8, driver_version);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_url);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, hardware_id);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, provider);
+ break;
+ case 8:
+ /* level 8 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumMonitors(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ struct spoolss_EnumMonitors r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_MonitorInfo *info;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumMonitors level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumMonitors_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumMonitors failed");
+
+ blob = data_blob_talloc_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumMonitors_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, needed, 4);
+
+ ctx->monitor_count[level] = count;
+ ctx->monitors[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->monitor_count[level], ctx->monitor_count[old_level],
+ "EnumMonitors invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->monitor_count[level];j++) {
+ union spoolss_MonitorInfo *cur = &ctx->monitors[level][j];
+ union spoolss_MonitorInfo *ref = &ctx->monitors[2][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info2, monitor_name);
+ break;
+ case 2:
+ torture_assert_str_equal(tctx, ref->info2.environment, ctx->environment, "invalid environment");
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrintProcessorInfo **info_p,
+ WERROR expected_result)
+{
+ struct spoolss_EnumPrintProcessors r;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcessorInfo *info;
+
+ r.in.servername = "";
+ r.in.environment = environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrintProcessors(%s) level %u\n",
+ r.in.environment, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+ "EnumPrintProcessors failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+ "EnumPrintProcessors failed");
+ }
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "EnumPrintProcessors failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+ uint16_t ok[] = {0, 1, 0, 0, 0, 0 };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, "phantasy", 1, NULL, NULL, WERR_INVALID_ENVIRONMENT),
+ "test_EnumPrintProcessors_level failed");
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+ WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, ctx->environment, levels[i], &count, &info, expected_result),
+ "test_EnumPrintProcessors_level failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessorDataTypes_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *print_processor_name,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrintProcDataTypesInfo **info_p,
+ WERROR expected_result)
+{
+ struct spoolss_EnumPrintProcessorDataTypes r;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcDataTypesInfo *info;
+
+ r.in.servername = "";
+ r.in.print_processor_name = print_processor_name;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrintProcessorDataTypes(%s) level %u\n",
+ r.in.print_processor_name, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r),
+ "EnumPrintProcessorDataTypes failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r),
+ "EnumPrintProcessorDataTypes failed");
+ }
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "EnumPrintProcessorDataTypes failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessorDataTypes, info, level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessorDataTypes(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+ uint16_t ok[] = {0, 1, 0, 0, 0, 0 };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, NULL, 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+ "test_EnumPrintProcessorDataTypes_level failed");
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, "nonexisting", 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+ "test_EnumPrintProcessorDataTypes_level failed");
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ uint32_t count;
+ union spoolss_PrintProcDataTypesInfo *info;
+ WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, "winprint", level, &count, &info, expected_result),
+ "test_EnumPrintProcessorDataTypes_level failed");
+ }
+
+ {
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, ctx->environment, 1, &count, &info, WERR_OK),
+ "test_EnumPrintProcessors_level failed");
+
+ for (i=0; i < count; i++) {
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, info[i].info1.print_processor_name, 1, NULL, NULL, WERR_OK),
+ "test_EnumPrintProcessorDataTypes_level failed");
+ }
+ }
+
+
+ return true;
+}
+
+static bool test_EnumPrinters(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = { 0, 1, 2, 4, 5 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrinters unexpected return code");
+
+ blob = data_blob_talloc_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ ctx->printer_count[level] = count;
+ ctx->printers[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->printer_count[level], ctx->printer_count[old_level],
+ "EnumPrinters invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->printer_count[level];j++) {
+ union spoolss_PrinterInfo *cur = &ctx->printers[level][j];
+ union spoolss_PrinterInfo *ref = &ctx->printers[2][j];
+ switch (level) {
+ case 0:
+ COMPARE_STRING(tctx, cur->info0, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info0, ref->info2, servername);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, cjobs);
+ /*COMPARE_UINT32(tctx, cur->info0, ref->info2, total_jobs);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, total_bytes);
+ COMPARE_SPOOLSS_TIME(cur->info0, ref->info2, spoolss_Time time);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, global_counter);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, total_pages);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, version);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown10);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown11);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown12);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, session_counter);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown14);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, printer_errors);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown16);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown17);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown18);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown19);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, change_id);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown21);*/
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, status);
+ /*COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown23);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, c_setprinter);
+ COMPARE_UINT16(cur->info0, ref->info2, unknown25);
+ COMPARE_UINT16(cur->info0, ref->info2, unknown26);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown27);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown28);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown29);*/
+ break;
+ case 1:
+ /*COMPARE_UINT32(tctx, cur->info1, ref->info2, flags);*/
+ /*COMPARE_STRING(tctx, cur->info1, ref->info2, name);*/
+ /*COMPARE_STRING(tctx, cur->info1, ref->info2, description);*/
+ COMPARE_STRING(tctx, cur->info1, ref->info2, comment);
+ break;
+ case 2:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ case 4:
+ COMPARE_STRING(tctx, cur->info4, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info4, ref->info2, servername);
+ COMPARE_UINT32(tctx, cur->info4, ref->info2, attributes);
+ break;
+ case 5:
+ COMPARE_STRING(tctx, cur->info5, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info5, ref->info2, portname);
+ COMPARE_UINT32(tctx, cur->info5, ref->info2, attributes);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info2, device_not_selected_timeout);
+ COMPARE_UINT32(tctx, cur->info5, ref->info2, transmission_retry_timeout);*/
+ break;
+ }
+ }
+ }
+
+ /* TODO:
+ * - verify that the port of a printer was in the list returned by EnumPorts
+ */
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *environment);
+
+bool test_GetPrinter_level_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ WERROR expected_werror,
+ union spoolss_PrinterInfo *info)
+{
+ struct spoolss_GetPrinter r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "GetPrinter failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "GetPrinter failed");
+ }
+
+ torture_assert_werr_equal(tctx,
+ r.out.result, expected_werror,
+ "GetPrinter failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info && r.out.info) {
+ *info = *r.out.info;
+ }
+
+ return true;
+}
+
+bool test_GetPrinter_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ union spoolss_PrinterInfo *info)
+{
+ return test_GetPrinter_level_exp(tctx, b, handle, level, WERR_OK, info);
+}
+
+static bool test_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *environment)
+{
+ uint32_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ union spoolss_PrinterInfo info;
+
+ ZERO_STRUCT(info);
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, levels[i], &info),
+ "failed to call GetPrinter");
+
+ if ((levels[i] == 2) && info.info2.drivername && strlen(info.info2.drivername)) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2(tctx, b, handle, info.info2.drivername, environment),
+ "failed to call test_GetPrinterDriver2");
+ }
+ }
+
+ return true;
+}
+
+static bool test_SetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct spoolss_SetPrinterInfoCtr *info_ctr,
+ struct spoolss_DevmodeContainer *devmode_ctr,
+ struct sec_desc_buf *secdesc_ctr,
+ enum spoolss_PrinterControl command)
+{
+ struct spoolss_SetPrinter r;
+
+ r.in.handle = handle;
+ r.in.info_ctr = info_ctr;
+ r.in.devmode_ctr = devmode_ctr;
+ r.in.secdesc_ctr = secdesc_ctr;
+ r.in.command = command;
+
+ torture_comment(tctx, "Testing SetPrinter level %d\n", r.in.info_ctr->level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+ torture_assert(tctx, (W_ERROR_EQUAL(r.out.result, WERR_OK)
+ || W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)),
+ "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_SetPrinter_errors(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_SetPrinter r;
+ uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ int i;
+
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = 0;
+
+ torture_comment(tctx, "Testing SetPrinter all zero\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "failed to call SetPrinter");
+
+ again:
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ struct spoolss_SetPrinterInfo0 info0;
+ struct spoolss_SetPrinterInfo1 info1;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_SetPrinterInfo4 info4;
+ struct spoolss_SetPrinterInfo5 info5;
+ struct spoolss_SetPrinterInfo6 info6;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfo8 info8;
+ struct spoolss_SetPrinterInfo9 info9;
+
+
+ info_ctr.level = levels[i];
+ switch (levels[i]) {
+ case 0:
+ ZERO_STRUCT(info0);
+ info_ctr.info.info0 = &info0;
+ break;
+ case 1:
+ ZERO_STRUCT(info1);
+ info_ctr.info.info1 = &info1;
+ break;
+ case 2:
+ ZERO_STRUCT(info2);
+ info_ctr.info.info2 = &info2;
+ break;
+ case 3:
+ ZERO_STRUCT(info3);
+ info_ctr.info.info3 = &info3;
+ break;
+ case 4:
+ ZERO_STRUCT(info4);
+ info_ctr.info.info4 = &info4;
+ break;
+ case 5:
+ ZERO_STRUCT(info5);
+ info_ctr.info.info5 = &info5;
+ break;
+ case 6:
+ ZERO_STRUCT(info6);
+ info_ctr.info.info6 = &info6;
+ break;
+ case 7:
+ ZERO_STRUCT(info7);
+ info_ctr.info.info7 = &info7;
+ break;
+ case 8:
+ ZERO_STRUCT(info8);
+ info_ctr.info.info8 = &info8;
+ break;
+ case 9:
+ ZERO_STRUCT(info9);
+ info_ctr.info.info9 = &info9;
+ break;
+ }
+
+ torture_comment(tctx, "Testing SetPrinter level %d, command %d\n",
+ info_ctr.level, r.in.command);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+
+ switch (r.in.command) {
+ case SPOOLSS_PRINTER_CONTROL_UNPAUSE: /* 0 */
+ /* is ignored for all levels other then 0 */
+ if (info_ctr.level > 0) {
+ /* ignored then */
+ break;
+ }
+
+ FALL_THROUGH;
+ case SPOOLSS_PRINTER_CONTROL_PAUSE: /* 1 */
+ case SPOOLSS_PRINTER_CONTROL_RESUME: /* 2 */
+ case SPOOLSS_PRINTER_CONTROL_PURGE: /* 3 */
+ if (info_ctr.level > 0) {
+ /* is invalid for all levels other then 0 */
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND,
+ "unexpected error code returned");
+ continue;
+ } else {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call SetPrinter with non 0 command");
+ continue;
+ }
+ break;
+
+ case SPOOLSS_PRINTER_CONTROL_SET_STATUS: /* 4 */
+ /* FIXME: gd needs further investigation */
+ default:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND,
+ "unexpected error code returned");
+ continue;
+ }
+
+ switch (info_ctr.level) {
+ case 1:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL,
+ "unexpected error code returned");
+ break;
+ case 2:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_PRINTER_DRIVER,
+ "unexpected error code returned");
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 7:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "unexpected error code returned");
+ break;
+ case 9:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "unexpected error code returned");
+ break;
+ default:
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call SetPrinter");
+ break;
+ }
+ }
+
+ if (r.in.command < 5) {
+ r.in.command++;
+ goto again;
+ }
+
+ return true;
+}
+
+static void clear_info2(struct spoolss_SetPrinterInfoCtr *r)
+{
+ if ((r->level == 2) && (r->info.info2)) {
+ r->info.info2->secdesc_ptr = 0;
+ r->info.info2->devmode_ptr = 0;
+ }
+}
+
+static bool test_PrinterInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter s;
+ struct spoolss_GetPrinter q;
+ struct spoolss_GetPrinter q0;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ union spoolss_PrinterInfo info;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ uint32_t needed = 0;
+ DATA_BLOB blob = data_blob_null;
+ bool ret = true;
+ int i;
+
+ uint32_t status_list[] = {
+ /* these do not stick
+ PRINTER_STATUS_PAUSED,
+ PRINTER_STATUS_ERROR,
+ PRINTER_STATUS_PENDING_DELETION, */
+ PRINTER_STATUS_PAPER_JAM,
+ PRINTER_STATUS_PAPER_OUT,
+ PRINTER_STATUS_MANUAL_FEED,
+ PRINTER_STATUS_PAPER_PROBLEM,
+ PRINTER_STATUS_OFFLINE,
+ PRINTER_STATUS_IO_ACTIVE,
+ PRINTER_STATUS_BUSY,
+ PRINTER_STATUS_PRINTING,
+ PRINTER_STATUS_OUTPUT_BIN_FULL,
+ PRINTER_STATUS_NOT_AVAILABLE,
+ PRINTER_STATUS_WAITING,
+ PRINTER_STATUS_PROCESSING,
+ PRINTER_STATUS_INITIALIZING,
+ PRINTER_STATUS_WARMING_UP,
+ PRINTER_STATUS_TONER_LOW,
+ PRINTER_STATUS_NO_TONER,
+ PRINTER_STATUS_PAGE_PUNT,
+ PRINTER_STATUS_USER_INTERVENTION,
+ PRINTER_STATUS_OUT_OF_MEMORY,
+ PRINTER_STATUS_DOOR_OPEN,
+ PRINTER_STATUS_SERVER_UNKNOWN,
+ PRINTER_STATUS_POWER_SAVE,
+ /* these do not stick
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000 */
+ };
+ uint32_t default_attribute = PRINTER_ATTRIBUTE_LOCAL;
+ uint32_t attribute_list[] = {
+ PRINTER_ATTRIBUTE_QUEUED,
+ /* fails with WERR_INVALID_DATATYPE:
+ PRINTER_ATTRIBUTE_DIRECT, */
+ /* does not stick
+ PRINTER_ATTRIBUTE_DEFAULT, */
+ PRINTER_ATTRIBUTE_SHARED,
+ /* does not stick
+ PRINTER_ATTRIBUTE_NETWORK, */
+ PRINTER_ATTRIBUTE_HIDDEN,
+ PRINTER_ATTRIBUTE_LOCAL,
+ PRINTER_ATTRIBUTE_ENABLE_DEVQ,
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS,
+ PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST,
+ PRINTER_ATTRIBUTE_WORK_OFFLINE,
+ /* does not stick
+ PRINTER_ATTRIBUTE_ENABLE_BIDI, */
+ /* fails with WERR_INVALID_DATATYPE:
+ PRINTER_ATTRIBUTE_RAW_ONLY, */
+ /* these do not stick
+ PRINTER_ATTRIBUTE_PUBLISHED,
+ PRINTER_ATTRIBUTE_FAX,
+ PRINTER_ATTRIBUTE_TS,
+ 0x00010000,
+ 0x00020000,
+ 0x00040000,
+ 0x00080000,
+ 0x00100000,
+ 0x00200000,
+ 0x00400000,
+ 0x00800000,
+ 0x01000000,
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000 */
+ };
+
+ torture_skip(tctx, "Printer Info test is currently broken, skipping");
+
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ s.in.handle = handle;
+ s.in.command = 0;
+ s.in.info_ctr = &info_ctr;
+ s.in.devmode_ctr = &devmode_ctr;
+ s.in.secdesc_ctr = &secdesc_ctr;
+
+ q.in.handle = handle;
+ q.out.info = &info;
+ q0 = q;
+
+#define TESTGETCALL(call, r, needed, blob) \
+ r.in.buffer = NULL; \
+ r.in.offered = 0;\
+ r.out.needed = &needed; \
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ }\
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {\
+ blob = data_blob_talloc_zero(tctx, needed); \
+ r.in.buffer = &blob; \
+ r.in.offered = needed; \
+ }\
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, win_errstr(r.out.result), __location__); \
+ ret = false; \
+ break; \
+ }
+
+
+#define TESTSETCALL_EXP(call, r, err) \
+ clear_info2(&info_ctr);\
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.info_ctr->level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(err)) { \
+ if (!W_ERROR_EQUAL(err, r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s, expected %s (%s)\n", \
+ r.in.info_ctr->level, win_errstr(r.out.result), win_errstr(err), __location__); \
+ ret = false; \
+ } \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.info_ctr->level, win_errstr(r.out.result), __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define TESTSETCALL(call, r) \
+ TESTSETCALL_EXP(call, r, WERR_OK)
+
+#define STRING_EQUAL(s1, s2, field) \
+ if ((s1 && !s2) || (s2 && !s1) || strcmp(s1, s2)) { \
+ torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \
+ #field, s2, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define MEM_EQUAL(s1, s2, length, field) \
+ if ((s1 && !s2) || (s2 && !s1) || memcmp(s1, s2, length)) { \
+ torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \
+ #field, (const char *)s2, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define INT_EQUAL(i1, i2, field) \
+ if (i1 != i2) { \
+ torture_comment(tctx, "Failed to set %s to 0x%llx - got 0x%llx (%s)\n", \
+ #field, (unsigned long long)i2, (unsigned long long)i1, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define SD_EQUAL(sd1, sd2, field) \
+ if (!security_descriptor_equal(sd1, sd2)) { \
+ torture_comment(tctx, "Failed to set %s (%s)\n", \
+ #field, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, lvl1, field1, lvl2, field2, value, err) do { \
+ void *p; \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ info_ctr.level = lvl1; \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ info_ctr.info.info ## lvl1->field1 = value;\
+ TESTSETCALL_EXP(SetPrinter, s, err) \
+ info_ctr.info.info ## lvl1->field1 = ""; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ info_ctr.info.info ## lvl1->field1 = value; \
+ STRING_EQUAL(info_ctr.info.info ## lvl1->field1, value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ p = (void *)&q.out.info->info ## lvl2; \
+ info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
+ STRING_EQUAL(info_ctr.info.info ## lvl2->field2, value, field2); \
+ } while (0)
+
+#define TEST_PRINTERINFO_STRING(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, lvl1, field1, lvl2, field2, value, WERR_OK); \
+ } while (0);
+
+#define TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, exp_value) do { \
+ void *p; \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ info_ctr.level = lvl1; \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ info_ctr.info.info ## lvl1->field1 = value; \
+ TESTSETCALL(SetPrinter, s) \
+ info_ctr.info.info ## lvl1->field1 = 0; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ INT_EQUAL(info_ctr.info.info ## lvl1->field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ p = (void *)&q.out.info->info ## lvl2; \
+ info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
+ INT_EQUAL(info_ctr.info.info ## lvl2->field2, exp_value, field1); \
+ } while (0)
+
+#define TEST_PRINTERINFO_INT(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value); \
+ } while (0)
+
+ q0.in.level = 0;
+ do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0);
+
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, comment, 1, comment, "xx2-1 comment");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, comment, 2, comment, "xx2-2 comment");
+
+ /* level 0 printername does not stick */
+/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 0, printername, "xx2-0 printer"); */
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 1, name, "xx2-1 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 2, printername, "xx2-2 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 4, printername, "xx2-4 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, printername, 5, printername, "xx2-5 printer");
+/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 0, printername, "xx4-0 printer"); */
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 1, name, "xx4-1 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 2, printername, "xx4-2 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 4, printername, "xx4-4 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 4, printername, 5, printername, "xx4-5 printer");
+/* TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 0, printername, "xx5-0 printer"); */
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 1, name, "xx5-1 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 2, printername, "xx5-2 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 4, printername, "xx5-4 printer");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 5, printername, 5, printername, "xx5-5 printer");
+
+ /* servername can be set but does not stick
+ TEST_PRINTERINFO_STRING(q, 2, servername, 0, servername, "xx2-0 servername");
+ TEST_PRINTERINFO_STRING(q, 2, servername, 2, servername, "xx2-2 servername");
+ TEST_PRINTERINFO_STRING(q, 2, servername, 4, servername, "xx2-4 servername");
+ */
+
+ /* passing an invalid port will result in WERR_UNKNOWN_PORT */
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, portname, 2, portname, "xx2-2 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, portname, 5, portname, "xx2-5 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 5, portname, 2, portname, "xx5-2 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 5, portname, 5, portname, "xx5-5 portname", WERR_UNKNOWN_PORT);
+
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, sharename, 2, sharename, "xx2-2 sharename");
+ /* passing an invalid driver will result in WERR_UNKNOWN_PRINTER_DRIVER */
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, drivername, 2, drivername, "xx2-2 drivername", WERR_UNKNOWN_PRINTER_DRIVER);
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, location, 2, location, "xx2-2 location");
+ /* passing an invalid sepfile will result in WERR_INVALID_SEPARATOR_FILE */
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, sepfile, 2, sepfile, "xx2-2 sepfile", WERR_INVALID_SEPARATOR_FILE);
+ /* passing an invalid printprocessor will result in WERR_UNKNOWN_PRINTPROCESSOR */
+ TEST_PRINTERINFO_STRING_EXP_ERR(q, s, needed, blob, 2, printprocessor, 2, printprocessor, "xx2-2 printprocessor", WERR_UNKNOWN_PRINTPROCESSOR);
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, datatype, 2, datatype, "xx2-2 datatype");
+ TEST_PRINTERINFO_STRING(q, s, needed, blob, 2, parameters, 2, parameters, "xx2-2 parameters");
+
+ for (i=0; i < ARRAY_SIZE(attribute_list); i++) {
+/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 2, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 4, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+/* TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(q, s, needed, blob, 5, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ }
+
+ for (i=0; i < ARRAY_SIZE(status_list); i++) {
+ /* level 2 sets do not stick
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 0, status, status_list[i]);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 2, status, status_list[i]);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, status, 6, status, status_list[i]); */
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 0, status, status_list[i]);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 2, status, status_list[i]);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 6, status, 6, status, status_list[i]);
+ }
+
+ /* priorities need to be between 0 and 99
+ passing an invalid priority will result in WERR_INVALID_PRIORITY */
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 0);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 1);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 99);
+ /* TEST_PRINTERINFO_INT(q, s, needed, blob, 2, priority, 2, priority, 100); */
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 0);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 1);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 99);
+ /* TEST_PRINTERINFO_INT(q, s, needed, blob, 2, defaultpriority,2, defaultpriority, 100); */
+
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, starttime, 2, starttime, __LINE__);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, untiltime, 2, untiltime, __LINE__);
+
+ /* does not stick
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, cjobs, 2, cjobs, __LINE__);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 2, averageppm, 2, averageppm, __LINE__); */
+
+ /* does not stick
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 5, device_not_selected_timeout, 5, device_not_selected_timeout, __LINE__);
+ TEST_PRINTERINFO_INT(q, s, needed, blob, 5, transmission_retry_timeout, 5, transmission_retry_timeout, __LINE__); */
+
+ /* FIXME: gd also test devmode and secdesc behavior */
+
+ {
+ /* verify composition of level 1 description field */
+ const char *description;
+ const char *tmp;
+
+ q0.in.level = 1;
+ do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0);
+
+ description = talloc_strdup(tctx, q0.out.info->info1.description);
+
+ q0.in.level = 2;
+ do { TESTGETCALL(GetPrinter, q0, needed, blob) } while (0);
+
+ tmp = talloc_asprintf(tctx, "%s,%s,%s",
+ q0.out.info->info2.printername,
+ q0.out.info->info2.drivername,
+ q0.out.info->info2.location);
+
+ do { STRING_EQUAL(description, tmp, "description")} while (0);
+ }
+
+ return ret;
+}
+
+static bool test_security_descriptor_equal(struct torture_context *tctx,
+ const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2)
+{
+ if (sd1 == sd2) {
+ return true;
+ }
+
+ if (!sd1 || !sd2) {
+ torture_comment(tctx, "%s\n", __location__);
+ return false;
+ }
+
+ torture_assert_int_equal(tctx, sd1->revision, sd2->revision, "revision mismatch");
+ torture_assert_int_equal(tctx, sd1->type, sd2->type, "type mismatch");
+
+ torture_assert_sid_equal(tctx, sd1->owner_sid, sd2->owner_sid, "owner mismatch");
+ torture_assert_sid_equal(tctx, sd1->group_sid, sd2->group_sid, "group mismatch");
+
+ if (!security_acl_equal(sd1->sacl, sd2->sacl)) {
+ torture_comment(tctx, "%s: sacl mismatch\n", __location__);
+ NDR_PRINT_DEBUG(security_acl, sd1->sacl);
+ NDR_PRINT_DEBUG(security_acl, sd2->sacl);
+ return false;
+ }
+ if (!security_acl_equal(sd1->dacl, sd2->dacl)) {
+ torture_comment(tctx, "%s: dacl mismatch\n", __location__);
+ NDR_PRINT_DEBUG(security_acl, sd1->dacl);
+ NDR_PRINT_DEBUG(security_acl, sd2->dacl);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_sd_set_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ struct security_descriptor *sd)
+{
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo3 info3;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ switch (level) {
+ case 2: {
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ break;
+ }
+ case 3: {
+
+ info3.sec_desc_ptr = 0;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ secdesc_ctr.sd = sd;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+
+ return true;
+}
+
+static bool test_PrinterInfo_SDs(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct security_descriptor *sd1, *sd2;
+ int i;
+
+ /* just compare level 2 and level 3 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 3, &info), "");
+
+ sd2 = info.info3.secdesc;
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 3");
+
+
+ /* query level 2, set level 2, query level 2 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 2, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd2 = info.info2.secdesc;
+ if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
+ torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n");
+ sd1->type &= ~SEC_DESC_DACL_DEFAULTED;
+ }
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 2 after SD has been set via level 2");
+
+
+ /* query level 2, set level 3, query level 2 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd2 = info.info2.secdesc;
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 2 after SD has been set via level 3");
+
+ /* set modified sd level 3, query level 2 */
+
+ for (i=0; i < 93; i++) {
+ struct security_ace a = {};
+ const char *sid_string = talloc_asprintf(tctx, "S-1-5-32-9999%i", i);
+ a.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ a.flags = 0;
+ a.size = 0; /* autogenerated */
+ a.access_mask = 0;
+ a.trustee = *dom_sid_parse_talloc(tctx, sid_string);
+ torture_assert_ntstatus_ok(tctx, security_descriptor_dacl_add(sd1, &a), "");
+ }
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ sd2 = info.info2.secdesc;
+
+ if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
+ torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n");
+ sd1->type &= ~SEC_DESC_DACL_DEFAULTED;
+ }
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "modified SD level 2 != SD level 2 after SD has been set via level 3");
+
+
+ return true;
+}
+
+/*
+ * wrapper call that saves original sd, runs tests, and restores sd
+ */
+
+static bool test_PrinterInfo_SD(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct security_descriptor *sd;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing Printer Security Descriptors\n");
+
+ /* save original sd */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to get initial security descriptor");
+
+ sd = security_descriptor_copy(tctx, info.info2.secdesc);
+
+ /* run tests */
+
+ ret = test_PrinterInfo_SDs(tctx, b, handle);
+
+ /* restore original sd */
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd),
+ "failed to restore initial security descriptor");
+
+ torture_comment(tctx, "Printer Security Descriptors test %s\n\n",
+ ret ? "succeeded" : "failed");
+
+
+ return ret;
+}
+
+static bool test_devmode_set_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ struct spoolss_DeviceMode *devmode)
+{
+ struct spoolss_SetPrinterInfo8 info8;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ switch (level) {
+ case 2: {
+ union spoolss_PrinterInfo info;
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ break;
+ }
+ case 8: {
+ info8.devmode_ptr = 0;
+
+ info_ctr.level = 8;
+ info_ctr.info.info8 = &info8;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ devmode_ctr.devmode = devmode;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+
+ return true;
+}
+
+
+static bool test_devicemode_equal(struct torture_context *tctx,
+ const struct spoolss_DeviceMode *d1,
+ const struct spoolss_DeviceMode *d2)
+{
+ if (d1 == d2) {
+ return true;
+ }
+
+ if (!d1 || !d2) {
+ torture_comment(tctx, "%s\n", __location__);
+ return false;
+ }
+ torture_assert_str_equal(tctx, d1->devicename, d2->devicename, "devicename mismatch");
+ torture_assert_int_equal(tctx, d1->specversion, d2->specversion, "specversion mismatch");
+ torture_assert_int_equal(tctx, d1->driverversion, d2->driverversion, "driverversion mismatch");
+ torture_assert_int_equal(tctx, d1->size, d2->size, "size mismatch");
+ torture_assert_int_equal(tctx, d1->__driverextra_length, d2->__driverextra_length, "__driverextra_length mismatch");
+ torture_assert_int_equal(tctx, d1->fields, d2->fields, "fields mismatch");
+ torture_assert_int_equal(tctx, d1->orientation, d2->orientation, "orientation mismatch");
+ torture_assert_int_equal(tctx, d1->papersize, d2->papersize, "papersize mismatch");
+ torture_assert_int_equal(tctx, d1->paperlength, d2->paperlength, "paperlength mismatch");
+ torture_assert_int_equal(tctx, d1->paperwidth, d2->paperwidth, "paperwidth mismatch");
+ torture_assert_int_equal(tctx, d1->scale, d2->scale, "scale mismatch");
+ torture_assert_int_equal(tctx, d1->copies, d2->copies, "copies mismatch");
+ torture_assert_int_equal(tctx, d1->defaultsource, d2->defaultsource, "defaultsource mismatch");
+ torture_assert_int_equal(tctx, d1->printquality, d2->printquality, "printquality mismatch");
+ torture_assert_int_equal(tctx, d1->color, d2->color, "color mismatch");
+ torture_assert_int_equal(tctx, d1->duplex, d2->duplex, "duplex mismatch");
+ torture_assert_int_equal(tctx, d1->yresolution, d2->yresolution, "yresolution mismatch");
+ torture_assert_int_equal(tctx, d1->ttoption, d2->ttoption, "ttoption mismatch");
+ torture_assert_int_equal(tctx, d1->collate, d2->collate, "collate mismatch");
+ torture_assert_str_equal(tctx, d1->formname, d2->formname, "formname mismatch");
+ torture_assert_int_equal(tctx, d1->logpixels, d2->logpixels, "logpixels mismatch");
+ torture_assert_int_equal(tctx, d1->bitsperpel, d2->bitsperpel, "bitsperpel mismatch");
+ torture_assert_int_equal(tctx, d1->pelswidth, d2->pelswidth, "pelswidth mismatch");
+ torture_assert_int_equal(tctx, d1->pelsheight, d2->pelsheight, "pelsheight mismatch");
+ torture_assert_int_equal(tctx, d1->displayflags, d2->displayflags, "displayflags mismatch");
+ torture_assert_int_equal(tctx, d1->displayfrequency, d2->displayfrequency, "displayfrequency mismatch");
+ torture_assert_int_equal(tctx, d1->icmmethod, d2->icmmethod, "icmmethod mismatch");
+ torture_assert_int_equal(tctx, d1->icmintent, d2->icmintent, "icmintent mismatch");
+ torture_assert_int_equal(tctx, d1->mediatype, d2->mediatype, "mediatype mismatch");
+ torture_assert_int_equal(tctx, d1->dithertype, d2->dithertype, "dithertype mismatch");
+ torture_assert_int_equal(tctx, d1->reserved1, d2->reserved1, "reserved1 mismatch");
+ torture_assert_int_equal(tctx, d1->reserved2, d2->reserved2, "reserved2 mismatch");
+ torture_assert_int_equal(tctx, d1->panningwidth, d2->panningwidth, "panningwidth mismatch");
+ torture_assert_int_equal(tctx, d1->panningheight, d2->panningheight, "panningheight mismatch");
+ torture_assert_data_blob_equal(tctx, d1->driverextra_data, d2->driverextra_data, "driverextra_data mismatch");
+
+ return true;
+}
+
+static bool test_devicemode_full(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_SetPrinter s;
+ struct spoolss_GetPrinter q;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo8 info8;
+ union spoolss_PrinterInfo info;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ uint32_t needed = 0;
+ DATA_BLOB blob = data_blob_null;
+ bool ret = true;
+ NTSTATUS status;
+
+#define TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, exp_value, expected_result) do { \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ info_ctr.level = lvl1; \
+ if (lvl1 == 2) {\
+ void *p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ } else if (lvl1 == 8) {\
+ info_ctr.info.info ## lvl1 = &info8; \
+ }\
+ devmode_ctr.devmode = q.out.info->info ## lvl1.devmode; \
+ devmode_ctr.devmode->field1 = value; \
+ TESTSETCALL_EXP(SetPrinter, s, expected_result) \
+ if (W_ERROR_IS_OK(expected_result)) { \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q, needed, blob) \
+ INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+ }\
+ } while (0)
+
+#define TEST_DEVMODE_INT_EXP(q, s, needed, blob, lvl1, field1, lvl2, field2, value, expected_result) do { \
+ TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value, expected_result); \
+ } while (0)
+
+#define TEST_DEVMODE_INT(q, s, needed, blob, lvl1, field1, lvl2, field2, value) do { \
+ TEST_DEVMODE_INT_EXP_RESULT(q, s, needed, blob, lvl1, field1, lvl2, field2, value, value, WERR_OK); \
+ } while (0)
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(info8);
+
+ s.in.handle = handle;
+ s.in.command = 0;
+ s.in.info_ctr = &info_ctr;
+ s.in.devmode_ctr = &devmode_ctr;
+ s.in.secdesc_ctr = &secdesc_ctr;
+
+ q.in.handle = handle;
+ q.out.info = &info;
+
+#if 0
+ const char *devicename;/* [charset(UTF16)] */
+ enum spoolss_DeviceModeSpecVersion specversion;
+ uint16_t driverversion;
+ uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */
+ uint32_t fields;
+#endif
+ TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, __LINE__, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, 0, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, 0xffff, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+ devmode_ctr.devmode->driverextra_data = data_blob_string_const("foobar");
+ torture_assert(tctx,
+ test_devmode_set_level(tctx, b, handle, 8, devmode_ctr.devmode),
+ "failed to set devmode");
+
+ TEST_DEVMODE_INT_EXP(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, orientation, 8, orientation, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, papersize, 8, papersize, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, paperlength, 8, paperlength, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, paperwidth, 8, paperwidth, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, scale, 8, scale, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, copies, 8, copies, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, defaultsource, 8, defaultsource, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, printquality, 8, printquality, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, color, 8, color, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, duplex, 8, duplex, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, yresolution, 8, yresolution, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, ttoption, 8, ttoption, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, collate, 8, collate, __LINE__);
+#if 0
+ const char *formname;/* [charset(UTF16)] */
+#endif
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, logpixels, 8, logpixels, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, bitsperpel, 8, bitsperpel, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, pelswidth, 8, pelswidth, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, pelsheight, 8, pelsheight, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, displayflags, 8, displayflags, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, displayfrequency, 8, displayfrequency, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, icmmethod, 8, icmmethod, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, icmintent, 8, icmintent, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, mediatype, 8, mediatype, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, dithertype, 8, dithertype, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, reserved1, 8, reserved1, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, reserved2, 8, reserved2, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, panningwidth, 8, panningwidth, __LINE__);
+ TEST_DEVMODE_INT(q, s, needed, blob, 8, panningheight, 8, panningheight, __LINE__);
+
+ return ret;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ struct spoolss_DeviceMode *devmode,
+ struct policy_handle *handle);
+
+static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *name)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_DeviceMode *devmode;
+ struct spoolss_DeviceMode *devmode2;
+ struct policy_handle handle_devmode;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* simply compare level8 and level2 devmode */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode = info.info8.devmode;
+
+ if (devmode && devmode->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ if (devmode2 && devmode2->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "DM level 8 != DM level 2");
+
+
+ /* set devicemode level 8 and see if it persists */
+
+ devmode->copies = 93;
+ devmode->formname = talloc_strdup(tctx, "Legal");
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 8 after DM has been set via level 8");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 2");
+
+
+ /* set devicemode level 2 and see if it persists */
+
+ devmode->copies = 39;
+ devmode->formname = talloc_strdup(tctx, "Executive");
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 2, devmode), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 8 after DM has been set via level 2");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 2");
+
+
+ /* check every single bit in public part of devicemode */
+
+ torture_assert(tctx, test_devicemode_full(tctx, b, handle),
+ "failed to set every single devicemode component");
+
+
+ /* change formname upon open and see if it persists in getprinter calls */
+
+ devmode->formname = talloc_strdup(tctx, "A4");
+ devmode->copies = 42;
+
+ torture_assert(tctx, call_OpenPrinterEx(tctx, p, name, devmode, &handle_devmode),
+ "failed to open printer handle");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ if (strequal(devmode->devicename, devmode2->devicename)) {
+ torture_warning(tctx, "devicenames are the same\n");
+ } else {
+ torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename);
+ torture_comment(tctx, "devicename after level 8 get: %s\n", devmode2->devicename);
+ }
+
+ if (strequal(devmode->formname, devmode2->formname)) {
+ torture_warning(tctx, "formname are the same\n");
+ } else {
+ torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname);
+ torture_comment(tctx, "formname after level 8 get: %s\n", devmode2->formname);
+ }
+
+ if (devmode->copies == devmode2->copies) {
+ torture_warning(tctx, "copies are the same\n");
+ } else {
+ torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies);
+ torture_comment(tctx, "copies after level 8 get: %d\n", devmode2->copies);
+ }
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ if (strequal(devmode->devicename, devmode2->devicename)) {
+ torture_warning(tctx, "devicenames are the same\n");
+ } else {
+ torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename);
+ torture_comment(tctx, "devicename after level 2 get: %s\n", devmode2->devicename);
+ }
+
+ if (strequal(devmode->formname, devmode2->formname)) {
+ torture_warning(tctx, "formname is the same\n");
+ } else {
+ torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname);
+ torture_comment(tctx, "formname after level 2 get: %s\n", devmode2->formname);
+ }
+
+ if (devmode->copies == devmode2->copies) {
+ torture_warning(tctx, "copies are the same\n");
+ } else {
+ torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies);
+ torture_comment(tctx, "copies after level 2 get: %d\n", devmode2->copies);
+ }
+
+ test_ClosePrinter(tctx, b, &handle_devmode);
+
+ return true;
+}
+
+/*
+ * wrapper call that saves original devmode, runs tests, and restores devmode
+ */
+
+static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *name,
+ struct spoolss_DeviceMode *addprinter_devmode)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_DeviceMode *devmode;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing Printer Devicemodes\n");
+
+ /* save original devmode */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info),
+ "failed to get initial global devicemode");
+
+ devmode = info.info8.devmode;
+
+ if (devmode && devmode->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ if (addprinter_devmode) {
+ if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) {
+ torture_warning(tctx, "current global DM is != DM provided in addprinter");
+ }
+ }
+
+ /* run tests */
+
+ ret = test_PrinterInfo_DevModes(tctx, p, handle, name);
+
+ /* restore original devmode */
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode),
+ "failed to restore initial global device mode");
+
+ torture_comment(tctx, "Printer Devicemodes test %s\n\n",
+ ret ? "succeeded" : "failed");
+
+
+ return ret;
+}
+
+bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing ClosePrinter\n");
+
+ status = dcerpc_spoolss_ClosePrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result, "ClosePrinter failed");
+
+ return true;
+}
+
+static bool test_GetForm_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_FormInfo *info_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetForm r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetForm(%s) level %d\n", form_name, r.in.level);
+
+ status = dcerpc_spoolss_GetForm_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetForm failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_GetForm_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
+
+ torture_assert(tctx, r.out.info, "No form info returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+static bool test_GetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level)
+{
+ return test_GetForm_args(tctx, b, handle, form_name, level, NULL);
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_FormInfo **info_p)
+{
+ struct spoolss_EnumForms r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_FormInfo *info;
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumForms level %d\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
+
+ if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL))) {
+ torture_skip(tctx, "EnumForms level 2 not supported");
+ }
+
+ if (print_server && W_ERROR_EQUAL(r.out.result, WERR_INVALID_HANDLE)) {
+ torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
+
+ torture_assert(tctx, info, "No forms returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, needed, 4);
+
+ if (info_p) {
+ *info_p = info;
+ }
+ if (count_p) {
+ *count_p = count;
+ }
+
+ return true;
+}
+
+static bool test_EnumForms_all(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server)
+{
+ uint32_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ uint32_t count = 0;
+ union spoolss_FormInfo *info = NULL;
+
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, levels[i], &count, &info),
+ "failed to enum forms");
+
+ for (j = 0; j < count; j++) {
+ if (!print_server) {
+ torture_assert(tctx,
+ test_GetForm(tctx, b, handle, info[j].info1.form_name, levels[i]),
+ "failed to get form");
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumForms_find_one(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *form_name)
+{
+ union spoolss_FormInfo *info = NULL;
+ uint32_t count = 0;
+ bool found = false;
+ int i;
+
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, 1, &count, &info),
+ "failed to enumerate forms");
+
+ for (i=0; i<count; i++) {
+ if (strequal(form_name, info[i].info1.form_name)) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool test_DeleteForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ WERROR expected_result)
+{
+ struct spoolss_DeleteForm r;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+
+ torture_comment(tctx, "Testing DeleteForm(%s)\n", form_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "DeleteForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeleteForm gave unexpected result");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "2nd DeleteForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_FORM_NAME,
+ "2nd DeleteForm failed");
+ }
+
+ return true;
+}
+
+static bool test_AddForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ union spoolss_AddFormInfo *info,
+ WERROR expected_result)
+{
+ struct spoolss_AddForm r;
+ struct spoolss_AddFormInfoCtr info_ctr;
+
+ info_ctr.level = level;
+ info_ctr.info = *info;
+
+ if (level != 1) {
+ torture_skip(tctx, "only level 1 supported");
+ }
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+
+ torture_comment(tctx, "Testing AddForm(%s) level %d, type %d\n",
+ r.in.info_ctr->info.info1->form_name, level,
+ r.in.info_ctr->info.info1->flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "AddForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "AddForm gave unexpected result");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "2nd AddForm failed");
+ if (W_ERROR_EQUAL(expected_result, WERR_INVALID_PARAMETER)) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "2nd AddForm gave unexpected result");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_FILE_EXISTS,
+ "2nd AddForm gave unexpected result");
+ }
+
+ return true;
+}
+
+static bool test_SetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_AddFormInfo *info)
+{
+ struct spoolss_SetForm r;
+ struct spoolss_AddFormInfoCtr info_ctr;
+
+ info_ctr.level = level;
+ info_ctr.info = *info;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.info_ctr = &info_ctr;
+
+ torture_comment(tctx, "Testing SetForm(%s) level %d\n",
+ form_name, level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r),
+ "SetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetForm failed");
+
+ return true;
+}
+
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data);
+
+static bool test_Forms_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *info1,
+ WERROR expected_add_result,
+ WERROR expected_delete_result)
+{
+ union spoolss_FormInfo info;
+ union spoolss_AddFormInfo add_info;
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ add_info.info1 = info1;
+
+ torture_assert(tctx,
+ test_AddForm(tctx, b, handle, 1, &add_info, expected_add_result),
+ "failed to add form");
+
+ if (winreg_handle && hive_handle && W_ERROR_IS_OK(expected_add_result)) {
+
+ struct spoolss_FormInfo1 i1;
+
+ torture_assert(tctx,
+ test_GetForm_winreg(tctx, winreg_handle, hive_handle, TOP_LEVEL_CONTROL_FORMS_KEY, form_name, &w_type, &w_size, &w_length, &w_data),
+ "failed to get form via winreg");
+
+ i1.size.width = IVAL(w_data, 0);
+ i1.size.height = IVAL(w_data, 4);
+ i1.area.left = IVAL(w_data, 8);
+ i1.area.top = IVAL(w_data, 12);
+ i1.area.right = IVAL(w_data, 16);
+ i1.area.bottom = IVAL(w_data, 20);
+ /* skip index here */
+ i1.flags = IVAL(w_data, 28);
+
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");
+ torture_assert_int_equal(tctx, w_size, 0x20, "unexpected size");
+ torture_assert_int_equal(tctx, w_length, 0x20, "unexpected length");
+ torture_assert_int_equal(tctx, i1.size.width, add_info.info1->size.width, "width mismatch");
+ torture_assert_int_equal(tctx, i1.size.height, add_info.info1->size.height, "height mismatch");
+ torture_assert_int_equal(tctx, i1.area.left, add_info.info1->area.left, "left mismatch");
+ torture_assert_int_equal(tctx, i1.area.top, add_info.info1->area.top, "top mismatch");
+ torture_assert_int_equal(tctx, i1.area.right, add_info.info1->area.right, "right mismatch");
+ torture_assert_int_equal(tctx, i1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, i1.flags, add_info.info1->flags, "flags mismatch");
+ }
+
+ if (!print_server && W_ERROR_IS_OK(expected_add_result)) {
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get added form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+ torture_assert_int_equal(tctx, info.info1.size.height, add_info.info1->size.height, "height mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.left, add_info.info1->area.left, "left mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.top, add_info.info1->area.top, "top mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.right, add_info.info1->area.right, "right mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, info.info1.flags, add_info.info1->flags, "flags mismatch");
+
+ if (winreg_handle && hive_handle) {
+
+ struct spoolss_FormInfo1 i1;
+
+ i1.size.width = IVAL(w_data, 0);
+ i1.size.height = IVAL(w_data, 4);
+ i1.area.left = IVAL(w_data, 8);
+ i1.area.top = IVAL(w_data, 12);
+ i1.area.right = IVAL(w_data, 16);
+ i1.area.bottom = IVAL(w_data, 20);
+ /* skip index here */
+ i1.flags = IVAL(w_data, 28);
+
+ torture_assert_int_equal(tctx, i1.size.width, info.info1.size.width, "width mismatch");
+ torture_assert_int_equal(tctx, i1.size.height, info.info1.size.height, "height mismatch");
+ torture_assert_int_equal(tctx, i1.area.left, info.info1.area.left, "left mismatch");
+ torture_assert_int_equal(tctx, i1.area.top, info.info1.area.top, "top mismatch");
+ torture_assert_int_equal(tctx, i1.area.right, info.info1.area.right, "right mismatch");
+ torture_assert_int_equal(tctx, i1.area.bottom, info.info1.area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, i1.flags, info.info1.flags, "flags mismatch");
+ }
+
+ add_info.info1->size.width = 1234;
+
+ torture_assert(tctx,
+ test_SetForm(tctx, b, handle, form_name, 1, &add_info),
+ "failed to set form");
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get set form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+ }
+
+ if (!W_ERROR_EQUAL(expected_add_result, WERR_INVALID_PARAMETER)) {
+ torture_assert(tctx,
+ test_EnumForms_find_one(tctx, b, handle, print_server, form_name),
+ "Newly added form not found in enum call");
+ }
+
+ torture_assert(tctx,
+ test_DeleteForm(tctx, b, handle, form_name, expected_delete_result),
+ "failed to delete form");
+
+ return true;
+}
+
+static bool test_Forms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ const struct spoolss_FormSize size = {
+ .width = 50,
+ .height = 25
+ };
+ const struct spoolss_FormArea area = {
+ .left = 5,
+ .top = 10,
+ .right = 45,
+ .bottom = 15
+ };
+ int i;
+
+ struct {
+ struct spoolss_AddFormInfo1 info1;
+ WERROR expected_add_result;
+ WERROR expected_delete_result;
+ } forms[] = {
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_USER,
+ .form_name = "testform_user",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_OK
+ },
+/*
+ weird, we can add a builtin form but we can never remove it
+ again - gd
+
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_BUILTIN,
+ .form_name = "testform_builtin",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_INVALID_PARAMETER,
+ },
+*/
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_PRINTER,
+ .form_name = "testform_printer",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_OK
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_USER,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_BUILTIN,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_PRINTER,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = 12345,
+ .form_name = "invalid_flags",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_INVALID_PARAMETER,
+ .expected_delete_result = WERR_INVALID_FORM_NAME
+ }
+
+ };
+
+ for (i=0; i < ARRAY_SIZE(forms); i++) {
+ torture_assert(tctx,
+ test_Forms_args(tctx, b, handle, print_server, printer_name,
+ winreg_handle, hive_handle,
+ forms[i].info1.form_name,
+ &forms[i].info1,
+ forms[i].expected_add_result,
+ forms[i].expected_delete_result),
+ talloc_asprintf(tctx, "failed to test form '%s'", forms[i].info1.form_name));
+ }
+
+ return true;
+}
+
+static bool test_EnumPorts_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.level = 2;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPorts\n");
+
+ status = dcerpc_spoolss_EnumPorts_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ torture_assert(tctx, info, "No ports returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, needed, 4);
+
+ return true;
+}
+
+static bool test_AddPort(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct spoolss_AddPort r;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.unknown = 0;
+ r.in.monitor_name = "foo";
+
+ torture_comment(tctx, "Testing AddPort\n");
+
+ status = dcerpc_spoolss_AddPort_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "AddPort failed");
+
+ /* win2k3 returns WERR_NOT_SUPPORTED */
+
+#if 0
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("AddPort failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+static bool test_GetJob_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ uint32_t level,
+ union spoolss_JobInfo *info_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetJob r;
+ union spoolss_JobInfo info;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing GetJob(%d), level %d\n", job_id, r.in.level);
+
+ status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+ if (level == 0) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "Unexpected return code");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+ torture_assert(tctx, r.out.info, "No job info returned");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+#if 0
+static bool test_GetJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ uint32_t levels[] = {0, 1, 2 /* 3, 4 */};
+ uint32_t i;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, job_id, levels[i], NULL),
+ "GetJob failed");
+ }
+
+ return true;
+}
+#endif
+
+static bool test_SetJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ struct spoolss_JobInfoContainer *ctr,
+ enum spoolss_JobControl command)
+{
+ NTSTATUS status;
+ struct spoolss_SetJob r;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.ctr = ctr;
+ r.in.command = command;
+
+ switch (command) {
+ case SPOOLSS_JOB_CONTROL_PAUSE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_PAUSE\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RESUME:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESUME\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_CANCEL:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_CANCEL\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RESTART:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESTART\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_DELETE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_DELETE\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RETAIN:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RETAIN\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RELEASE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RELEASE\n", job_id);
+ break;
+ default:
+ torture_comment(tctx, "Testing SetJob(%d)\n", job_id);
+ break;
+ }
+
+ status = dcerpc_spoolss_SetJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SetJob failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetJob failed");
+
+ return true;
+}
+
+static bool test_AddJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_AddJob r;
+ uint32_t needed;
+
+ r.in.level = 0;
+ r.in.handle = handle;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.in.buffer = r.out.buffer = NULL;
+
+ torture_comment(tctx, "Testing AddJob\n");
+
+ status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "AddJob failed");
+
+ r.in.level = 1;
+
+ status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, "AddJob failed");
+
+ return true;
+}
+
+
+static bool test_EnumJobs_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ WERROR werr_expected,
+ uint32_t *count_p,
+ union spoolss_JobInfo **info_p)
+{
+ NTSTATUS status;
+ struct spoolss_EnumJobs r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_JobInfo *info;
+
+ r.in.handle = handle;
+ r.in.firstjob = 0;
+ r.in.numjobs = 0xffffffff;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumJobs level %d\n", level);
+
+ status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+ torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+ "EnumJobs failed");
+ torture_assert(tctx, info, "No jobs returned");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, needed, 4);
+
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+ "EnumJobs failed");
+ }
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_JobPropertiesEnum(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ struct spoolss_EnumJobNamedProperties r;
+ uint32_t pcProperties = 0;
+ struct spoolss_PrintNamedProperty *ppProperties = NULL;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.out.pcProperties = &pcProperties;
+ r.out.ppProperties = &ppProperties;
+
+ torture_comment(tctx, "Testing EnumJobNamedProperties(%d)\n", job_id);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumJobNamedProperties_r(b, tctx, &r),
+ "spoolss_EnumJobNamedProperties failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_EnumJobNamedProperties failed");
+
+ return true;
+}
+
+static bool test_JobPropertySet(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ struct spoolss_PrintNamedProperty *property)
+{
+ struct spoolss_SetJobNamedProperty r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pProperty = property;
+
+ torture_comment(tctx, "Testing SetJobNamedProperty(%d) %s - %d\n",
+ job_id, property->propertyName,
+ property->propertyValue.ePropertyType);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SetJobNamedProperty_r(b, tctx, &r),
+ "spoolss_SetJobNamedProperty failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_SetJobNamedProperty failed");
+
+ return true;
+}
+
+static bool test_JobPropertyGetValue(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ const char *property_name,
+ struct spoolss_PrintPropertyValue *value)
+{
+ struct spoolss_GetJobNamedPropertyValue r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pszName = property_name;
+ r.out.pValue = value;
+
+ torture_comment(tctx, "Testing GetJobNamedPropertyValue(%d) %s\n",
+ job_id, property_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetJobNamedPropertyValue_r(b, tctx, &r),
+ "spoolss_GetJobNamedPropertyValue failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_GetJobNamedPropertyValue failed");
+
+ return true;
+}
+
+static bool test_JobPropertyDelete(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ const char *property_name)
+{
+ struct spoolss_DeleteJobNamedProperty r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pszName = property_name;
+
+ torture_comment(tctx, "Testing DeleteJobNamedProperty(%d) %s\n",
+ job_id, property_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteJobNamedProperty_r(b, tctx, &r),
+ "spoolss_DeleteJobNamedProperty failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_DeleteJobNamedProperty failed");
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job_common(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ const char *datatype,
+ uint32_t *job_id)
+{
+ NTSTATUS status;
+ struct spoolss_StartDocPrinter s;
+ struct spoolss_DocumentInfoCtr info_ctr;
+ struct spoolss_DocumentInfo1 info1;
+ struct spoolss_StartPagePrinter sp;
+ struct spoolss_WritePrinter w;
+ struct spoolss_EndPagePrinter ep;
+ struct spoolss_EndDocPrinter e;
+ int i;
+ uint32_t num_written;
+
+ torture_comment(tctx, "Testing StartDocPrinter\n");
+
+ s.in.handle = handle;
+ s.in.info_ctr = &info_ctr;
+ s.out.job_id = job_id;
+
+ info1.document_name = document_name;
+ info1.output_file = NULL;
+ info1.datatype = datatype;
+
+ info_ctr.level = 1;
+ info_ctr.info.info1 = &info1;
+
+ status = dcerpc_spoolss_StartDocPrinter_r(b, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed");
+ torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
+
+ for (i=1; i < 4; i++) {
+ union spoolss_JobInfo ginfo;
+ bool ok;
+
+ torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+ sp.in.handle = handle;
+
+ status = dcerpc_spoolss_StartPagePrinter_r(b, tctx, &sp);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_StartPagePrinter failed");
+ torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+
+ ok = test_GetJob_args(tctx, b, handle, *job_id, 1, &ginfo);
+ if (!ok) {
+ torture_comment(tctx, "test_GetJob failed for JobId[%d]\n", *job_id);
+ }
+
+ torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+ w.in.handle = handle;
+ w.in.data = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i));
+ w.out.num_written = &num_written;
+
+ status = dcerpc_spoolss_WritePrinter_r(b, tctx, &w);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_WritePrinter failed");
+ torture_assert_werr_ok(tctx, w.out.result, "WritePrinter failed");
+
+ torture_comment(tctx, "Testing EndPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+ ep.in.handle = handle;
+
+ status = dcerpc_spoolss_EndPagePrinter_r(b, tctx, &ep);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndPagePrinter failed");
+ torture_assert_werr_ok(tctx, ep.out.result, "EndPagePrinter failed");
+ }
+
+ torture_comment(tctx, "Testing EndDocPrinter: JobId[%d]\n", *job_id);
+
+ e.in.handle = handle;
+
+ status = dcerpc_spoolss_EndDocPrinter_r(b, tctx, &e);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed");
+ torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed");
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ uint32_t *job_id)
+{
+ test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "RAW", job_id);
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job_v4(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ uint32_t *job_id)
+{
+ test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "XPS_PASS", job_id);
+
+ return true;
+}
+
+
+static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t num_jobs,
+ uint32_t *job_ids)
+{
+ uint32_t count;
+ union spoolss_JobInfo *info = NULL;
+ int i;
+
+ torture_assert(tctx,
+ test_AddJob(tctx, b, handle),
+ "AddJob failed");
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, handle, 1, WERR_OK, &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, num_jobs, "unexpected number of jobs in queue");
+
+ for (i=0; i < num_jobs; i++) {
+ union spoolss_JobInfo ginfo;
+ const char *document_name;
+ const char *new_document_name = "any_other_docname";
+ struct spoolss_JobInfoContainer ctr;
+ struct spoolss_SetJobInfo1 info1;
+
+ torture_assert_int_equal(tctx, info[i].info1.job_id, job_ids[i], "job id mismatch");
+
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo),
+ "failed to call test_GetJob");
+
+ torture_assert_int_equal(tctx, ginfo.info1.job_id, info[i].info1.job_id, "job id mismatch");
+
+ document_name = ginfo.info1.document_name;
+
+ info1.job_id = ginfo.info1.job_id;
+ info1.printer_name = ginfo.info1.printer_name;
+ info1.server_name = ginfo.info1.server_name;
+ info1.user_name = ginfo.info1.user_name;
+ info1.document_name = new_document_name;
+ info1.data_type = ginfo.info1.data_type;
+ info1.text_status = ginfo.info1.text_status;
+ info1.status = ginfo.info1.status;
+ info1.priority = ginfo.info1.priority;
+ info1.position = ginfo.info1.position;
+ info1.total_pages = ginfo.info1.total_pages;
+ info1.pages_printed = ginfo.info1.pages_printed;
+ info1.submitted = ginfo.info1.submitted;
+
+ ctr.level = 1;
+ ctr.info.info1 = &info1;
+
+ torture_assert(tctx,
+ test_SetJob(tctx, b, handle, info[i].info1.job_id, &ctr, 0),
+ "failed to call test_SetJob level 1");
+
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo),
+ "failed to call test_GetJob");
+
+ if (strequal(ginfo.info1.document_name, document_name)) {
+ torture_warning(tctx,
+ "document_name did *NOT* change from '%s' to '%s'\n",
+ document_name, new_document_name);
+ }
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_PAUSE)) {
+ torture_warning(tctx, "failed to pause printjob\n");
+ }
+ if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_RESUME)) {
+ torture_warning(tctx, "failed to resume printjob\n");
+ }
+ }
+
+ return true;
+}
+
+static bool test_DoPrintTest(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+
+ torture_comment(tctx, "Testing real print operations\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job_v4(tctx, b, handle, "TorturePrintJob v4", &job_ids[i]);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ if (ret == true) {
+ torture_comment(tctx, "real print operations test succeeded\n\n");
+ }
+
+ return ret;
+}
+
+static bool test_DoPrintTest_extended(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ torture_comment(tctx, "Testing real print operations (extended)\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
+ }
+
+ ret &= test_DoPrintTest_check_jobs(tctx, b, handle, num_jobs, job_ids);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ if (ret == true) {
+ torture_comment(tctx, "real print operations (extended) test succeeded\n\n");
+ }
+
+ return ret;
+}
+
+static bool test_JobPrintProperties_equal(struct torture_context *tctx,
+ struct spoolss_PrintPropertyValue *got,
+ struct spoolss_PrintNamedProperty *exp)
+{
+ torture_assert_int_equal(tctx,
+ got->ePropertyType,
+ exp->propertyValue.ePropertyType,
+ "ePropertyType");
+
+ switch (exp->propertyValue.ePropertyType) {
+ case kRpcPropertyTypeString:
+ torture_assert_str_equal(tctx,
+ got->value.propertyString,
+ exp->propertyValue.value.propertyString,
+ "propertyString");
+ break;
+ case kRpcPropertyTypeInt32:
+ torture_assert_int_equal(tctx,
+ got->value.propertyInt32,
+ exp->propertyValue.value.propertyInt32,
+ "propertyInt32");
+ break;
+ case kRpcPropertyTypeInt64:
+ torture_assert_u64_equal(tctx,
+ got->value.propertyInt64,
+ exp->propertyValue.value.propertyInt64,
+ "propertyInt64");
+ break;
+ case kRpcPropertyTypeByte:
+ torture_assert_int_equal(tctx,
+ got->value.propertyByte,
+ exp->propertyValue.value.propertyByte,
+ "propertyByte");
+ break;
+ case kRpcPropertyTypeBuffer:
+ torture_assert_int_equal(tctx,
+ got->value.propertyBlob.cbBuf,
+ exp->propertyValue.value.propertyBlob.cbBuf,
+ "propertyBlob.cbBuf");
+ torture_assert_mem_equal(tctx,
+ got->value.propertyBlob.pBuf,
+ exp->propertyValue.value.propertyBlob.pBuf,
+ exp->propertyValue.value.propertyBlob.cbBuf,
+ "propertyBlob.pBuf");
+
+ break;
+
+ }
+
+ return true;
+}
+
+static bool test_JobPrintProperties(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ struct spoolss_PrintNamedProperty in;
+ struct spoolss_PrintPropertyValue out;
+ int i;
+ DATA_BLOB blob = data_blob_string_const("blob");
+ struct {
+ const char *property_name;
+ enum spoolss_EPrintPropertyType type;
+ union spoolss_PrintPropertyValueUnion value;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .property_name = "torture_property_string",
+ .type = kRpcPropertyTypeString,
+ .value.propertyString = "torture_property_value_string",
+ },{
+ .property_name = "torture_property_int32",
+ .type = kRpcPropertyTypeInt32,
+ .value.propertyInt32 = 42,
+ },{
+ .property_name = "torture_property_int64",
+ .type = kRpcPropertyTypeInt64,
+ .value.propertyInt64 = 0xaffe,
+ },{
+ .property_name = "torture_property_byte",
+ .type = kRpcPropertyTypeByte,
+ .value.propertyByte = 0xab,
+ },{
+ .property_name = "torture_property_buffer",
+ .type = kRpcPropertyTypeBuffer,
+ .value.propertyBlob.cbBuf = blob.length,
+ .value.propertyBlob.pBuf = blob.data,
+ }
+ };
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ for (i=0; i <ARRAY_SIZE(tests); i++) {
+
+ in.propertyName = tests[i].property_name;
+ in.propertyValue.ePropertyType = tests[i].type;
+ in.propertyValue.value = tests[i].value;
+
+ torture_assert(tctx,
+ test_JobPropertySet(tctx, b, handle, job_id, &in),
+ "failed to set property");
+
+ torture_assert(tctx,
+ test_JobPropertyGetValue(tctx, b, handle, job_id, in.propertyName, &out),
+ "failed to get property");
+
+ torture_assert(tctx,
+ test_JobPrintProperties_equal(tctx, &out, &in),
+ "property unequal");
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ torture_assert(tctx,
+ test_JobPropertyDelete(tctx, b, handle, job_id, in.propertyName),
+ "failed to delete job property");
+ }
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ return true;
+}
+
+static bool test_DoPrintTest_properties(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ torture_comment(tctx, "Testing real print operations (properties)\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]),
+ "failed to create print job");
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_JobPrintProperties(tctx, b, handle, job_ids[i]),
+ "failed to test job properties");
+ }
+
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE),
+ "failed to delete printjob");
+ }
+
+ torture_comment(tctx, "real print operations (properties) test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_PausePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_PAUSE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_ResumePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_RESUME;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_printer_purge(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_PURGE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PURGE\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_GetPrinterData_checktype(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *expected_type,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData r;
+ uint32_t needed;
+ enum winreg_Type type;
+ union spoolss_PrinterData data;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.type = &type;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+
+ torture_comment(tctx, "Testing GetPrinterData(%s)\n", r.in.value_name);
+
+ status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ if (expected_type) {
+ torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+ }
+ r.in.offered = needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+ status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "GetPrinterData(%s) failed", r.in.value_name));
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
+
+ if (type_p) {
+ *type_p = type;
+ }
+
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ if (needed_p) {
+ *needed_p = needed;
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ return test_GetPrinterData_checktype(tctx, b, handle, value_name,
+ NULL, type_p, data_p, needed_p);
+}
+
+static bool test_GetPrinterDataEx_checktype(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type *expected_type,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDataEx r;
+ enum winreg_Type type;
+ uint32_t needed;
+ union spoolss_PrinterData data;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+ r.out.type = &type;
+ r.out.needed = &needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+
+ torture_comment(tctx, "Testing GetPrinterDataEx(%s - %s)\n",
+ r.in.key_name, r.in.value_name);
+
+ status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ torture_skip(tctx, "GetPrinterDataEx not supported by server\n");
+ }
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ if (expected_type) {
+ torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+ }
+ r.in.offered = needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+ status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "GetPrinterDataEx(%s - %s) failed", r.in.key_name, r.in.value_name));
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
+
+ if (type_p) {
+ *type_p = type;
+ }
+
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ if (needed_p) {
+ *needed_p = needed;
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ return test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name,
+ NULL, type_p, data_p, needed_p);
+}
+
+static bool test_get_environment(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char **architecture)
+{
+ DATA_BLOB blob;
+ enum winreg_Type type;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx,
+ test_GetPrinterData(tctx, b, handle, "Architecture", &type, &data, &needed),
+ "failed to get Architecture");
+
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");
+
+ blob = data_blob_const(data, needed);
+ *architecture = reg_val_data_string(tctx, REG_SZ, blob);
+
+ return true;
+}
+
+static bool test_GetPrinterData_list(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *list[] = {
+ "W3SvcInstalled",
+ "BeepEnabled",
+ "EventLog",
+ /* "NetPopup", not on w2k8 */
+ /* "NetPopupToComputer", not on w2k8 */
+ "MajorVersion",
+ "MinorVersion",
+ "DefaultSpoolDirectory",
+ "Architecture",
+ "DsPresent",
+ "OSVersion",
+ /* "OSVersionEx", not on s3 */
+ "DNSMachineName"
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(list); i++) {
+ enum winreg_Type type = REG_NONE;
+ enum winreg_Type type_ex1 = REG_NONE;
+ enum winreg_Type type_ex2 = REG_NONE;
+ uint8_t *data;
+ uint8_t *data_ex1 = NULL;
+ uint8_t *data_ex2 = NULL;
+ uint32_t needed;
+ uint32_t needed_ex1 = 0;
+ uint32_t needed_ex2 = 0;
+
+ torture_assert(tctx, test_GetPrinterData(tctx, b, &ctx->server_handle, list[i], &type, &data, &needed),
+ talloc_asprintf(tctx, "GetPrinterData failed on %s\n", list[i]));
+ torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "random_string", list[i], &type_ex1, &data_ex1, &needed_ex1),
+ talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
+ torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "", list[i], &type_ex2, &data_ex2, &needed_ex2),
+ talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
+ torture_assert_int_equal(tctx, type, type_ex1, "type mismatch");
+ torture_assert_int_equal(tctx, type, type_ex2, "type mismatch");
+ torture_assert_int_equal(tctx, needed, needed_ex1, "needed mismatch");
+ torture_assert_int_equal(tctx, needed, needed_ex2, "needed mismatch");
+ torture_assert_mem_equal(tctx, data, data_ex1, needed, "data mismatch");
+ torture_assert_mem_equal(tctx, data, data_ex2, needed, "data mismatch");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ uint32_t enum_index,
+ uint32_t value_offered,
+ uint32_t data_offered,
+ enum winreg_Type *type_p,
+ uint32_t *value_needed_p,
+ uint32_t *data_needed_p,
+ const char **value_name_p,
+ uint8_t **data_p,
+ WERROR *result_p)
+{
+ struct spoolss_EnumPrinterData r;
+ uint32_t data_needed;
+ uint32_t value_needed;
+ enum winreg_Type type;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.handle = handle;
+ r.in.enum_index = enum_index;
+ r.in.value_offered = value_offered;
+ r.in.data_offered = data_offered;
+ r.out.data_needed = &data_needed;
+ r.out.value_needed = &value_needed;
+ r.out.type = &type;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.data_offered);
+ r.out.value_name = talloc_zero_array(tctx, const char, r.in.value_offered);
+
+ torture_comment(tctx, "Testing EnumPrinterData(%d)\n", enum_index);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterData_r(b, tctx, &r),
+ "EnumPrinterData failed");
+
+ if (type_p) {
+ *type_p = type;
+ }
+ if (value_needed_p) {
+ *value_needed_p = value_needed;
+ }
+ if (data_needed_p) {
+ *data_needed_p = data_needed;
+ }
+ if (value_name_p) {
+ *value_name_p = r.out.value_name;
+ }
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+ if (result_p) {
+ *result_p = r.out.result;
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrinterData_all(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t enum_index = 0;
+ enum winreg_Type type;
+ uint32_t value_needed;
+ uint32_t data_needed;
+ uint8_t *data;
+ const char *value_name;
+ WERROR result;
+
+ torture_comment(tctx, "Testing EnumPrinterData\n");
+
+ do {
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, enum_index, 0, 0,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
+ break;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, enum_index, value_needed, data_needed,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
+ break;
+ }
+
+ enum_index++;
+
+ } while (W_ERROR_IS_OK(result));
+
+ torture_comment(tctx, "EnumPrinterData test succeeded\n");
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ uint32_t *count_p,
+ struct spoolss_PrinterEnumValues **info_p)
+{
+ struct spoolss_EnumPrinterDataEx r;
+ struct spoolss_PrinterEnumValues *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key_name);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r),
+ "EnumPrinterDataEx failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r),
+ "EnumPrinterDataEx failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDataEx failed");
+
+ CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, needed, 1);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered);
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name);
+
+static bool test_EnumPrinterData_consistency(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t count;
+ struct spoolss_PrinterEnumValues *info;
+ int i;
+ uint32_t value_needed, data_needed;
+ uint32_t value_offered, data_offered;
+ WERROR result;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ enum winreg_Type type;
+ DATA_BLOB blob;
+
+ torture_comment(tctx, "Testing EnumPrinterData vs EnumPrinterDataEx consistency\n");
+
+ torture_assert(tctx, push_reg_sz(tctx, &blob, "torture_data1"), "");
+ type = REG_SZ;
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value1", type, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ blob = data_blob_string_const("torture_data2");
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value2", REG_BINARY, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ blob = data_blob_talloc(tctx, NULL, 4);
+ SIVAL(blob.data, 0, 0x11223344);
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value3", type, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, "PrinterDriverData", &count, &info),
+ "failed to call EnumPrinterDataEx");
+
+ /* get the max sizes for value and data */
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, 0, 0, 0,
+ NULL, &value_needed, &data_needed,
+ NULL, NULL, &result),
+ "EnumPrinterData failed");
+ torture_assert_werr_ok(tctx, result, "unexpected result");
+
+ /* check if the reply from the EnumPrinterData really matches max values */
+
+ for (i=0; i < count; i++) {
+ if (info[i].value_name_len > value_needed) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "EnumPrinterDataEx gave a reply with value length %d which is larger then expected max value length %d from EnumPrinterData",
+ info[i].value_name_len, value_needed));
+ }
+ if (info[i].data_length > data_needed) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "EnumPrinterDataEx gave a reply with data length %d which is larger then expected max data length %d from EnumPrinterData",
+ info[i].data_length, data_needed));
+ }
+ }
+
+ /* assuming that both EnumPrinterData and EnumPrinterDataEx do either
+ * sort or not sort the replies by value name, we should be able to do
+ * the following entry comparison */
+
+ data_offered = data_needed;
+ value_offered = value_needed;
+
+ for (i=0; i < count; i++) {
+
+ const char *value_name;
+ uint8_t *data;
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, i, value_offered, data_offered,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (i -1 == count) {
+ torture_assert_werr_equal(tctx, result, WERR_NO_MORE_ITEMS,
+ "unexpected result");
+ break;
+ } else {
+ torture_assert_werr_ok(tctx, result, "unexpected result");
+ }
+
+ torture_assert_int_equal(tctx, type, info[i].type, "type mismatch");
+ torture_assert_int_equal(tctx, value_needed, info[i].value_name_len, "value name length mismatch");
+ torture_assert_str_equal(tctx, value_name, info[i].value_name, "value name mismatch");
+ torture_assert_int_equal(tctx, data_needed, info[i].data_length, "data length mismatch");
+ torture_assert_mem_equal(tctx, data, info[i].data->data, info[i].data_length, "data mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value1"),
+ "DeletePrinterData failed");
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value2"),
+ "DeletePrinterData failed");
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value3"),
+ "DeletePrinterData failed");
+
+ torture_comment(tctx, "EnumPrinterData vs EnumPrinterDataEx consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name)
+{
+ NTSTATUS status;
+ struct spoolss_DeletePrinterData r;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+
+ torture_comment(tctx, "Testing DeletePrinterData(%s)\n",
+ r.in.value_name);
+
+ status = dcerpc_spoolss_DeletePrinterData_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed");
+ torture_assert_werr_ok(tctx, r.out.result, "DeletePrinterData failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name)
+{
+ struct spoolss_DeletePrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+
+ torture_comment(tctx, "Testing DeletePrinterDataEx(%s - %s)\n",
+ r.in.key_name, r.in.value_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDataEx_r(b, tctx, &r),
+ "DeletePrinterDataEx failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterDataEx failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name)
+{
+ struct spoolss_DeletePrinterKey r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+
+ torture_comment(tctx, "Testing DeletePrinterKey(%s)\n", r.in.key_name);
+
+ if (strequal(key_name, "") && !torture_setting_bool(tctx, "dangerous", false)) {
+ torture_skip(tctx, "not wiping out printer registry - enable dangerous tests to use\n");
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterKey_r(b, tctx, &r),
+ "DeletePrinterKey failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterKey failed");
+
+ return true;
+}
+
+static bool test_winreg_OpenHKLM(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_OpenHKLM r;
+
+ r.in.system_name = NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing winreg_OpenHKLM\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenHKLM_r(b, tctx, &r), "OpenHKLM failed");
+ torture_assert_werr_ok(tctx, r.out.result, "OpenHKLM failed");
+
+ return true;
+}
+
+static void init_winreg_String(struct winreg_String *name, const char *s)
+{
+ name->name = s;
+ if (s) {
+ name->name_len = 2 * (strlen_m(s) + 1);
+ name->name_size = name->name_len;
+ } else {
+ name->name_len = 0;
+ name->name_size = 0;
+ }
+}
+
+static bool test_winreg_OpenKey_opts(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *hive_handle,
+ const char *keyname,
+ uint32_t options,
+ struct policy_handle *key_handle)
+{
+ struct winreg_OpenKey r;
+
+ r.in.parent_handle = hive_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.options = options;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = key_handle;
+
+ torture_comment(tctx, "Testing winreg_OpenKey(%s)\n", keyname);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey_r(b, tctx, &r), "OpenKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "OpenKey failed");
+
+ return true;
+}
+
+static bool test_winreg_OpenKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *hive_handle,
+ const char *keyname,
+ struct policy_handle *key_handle)
+{
+ return test_winreg_OpenKey_opts(tctx, b, hive_handle, keyname,
+ REG_OPTION_NON_VOLATILE, key_handle);
+}
+
+static bool test_winreg_CloseKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing winreg_CloseKey\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r), "CloseKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
+
+ return true;
+}
+
+bool test_winreg_QueryValue(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint32_t *data_size_p,
+ uint32_t *data_length_p,
+ uint8_t **data_p)
+{
+ struct winreg_QueryValue r;
+ enum winreg_Type type = REG_NONE;
+ uint32_t data_size = 0;
+ uint32_t data_length = 0;
+ struct winreg_String valuename;
+ uint8_t *data = NULL;
+
+ init_winreg_String(&valuename, value_name);
+
+ data = talloc_zero_array(tctx, uint8_t, 0);
+
+ r.in.handle = handle;
+ r.in.value_name = &valuename;
+ r.in.type = &type;
+ r.in.data_size = &data_size;
+ r.in.data_length = &data_length;
+ r.in.data = data;
+ r.out.type = &type;
+ r.out.data = data;
+ r.out.data_size = &data_size;
+ r.out.data_length = &data_length;
+
+ torture_comment(tctx, "Testing winreg_QueryValue(%s)\n", value_name);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.data_size = *r.out.data_size;
+ data = talloc_zero_array(tctx, uint8_t, *r.in.data_size);
+ r.in.data = data;
+ r.out.data = data;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ }
+ torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed");
+
+ if (type_p) {
+ *type_p = *r.out.type;
+ }
+ if (data_size_p) {
+ *data_size_p = *r.out.data_size;
+ }
+ if (data_length_p) {
+ *data_length_p = *r.out.data_length;
+ }
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ return true;
+}
+
+static bool test_winreg_query_printerdata(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data)
+{
+ const char *printer_key;
+ struct policy_handle key_handle;
+
+ printer_key = talloc_asprintf(tctx, "%s\\%s\\%s",
+ TOP_LEVEL_PRINT_PRINTERS_KEY, printer_name, key_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, b, handle, printer_key, &key_handle), "");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle, value_name, w_type, w_size, w_length, w_data), "");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle), "");
+
+ return true;
+}
+
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data)
+{
+ struct policy_handle key_handle;
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, b, handle, key_name, &key_handle), "");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle, form_name, w_type, w_size, w_length, w_data), "");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle), "");
+
+ return true;
+}
+
+static bool test_winreg_symbolic_link(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *symlink_keyname,
+ const char *symlink_destination)
+{
+ /* check if the first key is a symlink to the second key */
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+ struct policy_handle key_handle;
+ DATA_BLOB blob;
+ const char *str;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip winreg symlink test against samba");
+ }
+
+ torture_assert(tctx,
+ test_winreg_OpenKey_opts(tctx, b, handle, symlink_keyname, REG_OPTION_OPEN_LINK, &key_handle),
+ "failed to open key link");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle,
+ "SymbolicLinkValue",
+ &w_type, &w_size, &w_length, &w_data),
+ "failed to query for 'SymbolicLinkValue' attribute");
+
+ torture_assert_int_equal(tctx, w_type, REG_LINK, "unexpected type");
+
+ blob = data_blob(w_data, w_size);
+ str = reg_val_data_string(tctx, REG_SZ, blob);
+
+ torture_assert_str_equal(tctx, str, symlink_destination, "unexpected symlink target string");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle),
+ "failed to close key link");
+
+ return true;
+}
+
+static const char *strip_unc(const char *unc)
+{
+ char *name;
+
+ if (!unc) {
+ return NULL;
+ }
+
+ if (unc[0] == '\\' && unc[1] == '\\') {
+ unc +=2;
+ }
+
+ name = strchr(unc, '\\');
+ if (name) {
+ return name+1;
+ }
+
+ return unc;
+}
+
+static bool test_GetPrinterInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrinterInfo info;
+ const char *keys[] = {
+ TOP_LEVEL_CONTROL_PRINTERS_KEY,
+ TOP_LEVEL_PRINT_PRINTERS_KEY
+ };
+ int i;
+ const char *printername, *sharename;
+
+ torture_comment(tctx, "Testing Printer Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to get printer info level 2");
+
+ printername = strip_unc(info.info2.printername);
+ sharename = strip_unc(info.info2.sharename);
+
+#define test_sz(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ const char *str;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_SZ, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ str = reg_val_data_string(tctx, REG_SZ, blob);\
+ if (w_size == 2 && iname == NULL) {\
+ /*torture_comment(tctx, "%s: \"\", %s: (null)\n", #wname, #iname);\ */\
+ } else {\
+ torture_assert_str_equal(tctx, str, iname,\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
+ }\
+} while(0);
+
+#define test_dword(wname, iname) \
+do {\
+ uint32_t value;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_DWORD, "unexpected type");\
+ torture_assert_int_equal(tctx, w_size, 4, "unexpected size");\
+ torture_assert_int_equal(tctx, w_length, 4, "unexpected length");\
+ value = IVAL(w_data, 0);\
+ torture_assert_int_equal(tctx, value, iname,\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
+} while(0);
+
+#define test_binary(wname, iname) \
+do {\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ torture_assert_int_equal(tctx, w_size, iname.length, "unexpected length");\
+ torture_assert_mem_equal(tctx, w_data, iname.data, w_size, \
+ "binary unequal");\
+} while(0);
+
+
+#define test_dm(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ struct spoolss_DeviceMode dm;\
+ enum ndr_err_code ndr_err;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &dm,\
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);\
+ torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall dm");\
+ torture_assert(tctx, test_devicemode_equal(tctx, &dm, iname),\
+ "dm unequal");\
+} while(0);
+
+#define test_sd(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ struct security_descriptor sd;\
+ enum ndr_err_code ndr_err;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd,\
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);\
+ torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall sd");\
+ torture_assert(tctx, test_security_descriptor_equal(tctx, &sd, iname),\
+ "sd unequal");\
+} while(0);
+
+#define test_multi_sz(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ const char **array;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ int i;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_MULTI_SZ, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ torture_assert(tctx, \
+ pull_reg_multi_sz(tctx, &blob, &array),\
+ "failed to pull multi sz");\
+ for (i=0; array[i] != NULL; i++) {\
+ torture_assert_str_equal(tctx, array[i], iname[i],\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, iname[i]));\
+ }\
+} while(0);
+
+ if (!test_winreg_symbolic_link(tctx, winreg_handle, hive_handle,
+ TOP_LEVEL_CONTROL_PRINTERS_KEY,
+ "\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"))
+ {
+ torture_warning(tctx, "failed to check for winreg symlink");
+ }
+
+ for (i=0; i < ARRAY_SIZE(keys); i++) {
+
+ const char *printer_key;
+ struct policy_handle key_handle;
+
+ printer_key = talloc_asprintf(tctx, "%s\\%s",
+ keys[i], printer_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, printer_key, &key_handle), "");
+
+ test_sz("Name", printername);
+ test_sz("Share Name", sharename);
+ test_sz("Port", info.info2.portname);
+ test_sz("Printer Driver", info.info2.drivername);
+ test_sz("Description", info.info2.comment);
+ test_sz("Location", info.info2.location);
+ test_sz("Separator File", info.info2.sepfile);
+ test_sz("Print Processor", info.info2.printprocessor);
+ test_sz("Datatype", info.info2.datatype);
+ test_sz("Parameters", info.info2.parameters);
+ /* winreg: 0, spoolss not */
+/* test_dword("Attributes", info.info2.attributes); */
+ test_dword("Priority", info.info2.priority);
+ test_dword("Default Priority", info.info2.defaultpriority);
+ /* winreg: 60, spoolss: 0 */
+/* test_dword("StartTime", info.info2.starttime); */
+/* test_dword("UntilTime", info.info2.untiltime); */
+ /* winreg != spoolss */
+/* test_dword("Status", info.info2.status); */
+ test_dm("Default DevMode", info.info2.devmode);
+ test_sd("Security", info.info2.secdesc);
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+ }
+
+#undef test_dm
+
+ torture_comment(tctx, "Printer Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_GetPrintserverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrinterInfo info;
+ struct policy_handle key_handle;
+
+ torture_comment(tctx,
+ "Testing Printserver Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 3, &info),
+ "failed to get printer info level 2");
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle,
+ TOP_LEVEL_CONTROL_KEY, &key_handle), "");
+
+ test_sd("ServerSecurityDescriptor", info.info3.secdesc);
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+
+#undef test_sd
+
+ torture_comment(tctx,
+ "Printserver Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+
+static bool test_PrintProcessors(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+ int i;
+
+ torture_comment(tctx, "Testing Print Processor Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, environment, 1, &count, &info, WERR_OK),
+ "failed to enum print processors level 1");
+
+ for (i=0; i < count; i++) {
+
+ const char *processor_key;
+ struct policy_handle key_handle;
+
+ processor_key = talloc_asprintf(tctx, "%s\\%s\\Print Processors\\%s",
+ TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+ environment,
+ info[i].info1.print_processor_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, processor_key, &key_handle), "");
+
+ /* nothing to check in there so far */
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+ }
+
+ torture_comment(tctx, "Print Processor Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture,
+ uint32_t level,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info_p,
+ WERROR *result);
+
+static const char *strip_path(const char *path)
+{
+ char *p;
+
+ if (path == NULL) {
+ return NULL;
+ }
+
+ p = strrchr(path, '\\');
+ if (p) {
+ return p+1;
+ }
+
+ return path;
+}
+
+static const char **strip_paths(const char **path_array)
+{
+ int i;
+
+ if (path_array == NULL) {
+ return NULL;
+ }
+
+ for (i=0; path_array[i] != NULL; i++) {
+ path_array[i] = strip_path(path_array[i]);
+ }
+
+ return path_array;
+}
+
+static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
+{
+ time_t t;
+ struct tm *tm;
+
+ if (nt == 0) {
+ return talloc_strdup(mem_ctx, "01/01/1601");
+ }
+
+ t = nt_time_to_unix(nt);
+ tm = localtime(&t);
+
+ return talloc_asprintf(mem_ctx, "%02d/%02d/%04d",
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+}
+
+static const char *driver_winreg_version(TALLOC_CTX *mem_ctx, uint64_t v)
+{
+ return talloc_asprintf(mem_ctx, "%u.%u.%u.%u",
+ (unsigned)((v >> 48) & 0xFFFF),
+ (unsigned)((v >> 32) & 0xFFFF),
+ (unsigned)((v >> 16) & 0xFFFF),
+ (unsigned)(v & 0xFFFF));
+}
+
+static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *driver_name,
+ const char *environment,
+ enum spoolss_DriverOSVersion version,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle,
+ const char *server_name_slash)
+{
+ WERROR result = WERR_OK;
+ union spoolss_DriverInfo info;
+ const char *driver_key;
+ struct policy_handle key_handle;
+
+ const char *driver_path;
+ const char *data_file;
+ const char *config_file;
+ const char *help_file;
+ const char **dependent_files;
+
+ const char *driver_date;
+ const char *inbox_driver_date;
+
+ const char *driver_version;
+ const char *inbox_driver_version;
+
+ ZERO_STRUCT(key_handle);
+
+ torture_comment(tctx, "Testing Driver Info and winreg consistency\n");
+
+ driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
+ TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+ environment,
+ version,
+ driver_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
+ "failed to open driver key");
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "w2k3", false)) {
+ goto try_level6;
+ }
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, version, 0, &info, &result),
+ "failed to get driver info level 8");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 8, driver_name, &info),
+ "failed to get driver info level 8");
+ }
+
+ if (W_ERROR_EQUAL(result, WERR_INVALID_LEVEL)) {
+ goto try_level6;
+ }
+
+ driver_path = strip_path(info.info8.driver_path);
+ data_file = strip_path(info.info8.data_file);
+ config_file = strip_path(info.info8.config_file);
+ help_file = strip_path(info.info8.help_file);
+ dependent_files = strip_paths(info.info8.dependent_files);
+
+ driver_date = driver_winreg_date(tctx, info.info8.driver_date);
+ inbox_driver_date = driver_winreg_date(tctx, info.info8.min_inbox_driver_ver_date);
+
+ driver_version = driver_winreg_version(tctx, info.info8.driver_version);
+ inbox_driver_version = driver_winreg_version(tctx, info.info8.min_inbox_driver_ver_version);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info8.default_datatype);
+ test_sz("Driver", driver_path);
+ test_sz("DriverDate", driver_date);
+ test_sz("DriverVersion", driver_version);
+ test_sz("HardwareID", info.info8.hardware_id);
+ test_sz("Help File", help_file);
+ test_sz("InfPath", info.info8.inf_path);
+ test_sz("Manufacturer", info.info8.manufacturer_name);
+ test_sz("MinInboxDriverVerDate", inbox_driver_date);
+ test_sz("MinInboxDriverVerVersion", inbox_driver_version);
+ test_sz("Monitor", info.info8.monitor_name);
+ test_sz("OEM URL", info.info8.manufacturer_url);
+ test_sz("Print Processor", info.info8.print_processor);
+ test_sz("Provider", info.info8.provider);
+ test_sz("VendorSetup", info.info8.vendor_setup);
+ test_multi_sz("ColorProfiles", info.info8.color_profiles);
+ test_multi_sz("Dependent Files", dependent_files);
+ test_multi_sz("CoreDependencies", info.info8.core_driver_dependencies);
+ test_multi_sz("Previous Names", info.info8.previous_names);
+/* test_dword("Attributes", ?); */
+ test_dword("PrinterDriverAttributes", info.info8.printer_driver_attributes);
+ test_dword("Version", info.info8.version);
+/* test_dword("TempDir", ?); */
+
+ try_level6:
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, version, 0, &info, &result),
+ "failed to get driver info level 6");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 6, driver_name, &info),
+ "failed to get driver info level 6");
+ }
+
+ driver_path = strip_path(info.info6.driver_path);
+ data_file = strip_path(info.info6.data_file);
+ config_file = strip_path(info.info6.config_file);
+ help_file = strip_path(info.info6.help_file);
+ dependent_files = strip_paths(info.info6.dependent_files);
+
+ driver_date = driver_winreg_date(tctx, info.info6.driver_date);
+
+ driver_version = driver_winreg_version(tctx, info.info6.driver_version);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info6.default_datatype);
+ test_sz("Driver", driver_path);
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 8);
+ push_nttime(blob.data, 0, info.info6.driver_date);
+ test_binary("DriverDate", blob);
+ SBVAL(blob.data, 0, info.info6.driver_version);
+ test_binary("DriverVersion", blob);
+ } else {
+ test_sz("DriverDate", driver_date);
+ test_sz("DriverVersion", driver_version);
+ }
+ test_sz("HardwareID", info.info6.hardware_id);
+ test_sz("Help File", help_file);
+ test_sz("Manufacturer", info.info6.manufacturer_name);
+ test_sz("Monitor", info.info6.monitor_name);
+ test_sz("OEM URL", info.info6.manufacturer_url);
+ test_sz("Provider", info.info6.provider);
+ test_multi_sz("Dependent Files", dependent_files);
+ test_multi_sz("Previous Names", info.info6.previous_names);
+/* test_dword("Attributes", ?); */
+ test_dword("Version", info.info6.version);
+/* test_dword("TempDir", ?); */
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, version, 0, &info, &result),
+ "failed to get driver info level 3");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 3, driver_name, &info),
+ "failed to get driver info level 3");
+ }
+
+ driver_path = strip_path(info.info3.driver_path);
+ data_file = strip_path(info.info3.data_file);
+ config_file = strip_path(info.info3.config_file);
+ help_file = strip_path(info.info3.help_file);
+ dependent_files = strip_paths(info.info3.dependent_files);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info3.default_datatype);
+ test_sz("Driver", driver_path);
+ test_sz("Help File", help_file);
+ test_sz("Monitor", info.info3.monitor_name);
+ test_multi_sz("Dependent Files", dependent_files);
+/* test_dword("Attributes", ?); */
+ test_dword("Version", info.info3.version);
+/* test_dword("TempDir", ?); */
+
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+
+ torture_comment(tctx, "Driver Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+#undef test_sz
+#undef test_dword
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered)
+{
+ struct spoolss_SetPrinterData r;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.type = type;
+ r.in.data = data;
+ r.in.offered = offered;
+
+ torture_comment(tctx, "Testing SetPrinterData(%s)\n",
+ r.in.value_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SetPrinterData_r(b, tctx, &r),
+ "SetPrinterData failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetPrinterData failed");
+
+ return true;
+}
+
+static bool test_SetPrinterData_matrix(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ const char *values[] = {
+ "spootyfoot",
+ "spooty\\foot",
+#if 0
+ /* FIXME: not working with s3 atm. */
+ "spooty,foot",
+ "spooty,fo,ot",
+#endif
+ "spooty foot",
+#if 0
+ /* FIXME: not working with s3 atm. */
+ "spooty\\fo,ot",
+ "spooty,fo\\ot"
+#endif
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(values); i++) {
+
+ enum winreg_Type type, expected_type = REG_SZ;
+ DATA_BLOB blob;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx, push_reg_sz(tctx, &blob, "dog"), "");
+ type = REG_SZ;
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, values[i], REG_SZ, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ torture_assert(tctx,
+ test_GetPrinterData_checktype(tctx, b, handle, values[i], &expected_type, &type, &data, &needed),
+ "GetPrinterData failed");
+
+ torture_assert_int_equal(tctx, type, REG_SZ, "type mismatch");
+ torture_assert_int_equal(tctx, needed, blob.length, "size mismatch");
+ torture_assert_mem_equal(tctx, data, blob.data, blob.length, "buffer mismatch");
+
+ if (winreg_handle && hive_handle) {
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ torture_assert(tctx,
+ test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
+ printer_name, "PrinterDriverData", values[i],
+ &w_type, &w_size, &w_length, &w_data), "");
+
+ torture_assert_int_equal(tctx, w_type, REG_SZ, "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, values[i]),
+ "DeletePrinterData failed");
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***array);
+
+static bool test_SetPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+ r.in.type = type;
+ r.in.data = data;
+ r.in.offered = offered;
+
+ torture_comment(tctx, "Testing SetPrinterDataEx(%s - %s) type: %s, offered: 0x%08x\n",
+ r.in.key_name, r.in.value_name, str_regtype(r.in.type), r.in.offered);
+
+ status = dcerpc_spoolss_SetPrinterDataEx_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinterDataEx failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinterDataEx failed");
+
+ return true;
+}
+
+static bool test_SetPrinterDataEx_keys(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *value_name = "dog";
+ const char *keys[] = {
+ "torturedataex",
+ "torture data ex",
+ "torturedataex_with_subkey\\subkey",
+ "torturedataex_with_subkey\\subkey:0",
+ "torturedataex_with_subkey\\subkey:1",
+ "torturedataex_with_subkey\\subkey\\subsubkey",
+ "torturedataex_with_subkey\\subkey\\subsubkey:0",
+ "torturedataex_with_subkey\\subkey\\subsubkey:1",
+ "torture,data",
+ "torture,data,ex",
+ "torture,data\\ex",
+ "torture\\data,ex",
+ "torture/data",
+ "torture/data ex",
+ "torture/data ex/sub",
+ "torture//data",
+ "torture//data ex",
+ "torture//data ex/sub",
+ "torture//data ex//sub",
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(keys); i++) {
+
+ char *c;
+ const char *key;
+ enum winreg_Type type;
+ DATA_BLOB blob_in, blob_out;
+ const char **subkeys;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+ uint32_t needed;
+
+ blob_in = data_blob_talloc(tctx, NULL, 42);
+
+ generate_random_buffer(blob_in.data, blob_in.length);
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, keys[i], value_name, REG_BINARY, blob_in.data, blob_in.length),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &blob_out.data, &needed),
+ "failed to call GetPrinterDataEx");
+
+ blob_out.length = needed;
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, keys[i], &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+ torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
+ }
+
+ key = talloc_strdup(tctx, keys[i]);
+
+ if (!test_DeletePrinterDataEx(tctx, b, handle, keys[i], value_name)) {
+ return false;
+ }
+
+ c = strchr(key, '\\');
+ if (c) {
+ int k;
+
+ /* we have subkeys */
+
+ *c = 0;
+
+ if (!test_EnumPrinterKey(tctx, b, handle, key, &subkeys)) {
+ return false;
+ }
+
+ for (k=0; subkeys && subkeys[k]; k++) {
+
+ const char *current_key = talloc_asprintf(tctx, "%s\\%s", key, subkeys[k]);
+
+ if (!test_DeletePrinterKey(tctx, b, handle, current_key)) {
+ return false;
+ }
+ }
+
+ if (!test_DeletePrinterKey(tctx, b, handle, key)) {
+ return false;
+ }
+
+ } else {
+ if (!test_DeletePrinterKey(tctx, b, handle, key)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_SetPrinterDataEx_values(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *key = "torturedataex";
+ const char *values[] = {
+ "torture_value",
+ "torture value",
+ "torture,value",
+ "torture/value",
+ "torture\\value",
+ "torture\\\\value"
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(values); i++) {
+
+ enum winreg_Type type = REG_NONE;
+ DATA_BLOB blob_in = data_blob_null;
+ DATA_BLOB blob_out = data_blob_null;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+ uint32_t needed = 0;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ char *q;
+ q = strrchr(values[i], ',');
+ if (q) {
+ torture_comment(tctx, "skipping valuename '%s' including ',' character against Samba3\n",
+ values[i]);
+ continue;
+ }
+ }
+
+ blob_in = data_blob_talloc(tctx, NULL, 42);
+
+ generate_random_buffer(blob_in.data, blob_in.length);
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, key, values[i], REG_BINARY, blob_in.data, blob_in.length),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, key, values[i], &type, &blob_out.data, &needed),
+ "failed to call GetPrinterDataEx");
+
+ blob_out.length = needed;
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, key, &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+ torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, values[i], "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(values[i])*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDataEx(tctx, b, handle, key, values[i]),
+ "failed to call DeletePrinterDataEx");
+ }
+
+ return true;
+}
+
+
+static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printername,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *value_name = "dog";
+ const char *key_name = "torturedataex";
+ enum winreg_Type types[] = {
+ REG_SZ,
+ REG_MULTI_SZ,
+ REG_DWORD,
+ REG_BINARY
+ };
+ const char *str = "abcdefghi";
+ size_t t, s;
+
+ for (t=0; t < ARRAY_SIZE(types); t++) {
+ for (s=0; s < strlen(str); s++) {
+
+ enum winreg_Type type;
+ const char *string = talloc_strndup(tctx, str, s);
+ const char *array[2];
+ DATA_BLOB blob = data_blob_string_const(string);
+ DATA_BLOB data;
+ uint8_t *data_out;
+ uint32_t needed, offered = 0;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+
+ array[0] = talloc_strdup(tctx, string);
+ array[1] = NULL;
+
+ if (types[t] == REG_DWORD) {
+ s = 0xffff;
+ }
+
+ switch (types[t]) {
+ case REG_BINARY:
+ data = blob;
+ offered = blob.length;
+ break;
+ case REG_DWORD:
+ data = data_blob_talloc(tctx, NULL, 4);
+ SIVAL(data.data, 0, 0x12345678);
+ offered = 4;
+ break;
+ case REG_SZ:
+ torture_assert(tctx, push_reg_sz(tctx, &data, string), "");
+ type = REG_SZ;
+ offered = data.length;
+ /*strlen_m_term(data.string)*2;*/
+ break;
+ case REG_MULTI_SZ:
+ torture_assert(tctx, push_reg_multi_sz(tctx, &data, array), "");
+ type = REG_MULTI_SZ;
+ offered = data.length;
+ break;
+ default:
+ torture_fail(tctx, talloc_asprintf(tctx, "type %d untested\n", types[t]));
+ }
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, key_name, value_name, types[t], data.data, offered),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name, &types[t], &type, &data_out, &needed),
+ "failed to call GetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, key_name, &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, types[t], type, "type mismatch");
+ torture_assert_int_equal(tctx, needed, offered, "size mismatch");
+ torture_assert_mem_equal(tctx, data_out, data.data, offered, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, types[t], "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, offered, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, data.data, offered, "buffer mismatch");
+ }
+
+ if (winreg_handle && hive_handle) {
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ torture_assert(tctx,
+ test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
+ printername, key_name, value_name,
+ &w_type, &w_size, &w_length, &w_data), "");
+
+ torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, offered, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, offered, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, data.data, offered, "winreg buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDataEx(tctx, b, handle, key_name, value_name),
+ "failed to call DeletePrinterDataEx");
+ }
+ }
+
+ return true;
+}
+
+static bool test_PrinterData_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret &= test_SetPrinterData_matrix(tctx, b, handle, printer_name, b2, &hive_handle);
+ ret &= test_SetPrinterDataEx_matrix(tctx, p, handle, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_Forms_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name)
+{
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrinterInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetPrinterInfo_winreg(tctx, b, handle, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrintserverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetPrintserverInfo_winreg(tctx, b, handle, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+
+static bool test_DriverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *driver_name,
+ const char *environment,
+ enum spoolss_DriverOSVersion version)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, version, b2, &hive_handle, NULL);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrintProcessors_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment)
+{
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_PrintProcessors(tctx, b, environment, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrinterData_DsSpooler(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+ union spoolss_PrinterInfo info;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *pname;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ torture_comment(tctx, "Testing DsSpooler <-> SetPrinter relations\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+
+ torture_assert(tctx,
+ PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo),
+ "failed to convert");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+#define TEST_SZ(wname, iname) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ DATA_BLOB blob;\
+ const char *str;\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+ blob = data_blob_const(data, needed);\
+ torture_assert(tctx,\
+ pull_reg_sz(tctx, &blob, &str),\
+ "failed to pull REG_SZ");\
+ torture_assert_str_equal(tctx, str, iname, "unexpected result");\
+} while(0);
+
+
+#define TEST_SET_SZ(wname, iname, val) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ DATA_BLOB blob;\
+ const char *str;\
+ sinfo.info2->iname = val;\
+ torture_assert(tctx,\
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+ "failed to call SetPrinter");\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+ blob = data_blob_const(data, needed);\
+ torture_assert(tctx,\
+ pull_reg_sz(tctx, &blob, &str),\
+ "failed to pull REG_SZ");\
+ torture_assert_str_equal(tctx, str, val, "unexpected result");\
+} while(0);
+
+#define TEST_SET_DWORD(wname, iname, val) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ uint32_t value;\
+ sinfo.info2->iname = val;\
+ torture_assert(tctx,\
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+ "failed to call SetPrinter");\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_DWORD, "unexpected type");\
+ torture_assert_int_equal(tctx, needed, 4, "unexpected length");\
+ value = IVAL(data, 0); \
+ torture_assert_int_equal(tctx, value, val, "unexpected result");\
+} while(0);
+
+ TEST_SET_SZ("description", comment, "newval");
+ TEST_SET_SZ("location", location, "newval");
+ TEST_SET_SZ("driverName", drivername, "newval");
+/* TEST_SET_DWORD("priority", priority, 25); */
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+
+ TEST_SZ("description", info.info2.comment);
+ TEST_SZ("driverName", info.info2.drivername);
+ TEST_SZ("location", info.info2.location);
+
+ pname = strrchr(info.info2.printername, '\\');
+ if (pname == NULL) {
+ pname = info.info2.printername;
+ } else {
+ pname++;
+ }
+ TEST_SZ("printerName", pname);
+ /* TEST_SZ("printSeparatorFile", info.info2.sepfile); */
+ /* TEST_SZ("printShareName", info.info2.sharename); */
+
+ /* FIXME gd: complete the list */
+
+#undef TEST_SZ
+#undef TEST_SET_SZ
+#undef TEST_DWORD
+
+ torture_comment(tctx, "DsSpooler <-> SetPrinter relations test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_print_processors_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_PrintProcessors_winreg(tctx, b, ctx->environment);
+}
+
+static bool test_AddPrintProcessor(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ const char *path_name,
+ const char *print_processor_name,
+ WERROR expected_error)
+{
+ struct spoolss_AddPrintProcessor r;
+
+ r.in.server = NULL;
+ r.in.architecture = environment;
+ r.in.path_name = path_name;
+ r.in.print_processor_name = print_processor_name;
+
+ torture_comment(tctx, "Testing AddPrintProcessor(%s)\n",
+ print_processor_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrintProcessor_r(b, tctx, &r),
+ "spoolss_AddPrintProcessor failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_AddPrintProcessor failed");
+
+ return true;
+}
+
+static bool test_DeletePrintProcessor(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ const char *print_processor_name,
+ WERROR expected_error)
+{
+ struct spoolss_DeletePrintProcessor r;
+
+ r.in.server = NULL;
+ r.in.architecture = environment;
+ r.in.print_processor_name = print_processor_name;
+
+ torture_comment(tctx, "Testing DeletePrintProcessor(%s)\n",
+ print_processor_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrintProcessor_r(b, tctx, &r),
+ "spoolss_DeletePrintProcessor failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_DeletePrintProcessor failed");
+
+ return true;
+}
+
+static bool test_add_print_processor(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int i;
+
+ struct {
+ const char *environment;
+ const char *path_name;
+ const char *print_processor_name;
+ WERROR expected_add_result;
+ WERROR expected_del_result;
+ } tests[] = {
+ {
+ .environment = ctx->environment,
+ .path_name = "",
+ .print_processor_name = "winprint",
+ .expected_add_result = WERR_PRINT_PROCESSOR_ALREADY_INSTALLED,
+ .expected_del_result = WERR_CAN_NOT_COMPLETE
+ },{
+ .environment = ctx->environment,
+ .path_name = "",
+ .print_processor_name = "unknown",
+ .expected_add_result = WERR_MOD_NOT_FOUND,
+ .expected_del_result = WERR_UNKNOWN_PRINTPROCESSOR
+ }
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ torture_assert(tctx,
+ test_AddPrintProcessor(tctx, b,
+ tests[i].environment,
+ tests[i].path_name,
+ tests[i].print_processor_name,
+ tests[i].expected_add_result),
+ "add print processor failed");
+ torture_assert(tctx,
+ test_DeletePrintProcessor(tctx, b,
+ tests[i].environment,
+ tests[i].print_processor_name,
+ tests[i].expected_del_result),
+ "delete print processor failed");
+ }
+
+ return true;
+}
+
+static bool test_AddPerMachineConnection(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername,
+ const char *printserver,
+ const char *provider,
+ WERROR expected_error)
+{
+ struct spoolss_AddPerMachineConnection r;
+ const char *composed_printername = printername;
+
+ if (servername != NULL) {
+ composed_printername = talloc_asprintf(tctx, "%s\\%s",
+ servername,
+ printername);
+ }
+ r.in.server = servername;
+ r.in.printername = composed_printername;
+ r.in.printserver = printserver;
+ r.in.provider = provider;
+
+ torture_comment(tctx, "Testing AddPerMachineConnection(%s|%s|%s)\n",
+ printername, printserver, provider);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPerMachineConnection_r(b, tctx, &r),
+ "spoolss_AddPerMachineConnection failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_AddPerMachineConnection failed");
+
+ return true;
+}
+
+static bool test_DeletePerMachineConnection(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername,
+ WERROR expected_error)
+{
+ struct spoolss_DeletePerMachineConnection r;
+ const char *composed_printername = printername;
+
+ if (servername != NULL) {
+ composed_printername = talloc_asprintf(tctx, "%s\\%s",
+ servername,
+ printername);
+ }
+
+ r.in.server = servername;
+ r.in.printername = composed_printername;
+
+ torture_comment(tctx, "Testing DeletePerMachineConnection(%s)\n",
+ printername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePerMachineConnection_r(b, tctx, &r),
+ "spoolss_DeletePerMachineConnection failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_DeletePerMachineConnection failed");
+
+ return true;
+}
+
+static bool test_EnumPerMachineConnections(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername)
+{
+ struct spoolss_EnumPerMachineConnections r;
+ DATA_BLOB blob = data_blob_null;
+ struct spoolss_PrinterInfo4 *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.server = servername;
+ r.in.buffer = &blob;
+ r.in.offered = 0;
+
+ r.out.info = &info;
+ r.out.needed = &needed;
+ r.out.count = &count;
+
+ torture_comment(tctx, "Testing EnumPerMachineConnections(%s)\n",
+ servername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r),
+ "spoolss_EnumPerMachineConnections failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r),
+ "spoolss_EnumPerMachineConnections failed");
+ }
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_EnumPerMachineConnections failed");
+
+ return true;
+}
+
+static bool test_addpermachineconnection(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ int i;
+
+ struct {
+ const char *servername;
+ const char *printername;
+ const char *printserver;
+ const char *provider;
+ WERROR expected_add_result;
+ WERROR expected_del_result;
+ } tests[] = {
+ {
+ .servername = NULL,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "unknown",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = NULL,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "unknown",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = NULL,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "unknown",
+ .expected_add_result = WERR_FILE_NOT_FOUND,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "",
+ .expected_add_result = WERR_OK,
+ .expected_del_result = WERR_OK
+ },{
+ .servername = server_name_slash,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "unknown",
+ .expected_add_result = WERR_FILE_NOT_FOUND,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "",
+ .expected_add_result = WERR_OK,
+ .expected_del_result = WERR_OK
+ }
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ torture_assert(tctx,
+ test_AddPerMachineConnection(tctx, b,
+ tests[i].servername,
+ tests[i].printername,
+ tests[i].printserver,
+ tests[i].provider,
+ tests[i].expected_add_result),
+ "add per machine connection failed");
+ torture_assert(tctx,
+ test_EnumPerMachineConnections(tctx, b,
+ tests[i].servername),
+ "enum per machine connections failed");
+ torture_assert(tctx,
+ test_DeletePerMachineConnection(tctx, b,
+ tests[i].servername,
+ tests[i].printername,
+ tests[i].expected_del_result),
+ "delete per machine connection failed");
+ torture_assert(tctx,
+ test_EnumPerMachineConnections(tctx, b,
+ tests[i].servername),
+ "enum per machine connections failed");
+ }
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ enum winreg_Type type;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx,
+ test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed),
+ "failed to call GetPrinterData");
+
+ torture_assert(tctx, type == REG_DWORD, "unexpected type");
+ torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+ *change_id = IVAL(data, 0);
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ enum winreg_Type type = REG_NONE;
+ uint8_t *data = NULL;
+ uint32_t needed = 0;
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data, &needed),
+ "failed to call GetPrinterData");
+
+ torture_assert(tctx, type == REG_DWORD, "unexpected type");
+ torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+ *change_id = IVAL(data, 0);
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ union spoolss_PrinterInfo info;
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 0, &info),
+ "failed to query Printer level 0");
+
+ *change_id = info.info0.change_id;
+
+ return true;
+}
+
+static bool test_ChangeID(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t change_id, change_id_ex, change_id_info;
+ uint32_t change_id2, change_id_ex2, change_id_info2;
+ union spoolss_PrinterInfo info;
+ const char *comment;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing ChangeID: id change test #1\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+
+ torture_assert_int_equal(tctx, change_id, change_id_ex,
+ "change_ids should all be equal");
+ torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+ "change_ids should all be equal");
+
+
+ torture_comment(tctx, "Testing ChangeID: id change test #2\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+ torture_assert_int_equal(tctx, change_id, change_id_ex,
+ "change_id should not have changed");
+ torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+ "change_id should not have changed");
+
+
+ torture_comment(tctx, "Testing ChangeID: id change test #3\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+ comment = talloc_strdup(tctx, info.info2.comment);
+
+ {
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+ sinfo.info2->comment = "torture_comment";
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ sinfo.info2->comment = comment;
+
+ torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ }
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id2),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex2),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info2),
+ "failed to query for ChangeID");
+
+ torture_assert_int_equal(tctx, change_id2, change_id_ex2,
+ "change_ids should all be equal");
+ torture_assert_int_equal(tctx, change_id_ex2, change_id_info2,
+ "change_ids should all be equal");
+
+ torture_assert(tctx, (change_id < change_id2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id2, change_id));
+ torture_assert(tctx, (change_id_ex < change_id_ex2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id_ex2, change_id_ex));
+ torture_assert(tctx, (change_id_info < change_id_info2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id_info2, change_id_info));
+
+ torture_comment(tctx, "ChangeID tests succeeded\n\n");
+
+ return true;
+}
+
+static bool test_SecondaryClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct cli_credentials *anon_creds;
+ const struct dcerpc_binding *binding2;
+ struct dcerpc_pipe *p2;
+ struct spoolss_ClosePrinter cp;
+
+ /* only makes sense on SMB */
+ if (p->conn->transport.transport != NCACN_NP) {
+ return true;
+ }
+
+ torture_comment(tctx, "Testing close on secondary pipe\n");
+
+ anon_creds = cli_credentials_init_anon(tctx);
+ torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed");
+
+ binding2 = p->binding;
+ status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_spoolss,
+ anon_creds, tctx->lp_ctx,
+ tctx, &p2);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter_r(p2->binding_handle, tctx, &cp);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "ERROR: Allowed close on secondary connection");
+
+ talloc_free(p2);
+
+ return true;
+}
+
+static bool test_OpenPrinter_badname(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b, const char *name)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+ struct spoolss_OpenPrinterEx opEx;
+ struct policy_handle handle;
+ bool ret = true;
+
+ op.in.printername = name;
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = 0;
+ op.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s) with bad name\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+ torture_assert_werr_equal(tctx, op.out.result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result");
+
+ if (W_ERROR_IS_OK(op.out.result)) {
+ ret &=test_ClosePrinter(tctx, b, &handle);
+ }
+
+ opEx.in.printername = name;
+ opEx.in.datatype = NULL;
+ opEx.in.devmode_ctr.devmode = NULL;
+ opEx.in.access_mask = 0;
+ opEx.in.userlevel_ctr.level = 1;
+ opEx.in.userlevel_ctr.user_info.level1 = NULL;
+ opEx.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &opEx);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+ torture_assert_werr_equal(tctx, opEx.out.result, WERR_INVALID_PARAMETER,
+ "unexpected result");
+
+ if (W_ERROR_IS_OK(opEx.out.result)) {
+ ret &=test_ClosePrinter(tctx, b, &handle);
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinter_badname_list(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *badnames[] = {
+ "__INVALID_PRINTER__",
+ "\\\\__INVALID_HOST__",
+ "",
+ "\\\\\\",
+ "\\\\\\__INVALID_PRINTER__"
+ };
+ const char *badname;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ const char *server_name = dcerpc_server_name(p);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(badnames); i++) {
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badnames[i]),
+ "");
+ }
+
+ badname = talloc_asprintf(tctx, "\\\\%s\\", server_name);
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badname),
+ "");
+
+ badname = talloc_asprintf(tctx, "\\\\%s\\__INVALID_PRINTER__", server_name);
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badname),
+ "");
+
+ return true;
+}
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *environment,
+ bool open_only)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter r;
+ struct policy_handle handle;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.printername = name;
+ r.in.datatype = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
+
+ if (open_only) {
+ goto close_printer;
+ }
+
+ if (!test_GetPrinter(tctx, b, &handle, environment)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ close_printer:
+ if (!test_ClosePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *printername,
+ const char *datatype,
+ struct spoolss_DeviceMode *devmode,
+ uint32_t access_mask,
+ struct spoolss_UserLevelCtr *userlevel_ctr,
+ struct policy_handle *handle,
+ WERROR expected_result)
+{
+ struct spoolss_OpenPrinterEx r;
+
+ r.in.printername = printername;
+ r.in.datatype = datatype;
+ r.in.devmode_ctr.devmode= devmode;
+ r.in.access_mask = access_mask;
+ r.in.userlevel_ctr = *userlevel_ctr;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r),
+ "OpenPrinterEx failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "OpenPrinterEx failed");
+
+ return true;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ struct spoolss_DeviceMode *devmode,
+ struct policy_handle *handle)
+{
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct spoolss_UserLevel1 userlevel1;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &userlevel1;
+
+ return test_OpenPrinterEx(tctx, b, name, NULL, devmode,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &userlevel_ctr,
+ handle,
+ WERR_OK);
+}
+
+static bool test_printer_rename(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ bool ret = true;
+ union spoolss_PrinterInfo info;
+ union spoolss_SetPrinterInfo sinfo;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ const char *printer_name;
+ const char *printer_name_orig;
+ const char *printer_name_new = "SAMBA smbtorture Test Printer (Copy 2)";
+ struct policy_handle new_handle;
+ const char *q;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ torture_comment(tctx, "Testing Printer rename operations\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ printer_name_orig = talloc_strdup(tctx, info.info2.printername);
+
+ q = strrchr(info.info2.printername, '\\');
+ if (q) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", info.info2.printername);
+ }
+
+ torture_assert(tctx,
+ PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ sinfo.info2->printername = printer_name_new;
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &t->handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter level 2");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ printer_name = talloc_strdup(tctx, info.info2.printername);
+
+ q = strrchr(info.info2.printername, '\\');
+ if (q) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", info.info2.printername);
+ q++;
+ printer_name = q;
+ }
+
+ torture_assert_str_equal(tctx, printer_name, printer_name_new,
+ "new printer name was not set");
+
+ /* samba currently cannot fully rename printers */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, printer_name_orig),
+ "still can open printer with oldname after rename");
+ } else {
+ torture_warning(tctx, "*not* checking for open with oldname after rename for samba3");
+ }
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, printer_name_new, NULL, &new_handle),
+ "failed to open printer with new name");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ torture_assert_str_equal(tctx, info.info2.printername, printer_name_new,
+ "new printer name was not set");
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &new_handle),
+ "failed to close printer");
+
+ torture_comment(tctx, "Printer rename operations test succeeded\n\n");
+
+ return ret;
+}
+
+static bool test_openprinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *real_printername)
+{
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ struct spoolss_UserLevel1 userlevel1;
+ const char *printername = NULL;
+ int i;
+
+ struct {
+ const char *suffix;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .suffix = "rubbish",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", LocalOnl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", localOnly",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", localonl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",LocalOnl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",localOnl2",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", DrvConver2t",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", drvconvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",drvconvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", DrvConvert",
+ .expected_result = WERR_OK
+ },{
+ .suffix = " , DrvConvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",DrvConvert",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", DrvConvertsadfasdf",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ",DrvConvertasdfasd",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", LocalOnly",
+ .expected_result = WERR_OK
+ },{
+ .suffix = " , LocalOnly",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",LocalOnly",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", LocalOnlysagi4gjfkd",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ",LocalOnlysagi4gjfkd",
+ .expected_result = WERR_OK
+ }
+ };
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &userlevel1;
+
+ torture_comment(tctx, "Testing openprinterex printername pattern\n");
+
+ torture_assert(tctx,
+ test_OpenPrinterEx(tctx, b, real_printername, NULL, NULL, 0,
+ &userlevel_ctr, &handle,
+ WERR_OK),
+ "OpenPrinterEx failed");
+ test_ClosePrinter(tctx, b, &handle);
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+
+ printername = talloc_asprintf(tctx, "%s%s",
+ real_printername,
+ tests[i].suffix);
+
+ torture_assert(tctx,
+ test_OpenPrinterEx(tctx, b, printername, NULL, NULL, 0,
+ &userlevel_ctr, &handle,
+ tests[i].expected_result),
+ "OpenPrinterEx failed");
+ if (W_ERROR_IS_OK(tests[i].expected_result)) {
+ test_ClosePrinter(tctx, b, &handle);
+ }
+ }
+
+ return true;
+}
+
+
+static bool test_existing_printer_openprinterex(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *environment)
+{
+ struct policy_handle handle;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_openprinter(tctx, b, name)) {
+ return false;
+ }
+
+ if (!call_OpenPrinterEx(tctx, p, name, NULL, &handle)) {
+ return false;
+ }
+
+ if (!test_PrinterInfo_SD(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_GetPrinter(tctx, b, &handle, environment)) {
+ ret = false;
+ }
+
+ if (!test_EnumForms_all(tctx, b, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_Forms(tctx, b, &handle, false, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_Forms_winreg(tctx, b, &handle, false, name)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData_all(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterDataEx(tctx, b, &handle, "PrinterDriverData", NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData_consistency(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_printer_all_keys(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_PausePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_DoPrintTest(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ResumePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterData_matrix(tctx, b, &handle, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterDataEx_matrix(tctx, p, &handle, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_ClosePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 4, 5};
+ int i;
+ bool ret = true;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrinterInfo *info;
+ int j;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinters_r(b, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ if (!info) {
+ torture_comment(tctx, "No printers returned\n");
+ return true;
+ }
+
+ for (j=0;j<count;j++) {
+ if (r.in.level == 1) {
+ char *unc = talloc_strdup(tctx, info[j].info1.name);
+ char *slash, *name, *full_name;
+ name = unc;
+ if (unc[0] == '\\' && unc[1] == '\\') {
+ unc +=2;
+ }
+ slash = strchr(unc, '\\');
+ if (slash) {
+ slash++;
+ name = slash;
+ }
+ full_name = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), name);
+ if (!test_OpenPrinter(tctx, p, name, ctx->environment, true)) {
+ ret = false;
+ }
+ if (!test_OpenPrinter(tctx, p, full_name, ctx->environment, true)) {
+ ret = false;
+ }
+ if (!test_OpenPrinter(tctx, p, name, ctx->environment, false)) {
+ ret = false;
+ }
+ if (!test_existing_printer_openprinterex(tctx, p, name, ctx->environment)) {
+ ret = false;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t flags,
+ const char *servername,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrinterInfo **info_p)
+{
+ struct spoolss_EnumPrinters r;
+ union spoolss_PrinterInfo *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.flags = flags;
+ r.in.server = servername;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters(%s) level %u\n",
+ r.in.server, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "EnumPrinters failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "EnumPrinters failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static const char *get_short_printername(struct torture_context *tctx,
+ const char *name)
+{
+ const char *short_name;
+
+ if (name[0] == '\\' && name[1] == '\\') {
+ name += 2;
+ short_name = strchr(name, '\\');
+ if (short_name) {
+ return talloc_strdup(tctx, short_name+1);
+ }
+ }
+
+ return name;
+}
+
+static const char *get_full_printername(struct torture_context *tctx,
+ const char *name)
+{
+ const char *full_name = talloc_strdup(tctx, name);
+ char *p;
+
+ if (name && name[0] == '\\' && name[1] == '\\') {
+ name += 2;
+ p = strchr(name, '\\');
+ if (p) {
+ return full_name;
+ }
+ }
+
+ return NULL;
+}
+
+static bool test_OnePrinter_servername(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername)
+{
+ union spoolss_PrinterInfo info;
+ const char *short_name = get_short_printername(tctx, printername);
+ const char *full_name = get_full_printername(tctx, printername);
+
+ if (short_name) {
+ struct policy_handle handle;
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, short_name, NULL, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &handle, 2, &info),
+ "failed to get printer info");
+
+ torture_assert_casestr_equal(tctx, info.info2.servername, NULL,
+ "unexpected servername");
+ torture_assert_casestr_equal(tctx, info.info2.printername, short_name,
+ "unexpected printername");
+
+ if (info.info2.devmode) {
+ const char *expected_devicename;
+ expected_devicename = talloc_strndup(tctx, short_name, MIN(strlen(short_name), 31));
+ torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+ "unexpected devicemode devicename");
+ }
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &handle),
+ "failed to close printer");
+ }
+
+ if (full_name) {
+ struct policy_handle handle;
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, full_name, NULL, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &handle, 2, &info),
+ "failed to get printer info");
+
+ torture_assert_casestr_equal(tctx, info.info2.servername, servername,
+ "unexpected servername");
+ torture_assert_casestr_equal(tctx, info.info2.printername, full_name,
+ "unexpected printername");
+
+ if (info.info2.devmode) {
+ const char *expected_devicename;
+ expected_devicename = talloc_strndup(tctx, full_name, MIN(strlen(full_name), 31));
+ torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+ "unexpected devicemode devicename");
+ }
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &handle),
+ "failed to close printer");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinters_servername(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ const char *servername;
+ uint32_t flags = PRINTER_ENUM_NAME|PRINTER_ENUM_LOCAL;
+
+ torture_comment(tctx, "Testing servername behaviour in EnumPrinters and GetPrinters\n");
+
+ servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ torture_assert(tctx,
+ test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+ "failed to enumerate printers");
+
+ for (i=0; i < count; i++) {
+
+ torture_assert_casestr_equal(tctx, info[i].info2.servername, servername,
+ "unexpected servername");
+
+ torture_assert(tctx,
+ test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+ "failed to check printer");
+ }
+
+ servername = "";
+
+ torture_assert(tctx,
+ test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+ "failed to enumerate printers");
+
+ for (i=0; i < count; i++) {
+
+ torture_assert_casestr_equal(tctx, info[i].info2.servername, NULL,
+ "unexpected servername");
+
+ torture_assert(tctx,
+ test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+ "failed to check printer");
+ }
+
+
+ return true;
+}
+
+#if 0
+static bool test_GetPrinterDriver(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name)
+{
+ struct spoolss_GetPrinterDriver r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.architecture = "W32X86";
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinterDriver level %d\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r),
+ "failed to call GetPrinterDriver");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r),
+ "failed to call GetPrinterDriver");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call GetPrinterDriver");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
+
+ return true;
+}
+#endif
+
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture,
+ uint32_t level,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info_p,
+ WERROR *result_p)
+
+{
+ struct spoolss_GetPrinterDriver2 r;
+ uint32_t needed;
+ uint32_t server_major_version;
+ uint32_t server_minor_version;
+
+ r.in.handle = handle;
+ r.in.architecture = architecture;
+ r.in.client_major_version = client_major_version;
+ r.in.client_minor_version = client_minor_version;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.in.level = level;
+ r.out.needed = &needed;
+ r.out.server_major_version = &server_major_version;
+ r.out.server_minor_version = &server_minor_version;
+
+ torture_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n",
+ driver_name, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r),
+ "failed to call GetPrinterDriver2");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r),
+ "failed to call GetPrinterDriver2");
+ }
+
+ if (result_p) {
+ *result_p = r.out.result;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) {
+ switch (r.in.level) {
+ case 101:
+ case 8:
+ torture_comment(tctx,
+ "level %d not implemented, not considering as an error\n",
+ r.in.level);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call GetPrinterDriver2");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture)
+{
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 };
+ int i;
+
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, architecture, levels[i], 3, 0, NULL, NULL),
+ "");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6};
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, ctx->environment, levels[i], &count, &info),
+ "failed to enumerate drivers");
+
+ if (!info) {
+ torture_comment(tctx, "No printer drivers returned\n");
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool test_DeletePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_DeletePrinter r;
+
+ torture_comment(tctx, "Testing DeletePrinter\n");
+
+ r.in.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_DeletePrinter_r(b, tctx, &r),
+ "failed to delete printer");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to delete printer");
+
+ return true;
+}
+
+static bool test_EnumPrinters_findname(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t flags,
+ uint32_t level,
+ const char *name,
+ bool *found)
+{
+ struct spoolss_EnumPrinters e;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ uint32_t needed;
+ int i;
+
+ *found = false;
+
+ e.in.flags = flags;
+ e.in.server = NULL;
+ e.in.level = level;
+ e.in.buffer = NULL;
+ e.in.offered = 0;
+ e.out.count = &count;
+ e.out.info = &info;
+ e.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e),
+ "failed to enum printers");
+
+ if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ e.in.buffer = &blob;
+ e.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e),
+ "failed to enum printers");
+ }
+
+ torture_assert_werr_ok(tctx, e.out.result,
+ "failed to enum printers");
+
+ for (i=0; i < count; i++) {
+
+ const char *current = NULL;
+ const char *q;
+
+ switch (level) {
+ case 1:
+ current = info[i].info1.name;
+ break;
+ }
+
+ if (strequal(current, name)) {
+ *found = true;
+ break;
+ }
+
+ q = strrchr(current, '\\');
+ if (q) {
+ if (!e.in.server) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", current);
+ }
+ q++;
+ if (strequal(q, name)) {
+ *found = true;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinter_wellknown(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *printername,
+ bool ex)
+{
+ WERROR result;
+ struct spoolss_AddPrinter r;
+ struct spoolss_AddPrinterEx rex;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo1 info1;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ bool found = false;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(userlevel_ctr);
+ ZERO_STRUCT(info1);
+
+ torture_comment(tctx, "Testing AddPrinter%s(%s) level 1\n",
+ ex ? "Ex":"", printername);
+
+ /* try to add printer to wellknown printer list (level 1) */
+
+ userlevel_ctr.level = 1;
+
+ info_ctr.info.info1 = &info1;
+ info_ctr.level = 1;
+
+ rex.in.server = NULL;
+ rex.in.info_ctr = &info_ctr;
+ rex.in.devmode_ctr = &devmode_ctr;
+ rex.in.secdesc_ctr = &secdesc_ctr;
+ rex.in.userlevel_ctr = &userlevel_ctr;
+ rex.out.handle = &handle;
+
+ r.in.server = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result code");
+
+ info1.name = printername;
+ info1.flags = PRINTER_ATTRIBUTE_SHARED;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here,
+ better do a real check to see the printer is really there */
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_NETWORK, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+
+ torture_assert(tctx, found, "failed to find newly added printer");
+
+ info1.flags = 0;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here,
+ better do a real check to see the printer has really been removed
+ from the well known printer list */
+
+ found = false;
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_NETWORK, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+#if 0
+ torture_assert(tctx, !found, "printer still in well known printer list");
+#endif
+ return true;
+}
+
+static bool test_AddPrinter_normal(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle_p,
+ const char *printername,
+ const char *drivername,
+ const char *portname,
+ struct spoolss_DeviceMode *devmode,
+ bool ex)
+{
+ WERROR result;
+ struct spoolss_AddPrinter r;
+ struct spoolss_AddPrinterEx rex;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ bool found = false;
+ bool existing_printer_deleted = false;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(userlevel_ctr);
+
+ torture_comment(tctx, "Testing AddPrinter%s(%s) level 2\n",
+ ex ? "Ex":"", printername);
+
+ devmode_ctr.devmode = devmode;
+
+ userlevel_ctr.level = 1;
+
+ rex.in.server = NULL;
+ rex.in.info_ctr = &info_ctr;
+ rex.in.devmode_ctr = &devmode_ctr;
+ rex.in.secdesc_ctr = &secdesc_ctr;
+ rex.in.userlevel_ctr = &userlevel_ctr;
+ rex.out.handle = &handle;
+
+ r.in.server = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.out.handle = &handle;
+
+ again:
+
+ /* try to add printer to printer list (level 2) */
+
+ ZERO_STRUCT(info2);
+
+ info_ctr.info.info2 = &info2;
+ info_ctr.level = 2;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result code");
+
+ info2.printername = printername;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+
+ if (W_ERROR_EQUAL(result, WERR_PRINTER_ALREADY_EXISTS)) {
+ struct policy_handle printer_handle;
+
+ if (existing_printer_deleted) {
+ torture_fail(tctx, "already deleted printer still existing?");
+ }
+
+ torture_assert(tctx, call_OpenPrinterEx(tctx, p, printername, NULL, &printer_handle),
+ "failed to open printer handle");
+
+ torture_assert(tctx, test_DeletePrinter(tctx, b, &printer_handle),
+ "failed to delete printer");
+
+ torture_assert(tctx, test_ClosePrinter(tctx, b, &printer_handle),
+ "failed to close server handle");
+
+ existing_printer_deleted = true;
+
+ goto again;
+ }
+
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT,
+ "unexpected result code");
+
+ info2.portname = portname;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTER_DRIVER,
+ "unexpected result code");
+
+ info2.drivername = drivername;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+
+ /* w2k8r2 allows one to add printer w/o defining printprocessor */
+
+ if (!W_ERROR_IS_OK(result)) {
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
+ "unexpected result code");
+
+ info2.printprocessor = "winprint";
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_ok(tctx, result,
+ "failed to add printer");
+ }
+
+ *handle_p = handle;
+
+ /* we are paranoid, really check if the printer is there now */
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_LOCAL, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+ torture_assert(tctx, found, "failed to find newly added printer");
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ return true;
+}
+
+static bool test_printer_info(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ bool ret = true;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping printer info cross tests against samba 3");
+ }
+
+ if (!test_PrinterInfo(tctx, b, &t->handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinter_errors(tctx, b, &t->handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***array)
+{
+ struct spoolss_EnumPrinterKey r;
+ uint32_t needed = 0;
+ union spoolss_KeyNames key_buffer;
+ int32_t offered[] = { 0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, 256, 512, 1024, 2048 };
+ uint32_t _ndr_size;
+ int i;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.out.key_buffer = &key_buffer;
+ r.out.needed = &needed;
+ r.out._ndr_size = &_ndr_size;
+
+ for (i=0; i < ARRAY_SIZE(offered); i++) {
+
+ if (offered[i] < 0 && needed) {
+ if (needed <= 4) {
+ continue;
+ }
+ r.in.offered = needed + offered[i];
+ } else {
+ r.in.offered = offered[i];
+ }
+
+ ZERO_STRUCT(key_buffer);
+
+ torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+ "failed to call EnumPrinterKey");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+
+ torture_assert(tctx, (_ndr_size == r.in.offered/2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+ _ndr_size, r.in.offered/2));
+
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+ "failed to call EnumPrinterKey");
+ }
+
+ if (offered[i] > 0) {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call EnumPrinterKey");
+ }
+
+ torture_assert(tctx, (_ndr_size == r.in.offered/2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+ _ndr_size, r.in.offered/2));
+
+ torture_assert(tctx, (*r.out.needed <= r.in.offered),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= offered %d", *r.out.needed, r.in.offered));
+
+ torture_assert(tctx, (*r.out.needed <= _ndr_size * 2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= _ndr_size %d * 2", *r.out.needed, _ndr_size));
+
+ if (key_buffer.string_array) {
+ uint32_t calc_needed = 0;
+ int s;
+ for (s=0; key_buffer.string_array[s]; s++) {
+ calc_needed += strlen_m_term(key_buffer.string_array[s])*2;
+ }
+ if (!key_buffer.string_array[0]) {
+ calc_needed += 2;
+ }
+ calc_needed += 2;
+
+ torture_assert_int_equal(tctx, *r.out.needed, calc_needed,
+ "EnumPrinterKey unexpected size");
+ }
+ }
+
+ if (array) {
+ *array = key_buffer.string_array;
+ }
+
+ return true;
+}
+
+bool test_printer_all_keys(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ const char **key_array = NULL;
+ int i;
+
+ torture_comment(tctx, "Testing Printer Keys\n");
+
+ torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, "", &key_array),
+ "failed to call test_EnumPrinterKey");
+
+ for (i=0; key_array && key_array[i]; i++) {
+ torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, key_array[i], NULL),
+ "failed to call test_EnumPrinterKey");
+ }
+ for (i=0; key_array && key_array[i]; i++) {
+ torture_assert(tctx, test_EnumPrinterDataEx(tctx, b, handle, key_array[i], NULL, NULL),
+ "failed to call test_EnumPrinterDataEx");
+ }
+
+ torture_comment(tctx, "Printer Keys test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_openprinter_wrap(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *printername = t->info2.printername;
+
+ return test_openprinter(tctx, b, printername);
+}
+
+static bool test_csetprinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ const char *printername = talloc_asprintf(tctx, "%s2", t->info2.printername);
+ const char *drivername = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+ const char *portname = t->info2.portname;
+
+ union spoolss_PrinterInfo info;
+ struct policy_handle new_handle, new_handle2;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing c_setprinter\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on initial printer handle: %d\n",
+ info.info0.c_setprinter);
+
+ /* check if c_setprinter on 1st handle increases after a printer has
+ * been added */
+
+ torture_assert(tctx,
+ test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, NULL, false),
+ "failed to add new printer");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on initial printer handle (after add): %d\n",
+ info.info0.c_setprinter);
+
+ /* check if c_setprinter on new handle increases after a printer has
+ * been added */
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on created handle: %d\n",
+ info.info0.c_setprinter);
+
+ /* open the new printer and check if c_setprinter increases */
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, printername, NULL, &new_handle2),
+ "failed to open created printer");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle2, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on new handle (after openprinter): %d\n",
+ info.info0.c_setprinter);
+
+ /* cleanup */
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &new_handle2),
+ "failed to close printer");
+ torture_assert(tctx,
+ test_DeletePrinter(tctx, b, &new_handle),
+ "failed to delete new printer");
+
+ return true;
+}
+
+static bool compose_local_driver_directory(struct torture_context *tctx,
+ const char *environment,
+ const char *local_dir,
+ const char **path)
+{
+ char *p;
+
+ p = strrchr(local_dir, '/');
+ if (!p) {
+ return NULL;
+ }
+ p++;
+
+ if (strequal(environment, SPOOLSS_ARCHITECTURE_x64)) {
+ if (!strequal(p, "x64")) {
+ *path = talloc_asprintf(tctx, "%s/x64", local_dir);
+ }
+ } else if (strequal(environment, SPOOLSS_ARCHITECTURE_NT_X86)) {
+ if (!strequal(p, "i386")) {
+ *path = talloc_asprintf(tctx, "%s/i386", local_dir);
+ }
+ } else {
+ torture_assert(tctx, "unknown environment: '%s'\n", environment);
+ }
+
+ return true;
+}
+
+#if 0
+static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
+ const char *devicename)
+{
+ struct spoolss_DeviceMode *r;
+
+ r = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->devicename = talloc_strdup(r, devicename);
+ r->specversion = DMSPEC_NT4_AND_ABOVE;
+ r->driverversion = 0x0600;
+ r->size = 0x00dc;
+ r->__driverextra_length = 0;
+ r->fields = DEVMODE_FORMNAME |
+ DEVMODE_TTOPTION |
+ DEVMODE_PRINTQUALITY |
+ DEVMODE_DEFAULTSOURCE |
+ DEVMODE_COPIES |
+ DEVMODE_SCALE |
+ DEVMODE_PAPERSIZE |
+ DEVMODE_ORIENTATION;
+ r->orientation = DMORIENT_PORTRAIT;
+ r->papersize = DMPAPER_LETTER;
+ r->paperlength = 0;
+ r->paperwidth = 0;
+ r->scale = 100;
+ r->copies = 55;
+ r->defaultsource = DMBIN_FORMSOURCE;
+ r->printquality = DMRES_HIGH;
+ r->color = DMRES_MONOCHROME;
+ r->duplex = DMDUP_SIMPLEX;
+ r->yresolution = 0;
+ r->ttoption = DMTT_SUBDEV;
+ r->collate = DMCOLLATE_FALSE;
+ r->formname = talloc_strdup(r, "Letter");
+
+ return r;
+}
+#endif
+
+static bool test_architecture_buffer(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct spoolss_OpenPrinterEx r;
+ struct spoolss_UserLevel1 u1;
+ struct policy_handle handle;
+ uint32_t architectures[] = {
+ PROCESSOR_ARCHITECTURE_INTEL,
+ PROCESSOR_ARCHITECTURE_IA64,
+ PROCESSOR_ARCHITECTURE_AMD64
+ };
+ uint32_t needed[3];
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+ torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]);
+
+ u1.size = 0;
+ u1.client = NULL;
+ u1.user = NULL;
+ u1.build = 0;
+ u1.major = 3;
+ u1.minor = 0;
+ u1.processor = architectures[i];
+
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.datatype = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.userlevel_ctr.level = 1;
+ r.in.userlevel_ctr.user_info.level1 = &u1;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), "");
+ torture_assert_werr_ok(tctx, r.out.result, "");
+
+ {
+ struct spoolss_EnumPrinters e;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ e.in.flags = PRINTER_ENUM_LOCAL;
+ e.in.server = NULL;
+ e.in.level = 2;
+ e.in.buffer = NULL;
+ e.in.offered = 0;
+ e.out.count = &count;
+ e.out.info = &info;
+ e.out.needed = &needed[i];
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), "");
+#if 0
+ torture_comment(tctx, "needed was %d\n", needed[i]);
+#endif
+ }
+
+ torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), "");
+ }
+
+ for (i=1; i < ARRAY_SIZE(architectures); i++) {
+ if (needed[i-1] != needed[i]) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx, "needed size %d for architecture %d != needed size %d for architecture %d\n",
+ needed[i-1], architectures[i-1], needed[i], architectures[i]));
+ }
+ }
+
+ return true;
+}
+
+static bool test_get_core_printer_drivers_arch_guid(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *architecture,
+ const char *guid_str,
+ const char **package_id)
+{
+ struct spoolss_GetCorePrinterDrivers r;
+ struct spoolss_CorePrinterDriver core_printer_drivers;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 2);
+ const char **s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct GUID guid;
+
+ s = talloc_zero_array(tctx, const char *, 2);
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.architecture = "foobar";
+ r.in.core_driver_size = 0;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 0;
+ r.out.core_printer_drivers = &core_printer_drivers;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_hresult_equal(tctx, r.out.result, HRES_E_INVALIDARG,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ guid = GUID_random();
+ s[0] = GUID_string2(tctx, &guid);
+
+ torture_assert(tctx,
+ push_reg_multi_sz(tctx, &blob, s),
+ "push_reg_multi_sz failed");
+
+ r.in.core_driver_size = blob.length/2;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 1;
+ r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ r.in.architecture = architecture;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_NOT_FOUND,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ s[0] = talloc_strdup(s, guid_str);
+
+ torture_assert(tctx,
+ push_reg_multi_sz(tctx, &blob, s),
+ "push_reg_multi_sz failed");
+
+ r.in.core_driver_size = blob.length/2;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 1;
+ r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ if (package_id) {
+ *package_id = r.out.core_printer_drivers[0].szPackageID;
+ }
+
+ return true;
+}
+
+static bool test_get_core_printer_drivers(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *architectures[] = {
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ SPOOLSS_ARCHITECTURE_x64
+ };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+ torture_comment(tctx, "Testing GetCorePrinterDrivers(\"%s\",\"%s\")\n",
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV);
+
+ torture_assert(tctx,
+ test_get_core_printer_drivers_arch_guid(tctx, p,
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV,
+ NULL),
+ "");
+ }
+
+ return true;
+}
+
+static bool test_get_printer_driver_package_path(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *architectures[] = {
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ SPOOLSS_ARCHITECTURE_x64
+ };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+ struct spoolss_GetPrinterDriverPackagePath r;
+ uint32_t required = 0;
+ const char *package_id = NULL;
+
+ test_get_core_printer_drivers_arch_guid(tctx, p,
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV,
+ &package_id),
+
+ torture_comment(tctx, "Testing GetPrinterDriverPackagePath(\"%s\",\"%s\")\n",
+ architectures[i], package_id);
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.architecture = "foobar";
+ r.in.language = NULL;
+ r.in.package_id = "";
+ r.in.driver_package_cab_size = 0;
+ r.in.driver_package_cab = NULL;
+
+ r.out.required = &required;
+ r.out.driver_package_cab = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.architecture = architectures[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.package_id = package_id;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.driver_package_cab_size = required;
+ r.in.driver_package_cab = talloc_zero_array(tctx, char, required);
+ r.out.driver_package_cab = talloc_zero_array(tctx, char, required);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.servername = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INSUFFICIENT_BUFFER,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.driver_package_cab_size = required;
+ r.in.driver_package_cab = talloc_zero_array(tctx, char, required);
+ r.out.driver_package_cab = talloc_zero_array(tctx, char, required);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ }
+
+ return true;
+}
+
+static bool test_get_printer_printserverhandle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t levels[] = {0, 1, 2, /* 3,*/ 4, 5, 6, 7, 8};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ torture_assert(tctx,
+ test_GetPrinter_level_exp(tctx, b, &ctx->server_handle,
+ levels[i], WERR_INVALID_LEVEL,
+ NULL),
+ "failed to call GetPrinter");
+ }
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, NULL),
+ "failed to call GetPrinter");
+
+ return true;
+}
+
+#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500"
+
+static bool test_set_printer_printserverhandle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct security_descriptor *sd;
+ struct security_ace *ace;
+ struct dom_sid sid;
+ int i;
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ secdesc_ctr.sd = info.info3.secdesc;
+ secdesc_ctr.sd->owner_sid = NULL;
+ secdesc_ctr.sd->group_sid = NULL;
+
+ sd = security_descriptor_copy(tctx, secdesc_ctr.sd);
+ if (sd == NULL) {
+ return false;
+ }
+
+ ace = security_ace_create(tctx,
+ TEST_SID,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED,
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ torture_assert(tctx, ace, "failed to create ace");
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add ace");
+
+ secdesc_ctr.sd = sd;
+
+ info3.sec_desc_ptr = 0;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ ZERO_STRUCT(devmode_ctr);
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) {
+ if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) {
+ break;
+ }
+ }
+
+ if (i == info.info3.secdesc->dacl->num_aces) {
+ torture_fail(tctx, "ace not present");
+ }
+
+ torture_assert(tctx,
+ dom_sid_parse(TEST_SID, &sid),
+ "failed to parse sid");
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_del(info.info3.secdesc, &sid),
+ "failed to remove ace from sd");
+
+ secdesc_ctr.sd = info.info3.secdesc;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) {
+ if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) {
+ torture_fail(tctx, "ace still present");
+ }
+ }
+
+ return true;
+}
+
+
+static bool test_PrintServer_Forms_Winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_Forms_winreg(tctx, b, &ctx->server_handle, true, NULL);
+}
+
+static bool test_PrintServer_Forms(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_Forms(tctx, b, &ctx->server_handle, true, NULL, NULL, NULL);
+}
+
+static bool test_PrintServer_EnumForms(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_EnumForms_all(tctx, b, &ctx->server_handle, true);
+}
+
+static bool torture_rpc_spoolss_setup_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+ NTSTATUS status;
+
+ status = torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, t->spoolss_pipe, &t->server_handle),
+ "failed to open printserver");
+ torture_assert(tctx,
+ test_get_environment(tctx, t->spoolss_pipe->binding_handle, &t->server_handle, &t->environment),
+ "failed to get environment");
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_setup(struct torture_context *tctx, void **data)
+{
+ struct test_spoolss_context *t;
+
+ *data = t = talloc_zero(tctx, struct test_spoolss_context);
+
+ return torture_rpc_spoolss_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_teardown_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+ test_ClosePrinter(tctx, t->spoolss_pipe->binding_handle, &t->server_handle);
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_teardown(struct torture_context *tctx, void *data)
+{
+ struct test_spoolss_context *t = talloc_get_type(data, struct test_spoolss_context);
+ bool ret;
+
+ ret = torture_rpc_spoolss_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ const char *server_name_slash;
+ const char *driver_name;
+ const char *printer_name;
+ const char *port_name;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss),
+ "Error connecting to server");
+
+ p = t->spoolss_pipe;
+ b = p->binding_handle;
+ server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ t->driver.info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ t->driver.info8.driver_name = TORTURE_DRIVER;
+ t->driver.info8.driver_path = "pscript5.dll";
+ t->driver.info8.data_file = "cups6.ppd";
+ t->driver.info8.config_file = "ps5ui.dll";
+ t->driver.info8.help_file = "pscript.hlp";
+ t->driver.info8.default_datatype = "RAW";
+ t->driver.info8.dependent_files = talloc_zero(t, struct spoolss_StringArray);
+ t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1);
+ t->driver.info8.dependent_files->string[0] = "pscript5.dll";
+ t->driver.info8.dependent_files->string[1] = "cups6.ppd";
+ t->driver.info8.dependent_files->string[2] = "ps5ui.dll";
+ t->driver.info8.dependent_files->string[3] = "pscript.hlp";
+ t->driver.info8.dependent_files->string[4] = "pscript.ntf";
+ t->driver.info8.dependent_files->string[5] = "cups6.ini";
+ t->driver.info8.dependent_files->string[6] = "cupsps6.dll";
+ t->driver.info8.dependent_files->string[7] = "cupsui6.dll";
+
+ t->driver.local.driver_directory= "/usr/share/cups/drivers";
+
+ t->info2.portname = "LPT1:";
+
+ printer_name = t->info2.printername;
+ port_name = t->info2.portname;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, &t->driver),
+ "failed to fillup printserver info");
+
+ t->driver.info8.architecture = talloc_strdup(t, t->driver.remote.environment);
+
+ torture_assert(tctx,
+ compose_local_driver_directory(tctx, t->driver.remote.environment,
+ t->driver.local.driver_directory,
+ &t->driver.local.driver_directory),
+ "failed to compose local driver directory");
+
+ t->info2.drivername = "Microsoft XPS Document Writer";
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) is present on server\n",
+ t->info2.drivername, t->driver.remote.environment);
+ t->have_driver = true;
+ goto try_add;
+ }
+
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
+ t->info2.drivername, t->driver.remote.environment);
+
+ t->info2.drivername = "Microsoft XPS Document Writer v4";
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 4) is present on server\n",
+ t->info2.drivername, t->driver.remote.environment);
+ t->have_driver = true;
+ goto try_add;
+ }
+
+ torture_comment(tctx, "trying to upload own driver\n");
+
+ if (!directory_exist(t->driver.local.driver_directory)) {
+ torture_warning(tctx, "no local driver is available!");
+ t->have_driver = false;
+ goto try_add;
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &t->driver.info8, 0, false, NULL),
+ "failed to add driver");
+
+ t->added_driver = true;
+ t->have_driver = true;
+
+ try_add:
+ driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+
+ if (t->wellknown) {
+ torture_assert(tctx,
+ test_AddPrinter_wellknown(tctx, p, printer_name, t->ex),
+ "failed to add wellknown printer");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinter_normal(tctx, p, &t->handle, printer_name, driver_name, port_name, t->devmode, t->ex),
+ "failed to add printer");
+ }
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_printer_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = false;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER;
+ t->devmode = NULL;
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerex_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER_EX;
+ t->devmode = NULL;
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerwkn_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = false;
+ t->wellknown = true;
+ t->info2.printername = TORTURE_WELLKNOWN_PRINTER;
+ t->devmode = NULL;
+
+ /* FIXME */
+ if (t->wellknown) {
+ torture_skip(tctx, "skipping AddPrinter level 1");
+ }
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = true;
+ t->info2.printername = TORTURE_WELLKNOWN_PRINTER_EX;
+ t->devmode = NULL;
+
+ /* FIXME */
+ if (t->wellknown) {
+ torture_skip(tctx, "skipping AddPrinterEx level 1");
+ }
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+#if 0
+static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER_EX;
+ t->devmode = torture_devicemode(t, TORTURE_PRINTER_EX);
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+#endif
+
+static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version,
+ WERROR expected_result);
+
+static bool torture_rpc_spoolss_printer_teardown_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+ bool found = false;
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = NULL;
+ const char *server_name_slash;
+ bool ok = true;
+
+ if (p == NULL) {
+ return true;
+ }
+ b = p->binding_handle;
+
+ server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ if (!t->wellknown) {
+ const char *printer_name = t->info2.printername;
+
+ torture_assert_goto(tctx,
+ test_DeletePrinter(tctx, b, &t->handle),
+ ok,
+ remove_driver,
+ "failed to delete printer");
+
+ torture_assert_goto(tctx,
+ test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
+ printer_name, &found),
+ ok,
+ remove_driver,
+ "failed to enumerate printers");
+
+ torture_assert_goto(tctx,
+ !found,
+ ok,
+ remove_driver,
+ "deleted printer still there");
+ }
+
+
+remove_driver:
+ if (t->added_driver) {
+ ok = remove_printer_driver(tctx,
+ dcerpc_server_name(p),
+ &t->driver);
+ if (!ok) {
+ torture_warning(tctx,
+ "failed to remove printer driver\n");
+ }
+
+ ok = test_DeletePrinterDriverEx_exp(tctx, b,
+ server_name_slash,
+ t->driver.info8.driver_name,
+ t->driver.info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ t->driver.info8.version,
+ WERR_OK);
+ if (!ok) {
+ torture_warning(tctx,
+ "failed to delete printer driver via "
+ "spoolss\n");
+ }
+ }
+
+ return ok;
+}
+
+static bool torture_rpc_spoolss_printer_teardown(struct torture_context *tctx, void *data)
+{
+ struct torture_printer_context *t = talloc_get_type(data, struct torture_printer_context);
+ bool ret;
+
+ ret = torture_rpc_spoolss_printer_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool test_print_test(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ torture_assert(tctx,
+ test_DoPrintTest(tctx, b, &t->handle),
+ "failed to do print test");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_print_test_extended(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ bool ret = true;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ ret = test_DoPrintTest_extended(tctx, b, &t->handle);
+ if (ret == false) {
+ torture_comment(tctx, "WARNING! failed to do extended print test\n");
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx, "non-critical for samba3\n");
+ ret = true;
+ tctx->last_result = TORTURE_SKIP;
+ }
+ }
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return ret;
+}
+
+static bool test_print_test_properties(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer job property tests against samba");
+ }
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ torture_assert(tctx,
+ test_DoPrintTest_properties(tctx, b, &t->handle),
+ "failed to test print job properties");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+/* use smbd file IO to spool a print job */
+static bool test_print_test_smbd(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ uint32_t count;
+ union spoolss_JobInfo *info = NULL;
+ int i;
+
+ struct smb2_tree *tree;
+ struct smb2_handle job_h;
+ struct smbcli_options options;
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ /*
+ * Do not test against the dynamically added printers, printing via
+ * smbd means that a different spoolss process may handle the
+ * OpenPrinter request to the one that handled the AddPrinter request.
+ * This currently leads to an ugly race condition where one process
+ * sees the new printer and one doesn't.
+ */
+ const char *share = TORTURE_PRINTER_STATIC1;
+
+ torture_comment(tctx, "Testing smbd job spooling\n");
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ torture_setting_string(tctx, "host", NULL),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ share,
+ lpcfg_resolve_context(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ tctx->ev,
+ &options,
+ lpcfg_socket_options(tctx->lp_ctx),
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to SMB2 printer %s - %s\n",
+ share, nt_errstr(status));
+ return false;
+ }
+
+ status = torture_smb2_testfile(tree, "smbd_spooler_job", &job_h);
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job create");
+
+ status = smb2_util_write(tree, job_h, "exciting print job data", 0,
+ sizeof("exciting print job data"));
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job write");
+
+ /* check back end spoolss job was created */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ for (i = 0; i < count; i++) {
+ if (!strcmp(info[i].info1.document_name, "smbd_spooler_job")) {
+ break;
+ }
+ }
+ torture_assert(tctx, (i != count), "smbd_spooler_job not found");
+
+ status = smb2_util_close(tree, job_h);
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job close");
+
+ /* disconnect from printer share */
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+static bool test_print_test_purge(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ bool ret = true;
+ uint32_t count;
+ union spoolss_JobInfo *info;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+ for (i=0; i < num_jobs; i++) {
+ ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle,
+ "TorturePrintJob",
+ &job_ids[i]);
+ torture_assert(tctx, ret, "failed to add print job");
+ }
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, num_jobs,
+ "unexpected number of jobs in queue");
+
+ torture_assert(tctx,
+ test_printer_purge(tctx, b, &t->handle),
+ "failed to purge printer");
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, 0,
+ "unexpected number of jobs in queue");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_printer_sd(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_PrinterInfo_SD(tctx, b, &t->handle),
+ "failed to test security descriptors");
+
+ return true;
+}
+
+static bool test_printer_dm(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterInfo_DevMode(tctx, p, &t->handle, t->info2.printername, t->devmode),
+ "failed to test devicemodes");
+
+ return true;
+}
+
+static bool test_printer_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterInfo_winreg(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer info winreg");
+
+ return true;
+}
+
+static bool test_printserver_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *t =
+ (struct test_spoolss_context *)talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrintserverInfo_winreg(tctx, p, &t->server_handle),
+ "failed to test printserver info winreg");
+
+ return true;
+}
+
+
+static bool test_printer_change_id(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_ChangeID(tctx, p, &t->handle),
+ "failed to test change id");
+
+ return true;
+}
+
+static bool test_printer_keys(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_printer_all_keys(tctx, b, &t->handle),
+ "failed to test printer keys");
+
+ return true;
+}
+
+static bool test_printer_data_consistency(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_EnumPrinterData_consistency(tctx, p, &t->handle),
+ "failed to test printer data consistency");
+
+ return true;
+}
+
+static bool test_printer_data_keys(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_keys(tctx, p, &t->handle),
+ "failed to test printer data keys");
+
+ return true;
+}
+
+static bool test_printer_data_values(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_values(tctx, p, &t->handle),
+ "failed to test printer data values");
+
+ return true;
+}
+
+static bool test_printer_data_set(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_matrix(tctx, p, &t->handle, t->info2.printername, NULL, NULL),
+ "failed to test printer data set");
+
+ return true;
+}
+
+static bool test_printer_data_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterData_winreg(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer data winreg");
+
+ return true;
+}
+
+static bool test_printer_data_dsspooler(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterData_DsSpooler(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer data winreg dsspooler");
+
+ return true;
+}
+
+static bool test_printer_ic(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle gdi_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer information context tests against samba");
+ }
+
+ {
+ struct spoolss_CreatePrinterIC r;
+ struct spoolss_DevmodeContainer devmode_ctr;
+
+ ZERO_STRUCT(devmode_ctr);
+
+ r.in.handle = &t->handle;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.out.gdi_handle = &gdi_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_CreatePrinterIC_r(b, tctx, &r),
+ "CreatePrinterIC failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "CreatePrinterIC failed");
+ }
+
+ {
+ struct spoolss_PlayGDIScriptOnPrinterIC r;
+ DATA_BLOB in,out;
+ int i;
+ uint32_t num_fonts = 0;
+
+ in = data_blob_string_const("");
+
+ r.in.gdi_handle = &gdi_handle;
+ r.in.pIn = in.data;
+ r.in.cIn = in.length;
+ r.in.ul = 0;
+
+ for (i = 0; i < 4; i++) {
+
+ out = data_blob_talloc_zero(tctx, i);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_ENOUGH_MEMORY,
+ "PlayGDIScriptOnPrinterIC failed");
+ }
+
+ out = data_blob_talloc_zero(tctx, 4);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "PlayGDIScriptOnPrinterIC failed");
+
+ /* now we should have the required length, so retry with a
+ * buffer which is large enough to carry all font ids */
+
+ num_fonts = IVAL(r.out.pOut, 0);
+
+ torture_comment(tctx, "PlayGDIScriptOnPrinterIC gave font count of %d\n", num_fonts);
+
+ out = data_blob_talloc_zero(tctx,
+ num_fonts * sizeof(struct UNIVERSAL_FONT_ID) + 4);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "PlayGDIScriptOnPrinterIC failed");
+
+ }
+
+ {
+ struct spoolss_DeletePrinterIC r;
+
+ r.in.gdi_handle = &gdi_handle;
+ r.out.gdi_handle = &gdi_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterIC_r(b, tctx, &r),
+ "DeletePrinterIC failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterIC failed");
+
+ }
+
+ return true;
+}
+
+static bool test_printer_bidi(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_SendRecvBidiData r;
+ struct RPC_BIDI_REQUEST_CONTAINER bidi_req;
+ struct RPC_BIDI_RESPONSE_CONTAINER *bidi_rep = NULL;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer bidirectional tests against samba");
+ }
+
+ ZERO_STRUCT(bidi_req);
+
+ r.in.hPrinter = t->handle;
+ r.in.pAction = "foobar";
+ r.in.pReqData = &bidi_req;
+ r.out.ppRespData = &bidi_rep;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r),
+ "SendRecvBidiData failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "SendRecvBidiData failed");
+
+ if (!(t->info2.attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI)) {
+ torture_skip(tctx, "skipping further tests as printer is not BIDI enabled");
+ }
+
+ r.in.pAction = BIDI_ACTION_ENUM_SCHEMA;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r),
+ "SendRecvBidiData failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SendRecvBidiData failed");
+
+ return true;
+}
+
+static bool test_printer_set_publish(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info7.guid = "";
+ info7.action = DSPRINT_PUBLISH;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ info_ctr.level = 7;
+ info_ctr.info.info7 = &info7;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0), "");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "");
+ torture_assert(tctx,
+ (info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+ "info2 publish flag not set");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 7, &info),
+ "");
+ if (info.info7.action & DSPRINT_PENDING) {
+ torture_comment(tctx, "publish is pending\n");
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ (DSPRINT_PENDING | DSPRINT_PUBLISH),
+ "info7 publish flag not set");
+ } else {
+ struct GUID guid;
+ char *ref_guid;
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ DSPRINT_PUBLISH,
+ "info7 publish flag not set");
+
+ /* GUID_from_string is able to parse both plain and
+ * curly-braced guids */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(info.info7.guid,
+ &guid),
+ "invalid published printer GUID");
+
+ /* Build reference GUID string */
+ ref_guid = GUID_string2(tctx, &guid);
+ torture_assert_not_null(tctx, ref_guid, "ENOMEM");
+ ref_guid = talloc_strdup_upper(tctx, ref_guid);
+ torture_assert_not_null(tctx, ref_guid, "ENOMEM");
+ torture_assert_str_equal(tctx, info.info7.guid, ref_guid,
+ "invalid GUID format");
+ }
+
+ return true;
+}
+
+static bool test_printer_set_unpublish(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info7.action = DSPRINT_UNPUBLISH;
+ info7.guid = "";
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ info_ctr.level = 7;
+ info_ctr.info.info7 = &info7;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0), "");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "");
+ torture_assert(tctx,
+ !(info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+ "info2 publish flag still set");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 7, &info),
+ "");
+
+ if (info.info7.action & DSPRINT_PENDING) {
+ struct GUID guid;
+ torture_comment(tctx, "unpublish is pending\n");
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ (DSPRINT_PENDING | DSPRINT_UNPUBLISH),
+ "info7 unpublish flag not set");
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(info.info7.guid,
+ &guid),
+ "invalid printer GUID");
+ } else {
+ torture_assert_int_equal(tctx,
+ info.info7.action, DSPRINT_UNPUBLISH,
+ "info7 unpublish flag not set");
+ }
+
+ return true;
+}
+
+static bool test_printer_publish_toggle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle *handle = &t->handle;
+ union spoolss_PrinterInfo info7;
+ union spoolss_PrinterInfo info2;
+
+ /* check publish status via level 7 and level 2 */
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 7, &info7),
+ "");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info2),
+ "");
+
+ if (info2.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
+ torture_assert_int_equal(tctx,
+ info7.info7.action, DSPRINT_PUBLISH,
+ "info7 publish flag not set");
+ torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
+ torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+ } else {
+ torture_assert_int_equal(tctx,
+ info7.info7.action, DSPRINT_UNPUBLISH,
+ "info7 unpublish flag not set");
+ torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+ torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
+ }
+
+ return true;
+}
+
+static bool test_driver_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+
+ if (!t->have_driver) {
+ torture_skip(tctx, "skipping driver info winreg test as we don't have a driver");
+ }
+
+ torture_assert(tctx,
+ test_DriverInfo_winreg(tctx, p, &t->handle, t->info2.printername, driver_name, t->driver.remote.environment, 3),
+ "failed to test driver info winreg");
+
+ return true;
+}
+
+static bool test_print_job_enum(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ union spoolss_JobInfo *info = NULL;
+ uint32_t count;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ /* purge in case of any jobs from previous tests */
+ torture_assert(tctx,
+ test_printer_purge(tctx, b, &t->handle),
+ "failed to purge printer");
+
+ /* enum before jobs, valid level */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+ /* enum before jobs, invalid level - expect failure */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 100,
+ WERR_INVALID_LEVEL,
+ &count, &info),
+ "EnumJobs with invalid level");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle,
+ "TorturePrintJob",
+ &job_ids[i]);
+ torture_assert(tctx, ret, "failed to add print job");
+ }
+
+ /* enum after jobs, valid level */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+ /* enum after jobs, invalid level - expect failure */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 100,
+ WERR_INVALID_LEVEL,
+ &count, &info),
+ "EnumJobs with invalid level");
+
+ for (i = 0; i < num_jobs; i++) {
+ test_SetJob(tctx, b, &t->handle, job_ids[i], NULL,
+ SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_printer_log_jobinfo(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_BranchOfficeJobDataContainer info;
+ int i;
+
+ struct spoolss_LogJobInfoForBranchOffice r;
+
+ torture_comment(tctx, "Testing LogJobInfoForBranchOffice\n");
+
+ info.cJobDataEntries = 0;
+ info.JobData = NULL;
+
+ r.in.hPrinter = &t->handle;
+ r.in.pBranchOfficeJobDataContainer = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "LogJobInfoForBranchOffice failed");
+
+ info.cJobDataEntries = 1;
+ info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries);
+
+ info.JobData[0].eEventType = kLogOfflineFileFull;
+ info.JobData[0].JobId = 42;
+ info.JobData[0].JobInfo.LogOfflineFileFull.pMachineName = talloc_strdup(tctx, "mthelena");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "LogJobInfoForBranchOffice failed");
+
+ info.cJobDataEntries = 42;
+ info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries);
+
+ for (i=0; i < info.cJobDataEntries; i++) {
+ info.JobData[i].eEventType = kLogOfflineFileFull;
+ info.JobData[i].JobId = i;
+ info.JobData[i].JobInfo.LogOfflineFileFull.pMachineName = talloc_asprintf(tctx, "torture_%d", i);
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "LogJobInfoForBranchOffice failed");
+
+ return true;
+}
+
+static bool test_printer_os_versions(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ union spoolss_PrinterInfo info;
+ DATA_BLOB blob;
+ uint8_t *data;
+ uint32_t length;
+ struct spoolss_OSVersion osversion;
+ uint8_t os_major, os_minor;
+ uint16_t os_build;
+ struct policy_handle server_handle;
+
+ torture_comment(tctx, "Testing OSVersion vs. PRINTER_INFO_STRESS\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, p, &server_handle),
+ "failed to open printserver");
+
+ torture_assert(tctx,
+ test_GetPrinterData_checktype(tctx, b, &server_handle, "OSVersion",
+ NULL, NULL, &data, &length),
+ "failed to fetch OSVersion printer data");
+
+ test_ClosePrinter(tctx, b, &server_handle);
+
+ blob = data_blob_const(data, length);
+
+ torture_assert_ndr_success(tctx,
+ ndr_pull_struct_blob(&blob, tctx, &osversion,
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_OSVersion),
+ "failed to pull OSVersion");
+
+ os_major = CVAL(&info.info0.version, 0);
+ os_minor = CVAL(&info.info0.version, 1);
+ os_build = SVAL(&info.info0.version, 2);
+
+ torture_assert_int_equal(tctx, os_major, osversion.major, "major");
+ torture_assert_int_equal(tctx, os_minor, osversion.minor, "minor");
+ torture_assert_int_equal(tctx, os_build, osversion.build, "build");
+
+ return true;
+}
+
+
+void torture_tcase_printer(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter_wrap);
+ torture_tcase_add_simple_test(tcase, "csetprinter", test_csetprinter);
+ torture_tcase_add_simple_test(tcase, "print_test", test_print_test);
+ torture_tcase_add_simple_test(tcase, "print_test_extended", test_print_test_extended);
+ torture_tcase_add_simple_test(tcase, "print_test_smbd", test_print_test_smbd);
+ torture_tcase_add_simple_test(tcase, "print_test_properties", test_print_test_properties);
+ torture_tcase_add_simple_test(tcase, "print_test_purge", test_print_test_purge);
+ torture_tcase_add_simple_test(tcase, "printer_info", test_printer_info);
+ torture_tcase_add_simple_test(tcase, "sd", test_printer_sd);
+ torture_tcase_add_simple_test(tcase, "dm", test_printer_dm);
+ torture_tcase_add_simple_test(tcase, "printer_info_winreg", test_printer_info_winreg);
+ torture_tcase_add_simple_test(tcase, "change_id", test_printer_change_id);
+ torture_tcase_add_simple_test(tcase, "keys", test_printer_keys);
+ torture_tcase_add_simple_test(tcase, "printerdata_consistency", test_printer_data_consistency);
+ torture_tcase_add_simple_test(tcase, "printerdata_keys", test_printer_data_keys);
+ torture_tcase_add_simple_test(tcase, "printerdata_values", test_printer_data_values);
+ torture_tcase_add_simple_test(tcase, "printerdata_set", test_printer_data_set);
+ torture_tcase_add_simple_test(tcase, "printerdata_winreg", test_printer_data_winreg);
+ torture_tcase_add_simple_test(tcase, "printerdata_dsspooler", test_printer_data_dsspooler);
+ torture_tcase_add_simple_test(tcase, "driver_info_winreg", test_driver_info_winreg);
+ torture_tcase_add_simple_test(tcase, "printer_rename", test_printer_rename);
+ torture_tcase_add_simple_test(tcase, "printer_ic", test_printer_ic);
+ torture_tcase_add_simple_test(tcase, "bidi", test_printer_bidi);
+ torture_tcase_add_simple_test(tcase, "publish_toggle",
+ test_printer_publish_toggle);
+ torture_tcase_add_simple_test(tcase, "print_job_enum", test_print_job_enum);
+ torture_tcase_add_simple_test(tcase, "log_jobinfo", test_printer_log_jobinfo);
+ torture_tcase_add_simple_test(tcase, "os_versions", test_printer_os_versions);
+}
+
+struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "printer");
+ struct torture_tcase *tcase;
+
+ tcase = torture_suite_add_tcase(suite, "addprinter");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printer_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterex");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerex_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterwkn");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerwkn_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterexwkn");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerexwkn_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+#if 0
+ /* test is not correct */
+ tcase = torture_suite_add_tcase(suite, "addprinterdm");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerdm_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+#endif
+ return suite;
+}
+
+struct torture_suite *torture_rpc_spoolss(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_setup,
+ torture_rpc_spoolss_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter_badnamelist", test_OpenPrinter_badname_list);
+ torture_tcase_add_simple_test(tcase, "printer_data_list", test_GetPrinterData_list);
+ torture_tcase_add_simple_test(tcase, "enum_forms", test_PrintServer_EnumForms);
+ torture_tcase_add_simple_test(tcase, "forms", test_PrintServer_Forms);
+ torture_tcase_add_simple_test(tcase, "forms_winreg", test_PrintServer_Forms_Winreg);
+ torture_tcase_add_simple_test(tcase, "enum_ports", test_EnumPorts);
+ torture_tcase_add_simple_test(tcase, "add_port", test_AddPort);
+ torture_tcase_add_simple_test(tcase, "get_printer_driver_directory", test_GetPrinterDriverDirectory);
+ torture_tcase_add_simple_test(tcase, "get_print_processor_directory", test_GetPrintProcessorDirectory);
+ torture_tcase_add_simple_test(tcase, "enum_printer_drivers", test_EnumPrinterDrivers);
+ torture_tcase_add_simple_test(tcase, "enum_monitors", test_EnumMonitors);
+ torture_tcase_add_simple_test(tcase, "enum_print_processors", test_EnumPrintProcessors);
+ torture_tcase_add_simple_test(tcase, "print_processors_winreg", test_print_processors_winreg);
+ torture_tcase_add_simple_test(tcase, "add_processor", test_add_print_processor);
+ torture_tcase_add_simple_test(tcase, "enum_printprocdata", test_EnumPrintProcessorDataTypes);
+ torture_tcase_add_simple_test(tcase, "enum_printers", test_EnumPrinters);
+ torture_tcase_add_simple_test(tcase, "enum_ports_old", test_EnumPorts_old);
+ torture_tcase_add_simple_test(tcase, "enum_printers_old", test_EnumPrinters_old);
+ torture_tcase_add_simple_test(tcase, "enum_printers_servername", test_EnumPrinters_servername);
+ torture_tcase_add_simple_test(tcase, "enum_printer_drivers_old", test_EnumPrinterDrivers_old);
+ torture_tcase_add_simple_test(tcase, "architecture_buffer", test_architecture_buffer);
+ torture_tcase_add_simple_test(tcase, "get_core_printer_drivers", test_get_core_printer_drivers);
+ torture_tcase_add_simple_test(tcase, "get_printer_driver_package_path", test_get_printer_driver_package_path);
+ torture_tcase_add_simple_test(tcase, "get_printer", test_get_printer_printserverhandle);
+ torture_tcase_add_simple_test(tcase, "set_printer", test_set_printer_printserverhandle);
+ torture_tcase_add_simple_test(tcase, "printserver_info_winreg", test_printserver_info_winreg);
+ torture_tcase_add_simple_test(tcase, "addpermachineconnection", test_addpermachineconnection);
+
+ torture_suite_add_suite(suite, torture_rpc_spoolss_printer(suite));
+
+ return suite;
+}
+
+static bool test_GetPrinterDriverDirectory_getdir(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *environment,
+ const char **dir_p)
+{
+ struct spoolss_GetPrinterDriverDirectory r;
+ uint32_t needed;
+
+ r.in.server = server;
+ r.in.environment = environment;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+ "failed to query driver directory");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+ "failed to query driver directory");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to query driver directory");
+
+ if (dir_p) {
+ *dir_p = r.out.info->info1.directory_name;
+ }
+
+ return true;
+}
+
+static const char *get_driver_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+ if (info_ctr == NULL) {
+ return NULL;
+ }
+
+ switch (info_ctr->level) {
+ case 1:
+ return info_ctr->info.info1->driver_name;
+ case 2:
+ return info_ctr->info.info2->driver_name;
+ case 3:
+ return info_ctr->info.info3->driver_name;
+ case 4:
+ return info_ctr->info.info4->driver_name;
+ case 6:
+ return info_ctr->info.info6->driver_name;
+ case 8:
+ return info_ctr->info.info8->driver_name;
+ default:
+ return NULL;
+ }
+}
+
+static const char *get_environment_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+ if (info_ctr == NULL) {
+ return NULL;
+ }
+
+ switch (info_ctr->level) {
+ case 2:
+ return info_ctr->info.info2->architecture;
+ case 3:
+ return info_ctr->info.info3->architecture;
+ case 4:
+ return info_ctr->info.info4->architecture;
+ case 6:
+ return info_ctr->info.info6->architecture;
+ case 8:
+ return info_ctr->info.info8->architecture;
+ default:
+ return NULL;
+ }
+}
+
+
+static bool test_AddPrinterDriver_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ struct spoolss_AddDriverInfoCtr *info_ctr,
+ WERROR expected_result)
+{
+ struct spoolss_AddPrinterDriver r;
+ const char *drivername = get_driver_from_info(info_ctr);
+ const char *environment = get_environment_from_info(info_ctr);
+
+ r.in.servername = servername;
+ r.in.info_ctr = info_ctr;
+
+ torture_comment(tctx, "Testing AddPrinterDriver(%s) level: %d, environment: '%s'\n",
+ drivername, info_ctr->level, environment);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrinterDriver_r(b, tctx, &r),
+ "spoolss_AddPrinterDriver failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "spoolss_AddPrinterDriver failed with unexpected result");
+
+ return true;
+
+}
+
+static bool test_AddPrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ struct spoolss_AddDriverInfoCtr *info_ctr,
+ uint32_t flags,
+ WERROR expected_result)
+{
+ struct spoolss_AddPrinterDriverEx r;
+ const char *drivername = get_driver_from_info(info_ctr);
+ const char *environment = get_environment_from_info(info_ctr);
+
+ r.in.servername = servername;
+ r.in.info_ctr = info_ctr;
+ r.in.flags = flags;
+
+ torture_comment(tctx, "Testing AddPrinterDriverEx(%s) level: %d, environment: '%s'\n",
+ drivername, info_ctr->level, environment);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrinterDriverEx_r(b, tctx, &r),
+ "AddPrinterDriverEx failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "AddPrinterDriverEx failed with unexpected result");
+
+ return true;
+}
+
+#define ASSERT_DRIVER_PATH(tctx, path, driver_dir, cmt) \
+ if (path && strlen(path)) {\
+ torture_assert_strn_equal(tctx, path, driver_dir, strlen(driver_dir), cmt); \
+ }
+
+static bool test_AddPrinterDriver_args_level_1(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo1 info1;
+
+ ZERO_STRUCT(info1);
+
+ info_ctr.level = 1;
+ info_ctr.info.info1 = &info1;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriverEx level 1");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 1");
+ }
+
+ info1.driver_name = r->driver_name;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriverEx level 1");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 1");
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo2 info2;
+ union spoolss_DriverInfo info;
+
+ ZERO_STRUCT(info2);
+
+ info_ctr.level = 2;
+ info_ctr.info.info2 = &info2;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.driver_name = r->driver_name;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.version = r->version;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.architecture = r->architecture;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.driver_path = r->driver_path;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.data_file = r->data_file;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.config_file = r->config_file;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, 0, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx");
+ }
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ ASSERT_DRIVER_PATH(tctx, info.info2.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info2.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info2.config_file, remote_driver_dir, "unexpected path");
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo3 info3;
+ union spoolss_DriverInfo info;
+
+ info3.driver_name = r->driver_name;
+ info3.version = r->version;
+ info3.architecture = r->architecture;
+ info3.driver_path = r->driver_path;
+ info3.data_file = r->data_file;
+ info3.config_file = r->config_file;
+ info3.help_file = r->help_file;
+ info3.monitor_name = r->monitor_name;
+ info3.default_datatype = r->default_datatype;
+ info3._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info3.dependent_files = r->dependent_files;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 3");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 3");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info3.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info3.dependent_files && info.info3.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info3.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo4 info4;
+ union spoolss_DriverInfo info;
+
+ info4.version = r->version;
+ info4.driver_name = r->driver_name;
+ info4.architecture = r->architecture;
+ info4.driver_path = r->driver_path;
+ info4.data_file = r->data_file;
+ info4.config_file = r->config_file;
+ info4.help_file = r->help_file;
+ info4.monitor_name = r->monitor_name;
+ info4.default_datatype = r->default_datatype;
+ info4._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info4.dependent_files = r->dependent_files;
+ info4._ndr_size_previous_names = r->_ndr_size_previous_names;
+ info4.previous_names = r->previous_names;
+
+ info_ctr.level = 4;
+ info_ctr.info.info4 = &info4;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 4");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 4");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info4.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info4.dependent_files && info.info4.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info4.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo6 info6;
+ union spoolss_DriverInfo info;
+
+ info6.version = r->version;
+ info6.driver_name = r->driver_name;
+ info6.architecture = r->architecture;
+ info6.driver_path = r->driver_path;
+ info6.data_file = r->data_file;
+ info6.config_file = r->config_file;
+ info6.help_file = r->help_file;
+ info6.monitor_name = r->monitor_name;
+ info6.default_datatype = r->default_datatype;
+ info6._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info6.dependent_files = r->dependent_files;
+ info6._ndr_size_previous_names = r->_ndr_size_previous_names;
+ info6.previous_names = r->previous_names;
+ info6.driver_date = r->driver_date;
+ info6.driver_version = r->driver_version;
+ info6.manufacturer_name = r->manufacturer_name;
+ info6.manufacturer_url = r->manufacturer_url;
+ info6.hardware_id = r->hardware_id;
+ info6.provider = r->provider;
+
+ info_ctr.level = 6;
+ info_ctr.info.info6 = &info6;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 6");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 6");
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex) {
+ return true;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info6.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info6.dependent_files && info.info6.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info6.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ torture_assert_nttime_equal(tctx, info.info6.driver_date, info6.driver_date, "driverdate mismatch");
+ torture_assert_u64_equal(tctx, info.info6.driver_version, info6.driver_version, "driverversion mismatch");
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ union spoolss_DriverInfo info;
+
+ info_ctr.level = 8;
+ info_ctr.info.info8 = r;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 8");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 8");
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex) {
+ return true;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info8.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info8.dependent_files && info.info8.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info8.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ torture_assert_nttime_equal(tctx, info.info8.driver_date, r->driver_date, "driverdate mismatch");
+ torture_assert_u64_equal(tctx, info.info8.driver_version, r->driver_version, "driverversion mismatch");
+
+ return true;
+}
+
+#undef ASSERT_DRIVER_PATH
+
+static bool test_DeletePrinterDriver_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ WERROR expected_result)
+{
+ struct spoolss_DeletePrinterDriver r;
+
+ r.in.server = server;
+ r.in.architecture = environment;
+ r.in.driver = driver;
+
+ torture_comment(tctx, "Testing DeletePrinterDriver(%s)\n", driver);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDriver_r(b, tctx, &r),
+ "DeletePrinterDriver failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeletePrinterDriver failed with unexpected result");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version,
+ WERROR expected_result)
+{
+ struct spoolss_DeletePrinterDriverEx r;
+
+ r.in.server = server;
+ r.in.architecture = environment;
+ r.in.driver = driver;
+ r.in.delete_flags = delete_flags;
+ r.in.version = version;
+
+ torture_comment(tctx, "Testing DeletePrinterDriverEx(%s)\n", driver);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDriverEx_r(b, tctx, &r),
+ "DeletePrinterDriverEx failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeletePrinterDriverEx failed with unexpected result");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriver(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *driver,
+ const char *environment)
+{
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, "FOOBAR", WERR_INVALID_ENVIRONMENT),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK),
+ "failed to delete driver");
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
+ torture_fail(tctx, "deleted driver still enumerated");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_UNKNOWN_PRINTER_DRIVER),
+ "2nd delete failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriverEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version)
+{
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, "FOOBAR", delete_flags, version, WERR_INVALID_ENVIRONMENT),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_OK),
+ "failed to delete driver");
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
+ torture_fail(tctx, "deleted driver still enumerated");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_UNKNOWN_PRINTER_DRIVER),
+ "2nd delete failed");
+
+ return true;
+}
+
+static bool test_PrinterDriver_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ uint32_t level,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t add_flags,
+ uint32_t delete_flags,
+ uint32_t delete_version,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ bool ret = true;
+
+ switch (level) {
+ case 1:
+ ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 2:
+ ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 3:
+ ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 4:
+ ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 6:
+ ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 8:
+ ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ default:
+ return false;
+ }
+
+ if (ret == false) {
+ return ret;
+ }
+
+ if (level == 1) {
+ return ret;
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex && (level == 6 || level == 8)) {
+ return ret;
+ }
+
+ {
+ struct dcerpc_pipe *p2;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetDriverInfo_winreg(tctx, b, NULL, NULL, r->driver_name, r->architecture, r->version, b2, &hive_handle, server_name);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+ }
+
+ if (ex) {
+ return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version);
+ } else {
+ return test_DeletePrinterDriver(tctx, b, server_name, r->driver_name, r->architecture);
+ }
+}
+
+static bool fillup_printserver_info(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d)
+{
+ struct policy_handle server_handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, p, &server_handle),
+ "failed to open printserver");
+ torture_assert(tctx,
+ test_get_environment(tctx, b, &server_handle, &d->remote.environment),
+ "failed to get environment");
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &server_handle),
+ "failed to close printserver");
+
+ torture_assert(tctx,
+ test_GetPrinterDriverDirectory_getdir(tctx, b, server_name_slash,
+ d->local.environment ? d->local.environment : d->remote.environment,
+ &d->remote.driver_directory),
+ "failed to get driver directory");
+
+ return true;
+}
+
+static const char *driver_directory_dir(const char *driver_directory)
+{
+ char *p;
+
+ p = strrchr(driver_directory, '\\');
+ if (p) {
+ return p+1;
+ }
+
+ return NULL;
+}
+
+static const char *driver_directory_share(struct torture_context *tctx,
+ const char *driver_directory)
+{
+ const char *p;
+ char *tok;
+
+ if (driver_directory[0] == '\\' && driver_directory[1] == '\\') {
+ driver_directory += 2;
+ }
+
+ p = talloc_strdup(tctx, driver_directory);
+
+ torture_assert(tctx,
+ next_token_talloc(tctx, &p, &tok, "\\"),
+ "cannot explode uri");
+ torture_assert(tctx,
+ next_token_talloc(tctx, &p, &tok, "\\"),
+ "cannot explode uri");
+
+ return tok;
+}
+
+#define CREATE_PRINTER_DRIVER_PATH(_d, _file) \
+ talloc_asprintf((_d), "%s\\%s\\%s", (_d)->remote.driver_directory, (_d)->remote.driver_upload_directory, (_file))
+
+
+static bool create_printer_driver_directory(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d)
+{
+ char *driver_dir;
+
+ if (d->remote.driver_upload_directory == NULL) {
+ return true;
+ }
+
+ driver_dir = talloc_asprintf(tctx,
+ "%s\\%s",
+ driver_directory_dir(d->remote.driver_directory),
+ d->remote.driver_upload_directory);
+ torture_assert_not_null(tctx, driver_dir, "ENOMEM");
+
+ torture_comment(tctx,
+ "Create remote driver directory: %s\n",
+ driver_dir);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_mkdir(cli->tree,
+ driver_dir),
+ "Failed to create driver directory");
+
+ return true;
+}
+
+static bool upload_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ FILE *f;
+ int fnum;
+ uint8_t *buf;
+ int maxwrite = 64512;
+ off_t nread = 0;
+ size_t start = 0;
+ const char *remote_dir = driver_directory_dir(d->remote.driver_directory);
+ const char *remote_name;
+ const char *local_name;
+ const char *p;
+
+ if (!file_name || strlen(file_name) == 0) {
+ return true;
+ }
+
+ p = strrchr(file_name, '\\');
+ if (p == NULL) {
+ p = file_name;
+ } else {
+ p++;
+ }
+
+ local_name = talloc_asprintf(tctx, "%s/%s", d->local.driver_directory, p);
+ torture_assert_not_null(tctx, local_name, "ENOMEM");
+ if (d->remote.driver_upload_directory != NULL) {
+ remote_name = talloc_asprintf(tctx,
+ "%s\\%s\\%s",
+ remote_dir,
+ d->remote.driver_upload_directory,
+ p);
+ } else {
+ remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, p);
+ }
+ torture_assert_not_null(tctx, remote_name, "ENOMEM");
+
+ torture_comment(tctx, "Uploading %s to %s\n", local_name, remote_name);
+
+ fnum = smbcli_open(cli->tree, remote_name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ torture_fail(tctx, talloc_asprintf(tctx, "failed to open remote file: %s\n", remote_name));
+ }
+
+ f = fopen(local_name, "r");
+ if (f == NULL) {
+ torture_fail(tctx, talloc_asprintf(tctx, "failed to open local file: %s\n", local_name));
+ }
+
+ buf = talloc_array(tctx, uint8_t, maxwrite);
+ if (!buf) {
+ fclose(f);
+ return false;
+ }
+
+ while (!feof(f)) {
+ int n = maxwrite;
+ int ret;
+
+ if ((n = fread(buf, 1, n, f)) < 1) {
+ if((n == 0) && feof(f))
+ break; /* Empty local file. */
+
+ torture_warning(tctx,
+ "failed to read file: %s\n", strerror(errno));
+ break;
+ }
+
+ ret = smbcli_write(cli->tree, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ torture_warning(tctx,
+ "failed to write file: %s\n", smbcli_errstr(cli->tree));
+ break;
+ }
+
+ nread += n;
+ }
+
+ fclose(f);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_close(cli->tree, fnum),
+ "failed to close file");
+
+ return true;
+}
+
+static bool connect_printer_driver_share(struct torture_context *tctx,
+ const char *server_name,
+ const char *share_name,
+ struct smbcli_state **cli)
+{
+ struct smbcli_options smb_options;
+ struct smbcli_session_options smb_session_options;
+
+ torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
+ share_name, server_name);
+
+ lpcfg_smbcli_options(tctx->lp_ctx, &smb_options);
+ lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_full_connection(tctx, cli, server_name,
+ lpcfg_smb_ports(tctx->lp_ctx),
+ share_name, NULL,
+ lpcfg_socket_options(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(tctx->lp_ctx),
+ tctx->ev,
+ &smb_options,
+ &smb_session_options,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx)),
+ "failed to open driver share");
+
+ return true;
+}
+
+static bool upload_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
+ server_name, share_name);
+
+ torture_assert(tctx,
+ create_printer_driver_directory(tctx, cli, d),
+ "failed to create driver directory");
+
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+ "failed to upload driver_path");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.data_file),
+ "failed to upload data_file");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.config_file),
+ "failed to upload config_file");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.help_file),
+ "failed to upload help_file");
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+ "failed to upload dependent_files");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+}
+
+static bool check_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ const char *remote_arch_dir = driver_directory_dir(d->remote.driver_directory);
+ const char *remote_name = talloc_asprintf(tctx, "%s\\%d\\%s",
+ remote_arch_dir,
+ d->info8.version,
+ file_name);
+ int fnum;
+
+ torture_assert(tctx, (file_name && strlen(file_name) != 0), "invalid filename");
+
+ torture_comment(tctx, "checking for driver file at %s\n", remote_name);
+
+ fnum = smbcli_open(cli->tree, remote_name, O_RDONLY, DENY_NONE);
+ if (fnum == -1) {
+ return false;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_close(cli->tree, fnum),
+ "failed to close driver file");
+
+ return true;
+}
+
+static bool check_printer_driver_files(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d,
+ bool expect_exist)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "checking %sexistent driver files at \\\\%s\\%s\n",
+ (expect_exist ? "": "non-"),
+ server_name, share_name);
+
+ if (d->info8.driver_path && d->info8.driver_path[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.driver_path) == expect_exist,
+ "failed driver_path check");
+ }
+ if (d->info8.data_file && d->info8.data_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.data_file) == expect_exist,
+ "failed data_file check");
+ }
+ if (d->info8.config_file && d->info8.config_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.config_file) == expect_exist,
+ "failed config_file check");
+ }
+ if (d->info8.help_file && d->info8.help_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.help_file) == expect_exist,
+ "failed help_file check");
+ }
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]) == expect_exist,
+ "failed dependent_files check");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+}
+
+static bool remove_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ const char *remote_name;
+ const char *remote_dir = driver_directory_dir(d->remote.driver_directory);
+
+ if (!file_name || strlen(file_name) == 0) {
+ return true;
+ }
+
+ remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
+
+ torture_comment(tctx, "Removing %s\n", remote_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_unlink(cli->tree, remote_name),
+ "failed to unlink");
+
+ return true;
+}
+
+static bool remove_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "Removing printer driver files from \\\\%s\\%s\n",
+ server_name, share_name);
+
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+ "failed to remove driver_path");
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.data_file),
+ "failed to remove data_file");
+ if (!strequal(d->info8.config_file, d->info8.driver_path)) {
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.config_file),
+ "failed to remove config_file");
+ }
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.help_file),
+ "failed to remove help_file");
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ if (strequal(d->info8.dependent_files->string[i], d->info8.driver_path) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.data_file) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.config_file) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.help_file)) {
+ continue;
+ }
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+ "failed to remove dependent_files");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+
+}
+
+static bool test_add_driver_arg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d)
+{
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ uint32_t levels[] = { 1, 2, 3, 4, 6, 8 };
+ int i;
+ struct spoolss_AddDriverInfo8 info8;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = 0;
+
+ ZERO_STRUCT(info8);
+
+ torture_comment(tctx, "Testing PrinterDriver%s '%s' for environment '%s'\n",
+ d->ex ? "Ex" : "", d->info8.driver_name, d->local.environment);
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ info8 = d->info8;
+ if (d->info8.dependent_files) {
+ info8.dependent_files = talloc_zero(tctx, struct spoolss_StringArray);
+ if (d->info8.dependent_files->string) {
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ }
+ info8.dependent_files->string = talloc_zero_array(info8.dependent_files, const char *, i+1);
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ info8.dependent_files->string[i] = talloc_strdup(info8.dependent_files->string, d->info8.dependent_files->string[i]);
+ }
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ switch (levels[i]) {
+ case 2:
+ case 4:
+ torture_comment(tctx, "skipping level %d against samba\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ switch (levels[i]) {
+ case 8:
+ torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+
+ torture_comment(tctx,
+ "Testing PrinterDriver%s '%s' add & delete level %d\n",
+ d->ex ? "Ex" : "", info8.driver_name, levels[i]);
+
+ ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
+ }
+
+ info8.driver_path = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.driver_path);
+ info8.data_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.data_file);
+ if (d->info8.config_file) {
+ info8.config_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
+ }
+ if (d->info8.help_file) {
+ info8.help_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.help_file);
+ }
+ if (d->info8.dependent_files && d->info8.dependent_files->string) {
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ info8.dependent_files->string[i] = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.dependent_files->string[i]);
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ switch (levels[i]) {
+ case 2:
+ case 4:
+ continue;
+ default:
+ break;
+ }
+ }
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ switch (levels[i]) {
+ case 8:
+ torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+
+ torture_comment(tctx,
+ "Testing PrinterDriver%s '%s' add & delete level %d (full unc paths)\n",
+ d->ex ? "Ex" : "", info8.driver_name, levels[i]);
+
+ ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
+ }
+
+ torture_assert(tctx,
+ remove_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to remove printer driver");
+
+ torture_comment(tctx, "\n");
+
+ return ret;
+}
+
+static bool test_add_driver_ex_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_EX;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = true;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_ex_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_EX;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = true;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_ADD;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_ADD;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_adobe(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping adobe test which only works against samba3");
+ }
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, "Windows 4.0");
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_9X;
+ d->info8.driver_name = TORTURE_DRIVER_ADOBE;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV");
+ d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD");
+ d->info8.config_file = talloc_strdup(d, "ADOBEPS4.DRV");
+#if 0
+ d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP");
+ d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL");
+#endif
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_adobe_cupsaddsmb(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping cupsaddsmb test which only works against samba3");
+ }
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, "Windows 4.0");
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_9X;
+ d->info8.driver_name = TORTURE_DRIVER_ADOBE_CUPSADDSMB;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV");
+ d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD");
+ d->info8.config_file = NULL;
+ d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP");
+ d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL");
+ d->info8.default_datatype = talloc_strdup(d, "RAW");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ a->string = talloc_zero_array(a, const char *, 7);
+ a->string[0] = talloc_strdup(a->string, "ADOBEPS4.DRV");
+ a->string[1] = talloc_strdup(a->string, "DEFPRTR2.PPD");
+ a->string[2] = talloc_strdup(a->string, "ADOBEPS4.HLP");
+ a->string[3] = talloc_strdup(a->string, "PSMON.DLL");
+ a->string[4] = talloc_strdup(a->string, "ADFONTS.MFM");
+ a->string[5] = talloc_strdup(a->string, "ICONLIB.DLL");
+
+ d->info8.dependent_files = a;
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_timestamps(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct timeval t = timeval_current();
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_TIMESTAMPS;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.driver_date = timeval_to_nttime(&t);
+ d->ex = true;
+
+ torture_assert(tctx,
+ test_add_driver_arg(tctx, p, d),
+ "");
+
+ unix_to_nt_time(&d->info8.driver_date, 1);
+
+ torture_assert(tctx,
+ test_add_driver_arg(tctx, p, d),
+ "");
+
+ return true;
+}
+
+static bool test_multiple_drivers(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ int i;
+ struct spoolss_AddDriverInfo8 info8;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = 0;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.architecture = d->local.environment;
+ d->ex = true;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ info8 = d->info8;
+
+ for (i=0; i < 3; i++) {
+ info8.driver_name = talloc_asprintf(d, "torture_test_driver_%d", i);
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &info8, add_flags, true, NULL),
+ "failed to add driver");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_0", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_1", NULL),
+ "torture_test_driver_1 no longer on the server");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+ "torture_test_driver_2 no longer on the server");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_1", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+ "torture_test_driver_2 no longer on the server");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_2", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ remove_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to remove printer driver");
+
+ return true;
+}
+
+static bool test_driver_copy_from_directory(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *architecture)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+ uint32_t add_flags = APD_COPY_NEW_FILES|APD_COPY_FROM_DIRECTORY|APD_RETURN_BLOCKING_STATUS_CODE;
+ uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx,
+ "\\\\%s",
+ dcerpc_server_name(p));
+ struct GUID guid = GUID_random();
+ bool ok = false;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+ torture_assert_not_null(tctx, d, "ENOMEM");
+
+ d->local.environment = talloc_strdup(d, architecture);
+ torture_assert_not_null_goto(tctx, d->local.environment, ok, done, "ENOMEM");
+
+ if (strequal(architecture, SPOOLSS_ARCHITECTURE_x64)) {
+ d->local.driver_directory =
+ talloc_strdup(d, "/usr/share/cups/drivers/x64");
+ } else {
+ d->local.driver_directory =
+ talloc_strdup(d, "/usr/share/cups/drivers/i386");
+ }
+ torture_assert_not_null_goto(tctx, d->local.driver_directory, ok, done, "ENOMEM");
+
+ d->remote.driver_upload_directory = GUID_string2(d, &guid);
+ torture_assert_not_null_goto(tctx, d->remote.driver_upload_directory, ok, done, "ENOMEM");
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ d->ex = true;
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_COPY_DIR;
+ d->info8.architecture = d->local.environment;
+
+ d->info8.driver_path = CREATE_PRINTER_DRIVER_PATH(d, "pscript5.dll");
+ torture_assert_not_null_goto(tctx, d->info8.driver_path, ok, done, "ENOMEM");
+ d->info8.data_file = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ppd");
+ torture_assert_not_null_goto(tctx, d->info8.data_file, ok, done, "ENOMEM");
+ d->info8.config_file = CREATE_PRINTER_DRIVER_PATH(d, "cupsui6.dll");
+ torture_assert_not_null_goto(tctx, d->info8.config_file, ok, done, "ENOMEM");
+ d->info8.help_file = CREATE_PRINTER_DRIVER_PATH(d, "pscript.hlp");
+ torture_assert_not_null_goto(tctx, d->info8.help_file, ok, done, "ENOMEM");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ torture_assert_not_null_goto(tctx, a, ok, done, "ENOMEM");
+ a->string = talloc_zero_array(a, const char *, 3);
+ torture_assert_not_null_goto(tctx, a->string, ok, done, "ENOMEM");
+ a->string[0] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.inf");
+ torture_assert_not_null_goto(tctx, a->string[0], ok, done, "ENOMEM");
+ a->string[1] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ini");
+ torture_assert_not_null_goto(tctx, a->string[1], ok, done, "ENOMEM");
+
+ d->info8.dependent_files = a;
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx,
+ "Skipping Printer Driver test as no local drivers "
+ "are available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx,
+ b,
+ server_name_slash,
+ &d->info8,
+ add_flags,
+ true,
+ NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx,
+ b,
+ server_name_slash,
+ d->info8.driver_name,
+ d->local.environment,
+ delete_flags,
+ d->info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx,
+ dcerpc_server_name(p),
+ d,
+ false),
+ "printer driver file check failed");
+
+ ok = true;
+done:
+ talloc_free(d);
+ return ok;
+}
+
+static bool test_driver_copy_from_directory_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_x64);
+}
+
+static bool test_driver_copy_from_directory_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_NT_X86);
+}
+
+static bool test_del_driver_all_files(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->ex = true;
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_DELETER;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.help_file = talloc_strdup(d, "pscript.hlp");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ a->string = talloc_zero_array(a, const char *, 3);
+ a->string[0] = talloc_strdup(a->string, "cups6.inf");
+ a->string[1] = talloc_strdup(a->string, "cups6.ini");
+
+ d->info8.dependent_files = a;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash,
+ d->info8.driver_name,
+ d->info8.architecture,
+ delete_flags,
+ d->info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d, false),
+ "printer driver file check failed");
+
+ talloc_free(d);
+ return true;
+}
+
+static bool test_del_driver_unused_files(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d1;
+ struct torture_driver_context *d2;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ d1 = talloc_zero(tctx, struct torture_driver_context);
+ d1->ex = true;
+
+ d1->local.environment = talloc_strdup(d1, SPOOLSS_ARCHITECTURE_x64);
+ d1->local.driver_directory = talloc_strdup(d1, "/usr/share/cups/drivers/x64");
+
+ d1->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d1->info8.driver_name = TORTURE_DRIVER_DELETER;
+ d1->info8.architecture = NULL;
+ d1->info8.driver_path = talloc_strdup(d1, "pscript5.dll");
+ d1->info8.data_file = talloc_strdup(d1, "cups6.ppd");
+ d1->info8.config_file = talloc_strdup(d1, "cupsui6.dll");
+ d1->info8.help_file = talloc_strdup(d1, "pscript.hlp");
+ d1->info8.architecture = d1->local.environment;
+
+ d2 = talloc_zero(tctx, struct torture_driver_context);
+ d2->ex = true;
+
+ d2->local.environment = talloc_strdup(d2, SPOOLSS_ARCHITECTURE_x64);
+ d2->local.driver_directory = talloc_strdup(d2, "/usr/share/cups/drivers/x64");
+
+ d2->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d2->info8.driver_name = TORTURE_DRIVER_DELETERIN;
+ d2->info8.architecture = NULL;
+ d2->info8.driver_path = talloc_strdup(d2, "pscript5.dll"); /* overlapping */
+ d2->info8.data_file = talloc_strdup(d2, "cupsps6.dll");
+ d2->info8.config_file = talloc_strdup(d2, "cups6.ini");
+ d2->info8.help_file = talloc_strdup(d2, "pscript.hlp"); /* overlapping */
+ d2->info8.architecture = d2->local.environment;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d1),
+ "failed to fillup printserver info");
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d2),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d1->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d1),
+ "failed to upload printer driver");
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d1->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d2),
+ "failed to upload printer driver");
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d2->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ /* some files are in use by a separate driver, should fail */
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d1->info8.driver_name,
+ d1->info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ d1->info8.version,
+ WERR_PRINTER_DRIVER_IN_USE),
+ "invalid delete driver response");
+
+ /* should only delete files not in use by other driver */
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d1->info8.driver_name,
+ d1->info8.architecture,
+ DPD_DELETE_UNUSED_FILES,
+ d1->info8.version,
+ WERR_OK),
+ "failed to delete driver (unused files)");
+
+ /* check non-overlapping were deleted */
+ d1->info8.driver_path = NULL;
+ d1->info8.help_file = NULL;
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d1, false),
+ "printer driver file check failed");
+ /* d2 files should be uneffected */
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d2, true),
+ "printer driver file check failed");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d2->info8.driver_name,
+ d2->info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ d2->info8.version,
+ WERR_OK),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d2, false),
+ "printer driver file check failed");
+
+ talloc_free(d1);
+ talloc_free(d2);
+ return true;
+}
+
+struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.driver");
+
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
+ "driver", &ndr_table_spoolss);
+ torture_rpc_tcase_add_test(tcase, "add_driver_64", test_add_driver_64);
+ torture_rpc_tcase_add_test(tcase, "add_driver_ex_64", test_add_driver_ex_64);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_32", test_add_driver_32);
+ torture_rpc_tcase_add_test(tcase, "add_driver_ex_32", test_add_driver_ex_32);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_adobe", test_add_driver_adobe);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_adobe_cupsaddsmb", test_add_driver_adobe_cupsaddsmb);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_timestamps", test_add_driver_timestamps);
+
+ torture_rpc_tcase_add_test(tcase, "multiple_drivers", test_multiple_drivers);
+
+ torture_rpc_tcase_add_test(tcase,
+ "test_driver_copy_from_directory_64",
+ test_driver_copy_from_directory_64);
+
+ torture_rpc_tcase_add_test(tcase,
+ "test_driver_copy_from_directory_32",
+ test_driver_copy_from_directory_32);
+
+ torture_rpc_tcase_add_test(tcase, "del_driver_all_files", test_del_driver_all_files);
+
+ torture_rpc_tcase_add_test(tcase, "del_driver_unused_files", test_del_driver_unused_files);
+
+ return suite;
+}