s4-smbtorture: mention architecture of added drivers in debug output.
[metze/samba/wip.git] / source4 / torture / rpc / spoolss.c
index a3419a782e704b44bf12992c4c33a21837180b72..89de7b0924f3ce66f3c5f79a526d0e40d79c3b0e 100644 (file)
 #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/rpc.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 "lib/cmdline/popt_common.h"
+#include "system/filesys.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_EX              "torture_driver_ex"
+
+#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 {
        /* print server handle */
@@ -61,6 +77,44 @@ struct test_spoolss_context {
        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 *environment;
+       } remote;
+       struct spoolss_AddDriverInfo8 info8;
+       bool ex;
+};
+
+struct torture_printer_context {
+       struct spoolss_SetPrinterInfo2 info2;
+       struct torture_driver_context driver;
+       bool ex;
+       bool wellknown;
+       bool added_driver;
+       bool have_driver;
+};
+
+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);
+
 #define COMPARE_STRING(tctx, c,r,e) \
        torture_assert_str_equal(tctx, c.e, r.e, "invalid value")
 
@@ -152,12 +206,60 @@ struct test_spoolss_context {
        }\
 } 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;
@@ -167,7 +269,7 @@ static bool test_OpenPrinter_server(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername);
 
-       status = dcerpc_spoolss_OpenPrinter(p, tctx, &op);
+       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");
 
@@ -175,7 +277,7 @@ static bool test_OpenPrinter_server(struct torture_context *tctx,
 }
 
 static bool test_EnumPorts(struct torture_context *tctx,
-                          struct dcerpc_pipe *p,
+                          struct dcerpc_binding_handle *b,
                           struct test_spoolss_context *ctx)
 {
        NTSTATUS status;
@@ -200,7 +302,7 @@ static bool test_EnumPorts(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing EnumPorts level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+               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 */
@@ -209,12 +311,11 @@ static bool test_EnumPorts(struct torture_context *tctx,
                torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
                        "EnumPorts unexpected return code");
 
-               blob = data_blob_talloc(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(ctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+               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");
@@ -256,9 +357,10 @@ static bool test_EnumPorts(struct torture_context *tctx,
 
 static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
                                            struct dcerpc_pipe *p,
-                                           struct test_spoolss_context *ctx)
+                                           const char *environment)
 {
        NTSTATUS status;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        struct spoolss_GetPrintProcessorDirectory r;
        struct {
                uint16_t level;
@@ -274,10 +376,10 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
                        .server = ""
                },{
                        .level  = 1,
-                       .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+                       .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
                },{
                        .level  = 1024,
-                       .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+                       .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
                }
        };
        int i;
@@ -288,7 +390,7 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
                DATA_BLOB blob;
 
                r.in.server             = levels[i].server;
-               r.in.environment        = SPOOLSS_ARCHITECTURE_NT_X86;
+               r.in.environment        = environment;
                r.in.level              = level;
                r.in.buffer             = NULL;
                r.in.offered            = 0;
@@ -296,18 +398,17 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing GetPrintProcessorDirectory level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_GetPrintProcessorDirectory(p, ctx, &r);
+               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(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_GetPrintProcessorDirectory(p, ctx, &r);
+               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");
@@ -321,9 +422,10 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
 
 static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
                                           struct dcerpc_pipe *p,
-                                          struct test_spoolss_context *ctx)
+                                          const char *environment)
 {
        NTSTATUS status;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        struct spoolss_GetPrinterDriverDirectory r;
        struct {
                uint16_t level;
@@ -339,10 +441,10 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
                        .server = ""
                },{
                        .level  = 1,
-                       .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+                       .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
                },{
                        .level  = 1024,
-                       .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+                       .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
                }
        };
        int i;
@@ -353,7 +455,7 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
                DATA_BLOB blob;
 
                r.in.server             = levels[i].server;
-               r.in.environment        = SPOOLSS_ARCHITECTURE_NT_X86;
+               r.in.environment        = environment;
                r.in.level              = level;
                r.in.buffer             = NULL;
                r.in.offered            = 0;
@@ -361,18 +463,17 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r);
+               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(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r);
+               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");
@@ -383,58 +484,135 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
        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)
+{
+       struct spoolss_EnumPrinterDrivers r;
+       uint32_t needed;
+       uint32_t count;
+       union spoolss_DriverInfo *info;
+
+       r.in.server             = server_name;
+       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 EnumPrinterDrivers(%s) level %u\n",
+               r.in.environment, r.in.level);
+
+       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, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+
+       return true;
+
+}
+
+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)
+{
+       uint32_t count;
+       union spoolss_DriverInfo *info;
+       int i;
+
+       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;
+                       break;
+               case 3:
+                       driver_name_ret = info[i].info3.driver_name;
+                       break;
+               case 4:
+                       driver_name_ret = info[i].info4.driver_name;
+                       break;
+               case 5:
+                       driver_name_ret = info[i].info5.driver_name;
+                       break;
+               case 6:
+                       driver_name_ret = info[i].info6.driver_name;
+                       break;
+               case 7:
+                       driver_name_ret = info[i].info7.driver_name;
+                       break;
+               case 8:
+                       driver_name_ret = info[i].info8.driver_name;
+                       break;
+               default:
+                       break;
+               }
+               if (strequal(driver_name, driver_name_ret)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 static bool test_EnumPrinterDrivers(struct torture_context *tctx,
                                    struct dcerpc_pipe *p,
                                    struct test_spoolss_context *ctx,
                                    const char *architecture)
 {
-       NTSTATUS status;
-       struct spoolss_EnumPrinterDrivers r;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        uint16_t levels[] = { 1, 2, 3, 4, 5, 6, 8 };
        int i, j;
 
+       /* 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));
+
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                int level = levels[i];
-               DATA_BLOB blob;
-               uint32_t needed;
                uint32_t count;
                union spoolss_DriverInfo *info;
 
-               /* FIXME: gd, come back and fix "" as server, and handle
-                * priority of returned error codes in torture test and samba 3
-                * server */
-
-               r.in.server             = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-               r.in.environment        = architecture;
-               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 EnumPrinterDrivers level %u (%s)\n", r.in.level, r.in.environment);
-
-               status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
-               torture_assert_ntstatus_ok(tctx, status,
-                                          "dcerpc_spoolss_EnumPrinterDrivers failed");
-               if (W_ERROR_IS_OK(r.out.result)) {
-                       /* TODO: do some more checks here */
-                       continue;
-               }
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       blob = data_blob_talloc(ctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
-
-                       status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
-                       torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinterDrivers failed");
-               }
-
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
-
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               torture_assert(tctx,
+                       test_EnumPrinterDrivers_args(tctx, b, server_name, architecture, level, &count, &info),
+                       "failed to enumerate drivers");
 
                ctx->driver_count[level]        = count;
                ctx->drivers[level]             = info;
@@ -533,7 +711,7 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
 }
 
 static bool test_EnumMonitors(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct test_spoolss_context *ctx)
 {
        NTSTATUS status;
@@ -558,7 +736,7 @@ static bool test_EnumMonitors(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing EnumMonitors level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+               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 */
@@ -567,12 +745,11 @@ static bool test_EnumMonitors(struct torture_context *tctx,
                torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
                        "EnumMonitors failed");
 
-               blob = data_blob_talloc(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(ctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+               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");
@@ -610,8 +787,9 @@ static bool test_EnumMonitors(struct torture_context *tctx,
 }
 
 static bool test_EnumPrintProcessors(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p,
-                                    struct test_spoolss_context *ctx)
+                                    struct dcerpc_binding_handle *b,
+                                    struct test_spoolss_context *ctx,
+                                    const char *environment)
 {
        NTSTATUS status;
        struct spoolss_EnumPrintProcessors r;
@@ -626,7 +804,7 @@ static bool test_EnumPrintProcessors(struct torture_context *tctx,
                union spoolss_PrintProcessorInfo *info;
 
                r.in.servername = "";
-               r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+               r.in.environment = environment;
                r.in.level = level;
                r.in.buffer = NULL;
                r.in.offered = 0;
@@ -636,7 +814,7 @@ static bool test_EnumPrintProcessors(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing EnumPrintProcessors level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+               status = dcerpc_spoolss_EnumPrintProcessors_r(b, ctx, &r);
                torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
                if (W_ERROR_IS_OK(r.out.result)) {
                        /* TODO: do some more checks here */
@@ -645,12 +823,11 @@ static bool test_EnumPrintProcessors(struct torture_context *tctx,
                torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
                        "EnumPrintProcessors unexpected return code");
 
-               blob = data_blob_talloc(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(ctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+               status = dcerpc_spoolss_EnumPrintProcessors_r(b, ctx, &r);
                torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed");
@@ -687,8 +864,7 @@ static bool test_EnumPrintProcessors(struct torture_context *tctx,
 }
 
 static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
-                                       struct dcerpc_pipe *p,
-                                       struct test_spoolss_context *ctx)
+                                       struct dcerpc_binding_handle *b)
 {
        NTSTATUS status;
        struct spoolss_EnumPrintProcDataTypes r;
@@ -713,7 +889,7 @@ static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing EnumPrintProcDataTypes level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_EnumPrintProcDataTypes(p, ctx, &r);
+               status = dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r);
                torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataType failed");
                if (W_ERROR_IS_OK(r.out.result)) {
                        /* TODO: do some more checks here */
@@ -722,12 +898,11 @@ static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
                torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
                        "EnumPrintProcDataTypes unexpected return code");
 
-               blob = data_blob_talloc(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumPrintProcDataTypes(p, ctx, &r);
+               status = dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r);
                torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataTypes failed");
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcDataTypes failed");
@@ -741,7 +916,7 @@ static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
 
 
 static bool test_EnumPrinters(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct test_spoolss_context *ctx)
 {
        struct spoolss_EnumPrinters r;
@@ -767,7 +942,7 @@ static bool test_EnumPrinters(struct torture_context *tctx,
 
                torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
 
-               status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+               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 */
@@ -776,12 +951,11 @@ static bool test_EnumPrinters(struct torture_context *tctx,
                torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
                        "EnumPrinters unexpected return code");
 
-               blob = data_blob_talloc(ctx, NULL, needed);
-               data_blob_clear(&blob);
+               blob = data_blob_talloc_zero(ctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+               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");
@@ -869,12 +1043,13 @@ static bool test_EnumPrinters(struct torture_context *tctx,
 }
 
 static bool test_GetPrinterDriver2(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
+                                  struct dcerpc_binding_handle *b,
                                   struct policy_handle *handle,
-                                  const char *driver_name);
+                                  const char *driver_name,
+                                  const char *environment);
 
 bool test_GetPrinter_level(struct torture_context *tctx,
-                          struct dcerpc_pipe *p,
+                          struct dcerpc_binding_handle *b,
                           struct policy_handle *handle,
                           uint32_t level,
                           union spoolss_PrinterInfo *info)
@@ -890,16 +1065,15 @@ bool test_GetPrinter_level(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level);
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter(p, tctx, &r),
+       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(tctx, NULL, needed);
-               data_blob_clear(&blob);
+               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(p, tctx, &r),
+               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
                        "GetPrinter failed");
        }
 
@@ -916,8 +1090,9 @@ bool test_GetPrinter_level(struct torture_context *tctx,
 
 
 static bool test_GetPrinter(struct torture_context *tctx,
-                           struct dcerpc_pipe *p,
-                           struct policy_handle *handle)
+                           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;
@@ -928,12 +1103,12 @@ static bool test_GetPrinter(struct torture_context *tctx,
 
                ZERO_STRUCT(info);
 
-               torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, levels[i], &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, p, handle, info.info2.drivername),
+                               test_GetPrinterDriver2(tctx, b, handle, info.info2.drivername, environment),
                                "failed to call test_GetPrinterDriver2");
                }
        }
@@ -942,7 +1117,7 @@ static bool test_GetPrinter(struct torture_context *tctx,
 }
 
 static bool test_SetPrinter(struct torture_context *tctx,
-                           struct dcerpc_pipe *p,
+                           struct dcerpc_binding_handle *b,
                            struct policy_handle *handle,
                            struct spoolss_SetPrinterInfoCtr *info_ctr,
                            struct spoolss_DevmodeContainer *devmode_ctr,
@@ -959,7 +1134,7 @@ static bool test_SetPrinter(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing SetPrinter level %d\n", r.in.info_ctr->level);
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter(p, tctx, &r),
+       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
                "failed to call SetPrinter");
        torture_assert_werr_ok(tctx, r.out.result,
                "failed to call SetPrinter");
@@ -968,7 +1143,7 @@ static bool test_SetPrinter(struct torture_context *tctx,
 }
 
 static bool test_SetPrinter_errors(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
+                                  struct dcerpc_binding_handle *b,
                                   struct policy_handle *handle)
 {
        struct spoolss_SetPrinter r;
@@ -993,7 +1168,7 @@ static bool test_SetPrinter_errors(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing SetPrinter all zero\n");
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter(p, tctx, &r),
+       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_PARAM,
                "failed to call SetPrinter");
@@ -1060,7 +1235,7 @@ static bool test_SetPrinter_errors(struct torture_context *tctx,
                torture_comment(tctx, "Testing SetPrinter level %d, command %d\n",
                        info_ctr.level, r.in.command);
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter(p, tctx, &r),
+               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
                        "failed to call SetPrinter");
 
                switch (r.in.command) {
@@ -1137,7 +1312,7 @@ static void clear_info2(struct spoolss_SetPrinterInfoCtr *r)
 }
 
 static bool test_PrinterInfo(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
+                            struct dcerpc_binding_handle *b,
                             struct policy_handle *handle)
 {
        NTSTATUS status;
@@ -1247,7 +1422,7 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                r.in.buffer = NULL; \
                r.in.offered = 0;\
                r.out.needed = &needed; \
-               status = dcerpc_spoolss_ ##call(p, tctx, &r); \
+               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__); \
@@ -1255,12 +1430,11 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                        break; \
                }\
                if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {\
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed); \
-                       data_blob_clear(&blob); \
+                       DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); \
                        r.in.buffer = &blob; \
                        r.in.offered = needed; \
                }\
-               status = dcerpc_spoolss_ ##call(p, tctx, &r); \
+               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__); \
@@ -1277,7 +1451,7 @@ static bool test_PrinterInfo(struct torture_context *tctx,
 
 #define TESTSETCALL_EXP(call, r, err) \
                clear_info2(&info_ctr);\
-               status = dcerpc_spoolss_ ##call(p, tctx, &r); \
+               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__); \
@@ -1339,7 +1513,7 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
                info_ctr.level = lvl1; \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
                info_ctr.info.info ## lvl1->field1 = value;\
                TESTSETCALL_EXP(SetPrinter, s, err) \
                info_ctr.info.info ## lvl1->field1 = ""; \
@@ -1348,7 +1522,7 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                STRING_EQUAL(info_ctr.info.info ## lvl1->field1, value, field1); \
                q.in.level = lvl2; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)&q.out.info->info ## lvl2; \
+               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)(void *)&q.out.info->info ## lvl2; \
                STRING_EQUAL(info_ctr.info.info ## lvl2->field2, value, field2); \
        } while (0)
 
@@ -1361,16 +1535,16 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
                info_ctr.level = lvl1; \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
                info_ctr.info.info ## lvl1->field1 = value; \
                TESTSETCALL(SetPrinter, s) \
                info_ctr.info.info ## lvl1->field1 = 0; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
                INT_EQUAL(info_ctr.info.info ## lvl1->field1, exp_value, field1); \
                q.in.level = lvl2; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)&q.out.info->info ## lvl2; \
+               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)(void *)&q.out.info->info ## lvl2; \
                INT_EQUAL(info_ctr.info.info ## lvl2->field2, exp_value, field1); \
        } while (0)
 
@@ -1579,7 +1753,7 @@ static bool test_security_descriptor_equal(struct torture_context *tctx,
 }
 
 static bool test_sd_set_level(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct policy_handle *handle,
                              uint32_t level,
                              struct security_descriptor *sd)
@@ -1587,6 +1761,7 @@ static bool test_sd_set_level(struct torture_context *tctx,
        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);
@@ -1594,33 +1769,11 @@ static bool test_sd_set_level(struct torture_context *tctx,
        switch (level) {
        case 2: {
                union spoolss_PrinterInfo info;
-               struct spoolss_SetPrinterInfo2 info2;
-               torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
-
-               info2.servername        = info.info2.servername;
-               info2.printername       = info.info2.printername;
-               info2.sharename         = info.info2.sharename;
-               info2.portname          = info.info2.portname;
-               info2.drivername        = info.info2.drivername;
-               info2.comment           = info.info2.comment;
-               info2.location          = info.info2.location;
-               info2.devmode_ptr       = 0;
-               info2.sepfile           = info.info2.sepfile;
-               info2.printprocessor    = info.info2.printprocessor;
-               info2.datatype          = info.info2.datatype;
-               info2.parameters        = info.info2.parameters;
-               info2.secdesc_ptr       = 0;
-               info2.attributes        = info.info2.attributes;
-               info2.priority          = info.info2.priority;
-               info2.defaultpriority   = info.info2.defaultpriority;
-               info2.starttime         = info.info2.starttime;
-               info2.untiltime         = info.info2.untiltime;
-               info2.status            = info.info2.status;
-               info2.cjobs             = info.info2.cjobs;
-               info2.averageppm        = info.info2.averageppm;
+               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.info2 = &info2;
+               info_ctr.info = sinfo;
 
                break;
        }
@@ -1641,13 +1794,13 @@ static bool test_sd_set_level(struct torture_context *tctx,
        secdesc_ctr.sd = sd;
 
        torture_assert(tctx,
-               test_SetPrinter(tctx, p, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+               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_pipe *p,
+                                struct dcerpc_binding_handle *b,
                                 struct policy_handle *handle)
 {
        union spoolss_PrinterInfo info;
@@ -1656,11 +1809,11 @@ static bool test_PrinterInfo_SDs(struct torture_context *tctx,
 
        /* just compare level 2 and level 3 */
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        sd1 = info.info2.secdesc;
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 3, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 3, &info), "");
 
        sd2 = info.info3.secdesc;
 
@@ -1670,13 +1823,13 @@ static bool test_PrinterInfo_SDs(struct torture_context *tctx,
 
        /* query level 2, set level 2, query level 2 */
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        sd1 = info.info2.secdesc;
 
-       torture_assert(tctx, test_sd_set_level(tctx, p, handle, 2, sd1), "");
+       torture_assert(tctx, test_sd_set_level(tctx, b, handle, 2, sd1), "");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        sd2 = info.info2.secdesc;
        if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
@@ -1690,13 +1843,13 @@ static bool test_PrinterInfo_SDs(struct torture_context *tctx,
 
        /* query level 2, set level 3, query level 2 */
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        sd1 = info.info2.secdesc;
 
-       torture_assert(tctx, test_sd_set_level(tctx, p, handle, 3, sd1), "");
+       torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        sd2 = info.info2.secdesc;
 
@@ -1716,9 +1869,9 @@ static bool test_PrinterInfo_SDs(struct torture_context *tctx,
                torture_assert_ntstatus_ok(tctx, security_descriptor_dacl_add(sd1, &a), "");
        }
 
-       torture_assert(tctx, test_sd_set_level(tctx, p, handle, 3, sd1), "");
+       torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
        sd2 = info.info2.secdesc;
 
        if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
@@ -1738,32 +1891,32 @@ static bool test_PrinterInfo_SDs(struct torture_context *tctx,
  */
 
 static bool test_PrinterInfo_SD(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
+                               struct dcerpc_binding_handle *b,
                                struct policy_handle *handle)
 {
        union spoolss_PrinterInfo info;
        struct security_descriptor *sd;
        bool ret = true;
 
-       torture_comment(tctx, "\nTesting Printer Security Descriptors\n");
+       torture_comment(tctx, "Testing Printer Security Descriptors\n");
 
        /* save original sd */
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info),
+       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, p, handle);
+       ret = test_PrinterInfo_SDs(tctx, b, handle);
 
        /* restore original sd */
 
-       torture_assert(tctx, test_sd_set_level(tctx, p, handle, 3, 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",
+       torture_comment(tctx, "Printer Security Descriptors test %s\n\n",
                ret ? "succeeded" : "failed");
 
 
@@ -1771,7 +1924,7 @@ static bool test_PrinterInfo_SD(struct torture_context *tctx,
 }
 
 static bool test_devmode_set_level(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
+                                  struct dcerpc_binding_handle *b,
                                   struct policy_handle *handle,
                                   uint32_t level,
                                   struct spoolss_DeviceMode *devmode)
@@ -1779,6 +1932,7 @@ static bool test_devmode_set_level(struct torture_context *tctx,
        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);
@@ -1786,33 +1940,11 @@ static bool test_devmode_set_level(struct torture_context *tctx,
        switch (level) {
        case 2: {
                union spoolss_PrinterInfo info;
-               struct spoolss_SetPrinterInfo2 info2;
-               torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
-
-               info2.servername        = info.info2.servername;
-               info2.printername       = info.info2.printername;
-               info2.sharename         = info.info2.sharename;
-               info2.portname          = info.info2.portname;
-               info2.drivername        = info.info2.drivername;
-               info2.comment           = info.info2.comment;
-               info2.location          = info.info2.location;
-               info2.devmode_ptr       = 0;
-               info2.sepfile           = info.info2.sepfile;
-               info2.printprocessor    = info.info2.printprocessor;
-               info2.datatype          = info.info2.datatype;
-               info2.parameters        = info.info2.parameters;
-               info2.secdesc_ptr       = 0;
-               info2.attributes        = info.info2.attributes;
-               info2.priority          = info.info2.priority;
-               info2.defaultpriority   = info.info2.defaultpriority;
-               info2.starttime         = info.info2.starttime;
-               info2.untiltime         = info.info2.untiltime;
-               info2.status            = info.info2.status;
-               info2.cjobs             = info.info2.cjobs;
-               info2.averageppm        = info.info2.averageppm;
+               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.info2 = &info2;
+               info_ctr.info = sinfo;
 
                break;
        }
@@ -1833,7 +1965,7 @@ static bool test_devmode_set_level(struct torture_context *tctx,
        devmode_ctr.devmode = devmode;
 
        torture_assert(tctx,
-               test_SetPrinter(tctx, p, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
 
        return true;
 }
@@ -1890,6 +2022,103 @@ static bool test_devicemode_equal(struct torture_context *tctx,
        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_GetPrinter q0;
+       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;
+       bool ret = true;
+       NTSTATUS status;
+
+#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value) do { \
+               torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+               q.in.level = lvl1; \
+               TESTGETCALL(GetPrinter, q) \
+               info_ctr.level = lvl1; \
+               if (lvl1 == 2) {\
+                       info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
+               } 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(SetPrinter, s) \
+               TESTGETCALL(GetPrinter, q) \
+               INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
+               q.in.level = lvl2; \
+               TESTGETCALL(GetPrinter, q) \
+               INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+       } while (0)
+
+#define TEST_DEVMODE_INT(lvl1, field1, lvl2, field2, value) do { \
+        TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, value); \
+        } 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;
+       q0 = q;
+
+#if 0
+       const char *devicename;/* [charset(UTF16)] */
+       enum spoolss_DeviceModeSpecVersion specversion;
+       uint16_t driverversion;
+       uint16_t size;
+       uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */
+       uint32_t fields;
+#endif
+
+       TEST_DEVMODE_INT(8, orientation,        8, orientation, __LINE__);
+       TEST_DEVMODE_INT(8, papersize,          8, papersize, __LINE__);
+       TEST_DEVMODE_INT(8, paperlength,        8, paperlength, __LINE__);
+       TEST_DEVMODE_INT(8, paperwidth,         8, paperwidth, __LINE__);
+       TEST_DEVMODE_INT(8, scale,              8, scale, __LINE__);
+       TEST_DEVMODE_INT(8, copies,             8, copies, __LINE__);
+       TEST_DEVMODE_INT(8, defaultsource,      8, defaultsource, __LINE__);
+       TEST_DEVMODE_INT(8, printquality,       8, printquality, __LINE__);
+       TEST_DEVMODE_INT(8, color,              8, color, __LINE__);
+       TEST_DEVMODE_INT(8, duplex,             8, duplex, __LINE__);
+       TEST_DEVMODE_INT(8, yresolution,        8, yresolution, __LINE__);
+       TEST_DEVMODE_INT(8, ttoption,           8, ttoption, __LINE__);
+       TEST_DEVMODE_INT(8, collate,            8, collate, __LINE__);
+#if 0
+       const char *formname;/* [charset(UTF16)] */
+#endif
+       TEST_DEVMODE_INT(8, logpixels,          8, logpixels, __LINE__);
+       TEST_DEVMODE_INT(8, bitsperpel,         8, bitsperpel, __LINE__);
+       TEST_DEVMODE_INT(8, pelswidth,          8, pelswidth, __LINE__);
+       TEST_DEVMODE_INT(8, pelsheight,         8, pelsheight, __LINE__);
+       TEST_DEVMODE_INT(8, displayflags,       8, displayflags, __LINE__);
+       TEST_DEVMODE_INT(8, displayfrequency,   8, displayfrequency, __LINE__);
+       TEST_DEVMODE_INT(8, icmmethod,          8, icmmethod, __LINE__);
+       TEST_DEVMODE_INT(8, icmintent,          8, icmintent, __LINE__);
+       TEST_DEVMODE_INT(8, mediatype,          8, mediatype, __LINE__);
+       TEST_DEVMODE_INT(8, dithertype,         8, dithertype, __LINE__);
+       TEST_DEVMODE_INT(8, reserved1,          8, reserved1, __LINE__);
+       TEST_DEVMODE_INT(8, reserved2,          8, reserved2, __LINE__);
+       TEST_DEVMODE_INT(8, panningwidth,       8, panningwidth, __LINE__);
+       TEST_DEVMODE_INT(8, panningheight,      8, panningheight, __LINE__);
+
+       return ret;
+}
+
 static bool call_OpenPrinterEx(struct torture_context *tctx,
                               struct dcerpc_pipe *p,
                               const char *name,
@@ -1897,7 +2126,7 @@ static bool call_OpenPrinterEx(struct torture_context *tctx,
                               struct policy_handle *handle);
 
 static bool test_ClosePrinter(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct policy_handle *handle);
 
 static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
@@ -1909,14 +2138,15 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
        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, p, handle, 8, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
 
        devmode = info.info8.devmode;
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
@@ -1929,16 +2159,16 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
        devmode->copies = 93;
        devmode->formname = talloc_strdup(tctx, "Legal");
 
-       torture_assert(tctx, test_devmode_set_level(tctx, p, handle, 8, devmode), "");
+       torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode), "");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 8, &info), "");
+       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, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
@@ -1951,16 +2181,16 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
        devmode->copies = 39;
        devmode->formname = talloc_strdup(tctx, "Executive");
 
-       torture_assert(tctx, test_devmode_set_level(tctx, p, handle, 2, devmode), "");
+       torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 2, devmode), "");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 8, &info), "");
+       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, p, handle, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
@@ -1968,6 +2198,12 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
                "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");
@@ -1976,12 +2212,12 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
        torture_assert(tctx, call_OpenPrinterEx(tctx, p, name, devmode, &handle_devmode),
                "failed to open printer handle");
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, &handle_devmode, 8, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 8, &info), "");
 
        devmode2 = info.info8.devmode;
 
        if (strequal(devmode->devicename, devmode2->devicename)) {
-               torture_comment(tctx, "devicenames are the same\n");
+               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);
@@ -2001,12 +2237,12 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
                torture_comment(tctx, "copies after level 8 get: %d\n", devmode2->copies);
        }
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, &handle_devmode, 2, &info), "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
        if (strequal(devmode->devicename, devmode2->devicename)) {
-               torture_comment(tctx, "devicenames are the same\n");
+               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);
@@ -2026,7 +2262,7 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
                torture_comment(tctx, "copies after level 2 get: %d\n", devmode2->copies);
        }
 
-       test_ClosePrinter(tctx, p, &handle_devmode);
+       test_ClosePrinter(tctx, b, &handle_devmode);
 
        return true;
 }
@@ -2043,12 +2279,13 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
        union spoolss_PrinterInfo info;
        struct spoolss_DeviceMode *devmode;
        bool ret = true;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-       torture_comment(tctx, "\nTesting Printer Devicemodes\n");
+       torture_comment(tctx, "Testing Printer Devicemodes\n");
 
        /* save original devmode */
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 8, &info),
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info),
                "failed to get initial global devicemode");
 
        devmode = info.info8.devmode;
@@ -2059,10 +2296,10 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
 
        /* restore original devmode */
 
-       torture_assert(tctx, test_devmode_set_level(tctx, p, handle, 8, 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",
+       torture_comment(tctx, "Printer Devicemodes test %s\n\n",
                ret ? "succeeded" : "failed");
 
 
@@ -2070,7 +2307,7 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
 }
 
 static bool test_ClosePrinter(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct policy_handle *handle)
 {
        NTSTATUS status;
@@ -2081,18 +2318,19 @@ static bool test_ClosePrinter(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing ClosePrinter\n");
 
-       status = dcerpc_spoolss_ClosePrinter(p, tctx, &r);
+       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(struct torture_context *tctx,
-                        struct dcerpc_pipe *p,
-                        struct policy_handle *handle,
-                        const char *form_name,
-                        uint32_t level)
+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;
@@ -2105,17 +2343,16 @@ static bool test_GetForm(struct torture_context *tctx,
        r.in.offered = 0;
        r.out.needed = &needed;
 
-       torture_comment(tctx, "Testing GetForm level %d\n", r.in.level);
+       torture_comment(tctx, "Testing GetForm(%s) level %d\n", form_name, r.in.level);
 
-       status = dcerpc_spoolss_GetForm(p, tctx, &r);
+       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(tctx, NULL, needed);
-               data_blob_clear(&blob);
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
-               status = dcerpc_spoolss_GetForm(p, tctx, &r);
+               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");
@@ -2127,195 +2364,450 @@ static bool test_GetForm(struct torture_context *tctx,
 
        CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), 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_pipe *p,
-                          struct policy_handle *handle, bool print_server)
+                          struct dcerpc_binding_handle *b,
+                          struct policy_handle *handle,
+                          bool print_server,
+                          uint32_t level,
+                          uint32_t *count_p,
+                          union spoolss_FormInfo **info_p)
 {
-       NTSTATUS status;
        struct spoolss_EnumForms r;
-       bool ret = true;
        uint32_t needed;
        uint32_t count;
-       uint32_t levels[] = { 1, 2 };
-       int i;
+       union spoolss_FormInfo *info;
 
-       for (i=0; i<ARRAY_SIZE(levels); i++) {
+       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;
 
-               union spoolss_FormInfo *info;
+       torture_comment(tctx, "Testing EnumForms level %d\n", r.in.level);
 
-               r.in.handle = handle;
-               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_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+               "EnumForms failed");
 
-               torture_comment(tctx, "Testing EnumForms level %d\n", levels[i]);
+       if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_UNKNOWN_LEVEL))) {
+               torture_skip(tctx, "EnumForms level 2 not supported");
+       }
 
-               status = dcerpc_spoolss_EnumForms(p, tctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+       if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID)) {
+               torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+       }
 
-               if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_UNKNOWN_LEVEL))) {
-                       break;
-               }
+       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;
 
-               if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID))
-                       torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+                       "EnumForms failed");
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       int j;
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
+               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, lp_iconv_convenience(tctx->lp_ctx), 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++) {
 
-                       status = dcerpc_spoolss_EnumForms(p, tctx, &r);
+               uint32_t count = 0;
+               union spoolss_FormInfo *info = NULL;
 
-                       torture_assert(tctx, info, "No forms returned");
+               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)
-                                       ret &= test_GetForm(tctx, p, handle, info[j].info1.form_name, levels[i]);
+               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;
+}
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+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;
+       uint32_t count;
+       bool found = false;
+       int i;
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+       torture_assert(tctx,
+               test_EnumForms(tctx, b, handle, print_server, 1, &count, &info),
+               "failed to enumerate forms");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       for (i=0; i<count; i++) {
+               if (strequal(form_name, info[i].info1.form_name)) {
+                       found = true;
+                       break;
+               }
        }
 
-       return true;
+       return found;
 }
 
 static bool test_DeleteForm(struct torture_context *tctx,
-                           struct dcerpc_pipe *p,
+                           struct dcerpc_binding_handle *b,
                            struct policy_handle *handle,
-                           const char *form_name)
+                           const char *form_name,
+                           WERROR expected_result)
 {
-       NTSTATUS status;
        struct spoolss_DeleteForm r;
 
        r.in.handle = handle;
        r.in.form_name = form_name;
 
-       status = dcerpc_spoolss_DeleteForm(p, tctx, &r);
+       torture_comment(tctx, "Testing DeleteForm(%s)\n", form_name);
 
-       torture_assert_ntstatus_ok(tctx, status, "DeleteForm failed");
-
-       torture_assert_werr_ok(tctx, r.out.result, "DeleteForm failed");
+       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_pipe *p,
-                        struct policy_handle *handle, bool print_server)
+                        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_AddFormInfo1 addform;
-       const char *form_name = "testform3";
-       NTSTATUS status;
-       bool ret = true;
+
+       if (level != 1) {
+               torture_skip(tctx, "only level 1 supported");
+       }
 
        r.in.handle     = handle;
-       r.in.level      = 1;
-       r.in.info.info1 = &addform;
-       addform.flags           = SPOOLSS_FORM_USER;
-       addform.form_name       = form_name;
-       addform.size.width      = 50;
-       addform.size.height     = 25;
-       addform.area.left       = 5;
-       addform.area.top        = 10;
-       addform.area.right      = 45;
-       addform.area.bottom     = 15;
+       r.in.level      = level;
+       r.in.info       = *info;
+
+       torture_comment(tctx, "Testing AddForm(%s) level %d, type %d\n",
+               r.in.info.info1->form_name, r.in.level,
+               r.in.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_PARAM)) {
+               torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM,
+                       "2nd AddForm gave unexpected result");
+       } else {
+               torture_assert_werr_equal(tctx, r.out.result, WERR_FILE_EXISTS,
+                       "2nd AddForm gave unexpected result");
+       }
 
-       status = dcerpc_spoolss_AddForm(p, tctx, &r);
+       return true;
+}
 
-       torture_assert_ntstatus_ok(tctx, status, "AddForm failed");
+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;
 
-       torture_assert_werr_ok(tctx, r.out.result, "AddForm failed");
+       r.in.handle     = handle;
+       r.in.form_name  = form_name;
+       r.in.level      = level;
+       r.in.info       = *info;
 
-       if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name, 1);
+       torture_comment(tctx, "Testing SetForm(%s) level %d\n",
+               form_name, r.in.level);
 
-       {
-               struct spoolss_SetForm sf;
-               struct spoolss_AddFormInfo1 setform;
+       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r),
+               "SetForm failed");
 
-               sf.in.handle    = handle;
-               sf.in.form_name = form_name;
-               sf.in.level     = 1;
-               sf.in.info.info1= &setform;
-               setform.flags           = addform.flags;
-               setform.form_name       = addform.form_name;
-               setform.size            = addform.size;
-               setform.area            = addform.area;
+       torture_assert_werr_ok(tctx, r.out.result,
+               "SetForm failed");
 
-               setform.size.width      = 1234;
+       return true;
+}
 
-               status = dcerpc_spoolss_SetForm(p, tctx, &sf);
+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;
 
-               torture_assert_ntstatus_ok(tctx, status, "SetForm failed");
+       enum winreg_Type w_type;
+       uint32_t w_size;
+       uint32_t w_length;
+       uint8_t *w_data;
 
-               torture_assert_werr_ok(tctx, r.out.result, "SetForm failed");
-       }
+       add_info.info1 = info1;
 
-       if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name, 1);
+       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)) {
+
+               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");
+
+               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_mem_equal(tctx, &w_data[0], &add_info.info1->size.width, 4, "width mismatch");
+               torture_assert_mem_equal(tctx, &w_data[4], &add_info.info1->size.height, 4, "height mismatch");
+               torture_assert_mem_equal(tctx, &w_data[8], &add_info.info1->area.left, 4, "left mismatch");
+               torture_assert_mem_equal(tctx, &w_data[12], &add_info.info1->area.top, 4, "top mismatch");
+               torture_assert_mem_equal(tctx, &w_data[16], &add_info.info1->area.right, 4, "right mismatch");
+               torture_assert_mem_equal(tctx, &w_data[20], &add_info.info1->area.bottom, 4, "bottom mismatch");
+               /* skip index here */
+               torture_assert_mem_equal(tctx, &w_data[28], &add_info.info1->flags, 4, "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) {
+                       torture_assert_mem_equal(tctx, &w_data[0], &info.info1.size.width, 4, "width mismatch");
+                       torture_assert_mem_equal(tctx, &w_data[4], &info.info1.size.height, 4, "height mismatch");
+                       torture_assert_mem_equal(tctx, &w_data[8], &info.info1.area.left, 4, "left mismatch");
+                       torture_assert_mem_equal(tctx, &w_data[12], &info.info1.area.top, 4, "top mismatch");
+                       torture_assert_mem_equal(tctx, &w_data[16], &info.info1.area.right, 4, "right mismatch");
+                       torture_assert_mem_equal(tctx, &w_data[20], &info.info1.area.bottom, 4, "bottom mismatch");
+                       /* skip index here */
+                       torture_assert_mem_equal(tctx, &w_data[28], &info.info1.flags, 4, "flags mismatch");
+               }
 
-       {
-               struct spoolss_EnumForms e;
-               union spoolss_FormInfo *info;
-               uint32_t needed;
-               uint32_t count;
-               bool found = false;
+               add_info.info1->size.width = 1234;
 
-               e.in.handle = handle;
-               e.in.level = 1;
-               e.in.buffer = NULL;
-               e.in.offered = 0;
-               e.out.needed = &needed;
-               e.out.count = &count;
-               e.out.info = &info;
+               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 setted form");
 
-               torture_comment(tctx, "Testing EnumForms level 1\n");
+               torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+       }
 
-               status = dcerpc_spoolss_EnumForms(p, tctx, &e);
-               torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+       if (!W_ERROR_EQUAL(expected_add_result, WERR_INVALID_PARAM)) {
+               torture_assert(tctx,
+                       test_EnumForms_find_one(tctx, b, handle, print_server, form_name),
+                       "Newly added form not found in enum call");
+       }
 
-               if (print_server && W_ERROR_EQUAL(e.out.result, WERR_BADFID))
-                       torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+       torture_assert(tctx,
+               test_DeleteForm(tctx, b, handle, form_name, expected_delete_result),
+               "failed to delete form");
 
-               if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       int j;
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       e.in.buffer = &blob;
-                       e.in.offered = needed;
+       return true;
+}
 
-                       status = dcerpc_spoolss_EnumForms(p, tctx, &e);
+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;
 
-                       torture_assert(tctx, info, "No forms returned");
+       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
 
-                       for (j = 0; j < count; j++) {
-                               if (strequal(form_name, info[j].info1.form_name)) {
-                                       found = true;
-                                       break;
-                               }
-                       }
+               {
+                       .info1 = {
+                               .flags          = SPOOLSS_FORM_BUILTIN,
+                               .form_name      = "testform_builtin",
+                               .size           = size,
+                               .area           = area,
+                       },
+                       .expected_add_result    = WERR_OK,
+                       .expected_delete_result = WERR_INVALID_PARAM,
+               },
+*/
+               {
+                       .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_PARAM
+               },
+               {
+                       .info1 = {
+                               .flags          = SPOOLSS_FORM_BUILTIN,
+                               .form_name      = "Letter",
+                               .size           = size,
+                               .area           = area,
+                       },
+                       .expected_add_result    = WERR_FILE_EXISTS,
+                       .expected_delete_result = WERR_INVALID_PARAM
+               },
+               {
+                       .info1 = {
+                               .flags          = SPOOLSS_FORM_PRINTER,
+                               .form_name      = "Letter",
+                               .size           = size,
+                               .area           = area,
+                       },
+                       .expected_add_result    = WERR_FILE_EXISTS,
+                       .expected_delete_result = WERR_INVALID_PARAM
+               },
+               {
+                       .info1 = {
+                               .flags          = 12345,
+                               .form_name      = "invalid_flags",
+                               .size           = size,
+                               .area           = area,
+                       },
+                       .expected_add_result    = WERR_INVALID_PARAM,
+                       .expected_delete_result = WERR_INVALID_FORM_NAME
                }
-               torture_assert(tctx, found, "Newly added form not found in enum call");
-       }
 
-       if (!test_DeleteForm(tctx, p, handle, form_name)) {
-               ret = false;
+       };
+
+       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 ret;
+       return true;
 }
 
 static bool test_EnumPorts_old(struct torture_context *tctx,
@@ -2326,6 +2818,7 @@ static bool test_EnumPorts_old(struct torture_context *tctx,
        uint32_t needed;
        uint32_t count;
        union spoolss_PortInfo *info;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
        r.in.servername = talloc_asprintf(tctx, "\\\\%s",
                                          dcerpc_server_name(p));
@@ -2338,17 +2831,16 @@ static bool test_EnumPorts_old(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing EnumPorts\n");
 
-       status = dcerpc_spoolss_EnumPorts(p, tctx, &r);
+       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(tctx, NULL, needed);
-               data_blob_clear(&blob);
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumPorts(p, tctx, &r);
+               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");
 
@@ -2367,6 +2859,7 @@ static bool test_AddPort(struct torture_context *tctx,
 {
        NTSTATUS status;
        struct spoolss_AddPort r;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
        r.in.server_name = talloc_asprintf(tctx, "\\\\%s",
                                           dcerpc_server_name(p));
@@ -2375,7 +2868,7 @@ static bool test_AddPort(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing AddPort\n");
 
-       status = dcerpc_spoolss_AddPort(p, tctx, &r);
+       status = dcerpc_spoolss_AddPort_r(b, tctx, &r);
 
        torture_assert_ntstatus_ok(tctx, status, "AddPort failed");
 
@@ -2393,64 +2886,74 @@ static bool test_AddPort(struct torture_context *tctx,
        return true;
 }
 
-static bool test_GetJob(struct torture_context *tctx,
-                       struct dcerpc_pipe *p,
-                       struct policy_handle *handle, uint32_t job_id)
+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;
-       uint32_t levels[] = {1, 2 /* 3, 4 */};
-       uint32_t i;
 
        r.in.handle = handle;
        r.in.job_id = job_id;
-       r.in.level = 0;
+       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 level %d\n", r.in.level);
+       torture_comment(tctx, "Testing GetJob(%d), level %d\n", job_id, r.in.level);
 
-       status = dcerpc_spoolss_GetJob(p, tctx, &r);
-       torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, "Unexpected return code");
-
-       for (i = 0; i < ARRAY_SIZE(levels); i++) {
+       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_UNKNOWN_LEVEL, "Unexpected return code");
+       }
 
-               torture_comment(tctx, "Testing GetJob level %d\n", r.in.level);
+       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;
 
-               needed = 0;
+               status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
+               torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+       }
 
-               r.in.level = levels[i];
-               r.in.offered = 0;
-               r.in.buffer = NULL;
+       torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+       torture_assert(tctx, r.out.info, "No job info returned");
 
-               status = dcerpc_spoolss_GetJob(p, tctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
+       if (info_p) {
+               *info_p = *r.out.info;
+       }
 
-                       status = dcerpc_spoolss_GetJob(p, tctx, &r);
-                       torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+       return true;
+}
 
-               }
-               torture_assert(tctx, r.out.info, "No job info returned");
-               torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+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;
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       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;
 }
 
 static bool test_SetJob(struct torture_context *tctx,
-                       struct dcerpc_pipe *p,
+                       struct dcerpc_binding_handle *b,
                        struct policy_handle *handle, uint32_t job_id,
                        enum spoolss_JobControl command)
 {
@@ -2464,38 +2967,38 @@ static bool test_SetJob(struct torture_context *tctx,
 
        switch (command) {
        case SPOOLSS_JOB_CONTROL_PAUSE:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_PAUSE\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_PAUSE\n", job_id);
                break;
        case SPOOLSS_JOB_CONTROL_RESUME:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RESUME\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESUME\n", job_id);
                break;
        case SPOOLSS_JOB_CONTROL_CANCEL:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_CANCEL\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_CANCEL\n", job_id);
                break;
        case SPOOLSS_JOB_CONTROL_RESTART:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RESTART\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESTART\n", job_id);
                break;
        case SPOOLSS_JOB_CONTROL_DELETE:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_DELETE\n");
+               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: SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER\n");
+               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: SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED\n");
+               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: SPOOLSS_JOB_CONTROL_RETAIN\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RETAIN\n", job_id);
                break;
        case SPOOLSS_JOB_CONTROL_RELEASE:
-               torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RELEASE\n");
+               torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RELEASE\n", job_id);
                break;
        default:
-               torture_comment(tctx, "Testing SetJob\n");
+               torture_comment(tctx, "Testing SetJob(%d)\n", job_id);
                break;
        }
 
-       status = dcerpc_spoolss_SetJob(p, tctx, &r);
+       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");
 
@@ -2503,7 +3006,7 @@ static bool test_SetJob(struct torture_context *tctx,
 }
 
 static bool test_AddJob(struct torture_context *tctx,
-                       struct dcerpc_pipe *p,
+                       struct dcerpc_binding_handle *b,
                        struct policy_handle *handle)
 {
        NTSTATUS status;
@@ -2518,21 +3021,24 @@ static bool test_AddJob(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing AddJob\n");
 
-       status = dcerpc_spoolss_AddJob(p, tctx, &r);
+       status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
        torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, "AddJob failed");
 
        r.in.level = 1;
 
-       status = dcerpc_spoolss_AddJob(p, tctx, &r);
+       status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
        torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM, "AddJob failed");
 
        return true;
 }
 
 
-static bool test_EnumJobs(struct torture_context *tctx,
-                         struct dcerpc_pipe *p,
-                         struct policy_handle *handle)
+static bool test_EnumJobs_args(struct torture_context *tctx,
+                              struct dcerpc_binding_handle *b,
+                              struct policy_handle *handle,
+                              uint32_t level,
+                              uint32_t *count_p,
+                              union spoolss_JobInfo **info_p)
 {
        NTSTATUS status;
        struct spoolss_EnumJobs r;
@@ -2543,27 +3049,25 @@ static bool test_EnumJobs(struct torture_context *tctx,
        r.in.handle = handle;
        r.in.firstjob = 0;
        r.in.numjobs = 0xffffffff;
-       r.in.level = 1;
+       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\n");
+       torture_comment(tctx, "Testing EnumJobs level %d\n", level);
 
-       status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+       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)) {
-               int j;
-               DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-               data_blob_clear(&blob);
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
 
-               status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+               status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
 
                torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
@@ -2571,30 +3075,25 @@ static bool test_EnumJobs(struct torture_context *tctx,
 
                CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
 
-               for (j = 0; j < count; j++) {
-
-                       torture_assert(tctx, test_GetJob(tctx, p, handle, info[j].info1.job_id),
-                               "failed to call test_GetJob");
-
-                       /* FIXME - gd */
-                       if (!torture_setting_bool(tctx, "samba3", false)) {
-                               test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE);
-                               test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME);
-                       }
-               }
-
        } else {
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
        }
 
+       if (count_p) {
+               *count_p = count;
+       }
+       if (info_p) {
+               *info_p = info;
+       }
+
        return true;
 }
 
-static bool test_DoPrintTest(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            struct policy_handle *handle)
+static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        uint32_t *job_id)
 {
-       bool ret = true;
        NTSTATUS status;
        struct spoolss_StartDocPrinter s;
        struct spoolss_DocumentInfo1 info1;
@@ -2603,7 +3102,6 @@ static bool test_DoPrintTest(struct torture_context *tctx,
        struct spoolss_EndPagePrinter ep;
        struct spoolss_EndDocPrinter e;
        int i;
-       uint32_t job_id;
        uint32_t num_written;
 
        torture_comment(tctx, "Testing StartDocPrinter\n");
@@ -2611,62 +3109,125 @@ static bool test_DoPrintTest(struct torture_context *tctx,
        s.in.handle             = handle;
        s.in.level              = 1;
        s.in.info.info1         = &info1;
-       s.out.job_id            = &job_id;
+       s.out.job_id            = job_id;
        info1.document_name     = "TorturePrintJob";
        info1.output_file       = NULL;
        info1.datatype          = "RAW";
 
-       status = dcerpc_spoolss_StartDocPrinter(p, tctx, &s);
+       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++) {
-               torture_comment(tctx, "Testing StartPagePrinter: Page[%d]\n", i);
+               torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
 
                sp.in.handle            = handle;
 
-               status = dcerpc_spoolss_StartPagePrinter(p, tctx, &sp);
+               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");
 
-               torture_comment(tctx, "Testing WritePrinter: Page[%d]\n", i);
+               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(p, tctx, &w);
+               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]\n", i);
+               torture_comment(tctx, "Testing EndPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
 
                ep.in.handle            = handle;
 
-               status = dcerpc_spoolss_EndPagePrinter(p, tctx, &ep);
+               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\n");
+       torture_comment(tctx, "Testing EndDocPrinter: JobId[%d]\n", *job_id);
 
        e.in.handle = handle;
 
-       status = dcerpc_spoolss_EndDocPrinter(p, tctx, &e);
+       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");
 
-       ret &= test_AddJob(tctx, p, handle);
-       ret &= test_EnumJobs(tctx, p, handle);
+       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, &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;
 
-       ret &= test_SetJob(tctx, p, handle, job_id, SPOOLSS_JOB_CONTROL_DELETE);
+               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");
+       }
+
+       for (i=0; i < num_jobs; i++) {
+               torture_assert(tctx,
+                       test_SetJob(tctx, b, handle, info[i].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE),
+                       "failed to pause printjob");
+               torture_assert(tctx,
+                       test_SetJob(tctx, b, handle, info[i].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME),
+                       "failed to resume printjob");
+       }
+
+       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;
+
+       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, &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], SPOOLSS_JOB_CONTROL_DELETE);
+       }
 
        return ret;
 }
 
 static bool test_PausePrinter(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
+                             struct dcerpc_binding_handle *b,
                              struct policy_handle *handle)
 {
        NTSTATUS status;
@@ -2689,7 +3250,7 @@ static bool test_PausePrinter(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n");
 
-       status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+       status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
 
        torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
 
@@ -2699,7 +3260,7 @@ static bool test_PausePrinter(struct torture_context *tctx,
 }
 
 static bool test_ResumePrinter(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
+                              struct dcerpc_binding_handle *b,
                               struct policy_handle *handle)
 {
        NTSTATUS status;
@@ -2722,7 +3283,7 @@ static bool test_ResumePrinter(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n");
 
-       status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+       status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
 
        torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
 
@@ -2732,11 +3293,12 @@ static bool test_ResumePrinter(struct torture_context *tctx,
 }
 
 static bool test_GetPrinterData(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
+                               struct dcerpc_binding_handle *b,
                                struct policy_handle *handle,
                                const char *value_name,
                                enum winreg_Type *type_p,
-                               union spoolss_PrinterData *data_p)
+                               uint8_t **data_p,
+                               uint32_t *needed_p)
 {
        NTSTATUS status;
        struct spoolss_GetPrinterData r;
@@ -2749,17 +3311,17 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        r.in.offered = 0;
        r.out.needed = &needed;
        r.out.type = &type;
-       r.out.data = &data;
+       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(p, tctx, &r);
+       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)) {
                r.in.offered = needed;
-
-               status = dcerpc_spoolss_GetPrinterData(p, tctx, &r);
+               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");
        }
 
@@ -2773,7 +3335,11 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        }
 
        if (data_p) {
-               *data_p = data;
+               *data_p = r.out.data;
+       }
+
+       if (needed_p) {
+               *needed_p = needed;
        }
 
        return true;
@@ -2785,13 +3351,15 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
                                  const char *key_name,
                                  const char *value_name,
                                  enum winreg_Type *type_p,
-                                 union spoolss_PrinterData *data_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;
@@ -2799,15 +3367,14 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        r.in.offered = 0;
        r.out.type = &type;
        r.out.needed = &needed;
-       r.out.data = &data;
+       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(p, tctx, &r);
+       status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
        if (!NT_STATUS_IS_OK(status)) {
-               if (NT_STATUS_EQUAL(status,NT_STATUS_NET_WRITE_FAULT) &&
-                   p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+               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");
@@ -2815,7 +3382,8 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
 
        if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
                r.in.offered = needed;
-               status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r);
+               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");
        }
 
@@ -2829,16 +3397,44 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        }
 
        if (data_p) {
-               *data_p = data;
+               *data_p = r.out.data;
+       }
+
+       if (needed_p) {
+               *needed_p = needed;
        }
 
        return true;
 }
 
+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, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);
+
+       return true;
+}
+
 static bool test_GetPrinterData_list(struct torture_context *tctx,
                                     struct dcerpc_pipe *p,
-                                    struct policy_handle *handle)
+                                    struct policy_handle *handle,
+                                    const char **architecture)
 {
+       struct dcerpc_binding_handle *b = p->binding_handle;
        const char *list[] = {
                "W3SvcInstalled",
                "BeepEnabled",
@@ -2858,95 +3454,136 @@ static bool test_GetPrinterData_list(struct torture_context *tctx,
 
        for (i=0; i < ARRAY_SIZE(list); i++) {
                enum winreg_Type type, type_ex;
-               union spoolss_PrinterData data, data_ex;
+               uint8_t *data, *data_ex;
+               uint32_t needed, needed_ex;
 
-               torture_assert(tctx, test_GetPrinterData(tctx, p, handle, list[i], &type, &data),
+               torture_assert(tctx, test_GetPrinterData(tctx, b, handle, list[i], &type, &data, &needed),
                        talloc_asprintf(tctx, "GetPrinterData failed on %s\n", list[i]));
-               torture_assert(tctx, test_GetPrinterDataEx(tctx, p, handle, "random_string", list[i], &type_ex, &data_ex),
+               torture_assert(tctx, test_GetPrinterDataEx(tctx, p, handle, "random_string", list[i], &type_ex, &data_ex, &needed_ex),
                        talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
                torture_assert_int_equal(tctx, type, type_ex, "type mismatch");
-               switch (type) {
-               case REG_SZ:
-                       torture_assert_str_equal(tctx, data.string, data_ex.string, "REG_SZ mismatch");
-                       break;
-               case REG_DWORD:
-                       torture_assert_int_equal(tctx, data.value, data_ex.value, "REG_DWORD mismatch");
-                       break;
-               case REG_BINARY:
-                       torture_assert_data_blob_equal(tctx, data.binary, data_ex.binary, "REG_BINARY mismatch");
-                       break;
-               default:
-                       break;
+               torture_assert_int_equal(tctx, needed, needed_ex, "needed mismatch");
+               torture_assert_mem_equal(tctx, data, data_ex, needed, "data mismatch");
+
+               if (strequal(list[i], "Architecture")) {
+                       if (architecture) {
+                               DATA_BLOB blob = data_blob_const(data, needed);
+                               *architecture = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);
+                       }
                }
        }
 
        return true;
 }
 
-static bool test_EnumPrinterData(struct torture_context *tctx, struct dcerpc_pipe *p,
-                                struct policy_handle *handle)
+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)
 {
-       NTSTATUS status;
        struct spoolss_EnumPrinterData r;
+       uint32_t data_needed;
+       uint32_t value_needed;
+       enum winreg_Type type;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-       ZERO_STRUCT(r);
        r.in.handle = handle;
-       r.in.enum_index = 0;
+       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);
 
-       do {
-               uint32_t value_size = 0;
-               uint32_t data_size = 0;
-               enum winreg_Type type = 0;
+       torture_comment(tctx, "Testing EnumPrinterData(%d)\n", enum_index);
 
-               r.in.value_offered = value_size;
-               r.out.value_needed = &value_size;
-               r.in.data_offered = data_size;
-               r.out.data_needed = &data_size;
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_EnumPrinterData_r(b, tctx, &r),
+               "EnumPrinterData failed");
 
-               r.out.type = &type;
-               r.out.data = talloc_zero_array(tctx, uint8_t, 0);
+       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;
+       }
 
-               torture_comment(tctx, "Testing EnumPrinterData\n");
+       return true;
+}
 
-               status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
-               if (W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) {
-                       break;
-               }
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterData");
+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;
 
-               r.in.value_offered = value_size;
-               r.out.value_name = talloc_zero_array(tctx, const char, value_size);
-               r.in.data_offered = data_size;
-               r.out.data = talloc_zero_array(tctx, uint8_t, data_size);
+       torture_comment(tctx, "Testing EnumPrinterData\n");
 
-               status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
+       do {
+               torture_assert(tctx,
+                       test_EnumPrinterData(tctx, p, handle, enum_index, 0, 0,
+                                            &type, &value_needed, &data_needed,
+                                            &value_name, &data, &result),
+                       "EnumPrinterData failed");
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
-               if (W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) {
+               if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
                        break;
                }
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterData failed");
+               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");
 
-               torture_assert(tctx, test_GetPrinterData(tctx, p, handle, r.out.value_name, NULL, NULL),
-                       talloc_asprintf(tctx, "failed to call GetPrinterData for %s\n", r.out.value_name));
+               if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
+                       break;
+               }
 
-               torture_assert(tctx, test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", r.out.value_name, NULL, NULL),
-                       talloc_asprintf(tctx, "failed to call GetPrinterDataEx on PrinterDriverData for %s\n", r.out.value_name));
+               enum_index++;
 
-               r.in.enum_index++;
+       } while (W_ERROR_IS_OK(result));
 
-       } while (W_ERROR_IS_OK(r.out.result));
+       torture_comment(tctx, "EnumPrinterData test succeeded\n");
 
        return true;
 }
 
 static bool test_EnumPrinterDataEx(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
+                                  struct dcerpc_binding_handle *b,
                                   struct policy_handle *handle,
-                                  const char *key_name)
+                                  const char *key_name,
+                                  uint32_t *count_p,
+                                  struct spoolss_PrinterEnumValues **info_p)
 {
        struct spoolss_EnumPrinterDataEx r;
        struct spoolss_PrinterEnumValues *info;
@@ -2962,11 +3599,11 @@ static bool test_EnumPrinterDataEx(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key_name);
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r),
+       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(p, tctx, &r),
+               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r),
                        "EnumPrinterDataEx failed");
        }
 
@@ -2974,12 +3611,146 @@ static bool test_EnumPrinterDataEx(struct torture_context *tctx,
 
        CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, lp_iconv_convenience(tctx->lp_ctx), 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,
+               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                                 "REG_SZ", "torture_data1", &type, &blob), "");
+
+       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_pipe *p,
+                                  struct dcerpc_binding_handle *b,
                                   struct policy_handle *handle,
                                   const char *value_name)
 {
@@ -2992,7 +3763,7 @@ static bool test_DeletePrinterData(struct torture_context *tctx,
        torture_comment(tctx, "Testing DeletePrinterData(%s)\n",
                r.in.value_name);
 
-       status = dcerpc_spoolss_DeletePrinterData(p, tctx, &r);
+       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");
@@ -3001,7 +3772,7 @@ static bool test_DeletePrinterData(struct torture_context *tctx,
 }
 
 static bool test_DeletePrinterDataEx(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p,
+                                    struct dcerpc_binding_handle *b,
                                     struct policy_handle *handle,
                                     const char *key_name,
                                     const char *value_name)
@@ -3016,7 +3787,7 @@ static bool test_DeletePrinterDataEx(struct torture_context *tctx,
                r.in.key_name, r.in.value_name);
 
        torture_assert_ntstatus_ok(tctx,
-               dcerpc_spoolss_DeletePrinterDataEx(p, tctx, &r),
+               dcerpc_spoolss_DeletePrinterDataEx_r(b, tctx, &r),
                "DeletePrinterDataEx failed");
        torture_assert_werr_ok(tctx, r.out.result,
                "DeletePrinterDataEx failed");
@@ -3025,7 +3796,7 @@ static bool test_DeletePrinterDataEx(struct torture_context *tctx,
 }
 
 static bool test_DeletePrinterKey(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
+                                 struct dcerpc_binding_handle *b,
                                  struct policy_handle *handle,
                                  const char *key_name)
 {
@@ -3042,7 +3813,7 @@ static bool test_DeletePrinterKey(struct torture_context *tctx,
        }
 
        torture_assert_ntstatus_ok(tctx,
-               dcerpc_spoolss_DeletePrinterKey(p, tctx, &r),
+               dcerpc_spoolss_DeletePrinterKey_r(b, tctx, &r),
                "DeletePrinterKey failed");
        torture_assert_werr_ok(tctx, r.out.result,
                "DeletePrinterKey failed");
@@ -3050,1537 +3821,3853 @@ static bool test_DeletePrinterKey(struct torture_context *tctx,
        return true;
 }
 
-static bool test_SetPrinterData(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
-                               struct policy_handle *handle)
+static bool test_winreg_OpenHKLM(struct torture_context *tctx,
+                                struct dcerpc_binding_handle *b,
+                                struct policy_handle *handle)
 {
-       NTSTATUS status;
-       struct spoolss_SetPrinterData r;
-       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;
+       struct winreg_OpenHKLM r;
 
-       for (i=0; i < ARRAY_SIZE(values); i++) {
+       r.in.system_name = NULL;
+       r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       r.out.handle = handle;
 
-               enum winreg_Type type;
-               union spoolss_PrinterData data;
+       torture_comment(tctx, "Testing winreg_OpenHKLM\n");
 
-               r.in.handle = handle;
-               r.in.value_name = values[i];
-               r.in.type = REG_SZ;
-               r.in.data.string = "dog";
+       torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenHKLM_r(b, tctx, &r), "OpenHKLM failed");
+       torture_assert_werr_ok(tctx, r.out.result, "OpenHKLM failed");
 
-               torture_comment(tctx, "Testing SetPrinterData(%s)\n",
-                       r.in.value_name);
+       return true;
+}
 
-               status = dcerpc_spoolss_SetPrinterData(p, tctx, &r);
+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;
+       }
+}
 
-               torture_assert_ntstatus_ok(tctx, status, "SetPrinterData failed");
-               torture_assert_werr_ok(tctx, r.out.result, "SetPrinterData failed");
+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;
 
-               if (!test_GetPrinterData(tctx, p, handle, r.in.value_name, &type, &data)) {
-                       return false;
-               }
+       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_assert_int_equal(tctx, r.in.type, type, "type mismatch");
-               torture_assert_str_equal(tctx, r.in.data.string, data.string, "data mismatch");
+       torture_comment(tctx, "Testing winreg_OpenKey(%s)\n", keyname);
 
-               if (!test_DeletePrinterData(tctx, p, handle, r.in.value_name)) {
-                       return false;
-               }
-       }
+       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_EnumPrinterKey(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
-                               struct policy_handle *handle,
-                               const char *key_name,
-                               const char ***array);
+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_SetPrinterDataEx(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 struct policy_handle *handle)
+static bool test_winreg_CloseKey(struct torture_context *tctx,
+                                struct dcerpc_binding_handle *b,
+                                struct policy_handle *handle)
 {
-       NTSTATUS status;
-       struct spoolss_SetPrinterDataEx r;
-       const char *value_name = "dog";
-       const char *keys[] = {
-               "torturedataex",
-               "torture data ex",
-#if 0
-       /* FIXME: not working with s3 atm. */
-               "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",
-#endif
-               "torture,data",
-#if 0
-       /* FIXME: not working with s3 atm. */
+       struct winreg_CloseKey r;
 
-               "torture,data,ex",
-               "torture,data\\ex",
-               "torture\\data,ex"
-#endif
-       };
-       int i;
-       DATA_BLOB blob = data_blob_string_const("catfoobar");
+       r.in.handle = handle;
+       r.out.handle = handle;
 
+       torture_comment(tctx, "Testing winreg_CloseKey\n");
 
-       for (i=0; i < ARRAY_SIZE(keys); i++) {
+       torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r), "CloseKey failed");
+       torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
 
-               char *c;
-               const char *key;
-               enum winreg_Type type;
-               const char **subkeys;
-               union spoolss_PrinterData data;
+       return true;
+}
 
-               r.in.handle = handle;
-               r.in.key_name = keys[i];
-               r.in.value_name = value_name;
-               r.in.type = REG_BINARY;
-               r.in.data.binary = blob;
+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;
 
-               torture_comment(tctx, "Testing SetPrinterDataEx(%s - %s)\n", r.in.key_name, value_name);
+       init_winreg_String(&valuename, value_name);
 
-               status = dcerpc_spoolss_SetPrinterDataEx(p, tctx, &r);
+       data = talloc_zero_array(tctx, uint8_t, 0);
 
-               torture_assert_ntstatus_ok(tctx, status, "SetPrinterDataEx failed");
-               torture_assert_werr_ok(tctx, r.out.result, "SetPrinterDataEx failed");
+       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;
 
-               key = talloc_strdup(tctx, r.in.key_name);
+       torture_comment(tctx, "Testing winreg_QueryValue(%s)\n", value_name);
 
-               if (!test_GetPrinterDataEx(tctx, p, handle, r.in.key_name, value_name, &type, &data)) {
-                       return false;
-               }
+       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");
 
-               torture_assert_int_equal(tctx, r.in.type, type, "type mismatch");
-               torture_assert_data_blob_equal(tctx, blob, data.binary, "data mismatch");
+       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;
+       }
 
-               if (!test_EnumPrinterDataEx(tctx, p, handle, r.in.key_name)) {
-                       return false;
-               }
+       return true;
+}
 
-               if (!test_DeletePrinterDataEx(tctx, p, handle, r.in.key_name, value_name)) {
-                       return false;
-               }
+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;
 
-               c = strchr(key, '\\');
-               if (c) {
-                       int i;
+       printer_key = talloc_asprintf(tctx, "%s\\%s\\%s",
+               TOP_LEVEL_PRINT_PRINTERS_KEY, printer_name, key_name);
 
-                       /* we have subkeys */
+       torture_assert(tctx,
+               test_winreg_OpenKey(tctx, b, handle, printer_key, &key_handle), "");
 
-                       *c = 0;
+       torture_assert(tctx,
+               test_winreg_QueryValue(tctx, b, &key_handle, value_name, w_type, w_size, w_length, w_data), "");
 
-                       if (!test_EnumPrinterKey(tctx, p, handle, key, &subkeys)) {
-                               return false;
-                       }
+       torture_assert(tctx,
+               test_winreg_CloseKey(tctx, b, &key_handle), "");
 
-                       for (i=0; subkeys && subkeys[i]; i++) {
+       return true;
+}
 
-                               const char *current_key = talloc_asprintf(tctx, "%s\\%s", key, subkeys[i]);
+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;
 
-                               if (!test_DeletePrinterKey(tctx, p, handle, current_key)) {
-                                       return false;
-                               }
-                       }
+       torture_assert(tctx,
+               test_winreg_OpenKey(tctx, b, handle, key_name, &key_handle), "");
 
-                       if (!test_DeletePrinterKey(tctx, p, handle, key)) {
-                               return false;
-                       }
+       torture_assert(tctx,
+               test_winreg_QueryValue(tctx, b, &key_handle, form_name, w_type, w_size, w_length, w_data), "");
 
-               } else {
-                       if (!test_DeletePrinterKey(tctx, p, handle, key)) {
-                               return false;
-                       }
-               }
-       }
+       torture_assert(tctx,
+               test_winreg_CloseKey(tctx, b, &key_handle), "");
 
        return true;
 }
 
-static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
-                                        struct dcerpc_pipe *p,
-                                        struct policy_handle *handle,
-                                        uint32_t *change_id)
+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)
 {
-       enum winreg_Type type;
-       union spoolss_PrinterData data;
+       /* 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_GetPrinterData(tctx, p, handle, "ChangeID", &type, &data),
-               "failed to call GetPrinterData");
+               test_winreg_OpenKey_opts(tctx, b, handle, symlink_keyname, REG_OPTION_OPEN_LINK, &key_handle),
+                       "failed to open key link");
 
-       torture_assert(tctx, type == REG_DWORD, "unexpected type");
+       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");
 
-       *change_id = data.value;
+       torture_assert_int_equal(tctx, w_type, REG_LINK, "unexpected type");
+
+       blob = data_blob(w_data, w_size);
+       str = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), 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 bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
-                                          struct dcerpc_pipe *p,
-                                          struct policy_handle *handle,
-                                          uint32_t *change_id)
+static const char *strip_unc(const char *unc)
 {
-       enum winreg_Type type;
-       union spoolss_PrinterData data;
+       char *name;
 
-       torture_assert(tctx,
-               test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data),
-               "failed to call GetPrinterData");
+       if (!unc) {
+               return NULL;
+       }
 
-       torture_assert(tctx, type == REG_DWORD, "unexpected type");
+       if (unc[0] == '\\' && unc[1] == '\\') {
+               unc +=2;
+       }
 
-       *change_id = data.value;
+       name = strchr(unc, '\\');
+       if (name) {
+               return name+1;
+       }
 
-       return true;
+       return unc;
 }
 
-static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx,
-                                        struct dcerpc_pipe *p,
-                                        struct policy_handle *handle,
-                                        uint32_t *change_id)
+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_assert(tctx, test_GetPrinter_level(tctx, p, handle, 0, &info),
-               "failed to query Printer level 0");
+       torture_comment(tctx, "Testing Printer Info and winreg consistency\n");
 
-       *change_id = info.info0.change_id;
+       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, lp_iconv_convenience(tctx->lp_ctx), 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_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, lp_iconv_convenience(tctx->lp_ctx), &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, lp_iconv_convenience(tctx->lp_ctx), &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, lp_iconv_convenience(tctx->lp_ctx), &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
+#undef test_sd
+
+       torture_comment(tctx, "Printer Info and winreg consistency test succeeded\n\n");
 
        return true;
 }
 
-static bool test_ChangeID(struct torture_context *tctx,
-                         struct dcerpc_pipe *p,
-                         struct policy_handle *handle)
+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)
 {
-       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;
+       char *p;
 
+       if (path == NULL) {
+               return NULL;
+       }
 
-       torture_comment(tctx, "Testing ChangeID: id change test #1\n");
+       p = strrchr(path, '\\');
+       if (p) {
+               return p+1;
+       }
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, p, 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, p, handle, &change_id_info),
-               "failed to query for ChangeID");
+       return path;
+}
 
-       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");
+static const char **strip_paths(const char **path_array)
+{
+       int i;
 
+       if (path_array == NULL) {
+               return NULL;
+       }
 
-       torture_comment(tctx, "Testing ChangeID: id change test #2\n");
+       for (i=0; path_array[i] != NULL; i++) {
+               path_array[i] = strip_path(path_array[i]);
+       }
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, p, handle, &change_id),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, 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, p, 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");
+       return path_array;
+}
 
+static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
+{
+       time_t t;
+       struct tm *tm;
+       t = nt_time_to_unix(nt);
+       tm = localtime(&t);
 
-       torture_comment(tctx, "Testing ChangeID: id change test #3\n");
+       return talloc_asprintf(mem_ctx, "%02d/%02d/%04d",
+               tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+}
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, p, 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, p, handle, &change_id_info),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetPrinter_level(tctx, p, handle, 2, &info),
-               "failed to query Printer level 2");
-       comment = talloc_strdup(tctx, info.info2.comment);
+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));
+}
 
-       {
-               struct spoolss_SetPrinterInfoCtr info_ctr;
-               struct spoolss_DevmodeContainer devmode_ctr;
-               struct sec_desc_buf secdesc_ctr;
-               struct spoolss_SetPrinterInfo2 info2;
+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,
+                                     struct dcerpc_binding_handle *winreg_handle,
+                                     struct policy_handle *hive_handle)
+{
+       WERROR result;
+       union spoolss_DriverInfo info;
+       const char *driver_key;
+       struct policy_handle key_handle;
 
-               ZERO_STRUCT(info_ctr);
-               ZERO_STRUCT(devmode_ctr);
-               ZERO_STRUCT(secdesc_ctr);
+       const char *driver_path;
+       const char *data_file;
+       const char *config_file;
+       const char *help_file;
+       const char **dependent_files;
 
-               info2.servername        = info.info2.servername;
-               info2.printername       = info.info2.printername;
-               info2.sharename         = info.info2.sharename;
-               info2.portname          = info.info2.portname;
-               info2.drivername        = info.info2.drivername;
-               info2.comment           = "torture_comment";
-               info2.location          = info.info2.location;
-               info2.devmode_ptr       = 0;
-               info2.sepfile           = info.info2.sepfile;
-               info2.printprocessor    = info.info2.printprocessor;
-               info2.datatype          = info.info2.datatype;
-               info2.parameters        = info.info2.parameters;
-               info2.secdesc_ptr       = 0;
-               info2.attributes        = info.info2.attributes;
-               info2.priority          = info.info2.priority;
-               info2.defaultpriority   = info.info2.defaultpriority;
-               info2.starttime         = info.info2.starttime;
-               info2.untiltime         = info.info2.untiltime;
-               info2.status            = info.info2.status;
-               info2.cjobs             = info.info2.cjobs;
-               info2.averageppm        = info.info2.averageppm;
+       const char *driver_date;
+       const char *inbox_driver_date;
 
-               info_ctr.level = 2;
-               info_ctr.info.info2 = &info2;
+       const char *driver_version;
+       const char *inbox_driver_version;
 
-               torture_assert(tctx, test_SetPrinter(tctx, p, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
-                       "failed to call SetPrinter");
+       torture_comment(tctx, "Testing Driver Info and winreg consistency\n");
 
-               info2.comment           = comment;
+       driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
+                                    TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+                                    environment,
+                                    3,
+                                    driver_name);
 
-               torture_assert(tctx, test_SetPrinter(tctx, p, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
-                       "failed to call SetPrinter");
+       torture_assert(tctx,
+               test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
+               "failed to open driver key");
 
-       }
+       torture_assert(tctx,
+               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, 3, 0, &info, &result),
+               "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:
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, p, 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, p, handle, &change_id_info2),
-               "failed to query for ChangeID");
+       torture_assert(tctx,
+               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, 3, 0, &info, &result),
+               "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);
+       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",                   ?); */
 
-       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,
+               test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
 
-       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, "Driver Info and winreg consistency test succeeded\n\n");
 
        return true;
 }
 
-static bool test_SecondaryClosePrinter(struct torture_context *tctx,
-                                      struct dcerpc_pipe *p,
-                                      struct policy_handle *handle)
+#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)
 {
-       NTSTATUS status;
-       struct dcerpc_binding *b;
-       struct dcerpc_pipe *p2;
-       struct spoolss_ClosePrinter cp;
+       struct spoolss_SetPrinterData r;
 
-       /* only makes sense on SMB */
+       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;
+               DATA_BLOB blob;
+               uint8_t *data;
+               uint32_t needed;
+
+               torture_assert(tctx,
+                       reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                                         "REG_SZ", "dog", &type, &blob), "");
+
+               torture_assert(tctx,
+                       test_SetPrinterData(tctx, b, handle, values[i], REG_SZ, blob.data, blob.length),
+                       "SetPrinterData failed");
+
+               torture_assert(tctx,
+                       test_GetPrinterData(tctx, b, handle, values[i], &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_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 *keys[] = {
+               "torturedataex",
+               "torture data ex",
+#if 0
+       /* FIXME: not working with s3 atm. */
+               "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",
+#endif
+               "torture,data",
+#if 0
+       /* FIXME: not working with s3 atm. */
+
+               "torture,data,ex",
+               "torture,data\\ex",
+               "torture\\data,ex"
+#endif
+       };
+       enum winreg_Type types[] = {
+               REG_SZ,
+               REG_MULTI_SZ,
+               REG_DWORD,
+               REG_BINARY
+       };
+       const char *str = "abcdefghijklmnopqrstuvwxzy";
+       int i, t, s;
+
+
+       for (i=0; i < ARRAY_SIZE(keys); i++) {
+       for (t=0; t < ARRAY_SIZE(types); t++) {
+       for (s=0; s < strlen(str); s++) {
+
+               char *c;
+               const char *key;
+               enum winreg_Type type;
+               const char *string = talloc_strndup(tctx, str, s);
+               DATA_BLOB blob = data_blob_string_const(string);
+               const char **subkeys;
+               DATA_BLOB data;
+               uint8_t *data_out;
+               uint32_t needed, offered = 0;
+               uint32_t ecount;
+               struct spoolss_PrinterEnumValues *einfo;
+
+               if (types[t] == REG_DWORD) {
+                       s = 0xffff;
+               }
+
+               if (torture_setting_bool(tctx, "samba3", false)) {
+                       if ((types[t] == REG_MULTI_SZ) && s == 0) {
+                               torture_warning(tctx, "samba3 does not handle 4 byte emtpy REG_MULTI_SZ buffers");
+                               continue;
+                       }
+               }
+
+               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,
+                               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                                                 "REG_SZ", string, &type, &data), "");
+                       offered = data.length;
+                       /*strlen_m_term(data.string)*2;*/
+                       break;
+               case REG_MULTI_SZ:
+                       torture_assert(tctx,
+                               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                                                 "REG_SZ", string, &type, &data), "");
+                       torture_assert(tctx, data_blob_realloc(tctx, &data, data.length + 2), "");
+                       memset(&data.data[data.length - 2], '\0', 2);
+                       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, keys[i], value_name, types[t], data.data, offered),
+                       "failed to call SetPrinterDataEx");
+
+               torture_assert(tctx,
+                       test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &data_out, &needed),
+                       "failed to call GetPrinterDataEx");
+
+               torture_assert(tctx,
+                       test_EnumPrinterDataEx(tctx, b, handle, keys[i], &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, keys[i], 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");
+               }
+
+               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_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_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)
+{
+       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, b2, &hive_handle);
+
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+       talloc_free(p2);
+
+       return ret;
+}
+
+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;
+       uint8_t *data;
+       uint32_t needed;
+
+       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 dcerpc_binding *b;
+       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");
+       torture_comment(tctx, "Testing close on secondary pipe\n");
+
+       status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b);
+       torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding");
+
+       status = dcerpc_secondary_connection(p, &p2, b);
+       torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+       status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss);
+       torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on 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.level                   = 1;
+       opEx.in.userlevel.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_PARAM,
+               "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,
+                                         struct dcerpc_binding_handle *b,
+                                         const char *server_name)
+{
+       const char *badnames[] = {
+               "__INVALID_PRINTER__",
+               "\\\\__INVALID_HOST__",
+               "",
+               "\\\\\\",
+               "\\\\\\__INVALID_PRINTER__"
+       };
+       const char *badname;
+       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)
+{
+       NTSTATUS status;
+       struct spoolss_OpenPrinter r;
+       struct policy_handle handle;
+       bool ret = true;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       r.in.printername        = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), 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 (!test_GetPrinter(tctx, b, &handle, environment)) {
+               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 call_OpenPrinterEx(struct torture_context *tctx,
+                              struct dcerpc_pipe *p,
+                              const char *name,
+                              struct spoolss_DeviceMode *devmode,
+                              struct policy_handle *handle)
+{
+       struct spoolss_OpenPrinterEx r;
+       struct spoolss_UserLevel1 userlevel1;
+       NTSTATUS status;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       if (name && name[0]) {
+               r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s",
+                                                  dcerpc_server_name(p), name);
+       } else {
+               r.in.printername = talloc_asprintf(tctx, "\\\\%s",
+                                                  dcerpc_server_name(p));
+       }
+
+       r.in.datatype           = NULL;
+       r.in.devmode_ctr.devmode= devmode;
+       r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
+       r.in.level              = 1;
+       r.in.userlevel.level1   = &userlevel1;
+       r.out.handle = handle;
+
+       userlevel1.size = 1234;
+       userlevel1.client = "hello";
+       userlevel1.user = "spottyfoot!";
+       userlevel1.build = 1;
+       userlevel1.major = 2;
+       userlevel1.minor = 3;
+       userlevel1.processor = 4;
+
+       torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+       status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r);
+
+       torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+
+       torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed");
+
+       return true;
+}
+
+static bool test_printer_rename(struct torture_context *tctx,
+                               struct dcerpc_pipe *p,
+                               struct policy_handle *handle,
+                               const char *name)
+{
+       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, 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, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+               "failed to call SetPrinter level 2");
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, 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");
+
+       /* FIXME: we openend with servername! */
+       printer_name = talloc_asprintf(tctx, "\\\\%s\\%s",
+               dcerpc_server_name(p), printer_name_new);
+
+       torture_assert_str_equal(tctx, info.info2.printername, printer_name,
+               "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_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 (!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_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,
+                                 struct dcerpc_pipe *p,
+                                 const char *environment)
+{
+       struct spoolss_EnumPrinters r;
+       NTSTATUS status;
+       uint16_t levels[] = {1, 2, 4, 5};
+       int i;
+       bool ret = true;
+       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, lp_iconv_convenience(tctx->lp_ctx), 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;
+                               name = unc;
+                               if (unc[0] == '\\' && unc[1] == '\\') {
+                                       unc +=2;
+                               }
+                               slash = strchr(unc, '\\');
+                               if (slash) {
+                                       slash++;
+                                       name = slash;
+                               }
+                               if (!test_OpenPrinter(tctx, p, name, environment)) {
+                                       ret = false;
+                               }
+                               if (!test_OpenPrinterEx(tctx, p, name, environment)) {
+                                       ret = false;
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+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, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+
+       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_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, lp_iconv_convenience(tctx->lp_ctx), 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,
+                                       struct dcerpc_pipe *p,
+                                       const char *environment)
+{
+       uint16_t levels[] = {1, 2, 3, 4, 5, 6};
+       int i;
+       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, 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 level 1\n", ex ? "Ex":"");
+
+       /* 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,
+                                  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 level 2\n", ex ? "Ex":"");
+
+       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");
 
-       status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b);
-       torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding");
+       info2.portname = portname;
 
-       status = dcerpc_secondary_connection(p, &p2, b);
-       torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+       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");
 
-       status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss);
-       torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection");
+       info2.drivername = drivername;
 
-       cp.in.handle = handle;
-       cp.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;
 
-       status = dcerpc_spoolss_ClosePrinter(p2, tctx, &cp);
-       torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NET_WRITE_FAULT,
-                       "ERROR: Allowed close on secondary connection");
+       /* w2k8r2 allows to add printer w/o defining printprocessor */
 
-       torture_assert_int_equal(tctx, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
-                                "Unexpected fault code");
+       if (!W_ERROR_IS_OK(result)) {
+               torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
+                       "unexpected result code");
 
-       talloc_free(p2);
+               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_OpenPrinter_badname(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p, const char *name)
+static bool test_printer_info(struct torture_context *tctx,
+                             struct dcerpc_binding_handle *b,
+                             struct policy_handle *handle)
 {
-       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;
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping printer info cross tests against samba 3");
+       }
+
+       if (!test_PrinterInfo(tctx, b, handle)) {
+               ret = false;
+       }
+
+       if (!test_SetPrinter_errors(tctx, b, 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_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, "\nTesting OpenPrinter(%s) with bad name\n", op.in.printername);
+       torture_comment(tctx, "Printer Keys test succeeded\n\n");
 
-       status = dcerpc_spoolss_OpenPrinter(p, tctx, &op);
-       torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
-       if (!W_ERROR_EQUAL(WERR_INVALID_PRINTER_NAME,op.out.result)) {
-               torture_comment(tctx, "OpenPrinter(%s) unexpected result[%s] should be WERR_INVALID_PRINTER_NAME\n",
-                       name, win_errstr(op.out.result));
-       }
+       return true;
+}
 
-       if (W_ERROR_IS_OK(op.out.result)) {
-               ret &=test_ClosePrinter(tctx, p, &handle);
-       }
+static bool test_one_printer(struct torture_context *tctx,
+                            struct dcerpc_pipe *p,
+                            struct policy_handle *handle,
+                            const char *name,
+                            const char *drivername,
+                            const char *environment,
+                            bool have_driver)
+{
+       bool ret = true;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-       opEx.in.printername             = name;
-       opEx.in.datatype                = NULL;
-       opEx.in.devmode_ctr.devmode     = NULL;
-       opEx.in.access_mask             = 0;
-       opEx.in.level                   = 1;
-       opEx.in.userlevel.level1        = NULL;
-       opEx.out.handle                 = &handle;
+       if (!test_PausePrinter(tctx, b, handle)) {
+               ret = false;
+       }
 
-       torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
+       if (!test_DoPrintTest(tctx, b, handle)) {
+               ret = false;
+       }
 
-       status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &opEx);
-       torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
-       if (!W_ERROR_EQUAL(WERR_INVALID_PARAM,opEx.out.result)) {
-               torture_comment(tctx, "OpenPrinterEx(%s) unexpected result[%s] should be WERR_INVALID_PARAM\n",
-                       name, win_errstr(opEx.out.result));
+       if (!test_ResumePrinter(tctx, b, handle)) {
+               ret = false;
        }
 
-       if (W_ERROR_IS_OK(opEx.out.result)) {
-               ret &=test_ClosePrinter(tctx, p, &handle);
+       if (!test_printer_info(tctx, b, handle)) {
+               ret = false;
        }
 
-       return ret;
-}
+       if (!test_PrinterInfo_SD(tctx, b, handle)) {
+               ret = false;
+       }
 
-static bool test_OpenPrinter(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            const char *name)
-{
-       NTSTATUS status;
-       struct spoolss_OpenPrinter r;
-       struct policy_handle handle;
-       bool ret = true;
+       if (!test_PrinterInfo_DevMode(tctx, p, handle, name)) {
+               ret = false;
+       }
 
-       r.in.printername        = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name);
-       r.in.datatype           = NULL;
-       r.in.devmode_ctr.devmode= NULL;
-       r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
-       r.out.handle            = &handle;
+       if (!test_PrinterInfo_winreg(tctx, p, handle, name)) {
+               ret = false;
+       }
 
-       torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername);
+       if (!test_ChangeID(tctx, p, handle)) {
+               ret = false;
+       }
 
-       status = dcerpc_spoolss_OpenPrinter(p, tctx, &r);
+       if (!test_printer_keys(tctx, b, handle)) {
+               ret = false;
+       }
 
-       torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+       if (!test_EnumPrinterData_consistency(tctx, p, handle)) {
+               ret = false;
+       }
 
-       torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
+       if (!test_SetPrinterDataEx_matrix(tctx, p, handle, name, NULL, NULL)) {
+               ret = false;
+       }
 
-       if (!test_GetPrinter(tctx, p, &handle)) {
+       if (!test_PrinterData_winreg(tctx, p, handle, name)) {
                ret = false;
        }
 
-       if (!torture_setting_bool(tctx, "samba3", false)) {
-               if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+       if (have_driver) {
+               if (!test_DriverInfo_winreg(tctx, p, handle, name, drivername, environment)) {
                        ret = false;
                }
        }
 
-       if (!test_ClosePrinter(tctx, p, &handle)) {
+       if (!test_printer_rename(tctx, p, handle, name)) {
                ret = false;
        }
 
        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_csetprinter(struct torture_context *tctx,
+                            struct dcerpc_pipe *p,
+                            struct policy_handle *handle,
+                            const char *printername,
+                            const char *drivername,
+                            const char *portname)
 {
-       struct spoolss_OpenPrinterEx r;
-       struct spoolss_UserLevel1 userlevel1;
-       NTSTATUS status;
+       union spoolss_PrinterInfo info;
+       struct policy_handle new_handle, new_handle2;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-       if (name && name[0]) {
-               r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s",
-                                                  dcerpc_server_name(p), name);
-       } else {
-               r.in.printername = talloc_asprintf(tctx, "\\\\%s",
-                                                  dcerpc_server_name(p));
-       }
+       torture_comment(tctx, "Testing c_setprinter\n");
 
-       r.in.datatype           = NULL;
-       r.in.devmode_ctr.devmode= devmode;
-       r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
-       r.in.level              = 1;
-       r.in.userlevel.level1   = &userlevel1;
-       r.out.handle = handle;
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, handle, 0, &info),
+               "failed to get level 0 printer info");
+       torture_comment(tctx, "csetprinter on initial printer handle: %d\n",
+               info.info0.c_setprinter);
 
-       userlevel1.size = 1234;
-       userlevel1.client = "hello";
-       userlevel1.user = "spottyfoot!";
-       userlevel1.build = 1;
-       userlevel1.major = 2;
-       userlevel1.minor = 3;
-       userlevel1.processor = 4;
+       /* check if c_setprinter on 1st handle increases after a printer has
+        * been added */
 
-       torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+       torture_assert(tctx,
+               test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, false),
+               "failed to add new printer");
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, 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);
 
-       status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &r);
+       /* check if c_setprinter on new handle increases after a printer has
+        * been added */
 
-       torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+       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);
 
-       torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed");
+       /* 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 test_OpenPrinterEx(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
-                              const char *name)
+static bool test_add_printer_args_with_driver(struct torture_context *tctx,
+                                             struct dcerpc_pipe *p,
+                                             struct torture_printer_context *t)
 {
-       struct policy_handle handle;
        bool ret = true;
-
-       if (!call_OpenPrinterEx(tctx, p, name, NULL, &handle)) {
-               return false;
+       struct policy_handle handle;
+       bool found = false;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *printer_name = t->info2.printername;
+       const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+       const char *port_name = t->info2.portname;
+       const char *printer_name2 = talloc_asprintf(tctx, "%s2", printer_name);
+
+       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, &handle, printer_name, driver_name, port_name, t->ex),
+                       "failed to add printer");
        }
 
-       if (!test_PrinterInfo_SD(tctx, p, &handle)) {
+       if (!test_csetprinter(tctx, p, &handle, printer_name2, driver_name, port_name)) {
                ret = false;
        }
 
-       if (!test_GetPrinter(tctx, p, &handle)) {
+       if (!test_one_printer(tctx, p, &handle, printer_name, driver_name, t->driver.remote.environment, t->have_driver)) {
                ret = false;
        }
 
-       if (!test_EnumForms(tctx, p, &handle, false)) {
+       if (!test_DeletePrinter(tctx, b, &handle)) {
                ret = false;
        }
 
-       if (!test_AddForm(tctx, p, &handle, false)) {
+       if (!test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
+                                       printer_name, &found)) {
                ret = false;
        }
 
-       if (!test_EnumPrinterData(tctx, p, &handle)) {
-               ret = false;
-       }
+       torture_assert(tctx, !found, "deleted printer still there");
 
-       if (!test_EnumPrinterDataEx(tctx, p, &handle, "PrinterDriverData")) {
-               ret = false;
-       }
+       return ret;
+}
 
-       if (!test_printer_keys(tctx, p, &handle)) {
-               ret = false;
-       }
+static bool compose_local_driver_directory(struct torture_context *tctx,
+                                          const char *environment,
+                                          const char *local_dir,
+                                          const char **path)
+{
+       char *p;
 
-       if (!test_PausePrinter(tctx, p, &handle)) {
-               ret = false;
+       p = strrchr(local_dir, '/');
+       if (!p) {
+               return NULL;
        }
+       p++;
 
-       if (!test_DoPrintTest(tctx, p, &handle)) {
-               ret = false;
+       if (strequal(environment, "Windows x64")) {
+               if (!strequal(p, "x64")) {
+                       *path = talloc_asprintf(tctx, "%s/x64", local_dir);
+               }
+       } else if (strequal(environment, "Windows NT x86")) {
+               if (!strequal(p, "i386")) {
+                       *path = talloc_asprintf(tctx, "%s/i386", local_dir);
+               }
+       } else {
+               torture_assert(tctx, "unknown environment: '%s'\n", environment);
        }
 
-       if (!test_ResumePrinter(tctx, p, &handle)) {
-               ret = false;
-       }
+       return true;
+}
 
-       if (!test_SetPrinterData(tctx, p, &handle)) {
-               ret = false;
+static bool test_add_printer_args(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 struct torture_printer_context *t)
+{
+       bool ret = true;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       if (t->wellknown && torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping AddPrinter level 1 against samba");
        }
 
-       if (!test_SetPrinterDataEx(tctx, p, &handle)) {
-               ret = false;
+       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");
+
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername)) {
+               t->have_driver = true;
+               goto try_run;
        }
 
-       if (!torture_setting_bool(tctx, "samba3", false)) {
-               if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
-                       ret = false;
-               }
+       torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
+               t->info2.drivername, t->driver.remote.environment);
+       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_run;
        }
 
-       if (!test_ClosePrinter(tctx, p, &handle)) {
-               ret = false;
+       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),
+               "failed to add driver");
+
+       t->added_driver = true;
+       t->have_driver = true;
+
+ try_run:
+       ret = test_add_printer_args_with_driver(tctx, p, t);
+
+       if (t->added_driver) {
+               torture_assert(tctx,
+                       remove_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
+                       "failed to remove printer driver");
        }
 
        return ret;
 }
 
-static bool test_EnumPrinters_old(struct torture_context *tctx, struct dcerpc_pipe *p)
+static bool test_add_printer(struct torture_context *tctx,
+                            struct dcerpc_pipe *p,
+                            void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+
+       t->ex                   = false;
+       t->wellknown            = false;
+       t->info2.printername    = TORTURE_PRINTER;
+
+       return test_add_printer_args(tctx, p, t);
+}
+
+static bool test_add_printer_wellknown(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p,
+                                      void *private_data)
 {
-       struct spoolss_EnumPrinters r;
-       NTSTATUS status;
-       uint16_t levels[] = {1, 2, 4, 5};
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+
+       t->ex                   = false;
+       t->wellknown            = true;
+       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER;
+
+       return test_add_printer_args(tctx, p, t);
+}
+
+static bool test_add_printer_ex(struct torture_context *tctx,
+                               struct dcerpc_pipe *p,
+                               void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+
+       t->ex                   = true;
+       t->wellknown            = false;
+       t->info2.printername    = TORTURE_PRINTER_EX;
+
+       return test_add_printer_args(tctx, p, t);
+}
+
+static bool test_add_printer_ex_wellknown(struct torture_context *tctx,
+                                         struct dcerpc_pipe *p,
+                                         void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+
+       t->ex                   = true;
+       t->wellknown            = true;
+       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER_EX;
+
+       return test_add_printer_args(tctx, p, t);
+}
+
+static bool test_architecture_buffer(struct torture_context *tctx,
+                                    struct dcerpc_pipe *p)
+{
+       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;
-       bool ret = true;
+       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;
+       for (i=0; i < ARRAY_SIZE(architectures); i++) {
 
-               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 OpenPrinterEx with architecture %d\n", architectures[i]);
 
-               torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+               u1.size = 0;
+               u1.client = NULL;
+               u1.user = NULL;
+               u1.build = 0;
+               u1.major = 3;
+               u1.minor = 0;
+               u1.processor = architectures[i];
 
-               status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+               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.level               = 1;
+               r.in.userlevel.level1   = &u1;
+               r.out.handle            = &handle;
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
-                       status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
+               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]));
                }
+       }
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+       return true;
+}
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+bool torture_rpc_spoolss(struct torture_context *torture)
+{
+       NTSTATUS status;
+       struct dcerpc_pipe *p;
+       struct dcerpc_binding_handle *b;
+       bool ret = true;
+       struct test_spoolss_context *ctx;
+       const char *environment = SPOOLSS_ARCHITECTURE_NT_X86;
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       status = torture_rpc_connection(torture, &p, &ndr_table_spoolss);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+       b = p->binding_handle;
 
-               if (!info) {
-                       torture_comment(tctx, "No printers returned\n");
-                       return true;
-               }
+       ctx = talloc_zero(torture, struct test_spoolss_context);
 
-               for (j=0;j<count;j++) {
-                       if (r.in.level == 1) {
-                               char *unc = talloc_strdup(tctx, info[j].info1.name);
-                               char *slash, *name;
-                               name = unc;
-                               if (unc[0] == '\\' && unc[1] == '\\') {
-                                       unc +=2;
-                               }
-                               slash = strchr(unc, '\\');
-                               if (slash) {
-                                       slash++;
-                                       name = slash;
-                               }
-                               if (!test_OpenPrinter(tctx, p, name)) {
-                                       ret = false;
-                               }
-                               if (!test_OpenPrinterEx(tctx, p, name)) {
-                                       ret = false;
-                               }
-                       }
-               }
-       }
+       ret &= test_OpenPrinter_server(torture, p, &ctx->server_handle);
+       ret &= test_GetPrinterData_list(torture, p, &ctx->server_handle, &environment);
+       ret &= test_EnumForms_all(torture, b, &ctx->server_handle, true);
+       ret &= test_Forms(torture, b, &ctx->server_handle, true, NULL, NULL, NULL);
+       ret &= test_Forms_winreg(torture, b, &ctx->server_handle, true, NULL);
+       ret &= test_EnumPorts(torture, b, ctx);
+       ret &= test_GetPrinterDriverDirectory(torture, p, environment);
+       ret &= test_GetPrintProcessorDirectory(torture, p, environment);
+       ret &= test_EnumPrinterDrivers(torture, p, ctx, environment);
+       ret &= test_EnumPrinterDrivers(torture, p, ctx, SPOOLSS_ARCHITECTURE_ALL);
+       ret &= test_EnumMonitors(torture, b, ctx);
+       ret &= test_EnumPrintProcessors(torture, b, ctx, environment);
+       ret &= test_EnumPrintProcDataTypes(torture, b);
+       ret &= test_EnumPrinters(torture, b, ctx);
+       ret &= test_OpenPrinter_badname_list(torture, b, dcerpc_server_name(p));
+
+       ret &= test_AddPort(torture, p);
+       ret &= test_EnumPorts_old(torture, p);
+       ret &= test_EnumPrinters_old(torture, p, environment);
+       ret &= test_EnumPrinterDrivers_old(torture, p, environment);
+       ret &= test_architecture_buffer(torture, p);
 
        return ret;
 }
 
-static bool test_GetPrinterDriver(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 struct policy_handle *handle,
-                                 const char *driver_name)
+struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
 {
-       struct spoolss_GetPrinterDriver r;
-       uint32_t needed;
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-PRINTER");
 
-       r.in.handle = handle;
-       r.in.architecture = "W32X86";
-       r.in.level = 1;
-       r.in.buffer = NULL;
-       r.in.offered = 0;
-       r.out.needed = &needed;
+       struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
+                                                       "printer", &ndr_table_spoolss);
 
-       torture_comment(tctx, "Testing GetPrinterDriver level %d\n", r.in.level);
+       struct torture_printer_context *t;
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver(p, tctx, &r),
-               "failed to call GetPrinterDriver");
-       if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-               DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-               data_blob_clear(&blob);
-               r.in.buffer = &blob;
-               r.in.offered = needed;
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver(p, tctx, &r),
-                       "failed to call GetPrinterDriver");
-       }
+       t = talloc_zero(mem_ctx, struct torture_printer_context);
 
-       torture_assert_werr_ok(tctx, r.out.result,
-               "failed to call GetPrinterDriver");
+       t->driver.info8.version         = SPOOLSS_DRIVER_VERSION_200X;
+       t->driver.info8.driver_name     = TORTURE_DRIVER;
+       t->driver.info8.architecture    = "Windows NT x86";
+       t->driver.info8.driver_path     = "pscript5.dll";
+       t->driver.info8.data_file       = "cups6.ppd";
+       t->driver.info8.config_file     = "cupsui6.dll";
+       t->driver.local.environment     = "Windows NT x86";
+       t->driver.local.driver_directory= "/usr/share/cups/drivers/i386";
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       t->info2.drivername             = "Microsoft XPS Document Writer";
+       t->info2.portname               = "LPT1:";
 
-       return true;
+       torture_rpc_tcase_add_test_ex(tcase, "add_printer", test_add_printer, t);
+       torture_rpc_tcase_add_test_ex(tcase, "add_printer_wellknown", test_add_printer_wellknown, t);
+       torture_rpc_tcase_add_test_ex(tcase, "add_printer_ex", test_add_printer_ex, t);
+       torture_rpc_tcase_add_test_ex(tcase, "add_printer_ex_wellknown", test_add_printer_ex_wellknown, t);
+
+       return suite;
 }
 
-static bool test_GetPrinterDriver2(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
-                                  struct policy_handle *handle,
-                                  const char *driver_name)
+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_GetPrinterDriver2 r;
-       uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 };
+       struct spoolss_GetPrinterDriverDirectory r;
        uint32_t needed;
-       uint32_t server_major_version;
-       uint32_t server_minor_version;
-       int i;
 
-       r.in.handle = handle;
-       r.in.architecture = SPOOLSS_ARCHITECTURE_NT_X86;
-       r.in.client_major_version = 3;
-       r.in.client_minor_version = 0;
-       r.out.needed = &needed;
-       r.out.server_major_version = &server_major_version;
-       r.out.server_minor_version = &server_minor_version;
-
-       for (i=0;i<ARRAY_SIZE(levels);i++) {
-
-               r.in.buffer = NULL;
-               r.in.offered = 0;
-               r.in.level = levels[i];
+       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_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n",
-                       driver_name, r.in.level);
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+               "failed to query driver directory");
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r),
-                       "failed to call GetPrinterDriver2");
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
-                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver2(p, 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;
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) {
-                       switch (r.in.level) {
-                       case 101:
-                       case 8:
-                               continue;
-                       default:
-                               break;
-                       }
-               }
+               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 call GetPrinterDriver2");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "failed to query driver directory");
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       if (dir_p) {
+               *dir_p = r.out.info->info1.directory_name;
        }
 
        return true;
 }
 
-static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
-                                       struct dcerpc_pipe *p)
+static const char *get_driver_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
 {
-       struct spoolss_EnumPrinterDrivers r;
-       NTSTATUS status;
-       uint16_t levels[] = {1, 2, 3, 4, 5, 6};
-       int i;
-
-       for (i=0;i<ARRAY_SIZE(levels);i++) {
-
-               uint32_t needed;
-               uint32_t count;
-               union spoolss_DriverInfo *info;
-
-               r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-               r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
-               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 EnumPrinterDrivers level %u\n", r.in.level);
-
-               status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
+       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;
+       }
+}
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+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;
+       }
+}
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
-                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-                       data_blob_clear(&blob);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
-                       status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
-               }
 
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+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);
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+       r.in.servername = servername;
+       r.in.info_ctr = info_ctr;
 
-               if (!info) {
-                       torture_comment(tctx, "No printer drivers returned\n");
-                       break;
-               }
+       torture_comment(tctx, "Testing AddPrinterDriver(%s) level: %d, environment: '%s'\n",
+               drivername, info_ctr->level, environment);
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
-       }
+       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_DeletePrinter(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
-                              struct policy_handle *handle)
+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_DeletePrinter r;
+       struct spoolss_AddPrinterDriverEx r;
+       const char *drivername = get_driver_from_info(info_ctr);
+       const char *environment = get_environment_from_info(info_ctr);
 
-       torture_comment(tctx, "Testing DeletePrinter\n");
+       r.in.servername = servername;
+       r.in.info_ctr = info_ctr;
+       r.in.flags = flags;
 
-       r.in.handle = handle;
+       torture_comment(tctx, "Testing AddPrinterDriverEx(%s) level: %d, environment: '%s'\n",
+               drivername, info_ctr->level, environment);
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_DeletePrinter(p, tctx, &r),
-               "failed to delete printer");
-       torture_assert_werr_ok(tctx, r.out.result,
-               "failed to delete printer");
+       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;
 }
 
-static bool test_EnumPrinters_findname(struct torture_context *tctx,
-                                      struct dcerpc_pipe *p,
-                                      uint32_t flags,
-                                      uint32_t level,
-                                      const char *name,
-                                      bool *found)
+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)
 {
-       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;
+       struct spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo1 info1;
 
-       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters(p, tctx, &e),
-               "failed to enum printers");
+       ZERO_STRUCT(info1);
 
-       if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) {
-               DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
-               data_blob_clear(&blob);
-               e.in.buffer = &blob;
-               e.in.offered = needed;
+       info_ctr.level = 1;
+       info_ctr.info.info1 = &info1;
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters(p, tctx, &e),
-                       "failed to enum printers");
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriverEx level 1");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriver level 1");
        }
 
-       torture_assert_werr_ok(tctx, e.out.result,
-               "failed to enum printers");
-
-       for (i=0; i < count; i++) {
-
-               const char *current = NULL;
-               const char *p;
-
-               switch (level) {
-               case 1:
-                       current = info[i].info1.name;
-                       break;
-               }
-
-               if (strequal(current, name)) {
-                       *found = true;
-                       break;
-               }
+       info1.driver_name = r->driver_name;
 
-               p = strrchr(current, '\\');
-               if (p) {
-                       if (!e.in.server) {
-                               torture_warning(tctx,
-                                       "server returns printername %s incl. servername although we did not set servername", current);
-                       }
-                       p++;
-                       if (strequal(p, name)) {
-                               *found = true;
-                               break;
-                       }
-               }
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriverEx level 1");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriver level 1");
        }
 
        return true;
 }
 
-static bool test_AddPrinter_wellknown(struct torture_context *tctx,
-                                     struct dcerpc_pipe *p,
-                                     const char *printername,
-                                     bool ex)
+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)
 {
-       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 spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo2 info2;
 
-       ZERO_STRUCT(devmode_ctr);
-       ZERO_STRUCT(secdesc_ctr);
-       ZERO_STRUCT(userlevel_ctr);
-       ZERO_STRUCT(info1);
+       ZERO_STRUCT(info2);
 
-       torture_comment(tctx, "Testing AddPrinter%s level 1\n", ex ? "Ex":"");
+       info_ctr.level = 2;
+       info_ctr.info.info2 = &info2;
 
-       /* try to add printer to wellknown printer list (level 1) */
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriver level 2");
+       }
 
-       userlevel_ctr.level = 1;
+       info2.driver_name = r->driver_name;
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "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_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriver level 2");
+       }
 
-       info_ctr.info.info1 = &info1;
-       info_ctr.level = 1;
+       info2.architecture = r->architecture;
 
-       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;
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriver level 2");
+       }
 
-       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;
+       info2.driver_path = r->driver_path;
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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");
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriver level 2");
+       }
 
-       info1.name = printername;
-       info1.flags = PRINTER_ATTRIBUTE_SHARED;
+       info2.data_file = r->data_file;
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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");
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriver level 2");
+       }
 
-       /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here,
-          better do a real check to see the printer is really there */
+       info2.config_file = r->config_file;
 
-       torture_assert(tctx, test_EnumPrinters_findname(tctx, p,
-                                                       PRINTER_ENUM_NETWORK, 1,
-                                                       printername,
-                                                       &found),
-                       "failed to enum printers");
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, 0, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx");
+       }
 
-       torture_assert(tctx, found, "failed to find newly added printer");
+       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");
+       }
 
-       info1.flags = 0;
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name),
+               "failed to find added printer driver");
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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;
+}
 
-       /* 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 */
+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)
+{
+       struct spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo3 info3;
+
+       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");
+       }
 
-       found = false;
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name),
+               "failed to find added printer driver");
 
-       torture_assert(tctx, test_EnumPrinters_findname(tctx, p,
-                                                       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,
-                                  bool ex)
+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)
 {
-       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;
-
-       ZERO_STRUCT(devmode_ctr);
-       ZERO_STRUCT(secdesc_ctr);
-       ZERO_STRUCT(userlevel_ctr);
+       struct spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo4 info4;
+
+       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_comment(tctx, "Testing AddPrinter%s level 2\n", ex ? "Ex":"");
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name),
+               "failed to find added printer driver");
 
-       userlevel_ctr.level = 1;
+       return true;
+}
 
-       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;
+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)
+{
+       struct spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo6 info6;
+
+       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_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriver level 6");
+       }
 
-       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;
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
 
- again:
+       if (!ex) {
+               return true;
+       }
 
-       /* try to add printer to printer list (level 2) */
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name),
+               "failed to find added printer driver");
 
-       ZERO_STRUCT(info2);
+       return true;
+}
 
-       info_ctr.info.info2 = &info2;
-       info_ctr.level = 2;
+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)
+{
+       struct spoolss_AddDriverInfoCtr info_ctr;
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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");
+       info_ctr.level = 8;
+       info_ctr.info.info8 = r;
 
-       info2.printername = printername;
+       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_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriver level 8");
+       }
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, tctx, &r),
-               "failed to add printer");
-       result = ex ? rex.out.result : r.out.result;
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
 
-       if (W_ERROR_EQUAL(result, WERR_PRINTER_ALREADY_EXISTS)) {
-               struct policy_handle printer_handle;
+       if (!ex) {
+               return true;
+       }
 
-               if (existing_printer_deleted) {
-                       torture_fail(tctx, "already deleted printer still existing?");
-               }
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name),
+               "failed to find added printer driver");
 
-               torture_assert(tctx, call_OpenPrinterEx(tctx, p, printername, NULL, &printer_handle),
-                       "failed to open printer handle");
+       return true;
+}
 
-               torture_assert(tctx, test_DeletePrinter(tctx, p, &printer_handle),
-                       "failed to delete printer");
+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;
 
-               torture_assert(tctx, test_ClosePrinter(tctx, p, &printer_handle),
-                       "failed to close server handle");
+       r.in.server = server;
+       r.in.architecture = environment;
+       r.in.driver = driver;
 
-               existing_printer_deleted = true;
+       torture_comment(tctx, "Testing DeletePrinterDriver(%s)\n", driver);
 
-               goto again;
-       }
+       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");
 
-       torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT,
-               "unexpected result code");
+       return true;
+}
 
-       info2.portname = portname;
+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;
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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");
+       r.in.server = server;
+       r.in.architecture = environment;
+       r.in.driver = driver;
+       r.in.delete_flags = delete_flags;
+       r.in.version = version;
 
-       info2.drivername = drivername;
+       torture_comment(tctx, "Testing DeletePrinterDriverEx(%s)\n", driver);
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, tctx, &r),
-               "failed to add printer");
-       result = ex ? rex.out.result : r.out.result;
+       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");
 
-       /* w2k8r2 allows to add printer w/o defining printprocessor */
+       return true;
+}
 
-       if (!W_ERROR_IS_OK(result)) {
-               torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
-                       "unexpected result code");
+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");
 
-               info2.printprocessor = "winprint";
+       torture_assert(tctx,
+               test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK),
+               "failed to delete driver");
 
-               torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                                     dcerpc_spoolss_AddPrinter(p, tctx, &r),
-                       "failed to add printer");
-               result = ex ? rex.out.result : r.out.result;
-               torture_assert_werr_ok(tctx, result,
-                       "failed to add printer");
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver)) {
+               torture_fail(tctx, "deleted driver still enumerated");
        }
 
-       *handle_p = handle;
+       torture_assert(tctx,
+               test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_UNKNOWN_PRINTER_DRIVER),
+               "2nd delete failed");
 
-       /* we are paranoid, really check if the printer is there now */
+       return true;
+}
 
-       torture_assert(tctx, test_EnumPrinters_findname(tctx, p,
-                                                       PRINTER_ENUM_LOCAL, 1,
-                                                       printername,
-                                                       &found),
-                       "failed to enum printers");
-       torture_assert(tctx, found, "failed to find newly added printer");
+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_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx(p, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter(p, 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");
+       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)) {
+               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_AddPrinterEx(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
-                             struct policy_handle *handle_p,
-                             const char *printername,
-                             const char *drivername,
-                             const char *portname)
+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)
 {
        bool ret = true;
 
-       if (!torture_setting_bool(tctx, "samba3", false)) {
-               if (!test_AddPrinter_wellknown(tctx, p, TORTURE_WELLKNOWN_PRINTER_EX, true)) {
-                       torture_comment(tctx, "failed to add printer to well known list\n");
-                       ret = false;
-               }
+       switch (level) {
+       case 1:
+               ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex);
+               break;
+       case 2:
+               ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex);
+               break;
+       case 3:
+               ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex);
+               break;
+       case 4:
+               ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex);
+               break;
+       case 6:
+               ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex);
+               break;
+       case 8:
+               ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex);
+               break;
+       default:
+               return false;
        }
 
-       if (!test_AddPrinter_normal(tctx, p, handle_p,
-                                   printername, drivername, portname,
-                                   true)) {
-               torture_comment(tctx, "failed to add printer to printer list\n");
-               ret = false;
+       if (ret == false) {
+               return ret;
        }
 
-       return ret;
-}
+       if (level == 1) {
+               return ret;
+       }
 
-static bool test_AddPrinter(struct torture_context *tctx,
-                           struct dcerpc_pipe *p,
-                           struct policy_handle *handle_p,
-                           const char *printername,
-                           const char *drivername,
-                           const char *portname)
-{
-       bool ret = true;
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
 
-       if (!torture_setting_bool(tctx, "samba3", false)) {
-               if (!test_AddPrinter_wellknown(tctx, p, TORTURE_WELLKNOWN_PRINTER, false)) {
-                       torture_comment(tctx, "failed to add printer to well known list\n");
-                       ret = false;
-               }
+       if (!ex && (level == 6 || level == 8)) {
+               return ret;
        }
 
-       if (!test_AddPrinter_normal(tctx, p, handle_p,
-                                   printername, drivername, portname,
-                                   false)) {
-               torture_comment(tctx, "failed to add printer to printer list\n");
-               ret = false;
+       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);
        }
-
-       return ret;
 }
 
-static bool test_printer_info(struct torture_context *tctx,
-                             struct dcerpc_pipe *p,
-                             struct policy_handle *handle)
+static bool fillup_printserver_info(struct torture_context *tctx,
+                                   struct dcerpc_pipe *p,
+                                   struct torture_driver_context *d)
 {
-       bool ret = true;
+       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));
 
-       if (torture_setting_bool(tctx, "samba3", false)) {
-               torture_skip(tctx, "skipping printer info cross tests against samba 3");
-       }
+       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");
 
-       if (!test_PrinterInfo(tctx, p, handle)) {
-               ret = false;
-       }
+       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");
 
-       if (!test_SetPrinter_errors(tctx, p, handle)) {
-               ret = false;
+       return true;
+}
+
+static const char *driver_directory_dir(const char *driver_directory)
+{
+       char *p;
+
+       p = strrchr(driver_directory, '\\');
+       if (p) {
+               return p+1;
        }
 
-       return ret;
+       return NULL;
 }
 
-static bool test_EnumPrinterKey(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
-                               struct policy_handle *handle,
-                               const char *key_name,
-                               const char ***array)
+static const char *driver_directory_share(struct torture_context *tctx,
+                                         const char *driver_directory)
 {
-       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;
+       const char *p;
+       char *tok;
 
-       for (i=0; i < ARRAY_SIZE(offered); i++) {
+       if (driver_directory[0] == '\\' && driver_directory[1] == '\\') {
+               driver_directory += 2;
+       }
 
-               if (offered[i] < 0 && needed) {
-                       if (needed <= 4) {
-                               continue;
-                       }
-                       r.in.offered = needed + offered[i];
-               } else {
-                       r.in.offered = offered[i];
-               }
+       p = talloc_strdup(tctx, driver_directory);
 
-               ZERO_STRUCT(key_buffer);
+       torture_assert(tctx,
+               next_token_talloc(tctx, &p, &tok, "\\"),
+               "cannot explode uri");
+       torture_assert(tctx,
+               next_token_talloc(tctx, &p, &tok, "\\"),
+               "cannot explode uri");
 
-               torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered);
+       return tok;
+}
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey(p, tctx, &r),
-                       "failed to call EnumPrinterKey");
-               if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+static bool upload_printer_driver_file(struct torture_context *tctx,
+                                      struct smbcli_state *cli,
+                                      struct torture_driver_context *d,
+                                      const char *file_name)
+{
+       XFILE *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 *local_name = talloc_asprintf(tctx, "%s/%s", d->local.driver_directory, file_name);
+       const char *remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
+
+       if (!file_name) {
+               return true;
+       }
 
-                       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_comment(tctx, "Uploading %s to %s\n", local_name, remote_name);
 
-                       r.in.offered = needed;
-                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey(p, tctx, &r),
-                               "failed to call EnumPrinterKey");
-               }
+       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));
+       }
 
-               if (offered[i] > 0) {
-                       torture_assert_werr_ok(tctx, r.out.result,
-                               "failed to call EnumPrinterKey");
-               }
+       f = x_fopen(local_name, O_RDONLY, 0);
+       if (f == NULL) {
+               torture_fail(tctx, talloc_asprintf(tctx, "failed to open local file: %s\n", local_name));
+       }
 
-               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));
+       buf = talloc_array(tctx, uint8_t, maxwrite);
+       if (!buf) {
+               return false;
+       }
 
-               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));
+       while (!x_feof(f)) {
+               int n = maxwrite;
+               int ret;
 
-               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 ((n = x_fread(buf, 1, n, f)) < 1) {
+                       if((n == 0) && x_feof(f))
+                               break; /* Empty local file. */
 
-               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_warning(tctx,
+                               "failed to read file: %s\n", strerror(errno));
+                       break;
+               }
 
-                       torture_assert_int_equal(tctx, *r.out.needed, calc_needed,
-                               "EnumPrinterKey unexpected size");
+               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;
                }
-       }
 
-       if (array) {
-               *array = key_buffer.string_array;
+               nread += n;
        }
 
+       x_fclose(f);
+
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_close(cli->tree, fnum),
+               "failed to close file");
+
        return true;
 }
 
-bool test_printer_keys(struct torture_context *tctx,
-                      struct dcerpc_pipe *p,
-                      struct policy_handle *handle)
+static bool connect_printer_driver_share(struct torture_context *tctx,
+                                        const char *server_name,
+                                        const char *share_name,
+                                        struct smbcli_state **cli)
 {
-       const char **key_array = NULL;
-       int i;
+       struct smbcli_options smb_options;
+       struct smbcli_session_options smb_session_options;
 
-       torture_comment(tctx, "\nTesting Printer Keys\n");
+       torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
+               share_name, server_name);
 
-       torture_assert(tctx, test_EnumPrinterKey(tctx, p, handle, "", &key_array),
-               "failed to call test_EnumPrinterKey");
+       lp_smbcli_options(tctx->lp_ctx, &smb_options);
+       lp_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
 
-       for (i=0; key_array && key_array[i]; i++) {
-               torture_assert(tctx, test_EnumPrinterKey(tctx, p, 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, p, handle, key_array[i]),
-                       "failed to call test_EnumPrinterDataEx");
-       }
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_full_connection(tctx, cli, server_name,
+                                       lp_smb_ports(tctx->lp_ctx),
+                                       share_name, NULL,
+                                       lp_socket_options(tctx->lp_ctx),
+                                       cmdline_credentials,
+                                       lp_resolve_context(tctx->lp_ctx),
+                                       tctx->ev,
+                                       &smb_options,
+                                       &smb_session_options,
+                                       lp_iconv_convenience(tctx->lp_ctx),
+                                       lp_gensec_settings(tctx, tctx->lp_ctx)),
+               "failed to open driver share");
 
        return true;
 }
 
-static bool test_one_printer(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            struct policy_handle *handle,
-                            const char *name)
+static bool upload_printer_driver(struct torture_context *tctx,
+                                 const char *server_name,
+                                 struct torture_driver_context *d)
 {
-       bool ret = true;
-
-       if (!test_printer_info(tctx, p, handle)) {
-               ret = false;
-       }
+       struct smbcli_state *cli;
+       const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+       int i;
 
-       if (!test_PrinterInfo_SD(tctx, p, handle)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
 
-       if (!test_PrinterInfo_DevMode(tctx, p, handle, name)) {
-               ret = false;
-       }
+       torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
+               server_name, share_name);
 
-       if (!test_ChangeID(tctx, p, handle)) {
-               ret = false;
+       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");
+               }
        }
 
-       if (!test_printer_keys(tctx, p, handle)) {
-               ret = false;
-       }
+       talloc_free(cli);
 
-       return ret;
+       return true;
 }
 
-static bool test_printer(struct torture_context *tctx,
-                        struct dcerpc_pipe *p)
+static bool remove_printer_driver_file(struct torture_context *tctx,
+                                      struct smbcli_state *cli,
+                                      struct torture_driver_context *d,
+                                      const char *file_name)
 {
-       bool ret = true;
-       struct policy_handle handle[2];
-       bool found = false;
-       const char *drivername = "Microsoft XPS Document Writer";
-       const char *portname = "LPT1:";
+       const char *remote_name;
+       const char *remote_dir =  driver_directory_dir(d->remote.driver_directory);
 
-       /* test printer created via AddPrinter */
-
-       if (!test_AddPrinter(tctx, p, &handle[0], TORTURE_PRINTER, drivername, portname)) {
-               return false;
+       if (!file_name) {
+               return true;
        }
 
-       if (!test_one_printer(tctx, p, &handle[0], TORTURE_PRINTER)) {
-               ret = false;
-       }
+       remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
 
-       if (!test_DeletePrinter(tctx, p, &handle[0])) {
-               ret = false;
-       }
+       torture_comment(tctx, "Removing %s\n", remote_name);
 
-       if (!test_EnumPrinters_findname(tctx, p, PRINTER_ENUM_LOCAL, 1,
-                                       TORTURE_PRINTER, &found)) {
-               ret = false;
-       }
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_unlink(cli->tree, remote_name),
+               "failed to unlink");
 
-       torture_assert(tctx, !found, "deleted printer still there");
+       return true;
+}
 
-       /* test printer created via AddPrinterEx */
+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;
 
-       if (!test_AddPrinterEx(tctx, p, &handle[1], TORTURE_PRINTER_EX, drivername, portname)) {
-               return false;
-       }
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
 
-       if (!test_one_printer(tctx, p, &handle[1], TORTURE_PRINTER_EX)) {
-               ret = false;
-       }
+       torture_comment(tctx, "Removing printer driver files from \\\\%s\\%s\n",
+               server_name, share_name);
 
-       if (!test_DeletePrinter(tctx, p, &handle[1])) {
-               ret = false;
+       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");
+       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");
+               }
        }
 
-       if (!test_EnumPrinters_findname(tctx, p, PRINTER_ENUM_LOCAL, 1,
-                                       TORTURE_PRINTER_EX, &found)) {
-               ret = false;
-       }
+       talloc_free(cli);
 
-       torture_assert(tctx, !found, "deleted printer still there");
+       return true;
 
-       return ret;
 }
 
-static bool test_architecture_buffer(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p)
+static bool test_add_driver_arg(struct torture_context *tctx,
+                               struct dcerpc_pipe *p,
+                               struct torture_driver_context *d)
 {
-       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];
+       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;
 
-       for (i=0; i < ARRAY_SIZE(architectures); i++) {
-
-               torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]);
+       torture_comment(tctx, "Testing PrinterDriver%s '%s' for environment '%s'\n",
+               d->ex ? "Ex" : "", d->info8.driver_name, d->local.environment);
 
-               u1.size = 0;
-               u1.client = NULL;
-               u1.user = NULL;
-               u1.build = 0;
-               u1.major = 3;
-               u1.minor = 0;
-               u1.processor = architectures[i];
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
 
-               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.level               = 1;
-               r.in.userlevel.level1   = &u1;
-               r.out.handle            = &handle;
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx(p, tctx, &r), "");
-               torture_assert_werr_ok(tctx, r.out.result, "");
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
 
-               {
-                       struct spoolss_EnumPrinters e;
-                       uint32_t count;
-                       union spoolss_PrinterInfo *info;
+       info8.version           = d->info8.version;
+       info8.driver_name       = d->info8.driver_name;
+       info8.architecture      = d->local.environment;
+       info8.driver_path       = d->info8.driver_path;
+       info8.data_file         = d->info8.data_file;
+       info8.config_file       = d->info8.config_file;
 
-                       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];
+       for (i=0; i < ARRAY_SIZE(levels); i++) {
 
-                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters(p, tctx, &e), "");
-#if 0
-                       torture_comment(tctx, "needed was %d\n", needed[i]);
-#endif
+               if (torture_setting_bool(tctx, "samba3", false)) {
+                       switch (levels[i]) {
+                       case 2:
+                       case 4:
+                       case 8:
+                               torture_comment(tctx, "skipping level %d against samba\n", levels[i]);
+                               continue;
+                       default:
+                               break;
+                       }
                }
 
-               torture_assert(tctx, test_ClosePrinter(tctx, p, &handle), "");
+               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);
        }
 
-       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]));
+       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);
+       info8.config_file       = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
+
+       for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+               if (torture_setting_bool(tctx, "samba3", false)) {
+                       switch (levels[i]) {
+                       case 2:
+                       case 4:
+                       case 8:
+                               torture_comment(tctx, "skipping level %d against samba\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);
        }
 
-       return true;
+       torture_assert(tctx,
+               remove_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to remove printer driver");
+
+       torture_comment(tctx, "\n");
+
+       return ret;
 }
 
-bool torture_rpc_spoolss(struct torture_context *torture)
+static bool test_add_driver_ex_64(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 void *private_data)
 {
-       NTSTATUS status;
-       struct dcerpc_pipe *p;
-       bool ret = true;
-       struct test_spoolss_context *ctx;
+       struct torture_driver_context *d =
+               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
 
-       status = torture_rpc_connection(torture, &p, &ndr_table_spoolss);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
-       }
+       d->local.environment            = talloc_strdup(d, "Windows x64");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+       d->info8.driver_name            = TORTURE_DRIVER_EX;
+       d->ex                           = true;
 
-       ctx = talloc_zero(torture, struct test_spoolss_context);
+       return test_add_driver_arg(tctx, p, d);
+}
 
-       ret &= test_OpenPrinter_server(torture, p, &ctx->server_handle);
-       ret &= test_GetPrinterData_list(torture, p, &ctx->server_handle);
-       ret &= test_EnumForms(torture, p, &ctx->server_handle, true);
-       ret &= test_AddForm(torture, p, &ctx->server_handle, true);
-       ret &= test_EnumPorts(torture, p, ctx);
-       ret &= test_GetPrinterDriverDirectory(torture, p, ctx);
-       ret &= test_GetPrintProcessorDirectory(torture, p, ctx);
-       ret &= test_EnumPrinterDrivers(torture, p, ctx, SPOOLSS_ARCHITECTURE_NT_X86);
-       ret &= test_EnumPrinterDrivers(torture, p, ctx, SPOOLSS_ARCHITECTURE_ALL);
-       ret &= test_EnumMonitors(torture, p, ctx);
-       ret &= test_EnumPrintProcessors(torture, p, ctx);
-       ret &= test_EnumPrintProcDataTypes(torture, p, ctx);
-       ret &= test_EnumPrinters(torture, p, ctx);
-       ret &= test_OpenPrinter_badname(torture, p, "__INVALID_PRINTER__");
-       ret &= test_OpenPrinter_badname(torture, p, "\\\\__INVALID_HOST__");
-       ret &= test_OpenPrinter_badname(torture, p, "");
-       ret &= test_OpenPrinter_badname(torture, p, "\\\\\\");
-       ret &= test_OpenPrinter_badname(torture, p, "\\\\\\__INVALID_PRINTER__");
-       ret &= test_OpenPrinter_badname(torture, p, talloc_asprintf(torture, "\\\\%s\\", dcerpc_server_name(p)));
-       ret &= test_OpenPrinter_badname(torture, p,
-                                       talloc_asprintf(torture, "\\\\%s\\__INVALID_PRINTER__", dcerpc_server_name(p)));
+static bool test_add_driver_ex_32(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 void *private_data)
+{
+       struct torture_driver_context *d =
+               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
 
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->info8.driver_name            = TORTURE_DRIVER_EX;
+       d->ex                           = true;
 
-       ret &= test_AddPort(torture, p);
-       ret &= test_EnumPorts_old(torture, p);
-       ret &= test_EnumPrinters_old(torture, p);
-       ret &= test_EnumPrinterDrivers_old(torture, p);
-       ret &= test_architecture_buffer(torture, p);
+       return test_add_driver_arg(tctx, p, d);
+}
 
-       return ret;
+static bool test_add_driver_64(struct torture_context *tctx,
+                              struct dcerpc_pipe *p,
+                              void *private_data)
+{
+       struct torture_driver_context *d =
+               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+
+       d->local.environment            = talloc_strdup(d, "Windows x64");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+       d->info8.driver_name            = TORTURE_DRIVER;
+       d->ex                           = false;
+
+       return test_add_driver_arg(tctx, p, d);
 }
 
-struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
+static bool test_add_driver_32(struct torture_context *tctx,
+                              struct dcerpc_pipe *p,
+                              void *private_data)
 {
-       struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-PRINTER");
+       struct torture_driver_context *d =
+               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->info8.driver_name            = TORTURE_DRIVER;
+       d->ex                           = false;
+
+       return test_add_driver_arg(tctx, p, d);
+}
+
+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,
-                                                       "printer", &ndr_table_spoolss);
+                                                       "driver", &ndr_table_spoolss);
+       struct torture_driver_context *t;
+
+       t = talloc_zero(mem_ctx, struct torture_driver_context);
+
+       t->info8.version        = SPOOLSS_DRIVER_VERSION_200X;
+       t->info8.driver_name    = NULL;
+       t->info8.architecture   = NULL;
+       t->info8.driver_path    = talloc_strdup(t, "pscript5.dll");
+       t->info8.data_file      = talloc_strdup(t, "cups6.ppd");
+       t->info8.config_file    = talloc_strdup(t, "cupsui6.dll");
+
+       torture_rpc_tcase_add_test_ex(tcase, "add_driver_64", test_add_driver_64, t);
+       torture_rpc_tcase_add_test_ex(tcase, "add_driver_ex_64", test_add_driver_ex_64, t);
 
-       torture_rpc_tcase_add_test(tcase, "printer", test_printer);
+       torture_rpc_tcase_add_test_ex(tcase, "add_driver_32", test_add_driver_32, t);
+       torture_rpc_tcase_add_test_ex(tcase, "add_driver_ex_32", test_add_driver_ex_32, t);
 
        return suite;
 }