Finish removal of iconv_convenience in public API's.
[bbaumbach/samba-autobuild/.git] / source4 / torture / rpc / spoolss.c
index 1517b4d57e0f8df322c9edff4275ab9ba0ce0914..efe975c76eb5859a7bd9e084673aa42011939105 100644 (file)
 #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 */
@@ -69,6 +77,45 @@ 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;
+       struct spoolss_DeviceMode *devmode;
+};
+
+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")
 
@@ -127,9 +174,9 @@ struct test_spoolss_context {
 
 #define DO_ROUND(size, n) (((size)+((n)-1)) & ~((n)-1))
 
-#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn##_info(tctx, ic, level, count, info);\
+       uint32_t size = ndr_size_##fn##_info(tctx, level, count, info);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" level %d (count: %d) got unexpected needed size: %d, we calculated: %d", level, count, needed, round_size);\
@@ -138,9 +185,9 @@ struct test_spoolss_context {
        }\
 } while(0)
 
-#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn##_info(tctx, ic, count, info);\
+       uint32_t size = ndr_size_##fn##_info(tctx, count, info);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" (count: %d) got unexpected needed size: %d, we calculated: %d", count, needed, round_size);\
@@ -149,9 +196,9 @@ struct test_spoolss_context {
        }\
 } while(0)
 
-#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn(info, level, ic, 0);\
+       uint32_t size = ndr_size_##fn(info, level, 0);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" level %d got unexpected needed size: %d, we calculated: %d", level, needed, round_size);\
@@ -276,7 +323,7 @@ static bool test_EnumPorts(struct torture_context *tctx,
 
                torture_assert(tctx, info, "EnumPorts returned no info");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, needed, 4);
 
                ctx->port_count[level]  = count;
                ctx->ports[level]       = info;
@@ -367,7 +414,7 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed");
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 2);
+               CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, needed, 2);
        }
 
        return true;
@@ -432,10 +479,117 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 2);
+               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, needed, 2);
+       }
+
+       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, 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,
@@ -443,53 +597,23 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
                                    struct test_spoolss_context *ctx,
                                    const char *architecture)
 {
-       NTSTATUS status;
        struct dcerpc_binding_handle *b = p->binding_handle;
-       struct spoolss_EnumPrinterDrivers r;
        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_r(b, 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_zero(ctx, needed);
-                       r.in.buffer = &blob;
-                       r.in.offered = needed;
-
-                       status = dcerpc_spoolss_EnumPrinterDrivers_r(b, 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;
@@ -631,7 +755,7 @@ static bool test_EnumMonitors(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, needed, 4);
 
                ctx->monitor_count[level]       = count;
                ctx->monitors[level]            = info;
@@ -709,7 +833,7 @@ static bool test_EnumPrintProcessors(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, r.in.level, count, needed, 4);
 
                ctx->print_processor_count[level]       = count;
                ctx->print_processors[level]            = info;
@@ -784,7 +908,7 @@ static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcDataTypes failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcDataTypes, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcDataTypes, info, r.in.level, count, needed, 4);
 
        }
 
@@ -837,7 +961,7 @@ static bool test_EnumPrinters(struct torture_context *tctx,
 
                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);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
 
                ctx->printer_count[level]       = count;
                ctx->printers[level]            = info;
@@ -956,7 +1080,7 @@ bool test_GetPrinter_level(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "GetPrinter failed");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, needed, 4);
 
        if (info && r.out.info) {
                *info = *r.out.info;
@@ -2151,7 +2275,8 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
 static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
                                     struct dcerpc_pipe *p,
                                     struct policy_handle *handle,
-                                    const char *name)
+                                    const char *name,
+                                    struct spoolss_DeviceMode *addprinter_devmode)
 {
        union spoolss_PrinterInfo info;
        struct spoolss_DeviceMode *devmode;
@@ -2167,6 +2292,12 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
 
        devmode = info.info8.devmode;
 
+       if (addprinter_devmode) {
+               if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) {
+                       torture_warning(tctx, "current global DM is != DM provided in addprinter");
+               }
+       }
+
        /* run tests */
 
        ret = test_PrinterInfo_DevModes(tctx, p, handle, name);
@@ -2239,7 +2370,7 @@ static bool test_GetForm_args(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, needed, 4);
 
        if (info_p) {
                *info_p = *r.out.info;
@@ -2306,7 +2437,7 @@ static bool test_EnumForms(struct torture_context *tctx,
 
        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);
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, needed, 4);
 
        if (info_p) {
                *info_p = info;
@@ -2578,28 +2709,30 @@ static bool test_Forms(struct torture_context *tctx,
                       struct dcerpc_binding_handle *winreg_handle,
                       struct policy_handle *hive_handle)
 {
-
-       struct spoolss_FormSize size;
-       struct spoolss_FormArea area;
+       const struct spoolss_FormSize size = {
+               .width  = 50,
+               .height = 25
+       };
+       const struct spoolss_FormArea area = {
+               .left   = 5,
+               .top    = 10,
+               .right  = 45,
+               .bottom = 15
+       };
        int i;
 
-       size.width      = 50;
-       size.height     = 25;
-       area.left       = 5;
-       area.top        = 10;
-       area.right      = 45;
-       area.bottom     = 15;
-
        struct {
                struct spoolss_AddFormInfo1 info1;
                WERROR expected_add_result;
                WERROR expected_delete_result;
        } forms[] = {
                {
-                       .info1.flags            = SPOOLSS_FORM_USER,
-                       .info1.form_name        = "testform_user",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .info1 = {
+                               .flags          = SPOOLSS_FORM_USER,
+                               .form_name      = "testform_user",
+                               .size           = size,
+                               .area           = area,
+                       },
                        .expected_add_result    = WERR_OK,
                        .expected_delete_result = WERR_OK
                },
@@ -2608,51 +2741,63 @@ static bool test_Forms(struct torture_context *tctx,
                again - gd
 
                {
-                       .info1.flags            = SPOOLSS_FORM_BUILTIN,
-                       .info1.form_name        = "testform_builtin",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .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,
-                       .info1.form_name        = "testform_printer",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .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,
-                       .info1.form_name        = "Letter",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .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,
-                       .info1.form_name        = "Letter",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .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,
-                       .info1.form_name        = "Letter",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .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,
-                       .info1.form_name        = "invalid_flags",
-                       .info1.size             = size,
-                       .info1.area             = area,
+                       .info1 = {
+                               .flags          = 12345,
+                               .form_name      = "invalid_flags",
+                               .size           = size,
+                               .area           = area,
+                       },
                        .expected_add_result    = WERR_INVALID_PARAM,
                        .expected_delete_result = WERR_INVALID_FORM_NAME
                }
@@ -2712,7 +2857,7 @@ static bool test_EnumPorts_old(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
 
-       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, needed, 4);
 
        return true;
 }
@@ -2784,12 +2929,13 @@ static bool test_GetJob_args(struct torture_context *tctx,
 
                status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
                torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
-               torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
-               torture_assert(tctx, r.out.info, "No job info returned");
-
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
        }
 
+       torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+       torture_assert(tctx, r.out.info, "No job info returned");
+
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, needed, 4);
+
        if (info_p) {
                *info_p = *r.out.info;
        }
@@ -2816,7 +2962,9 @@ static bool test_GetJob(struct torture_context *tctx,
 
 static bool test_SetJob(struct torture_context *tctx,
                        struct dcerpc_binding_handle *b,
-                       struct policy_handle *handle, uint32_t job_id,
+                       struct policy_handle *handle,
+                       uint32_t job_id,
+                       struct spoolss_JobInfoContainer *ctr,
                        enum spoolss_JobControl command)
 {
        NTSTATUS status;
@@ -2824,7 +2972,7 @@ static bool test_SetJob(struct torture_context *tctx,
 
        r.in.handle     = handle;
        r.in.job_id     = job_id;
-       r.in.ctr        = NULL;
+       r.in.ctr        = ctr;
        r.in.command    = command;
 
        switch (command) {
@@ -2935,7 +3083,7 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
                torture_assert(tctx, info, "No jobs returned");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, needed, 4);
 
        } else {
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
@@ -3042,6 +3190,10 @@ static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
 
        for (i=0; i < num_jobs; i++) {
                union spoolss_JobInfo ginfo;
+               const char *document_name;
+               const char *new_document_name = "any_other_docname";
+               struct spoolss_JobInfoContainer ctr;
+               struct spoolss_SetJobInfo1 info1;
 
                torture_assert_int_equal(tctx, info[i].info1.job_id, job_ids[i], "job id mismatch");
 
@@ -3050,15 +3202,48 @@ static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
                        "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++) {
+               document_name = ginfo.info1.document_name;
+
+               info1.job_id            = ginfo.info1.job_id;
+               info1.printer_name      = ginfo.info1.printer_name;
+               info1.server_name       = ginfo.info1.server_name;
+               info1.user_name         = ginfo.info1.user_name;
+               info1.document_name     = new_document_name;
+               info1.data_type         = ginfo.info1.data_type;
+               info1.text_status       = ginfo.info1.text_status;
+               info1.status            = ginfo.info1.status;
+               info1.priority          = ginfo.info1.priority;
+               info1.position          = ginfo.info1.position;
+               info1.total_pages       = ginfo.info1.total_pages;
+               info1.pages_printed     = ginfo.info1.pages_printed;
+               info1.submitted         = ginfo.info1.submitted;
+
+               ctr.level = 1;
+               ctr.info.info1 = &info1;
+
                torture_assert(tctx,
-                       test_SetJob(tctx, b, handle, info[i].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE),
-                       "failed to pause printjob");
+                       test_SetJob(tctx, b, handle, info[i].info1.job_id, &ctr, 0),
+                       "failed to call test_SetJob level 1");
+
                torture_assert(tctx,
-                       test_SetJob(tctx, b, handle, info[i].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME),
-                       "failed to resume printjob");
+                       test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo),
+                       "failed to call test_GetJob");
+
+               if (strequal(ginfo.info1.document_name, document_name)) {
+                       torture_warning(tctx,
+                               talloc_asprintf(tctx, "document_name did *NOT* change from '%s' to '%s'\n",
+                                       document_name, new_document_name));
+               }
+       }
+
+       for (i=0; i < num_jobs; i++) {
+               if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_PAUSE)) {
+                       torture_warning(tctx, "failed to pause printjob\n");
+               }
+               if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_RESUME)) {
+                       torture_warning(tctx, "failed to resume printjob\n");
+               }
        }
 
        return true;
@@ -3082,7 +3267,7 @@ static bool test_DoPrintTest(struct torture_context *tctx,
        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);
+               ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
        }
 
        return ret;
@@ -3190,7 +3375,7 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                talloc_asprintf(tctx, "GetPrinterData(%s) failed", r.in.value_name));
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
 
        if (type_p) {
                *type_p = type;
@@ -3236,8 +3421,7 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
 
        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");
@@ -3253,7 +3437,7 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                talloc_asprintf(tctx, "GetPrinterDataEx(%s - %s) failed", r.in.key_name, r.in.value_name));
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
 
        if (type_p) {
                *type_p = type;
@@ -3270,6 +3454,28 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        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, REG_SZ, blob);
+
+       return true;
+}
+
 static bool test_GetPrinterData_list(struct torture_context *tctx,
                                     struct dcerpc_pipe *p,
                                     struct policy_handle *handle,
@@ -3309,7 +3515,7 @@ static bool test_GetPrinterData_list(struct torture_context *tctx,
                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);
+                               *architecture = reg_val_data_string(tctx, REG_SZ, blob);
                        }
                }
        }
@@ -3450,7 +3656,7 @@ static bool test_EnumPrinterDataEx(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDataEx failed");
 
-       CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, needed, 1);
 
        if (count_p) {
                *count_p = count;
@@ -3492,7 +3698,7 @@ static bool test_EnumPrinterData_consistency(struct torture_context *tctx,
        torture_comment(tctx, "Testing EnumPrinterData vs EnumPrinterDataEx consistency\n");
 
        torture_assert(tctx,
-               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+               reg_string_to_val(tctx, 
                                  "REG_SZ", "torture_data1", &type, &blob), "");
 
        torture_assert(tctx,
@@ -3870,6 +4076,10 @@ static bool test_winreg_symbolic_link(struct torture_context *tctx,
        DATA_BLOB blob;
        const char *str;
 
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skip winreg symlink test against samba");
+       }
+
        torture_assert(tctx,
                test_winreg_OpenKey_opts(tctx, b, handle, symlink_keyname, REG_OPTION_OPEN_LINK, &key_handle),
                        "failed to open key link");
@@ -3883,7 +4093,7 @@ static bool test_winreg_symbolic_link(struct torture_context *tctx,
        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);
+       str = reg_val_data_string(tctx, REG_SZ, blob);
 
        torture_assert_str_equal(tctx, str, symlink_destination, "unexpected symlink target string");
 
@@ -3938,7 +4148,7 @@ static bool test_GetPrinterInfo_winreg(struct torture_context *tctx,
        printername = strip_unc(info.info2.printername);
        sharename = strip_unc(info.info2.sharename);
 
-#define test_sz(key, wname, iname) \
+#define test_sz(wname, iname) \
 do {\
        DATA_BLOB blob;\
        const char *str;\
@@ -3952,7 +4162,7 @@ do {\
                "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);\
+       str = reg_val_data_string(tctx, REG_SZ, blob);\
        if (w_size == 2 && iname == NULL) {\
                /*torture_comment(tctx, "%s: \"\", %s: (null)\n", #wname, #iname);\ */\
        } else {\
@@ -3961,7 +4171,7 @@ do {\
        }\
 } while(0);
 
-#define test_dword(key, wname, iname) \
+#define test_dword(wname, iname) \
 do {\
        uint32_t value;\
        enum winreg_Type w_type;\
@@ -3980,7 +4190,7 @@ do {\
                talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
 } while(0);
 
-#define test_dm(key, wname, iname) \
+#define test_dm(wname, iname) \
 do {\
        DATA_BLOB blob;\
        struct spoolss_DeviceMode dm;\
@@ -3995,14 +4205,14 @@ do {\
                "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_err = ndr_pull_struct_blob(&blob, tctx, &dm,\
                (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);\
        torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall dm");\
        torture_assert(tctx, test_devicemode_equal(tctx, &dm, iname),\
                "dm unequal");\
 } while(0);
 
-#define test_sd(key, wname, iname) \
+#define test_sd(wname, iname) \
 do {\
        DATA_BLOB blob;\
        struct security_descriptor sd;\
@@ -4017,13 +4227,37 @@ do {\
                "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_err = ndr_pull_struct_blob(&blob, tctx, &sd,\
                (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);\
        torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall sd");\
        torture_assert(tctx, test_security_descriptor_equal(tctx, &sd, iname),\
                "sd unequal");\
 } while(0);
 
+#define test_multi_sz(wname, iname) \
+do {\
+       DATA_BLOB blob;\
+       const char **array;\
+       enum winreg_Type w_type;\
+       uint32_t w_size;\
+       uint32_t w_length;\
+       uint8_t *w_data;\
+       int i;\
+       torture_assert(tctx,\
+               test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+                                      &w_type, &w_size, &w_length, &w_data),\
+               "failed to query winreg");\
+       torture_assert_int_equal(tctx, w_type, REG_MULTI_SZ, "unexpected type");\
+       blob = data_blob(w_data, w_size);\
+       torture_assert(tctx, \
+               pull_reg_multi_sz(tctx, &blob, &array),\
+               "failed to pull multi sz");\
+       for (i=0; array[i] != NULL; i++) {\
+               torture_assert_str_equal(tctx, array[i], iname[i],\
+                       talloc_asprintf(tctx, "%s - %s mismatch", #wname, iname[i]));\
+       }\
+} while(0);
+
 
        if (!test_winreg_symbolic_link(tctx, winreg_handle, hive_handle,
                                       TOP_LEVEL_CONTROL_PRINTERS_KEY,
@@ -4044,34 +4278,32 @@ do {\
                torture_assert(tctx,
                        test_winreg_OpenKey(tctx, winreg_handle, hive_handle, printer_key, &key_handle), "");
 
-               test_sz(keys[i], "Name", printername);
-               test_sz(keys[i], "Share Name", sharename);
-               test_sz(keys[i], "Port", info.info2.portname);
-               test_sz(keys[i], "Printer Driver", info.info2.drivername);
-               test_sz(keys[i], "Description", info.info2.comment);
-               test_sz(keys[i], "Location", info.info2.location);
-               test_sz(keys[i], "Separator File", info.info2.sepfile);
-               test_sz(keys[i], "Print Processor", info.info2.printprocessor);
-               test_sz(keys[i], "Datatype", info.info2.datatype);
-               test_sz(keys[i], "Parameters", info.info2.parameters);
+               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(keys[i], "Attributes", info.info2.attributes); */
-               test_dword(keys[i], "Priority", info.info2.priority);
-               test_dword(keys[i], "Default Priority", info.info2.defaultpriority);
+/*             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(keys[i], "StartTime", info.info2.starttime); */
-/*             test_dword(keys[i], "UntilTime", info.info2.untiltime); */
+/*             test_dword("StartTime", info.info2.starttime); */
+/*             test_dword("UntilTime", info.info2.untiltime); */
                /* winreg != spoolss */
-/*             test_dword(keys[i], "Status", info.info2.status); */
-               test_dm(keys[i], "Default DevMode", info.info2.devmode);
-               test_sd(keys[i], "Security", info.info2.secdesc);
+/*             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_sz
-#undef test_dword
 #undef test_dm
 #undef test_sd
 
@@ -4080,6 +4312,225 @@ do {\
        return true;
 }
 
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        const char *driver_name,
+                                        const char *architecture,
+                                        uint32_t level,
+                                        uint32_t client_major_version,
+                                        uint32_t client_minor_version,
+                                        union spoolss_DriverInfo *info_p,
+                                        WERROR *result);
+
+static const char *strip_path(const char *path)
+{
+       char *p;
+
+       if (path == NULL) {
+               return NULL;
+       }
+
+       p = strrchr(path, '\\');
+       if (p) {
+               return p+1;
+       }
+
+       return path;
+}
+
+static const char **strip_paths(const char **path_array)
+{
+       int i;
+
+       if (path_array == NULL) {
+               return NULL;
+       }
+
+       for (i=0; path_array[i] != NULL; i++) {
+               path_array[i] = strip_path(path_array[i]);
+       }
+
+       return path_array;
+}
+
+static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
+{
+       time_t t;
+       struct tm *tm;
+       t = nt_time_to_unix(nt);
+       tm = localtime(&t);
+
+       return talloc_asprintf(mem_ctx, "%02d/%02d/%04d",
+               tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+}
+
+static const char *driver_winreg_version(TALLOC_CTX *mem_ctx, uint64_t v)
+{
+       return talloc_asprintf(mem_ctx, "%u.%u.%u.%u",
+               (unsigned)((v >> 48) & 0xFFFF),
+               (unsigned)((v >> 32) & 0xFFFF),
+               (unsigned)((v >> 16) & 0xFFFF),
+               (unsigned)(v & 0xFFFF));
+}
+
+static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
+                                     struct dcerpc_binding_handle *b,
+                                     struct policy_handle *handle,
+                                     const char *printer_name,
+                                     const char *driver_name,
+                                     const char *environment,
+                                     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;
+
+       const char *driver_path;
+       const char *data_file;
+       const char *config_file;
+       const char *help_file;
+       const char **dependent_files;
+
+       const char *driver_date;
+       const char *inbox_driver_date;
+
+       const char *driver_version;
+       const char *inbox_driver_version;
+
+       torture_comment(tctx, "Testing Driver Info and winreg consistency\n");
+
+       driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
+                                    TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+                                    environment,
+                                    3,
+                                    driver_name);
+
+       torture_assert(tctx,
+               test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
+               "failed to open driver key");
+
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               goto try_level3;
+       }
+
+       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_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",                   ?); */
+
+ try_level3:
+
+       torture_assert(tctx,
+               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, 3, 0, &info, &result),
+               "failed to get driver info level 3");
+
+       driver_path     = strip_path(info.info3.driver_path);
+       data_file       = strip_path(info.info3.data_file);
+       config_file     = strip_path(info.info3.config_file);
+       help_file       = strip_path(info.info3.help_file);
+       dependent_files = strip_paths(info.info3.dependent_files);
+
+       test_sz("Configuration File",           config_file);
+       test_sz("Data File",                    data_file);
+       test_sz("Datatype",                     info.info3.default_datatype);
+       test_sz("Driver",                       driver_path);
+       test_sz("Help File",                    help_file);
+       test_sz("Monitor",                      info.info3.monitor_name);
+       test_multi_sz("Dependent Files",        dependent_files);
+/*     test_dword("Attributes",                ?); */
+       test_dword("Version",                   info.info3.version);
+/*     test_dword("TempDir",                   ?); */
+
+
+       torture_assert(tctx,
+               test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+
+       torture_comment(tctx, "Driver Info and winreg consistency test succeeded\n\n");
+
+       return true;
+}
+
+#undef test_sz
+#undef test_dword
+
 static bool test_SetPrinterData(struct torture_context *tctx,
                                struct dcerpc_binding_handle *b,
                                struct policy_handle *handle,
@@ -4140,7 +4591,7 @@ static bool test_SetPrinterData_matrix(struct torture_context *tctx,
                uint32_t needed;
 
                torture_assert(tctx,
-                       reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                       reg_string_to_val(tctx, 
                                          "REG_SZ", "dog", &type, &blob), "");
 
                torture_assert(tctx,
@@ -4297,14 +4748,14 @@ static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
                        break;
                case REG_SZ:
                        torture_assert(tctx,
-                               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
+                               reg_string_to_val(tctx, 
                                                  "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_string_to_val(tctx, 
                                                  "REG_SZ", string, &type, &data), "");
                        torture_assert(tctx, data_blob_realloc(tctx, &data, data.length + 2), "");
                        memset(&data.data[data.length - 2], '\0', 2);
@@ -4481,33 +4932,185 @@ static bool test_PrinterInfo_winreg(struct torture_context *tctx,
        return ret;
 }
 
-static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        struct policy_handle *handle,
-                                        uint32_t *change_id)
+static bool test_DriverInfo_winreg(struct torture_context *tctx,
+                                  struct dcerpc_pipe *p,
+                                  struct policy_handle *handle,
+                                  const char *printer_name,
+                                  const char *driver_name,
+                                  const char *environment)
 {
-       enum winreg_Type type;
-       uint8_t *data;
-       uint32_t needed;
+       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(tctx,
-               test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed),
-               "failed to call GetPrinterData");
+       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, type == REG_DWORD, "unexpected type");
-       torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-       *change_id = IVAL(data, 0);
+       ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, b2, &hive_handle);
 
-       return true;
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+       talloc_free(p2);
+
+       return ret;
 }
 
-static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
-                                          struct dcerpc_pipe *p,
-                                          struct policy_handle *handle,
-                                          uint32_t *change_id)
+static bool test_PrinterData_DsSpooler(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p,
+                                      struct policy_handle *handle,
+                                      const char *printer_name)
 {
-       enum winreg_Type type;
+       struct spoolss_SetPrinterInfoCtr info_ctr;
+       struct spoolss_DevmodeContainer devmode_ctr;
+       struct sec_desc_buf secdesc_ctr;
+       union spoolss_SetPrinterInfo sinfo;
+       union spoolss_PrinterInfo info;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *pname;
+
+       ZERO_STRUCT(info_ctr);
+       ZERO_STRUCT(devmode_ctr);
+       ZERO_STRUCT(secdesc_ctr);
+
+       torture_comment(tctx, "Testing DsSpooler <-> SetPrinter relations\n");
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+
+       torture_assert(tctx,
+               PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo),
+               "failed to convert");
+
+       info_ctr.level = 2;
+       info_ctr.info = sinfo;
+
+#define TEST_SZ(wname, iname) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       DATA_BLOB blob;\
+       const char *str;\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+       blob = data_blob_const(data, needed);\
+       torture_assert(tctx,\
+               pull_reg_sz(tctx, &blob, &str),\
+               "failed to pull REG_SZ");\
+       torture_assert_str_equal(tctx, str, iname, "unexpected result");\
+} while(0);
+
+
+#define TEST_SET_SZ(wname, iname, val) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       DATA_BLOB blob;\
+       const char *str;\
+       sinfo.info2->iname = val;\
+       torture_assert(tctx,\
+               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+               "failed to call SetPrinter");\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+       blob = data_blob_const(data, needed);\
+       torture_assert(tctx,\
+               pull_reg_sz(tctx, &blob, &str),\
+               "failed to pull REG_SZ");\
+       torture_assert_str_equal(tctx, str, val, "unexpected result");\
+} while(0);
+
+#define TEST_SET_DWORD(wname, iname, val) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       uint32_t value;\
+       sinfo.info2->iname = val;\
+       torture_assert(tctx,\
+               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+               "failed to call SetPrinter");\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_DWORD, "unexpected type");\
+       torture_assert_int_equal(tctx, needed, 4, "unexpected length");\
+       value = IVAL(data, 0); \
+       torture_assert_int_equal(tctx, value, val, "unexpected result");\
+} while(0);
+
+       TEST_SET_SZ("description", comment, "newval");
+       TEST_SET_SZ("location", location, "newval");
+/*     TEST_SET_DWORD("priority", priority, 25); */
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+
+       TEST_SZ("description", info.info2.comment);
+       TEST_SZ("driverName", info.info2.drivername);
+       TEST_SZ("location", info.info2.location);
+
+       pname = strrchr(info.info2.printername, '\\');
+       if (pname == NULL) {
+               pname = info.info2.printername;
+       } else {
+               pname++;
+       }
+       TEST_SZ("printerName", pname);
+       /* TEST_SZ("printSeparatorFile", info.info2.sepfile); */
+       /* TEST_SZ("printShareName", info.info2.sharename); */
+
+       /* FIXME gd: complete the list */
+
+#undef TEST_SZ
+#undef TEST_SET_SZ
+#undef TEST_DWORD
+
+       torture_comment(tctx, "DsSpooler <-> SetPrinter relations test succeeded\n\n");
+
+       return true;
+}
+
+static bool test_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;
 
@@ -4659,7 +5262,7 @@ static bool test_SecondaryClosePrinter(struct torture_context *tctx,
                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");
@@ -4674,12 +5277,9 @@ static bool test_SecondaryClosePrinter(struct torture_context *tctx,
        cp.out.handle = handle;
 
        status = dcerpc_spoolss_ClosePrinter_r(p2->binding_handle, tctx, &cp);
-       torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NET_WRITE_FAULT,
+       torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
                        "ERROR: Allowed close on secondary connection");
 
-       torture_assert_int_equal(tctx, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
-                                "Unexpected fault code");
-
        talloc_free(p2);
 
        return true;
@@ -5075,7 +5675,7 @@ static bool test_EnumPrinters_old(struct torture_context *tctx,
 
                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);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
 
                if (!info) {
                        torture_comment(tctx, "No printers returned\n");
@@ -5138,65 +5738,98 @@ static bool test_GetPrinterDriver(struct torture_context *tctx,
        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);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
 
        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)
+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;
-       uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 };
        uint32_t needed;
        uint32_t server_major_version;
        uint32_t server_minor_version;
-       int i;
 
        r.in.handle = handle;
        r.in.architecture = architecture;
-       r.in.client_major_version = 3;
-       r.in.client_minor_version = 0;
+       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;
 
-       for (i=0;i<ARRAY_SIZE(levels);i++) {
+       torture_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n",
+               driver_name, r.in.level);
 
-               r.in.buffer = NULL;
-               r.in.offered = 0;
-               r.in.level = levels[i];
+       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");
+       }
 
-               torture_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n",
-                       driver_name, r.in.level);
+       if (result_p) {
+               *result_p = r.out.result;
+       }
 
-               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 (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;
                }
+       }
 
-               if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) {
-                       switch (r.in.level) {
-                       case 101:
-                       case 8:
-                               continue;
-                       default:
-                               break;
-                       }
-               }
+       torture_assert_werr_ok(tctx, r.out.result,
+               "failed to call GetPrinterDriver2");
 
-               torture_assert_werr_ok(tctx, r.out.result,
-                       "failed to call GetPrinterDriver2");
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
+
+       if (info_p) {
+               *info_p = *r.out.info;
+       }
+
+       return true;
+}
+
+static bool test_GetPrinterDriver2(struct torture_context *tctx,
+                                  struct dcerpc_binding_handle *b,
+                                  struct policy_handle *handle,
+                                  const char *driver_name,
+                                  const char *architecture)
+{
+       uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 };
+       int i;
+
+
+       for (i=0;i<ARRAY_SIZE(levels);i++) {
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               torture_assert(tctx,
+                       test_GetPrinterDriver2_level(tctx, b, handle, driver_name, architecture, levels[i], 3, 0, NULL, NULL),
+                       "");
        }
 
        return true;
@@ -5206,50 +5839,24 @@ static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
                                        struct dcerpc_pipe *p,
                                        const char *environment)
 {
-       struct spoolss_EnumPrinterDrivers r;
-       NTSTATUS status;
        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 needed;
                uint32_t count;
                union spoolss_DriverInfo *info;
 
-               r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-               r.in.environment = environment;
-               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_r(b, tctx, &r);
-
-               torture_assert_ntstatus_ok(tctx, status, "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;
-                       status = dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r);
-               }
-
-               torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
-
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+               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;
                }
-
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
        }
 
        return true;
@@ -5367,7 +5974,8 @@ static bool test_AddPrinter_wellknown(struct torture_context *tctx,
        ZERO_STRUCT(userlevel_ctr);
        ZERO_STRUCT(info1);
 
-       torture_comment(tctx, "Testing AddPrinter%s level 1\n", ex ? "Ex":"");
+       torture_comment(tctx, "Testing AddPrinter%s(%s) level 1\n",
+                       ex ? "Ex":"", printername);
 
        /* try to add printer to wellknown printer list (level 1) */
 
@@ -5449,6 +6057,7 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
                                   const char *printername,
                                   const char *drivername,
                                   const char *portname,
+                                  struct spoolss_DeviceMode *devmode,
                                   bool ex)
 {
        WERROR result;
@@ -5468,7 +6077,10 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
        ZERO_STRUCT(secdesc_ctr);
        ZERO_STRUCT(userlevel_ctr);
 
-       torture_comment(tctx, "Testing AddPrinter%s level 2\n", ex ? "Ex":"");
+       torture_comment(tctx, "Testing AddPrinter%s(%s) level 2\n",
+                       ex ? "Ex":"", printername);
+
+       devmode_ctr.devmode = devmode;
 
        userlevel_ctr.level = 1;
 
@@ -5585,58 +6197,6 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
        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)
-{
-       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;
-               }
-       }
-
-       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;
-       }
-
-       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;
-
-       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 (!test_AddPrinter_normal(tctx, p, handle_p,
-                                   printername, drivername, portname,
-                                   false)) {
-               torture_comment(tctx, "failed to add printer to printer list\n");
-               ret = false;
-       }
-
-       return ret;
-}
-
 static bool test_printer_info(struct torture_context *tctx,
                              struct dcerpc_binding_handle *b,
                              struct policy_handle *handle)
@@ -5772,7 +6332,11 @@ bool test_printer_keys(struct torture_context *tctx,
 static bool test_one_printer(struct torture_context *tctx,
                             struct dcerpc_pipe *p,
                             struct policy_handle *handle,
-                            const char *name)
+                            const char *name,
+                            const char *drivername,
+                            const char *environment,
+                            bool have_driver,
+                            struct spoolss_DeviceMode *devmode)
 {
        bool ret = true;
        struct dcerpc_binding_handle *b = p->binding_handle;
@@ -5797,7 +6361,7 @@ static bool test_one_printer(struct torture_context *tctx,
                ret = false;
        }
 
-       if (!test_PrinterInfo_DevMode(tctx, p, handle, name)) {
+       if (!test_PrinterInfo_DevMode(tctx, p, handle, name, devmode)) {
                ret = false;
        }
 
@@ -5825,6 +6389,16 @@ static bool test_one_printer(struct torture_context *tctx,
                ret = false;
        }
 
+       if (!test_PrinterData_DsSpooler(tctx, p, handle, name)) {
+               ret = false;
+       }
+
+       if (have_driver) {
+               if (!test_DriverInfo_winreg(tctx, p, handle, name, drivername, environment)) {
+                       ret = false;
+               }
+       }
+
        if (!test_printer_rename(tctx, p, handle, name)) {
                ret = false;
        }
@@ -5832,53 +6406,106 @@ static bool test_one_printer(struct torture_context *tctx,
        return ret;
 }
 
-static bool test_printer(struct torture_context *tctx,
-                        struct dcerpc_pipe *p)
+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)
 {
-       bool ret = true;
-       struct policy_handle handle[2];
-       bool found = false;
-       const char *drivername = "Microsoft XPS Document Writer";
-       const char *portname = "LPT1:";
+       union spoolss_PrinterInfo info;
+       struct policy_handle new_handle, new_handle2;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
-       /* test printer created via AddPrinter */
+       torture_comment(tctx, "Testing c_setprinter\n");
 
-       if (!test_AddPrinter(tctx, p, &handle[0], TORTURE_PRINTER, drivername, portname)) {
-               return false;
-       }
+       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);
 
-       if (!test_one_printer(tctx, p, &handle[0], TORTURE_PRINTER)) {
-               ret = false;
-       }
+       /* check if c_setprinter on 1st handle increases after a printer has
+        * been added */
 
-       if (!test_DeletePrinter(tctx, b, &handle[0])) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, NULL, false),
+               "failed to add new printer");
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, 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);
 
-       if (!test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
-                                       TORTURE_PRINTER, &found)) {
-               ret = false;
-       }
+       /* check if c_setprinter on new handle increases after a printer has
+        * been added */
 
-       torture_assert(tctx, !found, "deleted printer still there");
+       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);
 
-       /* test printer created via AddPrinterEx */
+       /* open the new printer and check if c_setprinter increases */
 
-       if (!test_AddPrinterEx(tctx, p, &handle[1], TORTURE_PRINTER_EX, drivername, portname)) {
-               return false;
+       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_add_printer_args_with_driver(struct torture_context *tctx,
+                                             struct dcerpc_pipe *p,
+                                             struct torture_printer_context *t)
+{
+       bool ret = true;
+       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->devmode, t->ex),
+                       "failed to add printer");
+       }
+
+       if (!test_csetprinter(tctx, p, &handle, printer_name2, driver_name, port_name)) {
+               ret = false;
        }
 
-       if (!test_one_printer(tctx, p, &handle[1], TORTURE_PRINTER_EX)) {
+       if (!test_one_printer(tctx, p, &handle, printer_name, driver_name, t->driver.remote.environment, t->have_driver, t->devmode)) {
                ret = false;
        }
 
-       if (!test_DeletePrinter(tctx, b, &handle[1])) {
+       if (!test_DeletePrinter(tctx, b, &handle)) {
                ret = false;
        }
 
        if (!test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
-                                       TORTURE_PRINTER_EX, &found)) {
+                                       printer_name, &found)) {
                ret = false;
        }
 
@@ -5887,6 +6514,211 @@ static bool test_printer(struct torture_context *tctx,
        return ret;
 }
 
+static bool compose_local_driver_directory(struct torture_context *tctx,
+                                          const char *environment,
+                                          const char *local_dir,
+                                          const char **path)
+{
+       char *p;
+
+       p = strrchr(local_dir, '/');
+       if (!p) {
+               return NULL;
+       }
+       p++;
+
+       if (strequal(environment, "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);
+       }
+
+       return true;
+}
+
+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");
+       }
+
+       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;
+       }
+
+       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;
+       }
+
+       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_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 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;
+       t->devmode              = NULL;
+
+       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;
+       t->devmode              = NULL;
+
+       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;
+       t->devmode              = NULL;
+
+       return test_add_printer_args(tctx, p, t);
+}
+
+static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
+                                                    const char *devicename)
+{
+       struct spoolss_DeviceMode *r;
+
+       r = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+       if (r == NULL) {
+               return NULL;
+       }
+
+       r->devicename           = talloc_strdup(r, devicename);
+       r->specversion          = DMSPEC_NT4_AND_ABOVE;
+       r->driverversion        = 0x0600;
+       r->size                 = 0x00dc;
+       r->__driverextra_length = 0;
+       r->fields               = DEVMODE_FORMNAME |
+                                 DEVMODE_TTOPTION |
+                                 DEVMODE_PRINTQUALITY |
+                                 DEVMODE_DEFAULTSOURCE |
+                                 DEVMODE_COPIES |
+                                 DEVMODE_SCALE |
+                                 DEVMODE_PAPERSIZE |
+                                 DEVMODE_ORIENTATION;
+       r->orientation          = DMORIENT_PORTRAIT;
+       r->papersize            = DMPAPER_LETTER;
+       r->paperlength          = 0;
+       r->paperwidth           = 0;
+       r->scale                = 100;
+       r->copies               = 55;
+       r->defaultsource        = DMBIN_FORMSOURCE;
+       r->printquality         = DMRES_HIGH;
+       r->color                = DMRES_MONOCHROME;
+       r->duplex               = DMDUP_SIMPLEX;
+       r->yresolution          = 0;
+       r->ttoption             = DMTT_SUBDEV;
+       r->collate              = DMCOLLATE_FALSE;
+       r->formname             = talloc_strdup(r, "Letter");
+
+       return r;
+}
+
+static bool test_add_printer_with_devmode(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;
+       t->devmode              = torture_devicemode(t, TORTURE_PRINTER_EX);
+
+       return test_add_printer_args(tctx, p, t);
+}
+
 static bool test_architecture_buffer(struct torture_context *tctx,
                                     struct dcerpc_pipe *p)
 {
@@ -6008,7 +6840,1115 @@ struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
        struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
                                                        "printer", &ndr_table_spoolss);
 
-       torture_rpc_tcase_add_test(tcase, "printer", test_printer);
+       struct torture_printer_context *t;
+
+       t = talloc_zero(mem_ctx, struct torture_printer_context);
+
+       t->driver.info8.version                 = SPOOLSS_DRIVER_VERSION_200X;
+       t->driver.info8.driver_name             = TORTURE_DRIVER;
+       t->driver.info8.driver_path             = "pscript5.dll";
+       t->driver.info8.data_file               = "cups6.ppd";
+       t->driver.info8.config_file             = "ps5ui.dll";
+       t->driver.info8.help_file               = "pscript.hlp";
+       t->driver.info8.default_datatype        = "RAW";
+       t->driver.info8.dependent_files         = talloc_zero(t, struct spoolss_StringArray);
+       t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1);
+       t->driver.info8.dependent_files->string[0] = "pscript5.dll";
+       t->driver.info8.dependent_files->string[1] = "cups6.ppd";
+       t->driver.info8.dependent_files->string[2] = "ps5ui.dll";
+       t->driver.info8.dependent_files->string[3] = "pscript.hlp";
+       t->driver.info8.dependent_files->string[4] = "pscript.ntf";
+       t->driver.info8.dependent_files->string[5] = "cups6.ini";
+       t->driver.info8.dependent_files->string[6] = "cupsps6.dll";
+       t->driver.info8.dependent_files->string[7] = "cupsui6.dll";
+
+       t->driver.local.driver_directory= "/usr/share/cups/drivers";
+
+       t->info2.drivername             = "Microsoft XPS Document Writer";
+       t->info2.portname               = "LPT1:";
+
+       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);
+
+       torture_rpc_tcase_add_test_ex(tcase, "add_printer_with_devmode", test_add_printer_with_devmode, t);
+
+       return suite;
+}
+
+static bool test_GetPrinterDriverDirectory_getdir(struct torture_context *tctx,
+                                                 struct dcerpc_binding_handle *b,
+                                                 const char *server,
+                                                 const char *environment,
+                                                 const char **dir_p)
+{
+       struct spoolss_GetPrinterDriverDirectory r;
+       uint32_t needed;
+
+       r.in.server             = server;
+       r.in.environment        = environment;
+       r.in.level              = 1;
+       r.in.buffer             = NULL;
+       r.in.offered            = 0;
+       r.out.needed            = &needed;
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+               "failed to query driver directory");
+
+       if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+               r.in.buffer = &blob;
+               r.in.offered = needed;
+
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+                       "failed to query driver directory");
+       }
+
+       torture_assert_werr_ok(tctx, r.out.result,
+               "failed to query driver directory");
+
+       if (dir_p) {
+               *dir_p = r.out.info->info1.directory_name;
+       }
+
+       return true;
+}
+
+static const char *get_driver_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+       if (info_ctr == NULL) {
+               return NULL;
+       }
+
+       switch (info_ctr->level) {
+       case 1:
+               return info_ctr->info.info1->driver_name;
+       case 2:
+               return info_ctr->info.info2->driver_name;
+       case 3:
+               return info_ctr->info.info3->driver_name;
+       case 4:
+               return info_ctr->info.info4->driver_name;
+       case 6:
+               return info_ctr->info.info6->driver_name;
+       case 8:
+               return info_ctr->info.info8->driver_name;
+       default:
+               return NULL;
+       }
+}
+
+static const char *get_environment_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+       if (info_ctr == NULL) {
+               return NULL;
+       }
+
+       switch (info_ctr->level) {
+       case 2:
+               return info_ctr->info.info2->architecture;
+       case 3:
+               return info_ctr->info.info3->architecture;
+       case 4:
+               return info_ctr->info.info4->architecture;
+       case 6:
+               return info_ctr->info.info6->architecture;
+       case 8:
+               return info_ctr->info.info8->architecture;
+       default:
+               return NULL;
+       }
+}
+
+
+static bool test_AddPrinterDriver_exp(struct torture_context *tctx,
+                                     struct dcerpc_binding_handle *b,
+                                     const char *servername,
+                                     struct spoolss_AddDriverInfoCtr *info_ctr,
+                                     WERROR expected_result)
+{
+       struct spoolss_AddPrinterDriver r;
+       const char *drivername = get_driver_from_info(info_ctr);
+       const char *environment = get_environment_from_info(info_ctr);
+
+       r.in.servername = servername;
+       r.in.info_ctr = info_ctr;
+
+       torture_comment(tctx, "Testing AddPrinterDriver(%s) level: %d, environment: '%s'\n",
+               drivername, info_ctr->level, environment);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_AddPrinterDriver_r(b, tctx, &r),
+               "spoolss_AddPrinterDriver failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "spoolss_AddPrinterDriver failed with unexpected result");
+
+       return true;
+
+}
+
+static bool test_AddPrinterDriverEx_exp(struct torture_context *tctx,
+                                       struct dcerpc_binding_handle *b,
+                                       const char *servername,
+                                       struct spoolss_AddDriverInfoCtr *info_ctr,
+                                       uint32_t flags,
+                                       WERROR expected_result)
+{
+       struct spoolss_AddPrinterDriverEx r;
+       const char *drivername = get_driver_from_info(info_ctr);
+       const char *environment = get_environment_from_info(info_ctr);
+
+       r.in.servername = servername;
+       r.in.info_ctr = info_ctr;
+       r.in.flags = flags;
+
+       torture_comment(tctx, "Testing AddPrinterDriverEx(%s) level: %d, environment: '%s'\n",
+               drivername, info_ctr->level, environment);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_AddPrinterDriverEx_r(b, tctx, &r),
+               "AddPrinterDriverEx failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "AddPrinterDriverEx failed with unexpected result");
+
+       return true;
+}
+
+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_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo1 info1;
+
+       ZERO_STRUCT(info1);
+
+       info_ctr.level = 1;
+       info_ctr.info.info1 = &info1;
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_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");
+       }
+
+       info1.driver_name = r->driver_name;
+
+       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_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)
+{
+       struct spoolss_AddDriverInfoCtr info_ctr;
+       struct spoolss_AddDriverInfo2 info2;
+
+       ZERO_STRUCT(info2);
+
+       info_ctr.level = 2;
+       info_ctr.info.info2 = &info2;
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_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.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");
+       }
+
+       info2.architecture = r->architecture;
+
+       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.driver_path = r->driver_path;
+
+       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.data_file = r->data_file;
+
+       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.config_file = r->config_file;
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, 0, WERR_INVALID_PARAM),
+                       "failed to test AddPrinterDriverEx");
+       }
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+                       "failed to test AddPrinterDriverEx level 2");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+                       "failed to test AddPrinterDriver level 2");
+       }
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name),
+               "failed to find added printer driver");
+
+       return true;
+}
+
+static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
+                                              struct dcerpc_binding_handle *b,
+                                              const char *server_name,
+                                              struct spoolss_AddDriverInfo8 *r,
+                                              uint32_t flags,
+                                              bool ex)
+{
+       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");
+       }
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name),
+               "failed to find added printer driver");
+
+       return true;
+}
+
+static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx,
+                                              struct dcerpc_binding_handle *b,
+                                              const char *server_name,
+                                              struct spoolss_AddDriverInfo8 *r,
+                                              uint32_t flags,
+                                              bool ex)
+{
+       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_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name),
+               "failed to find added printer driver");
+
+       return true;
+}
+
+static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
+                                              struct dcerpc_binding_handle *b,
+                                              const char *server_name,
+                                              struct spoolss_AddDriverInfo8 *r,
+                                              uint32_t flags,
+                                              bool ex)
+{
+       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");
+       }
+
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+       if (!ex) {
+               return true;
+       }
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name),
+               "failed to find added printer driver");
+
+       return true;
+}
+
+static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
+                                              struct dcerpc_binding_handle *b,
+                                              const char *server_name,
+                                              struct spoolss_AddDriverInfo8 *r,
+                                              uint32_t flags,
+                                              bool ex)
+{
+       struct spoolss_AddDriverInfoCtr info_ctr;
+
+       info_ctr.level = 8;
+       info_ctr.info.info8 = r;
+
+       if (ex) {
+               torture_assert(tctx,
+                       test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+                       "failed to test AddPrinterDriverEx level 8");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_UNKNOWN_LEVEL),
+                       "failed to test AddPrinterDriver level 8");
+       }
+
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+       if (!ex) {
+               return true;
+       }
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name),
+               "failed to find added printer driver");
+
+       return true;
+}
+
+static bool test_DeletePrinterDriver_exp(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        const char *server,
+                                        const char *driver,
+                                        const char *environment,
+                                        WERROR expected_result)
+{
+       struct spoolss_DeletePrinterDriver r;
+
+       r.in.server = server;
+       r.in.architecture = environment;
+       r.in.driver = driver;
+
+       torture_comment(tctx, "Testing DeletePrinterDriver(%s)\n", driver);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_DeletePrinterDriver_r(b, tctx, &r),
+               "DeletePrinterDriver failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "DeletePrinterDriver failed with unexpected result");
+
+       return true;
+}
+
+static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx,
+                                          struct dcerpc_binding_handle *b,
+                                          const char *server,
+                                          const char *driver,
+                                          const char *environment,
+                                          uint32_t delete_flags,
+                                          uint32_t version,
+                                          WERROR expected_result)
+{
+       struct spoolss_DeletePrinterDriverEx r;
+
+       r.in.server = server;
+       r.in.architecture = environment;
+       r.in.driver = driver;
+       r.in.delete_flags = delete_flags;
+       r.in.version = version;
+
+       torture_comment(tctx, "Testing DeletePrinterDriverEx(%s)\n", driver);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_DeletePrinterDriverEx_r(b, tctx, &r),
+               "DeletePrinterDriverEx failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "DeletePrinterDriverEx failed with unexpected result");
+
+       return true;
+}
+
+static bool test_DeletePrinterDriver(struct torture_context *tctx,
+                                    struct dcerpc_binding_handle *b,
+                                    const char *server_name,
+                                    const char *driver,
+                                    const char *environment)
+{
+       torture_assert(tctx,
+               test_DeletePrinterDriver_exp(tctx, b, server_name, driver, "FOOBAR", WERR_INVALID_ENVIRONMENT),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK),
+               "failed to delete driver");
+
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver)) {
+               torture_fail(tctx, "deleted driver still enumerated");
+       }
+
+       torture_assert(tctx,
+               test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_UNKNOWN_PRINTER_DRIVER),
+               "2nd delete failed");
+
+       return true;
+}
+
+static bool test_DeletePrinterDriverEx(struct torture_context *tctx,
+                                      struct dcerpc_binding_handle *b,
+                                      const char *server_name,
+                                      const char *driver,
+                                      const char *environment,
+                                      uint32_t delete_flags,
+                                      uint32_t version)
+{
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, "FOOBAR", delete_flags, version, WERR_INVALID_ENVIRONMENT),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_OK),
+               "failed to delete driver");
+
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver)) {
+               torture_fail(tctx, "deleted driver still enumerated");
+       }
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_UNKNOWN_PRINTER_DRIVER),
+               "2nd delete failed");
+
+       return true;
+}
+
+static bool test_PrinterDriver_args(struct torture_context *tctx,
+                                   struct dcerpc_binding_handle *b,
+                                   const char *server_name,
+                                   uint32_t level,
+                                   struct spoolss_AddDriverInfo8 *r,
+                                   uint32_t add_flags,
+                                   uint32_t delete_flags,
+                                   uint32_t delete_version,
+                                   bool ex)
+{
+       bool ret = true;
+
+       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 (ret == false) {
+               return ret;
+       }
+
+       if (level == 1) {
+               return ret;
+       }
+
+       /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+       if (!ex && (level == 6 || level == 8)) {
+               return ret;
+       }
+
+       if (ex) {
+               return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version);
+       } else {
+               return test_DeletePrinterDriver(tctx, b, server_name, r->driver_name, r->architecture);
+       }
+}
+
+static bool fillup_printserver_info(struct torture_context *tctx,
+                                   struct dcerpc_pipe *p,
+                                   struct torture_driver_context *d)
+{
+       struct policy_handle server_handle;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       torture_assert(tctx,
+               test_OpenPrinter_server(tctx, p, &server_handle),
+               "failed to open printserver");
+       torture_assert(tctx,
+               test_get_environment(tctx, b, &server_handle, &d->remote.environment),
+               "failed to get environment");
+       torture_assert(tctx,
+               test_ClosePrinter(tctx, b, &server_handle),
+               "failed to close printserver");
+
+       torture_assert(tctx,
+               test_GetPrinterDriverDirectory_getdir(tctx, b, server_name_slash,
+                       d->local.environment ? d->local.environment : d->remote.environment,
+                       &d->remote.driver_directory),
+               "failed to get driver directory");
+
+       return true;
+}
+
+static const char *driver_directory_dir(const char *driver_directory)
+{
+       char *p;
+
+       p = strrchr(driver_directory, '\\');
+       if (p) {
+               return p+1;
+       }
+
+       return NULL;
+}
+
+static const char *driver_directory_share(struct torture_context *tctx,
+                                         const char *driver_directory)
+{
+       const char *p;
+       char *tok;
+
+       if (driver_directory[0] == '\\' && driver_directory[1] == '\\') {
+               driver_directory += 2;
+       }
+
+       p = talloc_strdup(tctx, driver_directory);
+
+       torture_assert(tctx,
+               next_token_talloc(tctx, &p, &tok, "\\"),
+               "cannot explode uri");
+       torture_assert(tctx,
+               next_token_talloc(tctx, &p, &tok, "\\"),
+               "cannot explode uri");
+
+       return tok;
+}
+
+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_comment(tctx, "Uploading %s to %s\n", local_name, remote_name);
+
+       fnum = smbcli_open(cli->tree, remote_name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+       if (fnum == -1) {
+               torture_fail(tctx, talloc_asprintf(tctx, "failed to open remote file: %s\n", remote_name));
+       }
+
+       f = 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));
+       }
+
+       buf = talloc_array(tctx, uint8_t, maxwrite);
+       if (!buf) {
+               return false;
+       }
+
+       while (!x_feof(f)) {
+               int n = maxwrite;
+               int ret;
+
+               if ((n = x_fread(buf, 1, n, f)) < 1) {
+                       if((n == 0) && x_feof(f))
+                               break; /* Empty local file. */
+
+                       torture_warning(tctx,
+                               "failed to read file: %s\n", strerror(errno));
+                       break;
+               }
+
+               ret = smbcli_write(cli->tree, fnum, 0, buf, nread + start, n);
+
+               if (n != ret) {
+                       torture_warning(tctx,
+                               "failed to write file: %s\n", smbcli_errstr(cli->tree));
+                       break;
+               }
+
+               nread += n;
+       }
+
+       x_fclose(f);
+
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_close(cli->tree, fnum),
+               "failed to close file");
+
+       return true;
+}
+
+static bool connect_printer_driver_share(struct torture_context *tctx,
+                                        const char *server_name,
+                                        const char *share_name,
+                                        struct smbcli_state **cli)
+{
+       struct smbcli_options smb_options;
+       struct smbcli_session_options smb_session_options;
+
+       torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
+               share_name, server_name);
+
+       lp_smbcli_options(tctx->lp_ctx, &smb_options);
+       lp_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+
+       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_gensec_settings(tctx, tctx->lp_ctx)),
+               "failed to open driver share");
+
+       return true;
+}
+
+static bool upload_printer_driver(struct torture_context *tctx,
+                                 const char *server_name,
+                                 struct torture_driver_context *d)
+{
+       struct smbcli_state *cli;
+       const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+       int i;
+
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
+
+       torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
+               server_name, share_name);
+
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+               "failed to upload driver_path");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.data_file),
+               "failed to upload data_file");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.config_file),
+               "failed to upload config_file");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.help_file),
+               "failed to upload help_file");
+       if (d->info8.dependent_files) {
+               for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+                       torture_assert(tctx,
+                               upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+                               "failed to upload dependent_files");
+               }
+       }
+
+       talloc_free(cli);
+
+       return true;
+}
+
+static bool remove_printer_driver_file(struct torture_context *tctx,
+                                      struct smbcli_state *cli,
+                                      struct torture_driver_context *d,
+                                      const char *file_name)
+{
+       const char *remote_name;
+       const char *remote_dir =  driver_directory_dir(d->remote.driver_directory);
+
+       if (!file_name) {
+               return true;
+       }
+
+       remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
+
+       torture_comment(tctx, "Removing %s\n", remote_name);
+
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_unlink(cli->tree, remote_name),
+               "failed to unlink");
+
+       return true;
+}
+
+static bool remove_printer_driver(struct torture_context *tctx,
+                                 const char *server_name,
+                                 struct torture_driver_context *d)
+{
+       struct smbcli_state *cli;
+       const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+       int i;
+
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
+
+       torture_comment(tctx, "Removing printer driver files from \\\\%s\\%s\n",
+               server_name, share_name);
+
+       torture_assert(tctx,
+               remove_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+               "failed to remove driver_path");
+       torture_assert(tctx,
+               remove_printer_driver_file(tctx, cli, d, d->info8.data_file),
+               "failed to remove data_file");
+       torture_assert(tctx,
+               remove_printer_driver_file(tctx, cli, d, d->info8.config_file),
+               "failed to remove config_file");
+       torture_assert(tctx,
+               remove_printer_driver_file(tctx, cli, d, d->info8.help_file),
+               "failed to remove help_file");
+       if (d->info8.dependent_files) {
+               for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+                       if (strequal(d->info8.dependent_files->string[i], d->info8.driver_path) ||
+                           strequal(d->info8.dependent_files->string[i], d->info8.data_file) ||
+                           strequal(d->info8.dependent_files->string[i], d->info8.config_file) ||
+                           strequal(d->info8.dependent_files->string[i], d->info8.help_file)) {
+                               continue;
+                       }
+                       torture_assert(tctx,
+                               remove_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+                               "failed to remove dependent_files");
+               }
+       }
+
+       talloc_free(cli);
+
+       return true;
+
+}
+
+static bool test_add_driver_arg(struct torture_context *tctx,
+                               struct dcerpc_pipe *p,
+                               struct torture_driver_context *d)
+{
+       bool ret = true;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+       uint32_t levels[] = { 1, 2, 3, 4, 6, 8 };
+       int i;
+       struct spoolss_AddDriverInfo8 info8;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       uint32_t delete_flags = 0;
+
+       ZERO_STRUCT(info8);
+
+       torture_comment(tctx, "Testing PrinterDriver%s '%s' for environment '%s'\n",
+               d->ex ? "Ex" : "", d->info8.driver_name, d->local.environment);
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
+
+       info8.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;
+
+       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\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);
+       }
+
+       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);
+       }
+
+       torture_assert(tctx,
+               remove_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to remove printer driver");
+
+       torture_comment(tctx, "\n");
+
+       return ret;
+}
+
+static bool test_add_driver_ex_64(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 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_EX;
+       d->ex                           = true;
+
+       return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_ex_32(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 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;
+
+       return test_add_driver_arg(tctx, p, d);
+}
+
+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);
+}
+
+static bool test_add_driver_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;
+       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,
+                                                       "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_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;
 }