r1692: first commit :)
authorGünther Deschner <gd@samba.org>
Tue, 10 Aug 2004 14:27:17 +0000 (14:27 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:52:19 +0000 (10:52 -0500)
* add IA64 to the architecture table of printer-drivers

* add new "net"-subcommands:

  net rpc printer migrate {drivers|printers|forms|security|settings|all}
        [printer]
  net rpc share migrate {shares|files|all} [share]

  this is the first part of the migration suite. this will will (once
  feature-complete) allow to do 1:1 server-cloning in the best possible way by
  making heavy use of samba's rpc_client-functions. all migration-steps
  are implemented as rpc/smb-client-calls; net communicates via rpc/smb
  with two servers at the same time (a remote, source server and a
  destination server that currently defaults to the local smbd). this
  allows e. g. printer-driver migration including driverfiles, recursive
  mirroring of file-shares including file-acls, etc. almost any migration
  step can be called with a migrate-subcommand to provide more flexibility
  during a migration process (at the cost of quite some redundancy :) ).

  "net rpc printer migrate settings" is still in a bad condition (many
  open questions that hopefully can be adressed soon).

  "net rpc share migrate security" as an isolated call to just migrate
  share-ACLs will be added later.

  Before playing with it, make sure to use a test-server. Migration is a
  serious business and this tool-set can perfectly overwrite your
  existing file/print-shares.

* along with the migration functions had to make I the following
  changes:

        - implement setprinter level 3 client-side

        - implement net_add_share level 502 client-side

        - allow security descriptor to be set in setprinterdata level 2
          serverside

guenther

12 files changed:
source/Makefile.in
source/printing/nt_printing.c
source/rpc_client/cli_srvsvc.c
source/rpc_parse/parse_spoolss.c
source/rpc_parse/parse_srv.c
source/rpc_server/srv_spoolss_nt.c
source/rpcclient/cmd_spoolss.c
source/utils/net.c
source/utils/net.h
source/utils/net_help.c
source/utils/net_rpc.c
source/utils/net_rpc_printer.c [new file with mode: 0644]

index 58c8a7895d62f6d0b1b59676ae792915f89490ba..7ce45deb393d12c74b40af6919e16cf62407e16e 100644 (file)
@@ -533,7 +533,7 @@ NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
           utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
           utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
           utils/net_cache.o utils/net_groupmap.o utils/net_idmap.o \
-          utils/net_status.o
+          utils/net_status.o utils/net_rpc_printer.o
 
 NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
          $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
index 225ff20ec3eb52b8f7a1fb670308cd0f817ea8f6..1c88bf27a88a1573f8b159eca427d0b2e335f8ba 100644 (file)
@@ -209,6 +209,7 @@ struct table_node {
 #define SPL_ARCH_W32MIPS       "W32MIPS"
 #define SPL_ARCH_W32ALPHA      "W32ALPHA"
 #define SPL_ARCH_W32PPC                "W32PPC"
+#define SPL_ARCH_IA64          "IA64"
 
 static const struct table_node archi_table[]= {
 
@@ -217,6 +218,7 @@ static const struct table_node archi_table[]= {
        {"Windows NT R4000",     SPL_ARCH_W32MIPS,      2 },
        {"Windows NT Alpha_AXP", SPL_ARCH_W32ALPHA,     2 },
        {"Windows NT PowerPC",   SPL_ARCH_W32PPC,       2 },
+       {"Windows IA64",         SPL_ARCH_IA64,         3 },
        {NULL,                   "",            -1 }
 };
 
index 68eb17074f63fcb89bb6792f2e0f3fe3c596c77b..f8580d0fe2e765be23a9c51ede8c7233b520b73d 100644 (file)
@@ -269,7 +269,8 @@ WERROR cli_srvsvc_net_share_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
                                const char *netname, uint32 type, 
                                const char *remark, uint32 perms, 
                                uint32 max_uses, uint32 num_uses, 
-                               const char *path, const char *passwd)
+                               const char *path, const char *passwd,
+                               int level, SEC_DESC *sd)
 {
        prs_struct qbuf, rbuf;
        SRV_Q_NET_SHARE_ADD q;
@@ -285,7 +286,8 @@ WERROR cli_srvsvc_net_share_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
        prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
 
        init_srv_q_net_share_add(&q,cli->srv_name_slash, netname, type, remark,
-                                perms, max_uses, num_uses, path, passwd);
+                                perms, max_uses, num_uses, path, passwd, 
+                                level, sd);
 
        /* Marshall data and send request */
 
index ae087c7f7746844e1f11e27ed5fea6f2e4d2c609..7ae6a0d8932ed69aa5f70c9ffe91105287737226 100644 (file)
@@ -1053,6 +1053,28 @@ BOOL make_spoolss_printer_info_2(TALLOC_CTX *mem_ctx, SPOOL_PRINTER_INFO_LEVEL_2
        return True;
 }
 
+/*******************************************************************
+create a SPOOL_PRINTER_INFO_3 struct from a PRINTER_INFO_3 struct
+*******************************************************************/
+
+BOOL make_spoolss_printer_info_3(TALLOC_CTX *mem_ctx, SPOOL_PRINTER_INFO_LEVEL_3 **spool_info3, 
+                               PRINTER_INFO_3 *info)
+{
+
+       SPOOL_PRINTER_INFO_LEVEL_3 *inf;
+
+       /* allocate the necessary memory */
+       if (!(inf=(SPOOL_PRINTER_INFO_LEVEL_3*)talloc(mem_ctx, sizeof(SPOOL_PRINTER_INFO_LEVEL_3)))) {
+               DEBUG(0,("make_spoolss_printer_info_3: Unable to allocate SPOOL_PRINTER_INFO_LEVEL_3 sruct!\n"));
+               return False;
+       }
+       
+       inf->secdesc_ptr        = (info->secdesc!=NULL)?1:0;
+
+       *spool_info3 = inf;
+
+       return True;
+}
 
 /*******************************************************************
  * read a structure.
@@ -4112,6 +4134,20 @@ BOOL make_spoolss_q_setprinter(TALLOC_CTX *mem_ctx, SPOOL_Q_SETPRINTER *q_u,
                q_u->devmode_ctr.size = 0;
                q_u->devmode_ctr.devmode = NULL;
 #endif
+               break;
+       case 3:
+               secdesc = info->printers_3->secdesc;
+               
+               make_spoolss_printer_info_3 (mem_ctx, &q_u->info.info_3, info->printers_3);
+               
+               q_u->secdesc_ctr = (SEC_DESC_BUF*)malloc(sizeof(SEC_DESC_BUF));
+               if (!q_u->secdesc_ctr)
+                       return False;
+               q_u->secdesc_ctr->ptr = (secdesc != NULL) ? 1: 0;
+               q_u->secdesc_ctr->max_len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+               q_u->secdesc_ctr->len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+               q_u->secdesc_ctr->sec = secdesc;
+
                break;
        default: 
                DEBUG(0,("make_spoolss_q_setprinter: Unknown info level [%d]\n", level));
@@ -7358,7 +7394,7 @@ BOOL spoolss_io_r_enumprinterdataex(const char *desc, SPOOL_R_ENUMPRINTERDATAEX
 
        if (!prs_set_offset(ps, end_offset))
                return False;
-                                                                                                                                                       return True;
+       return True;
 }
 
 /*******************************************************************
index 6349fc1632575c25d5ed1706d2014a9bd69e9605..8313c82c93dcc49c1495c4704e03177e5b620a29 100644 (file)
@@ -1456,19 +1456,38 @@ BOOL srv_io_q_net_share_add(const char *desc, SRV_Q_NET_SHARE_ADD *q_n, prs_stru
 void init_srv_q_net_share_add(SRV_Q_NET_SHARE_ADD *q, const char *srvname,
                              const char *netname, uint32 type, const char *remark, 
                              uint32 perms, uint32 max_uses, uint32 num_uses,
-                             const char *path, const char *passwd)
-{
-       q->ptr_srv_name = 1;
-       init_unistr2(&q->uni_srv_name, srvname, UNI_STR_TERMINATE);
-       q->info.switch_value = q->info_level = 2;
-
-       q->info.ptr_share_ctr = 1;
-       init_srv_share_info2(&q->info.share.info2.info_2, netname, type,
-                            remark, perms, max_uses, num_uses, path, passwd);
-       init_srv_share_info2_str(&q->info.share.info2.info_2_str, netname,
-                                remark, path, passwd);
-       q->ptr_err_index = 1;
-       q->err_index = 0;
+                             const char *path, const char *passwd, 
+                             int level, SEC_DESC *sd)
+{
+       switch(level) {
+       case 502: {
+               size_t sd_size = sec_desc_size(sd);
+               q->ptr_srv_name = 1;
+               init_unistr2(&q->uni_srv_name, srvname, UNI_STR_TERMINATE);
+               q->info.switch_value = q->info_level = level;
+               q->info.ptr_share_ctr = 1;
+               init_srv_share_info502(&q->info.share.info502.info_502, netname, type,
+                                    remark, perms, max_uses, num_uses, path, passwd, sd, sd_size);
+               init_srv_share_info502_str(&q->info.share.info502.info_502_str, netname,
+                                        remark, path, passwd, sd, sd_size);
+               q->ptr_err_index = 1;
+               q->err_index = 0;
+               }
+               break;
+       case 2:
+       default:
+               q->ptr_srv_name = 1;
+               init_unistr2(&q->uni_srv_name, srvname, UNI_STR_TERMINATE);
+               q->info.switch_value = q->info_level = level;
+               q->info.ptr_share_ctr = 1;
+               init_srv_share_info2(&q->info.share.info2.info_2, netname, type,
+                                    remark, perms, max_uses, num_uses, path, passwd);
+               init_srv_share_info2_str(&q->info.share.info2.info_2_str, netname,
+                                        remark, path, passwd);
+               q->ptr_err_index = 1;
+               q->err_index = 0;
+               break;
+       }
 }
 
 
index e3c9ff08d93d18e92a0429ee6c924a4675d36e0b..ccff65688a7f862c5445d27cc5d699ee7555a3f8 100644 (file)
@@ -1972,6 +1972,7 @@ static int get_version_id (char * arch)
                {"Windows NT R4000",     "W32MIPS",     2 },    
                {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
                {"Windows NT PowerPC",   "W32PPC",      2 },
+               {"Windows IA64",         "IA64",        3 },
                {NULL,                   "",            -1 }
        };
  
@@ -6220,6 +6221,7 @@ WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SET
        DEVMODE_CTR devmode_ctr = q_u->devmode_ctr;
        SEC_DESC_BUF *secdesc_ctr = q_u->secdesc_ctr;
        uint32 command = q_u->command;
+       WERROR result;
 
        Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
        
@@ -6233,7 +6235,12 @@ WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SET
                case 0:
                        return control_printer(handle, command, p);
                case 2:
-                       return update_printer(p, handle, level, info, devmode_ctr.devmode);
+                       result = update_printer(p, handle, level, info, devmode_ctr.devmode);
+                       if (!W_ERROR_IS_OK(result)) 
+                               return result;
+                       if (secdesc_ctr)
+                               result = update_printer_sec(handle, level, info, p, secdesc_ctr);
+                       return result;
                case 3:
                        return update_printer_sec(handle, level, info, p,
                                                  secdesc_ctr);
index 38558ec3d472466aea8f8f7f2d63a8ca5cb97f25..47d659325dab6361b4dbaa07f918370e80cf7a94 100644 (file)
@@ -39,6 +39,7 @@ static const struct table_node archi_table[]= {
        {"Windows NT R4000",     "W32MIPS",     2 },
        {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
        {"Windows NT PowerPC",   "W32PPC",      2 },
+       {"Windows IA64",         "IA64",        3 },
        {NULL,                   "",            -1 }
 };
 
@@ -1126,6 +1127,13 @@ static WERROR cmd_spoolss_enum_drivers(struct cli_state *cli,
                                cli, mem_ctx, needed, NULL, info_level, 
                                archi_table[i].long_archi, &returned, &ctr);
 
+               if (W_ERROR_V(werror) == W_ERROR_V(WERR_INVALID_ENVIRONMENT)) {
+                       printf ("Server does not support environment [%s]\n", 
+                               archi_table[i].long_archi);
+                       werror = WERR_OK;
+                       continue;
+               }
+
                if (returned == 0)
                        continue;
                        
index 67b138a43bed4578b6bedfe1dd9e14fcc63471cc..30e94ac8a1bb2ba46c3ef5b357137563826707fe 100644 (file)
@@ -78,6 +78,8 @@ BOOL opt_localgroup = False;
 BOOL opt_domaingroup = False;
 const char *opt_newntname = "";
 int opt_rid = 0;
+int opt_acls = 0;
+const char *opt_exclude = NULL;
 
 BOOL opt_have_ip = False;
 struct in_addr opt_dest_ip;
@@ -126,12 +128,13 @@ int net_run_function(int argc, const char **argv, struct functable *table,
        return usage_fn(argc, argv);
 }
 
-
 /****************************************************************************
-connect to \\server\ipc$  
+connect to \\server\service 
 ****************************************************************************/
-NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
-                                       const char *server_name)
+NTSTATUS connect_to_service(struct cli_state **c, struct in_addr *server_ip,
+                                       const char *server_name, 
+                                       const char *service_name, 
+                                       const char *service_type)
 {
        NTSTATUS nt_status;
 
@@ -144,7 +147,7 @@ NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
        
        nt_status = cli_full_connection(c, NULL, server_name, 
                                        server_ip, opt_port,
-                                       "IPC$", "IPC",  
+                                       service_name, service_type,  
                                        opt_user_name, opt_workgroup,
                                        opt_password, 0, Undefined, NULL);
        
@@ -171,6 +174,16 @@ NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
        }
 }
 
+
+/****************************************************************************
+connect to \\server\ipc$  
+****************************************************************************/
+NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
+                                       const char *server_name)
+{
+       return connect_to_service(c, server_ip, server_name, "IPC$", "IPC");
+}
+
 /****************************************************************************
 connect to \\server\ipc$ anonymously
 ****************************************************************************/
@@ -193,6 +206,40 @@ NTSTATUS connect_to_ipc_anonymous(struct cli_state **c,
        }
 }
 
+/**
+ * Connect the local server and open a given pipe
+ *
+ * @param cli_local            A cli_state to the local spoolss-server
+ * @param pipe                 The pipe to open
+ * @param got_pipe             boolean to that stores if got a pipe
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS connect_local_pipe(struct cli_state **cli_local, int pipe, BOOL *got_pipe)
+{
+       NTSTATUS nt_status;
+       extern struct in_addr loopback_ip;
+       char *server_name = strdup("127.0.0.1");
+       struct cli_state *cli_tmp = NULL;
+
+       /* make a connection to smbd via loopback */
+       nt_status = connect_to_ipc(&cli_tmp, &loopback_ip, server_name);
+       if (!NT_STATUS_IS_OK(nt_status)) 
+               return nt_status;
+
+       if (!cli_nt_session_open(cli_tmp, pipe)) {
+               DEBUG(0, ("couldn't not initialise spoolss pipe\n"));
+               cli_shutdown(cli_tmp);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       *cli_local = cli_tmp;
+       *got_pipe = True;
+
+       return nt_status;
+}
+
+
 /****************************************************************************
  Use the local machine's password for this session
 ****************************************************************************/
@@ -690,6 +737,9 @@ static struct functable net_func[] = {
                {"domain",      'D', POPT_ARG_NONE,   &opt_domaingroup},
                {"ntname",      'N', POPT_ARG_STRING, &opt_newntname},
                {"rid",         'R', POPT_ARG_INT,    &opt_rid},
+               /* Options for 'net rpc share migrate' */
+               {"acls",        'a', POPT_ARG_NONE,   &opt_acls},
+               {"exclude",     'e', POPT_ARG_STRING, &opt_exclude},
 
                POPT_COMMON_SAMBA
                { 0, 0, 0, 0}
index ba3a4e14352c317e48cc4bdfea795f4cd7f367b9..cb38907ff95da46903a3f8f627734f4c2e8bc517 100644 (file)
@@ -62,6 +62,8 @@ extern BOOL opt_localgroup;
 extern BOOL opt_domaingroup;
 extern const char *opt_newntname;
 extern int opt_rid;
+extern int opt_acls;
+extern const char *opt_exclude;
 
 extern BOOL opt_have_ip;
 extern struct in_addr opt_dest_ip;
index 38261be90a7ef6de6cac3c3f5cd3cf89ba6d1a0f..c17824cb27107c98f385a92c989ffcd76d34be11 100644 (file)
@@ -122,13 +122,25 @@ int net_help_share(int argc, const char **argv)
         "on target server\n\n"
         "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
         "\n\tAdds a share from a server (makes the export active)\n\n"
-        "net [<method>] share DELETE <sharename> [misc. options] [targets]\n"
-        "\n\tDeletes a share from a server (makes the export inactive)\n");
+        "net [<method>] share DELETE <sharename> [misc. options] [targets]"
+        "\n\tDeletes a share from a server (makes the export inactive)\n\n"
+        "net [<method>] share MIGRATE FILES <sharename> [misc. options] [targets]"
+        "\n\tMigrates files from remote to local server\n\n"
+        "net [<method>] share MIGRATE SHARES <sharename> [misc. options] [targets]"
+        "\n\tMigrates shares from remote to local server\n\n"
+/*      "net [<method>] share MIGRATE SECURITY <sharename> [misc. options] [targets]"
+        "\n\tMigrates share-ACLs from remote to local server\n\n" */
+        "net [<method>] share MIGRATE ALL <sharename> [misc. options] [targets]"
+        "\n\tMigrates shares (including directories, files) from remote\n"
+        "\tto local server\n\n"
+       );
        net_common_methods_usage(argc, argv);
        net_common_flags_usage(argc, argv);
        d_printf(
         "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"
-        "\t-M or --maxusers=<num>\t\tmax users allowed for share\n");
+        "\t-M or --maxusers=<num>\t\tmax users allowed for share\n"
+        "\t-a or --acls\t\t\tcopies ACLs as well\n"
+        "\t-e or --exclude\t\t\tlist of shares to be excluded from mirroring\n");
        return -1;
 }
 
@@ -149,6 +161,31 @@ int net_help_file(int argc, const char **argv)
        return -1;
 }
 
+int net_help_printer(int argc, const char **argv)
+{
+       d_printf("net rpc printer LIST [printer]\n"\
+                "\tlists all printers on print-server\n\n");
+       d_printf("net rpc printer DRIVER [printer]\n"\
+                "\tlists all printer-drivers on print-server\n\n");
+       d_printf("net rpc printer MIGRATE PRINTERS [printer]"\
+                "\n\tmigrates printers from remote to local server\n\n");
+       d_printf("net rpc printer MIGRATE SETTINGS [printer]"\
+                "\n\tmigrates printer-settings from remote to local server\n\n");
+       d_printf("net rpc printer MIGRATE DRIVERS [printer]"\
+                "\n\tmigrates printer-drivers from remote to local server\n\n");
+       d_printf("net rpc printer MIGRATE FORMS [printer]"\
+                "\n\tmigrates printer-forms from remote to local server\n\n");
+       d_printf("net rpc printer MIGRATE SECURITY [printer]"\
+                "\n\tmigrates printer-ACLs from remote to local server\n\n");
+       d_printf("net rpc printer MIGRATE ALL [printer]\n"\
+                "\tmigrates drivers, forms, queues, settings and acls from\n"\
+                "\tremote to local print-server\n\n");
+       net_common_methods_usage(argc, argv);
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
 int net_help_status(int argc, const char **argv)
 {
        d_printf("  net status sessions [parseable] "
index e21f79df303a698be265f8b16fb6ea557d0dcc00..47e47f079f94786e7b0aecb6fa16e9467c02790c 100644 (file)
@@ -2351,6 +2351,7 @@ rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
        uint32 type=0; /* only allow disk shares to be added */
        uint32 num_users=0, perms=0;
        char *password=NULL; /* don't allow a share password */
+       uint32 level = 2;
 
        path = strchr(sharename, '=');
        if (!path)
@@ -2359,7 +2360,8 @@ rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
 
        result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type,
                                          opt_comment, perms, opt_maxusers,
-                                         num_users, path, password);
+                                         num_users, path, password, 
+                                         level, NULL);
        return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
 }
 
@@ -2491,6 +2493,458 @@ rpc_share_list_internals(const DOM_SID *domain_sid, const char *domain_name,
        return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
 }
 
+/** 
+ * Migrate shares from a remote RPC server to the local RPC srever
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+rpc_share_migrate_shares_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                  struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                  int argc, const char **argv)
+{
+       WERROR result;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       SRV_SHARE_INFO_CTR ctr_src;
+       ENUM_HND hnd;
+       uint32 type = 0; /* only allow disk shares to be added */
+       uint32 num_uses = 0, perms = 0, max_uses = 0;
+       char *password = NULL; /* don't allow a share password */
+       uint32 preferred_len = 0xffffffff, i;
+       BOOL got_dst_srvsvc_pipe = False;
+       struct cli_state *cli_dst = NULL;
+       uint32 level = 502; /* includes secdesc */
+       SEC_DESC *share_sd = NULL;
+
+       init_enum_hnd(&hnd, 0);
+
+       result = cli_srvsvc_net_share_enum(
+                       cli, mem_ctx, level, &ctr_src, preferred_len, &hnd);
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* connect local PI_SRVSVC */
+        nt_status = connect_local_pipe(&cli_dst, PI_SRVSVC, &got_dst_srvsvc_pipe);
+        if (!NT_STATUS_IS_OK(nt_status))
+                return nt_status;
+
+
+       for (i = 0; i < ctr_src.num_entries; i++) {
+
+               fstring netname = "", remark = "", path = "";
+               /* reset error-code */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+
+               rpcstr_pull_unistr2_fstring(
+                       netname, &ctr_src.share.info502[i].info_502_str.uni_netname);
+               rpcstr_pull_unistr2_fstring(
+                       remark, &ctr_src.share.info502[i].info_502_str.uni_remark);
+               rpcstr_pull_unistr2_fstring(
+                       path, &ctr_src.share.info502[i].info_502_str.uni_path);
+               num_uses        = ctr_src.share.info502[i].info_502.num_uses;
+               max_uses        = ctr_src.share.info502[i].info_502.max_uses;
+               perms           = ctr_src.share.info502[i].info_502.perms;
+
+
+               if (opt_acls)
+                       share_sd = dup_sec_desc(
+                               mem_ctx, ctr_src.share.info502[i].info_502_str.sd);
+
+               /* since we do not have NetShareGetInfo implemented in samba3 we 
+                  only can skip inside the enum-ctr_src */
+               if (argc == 1) {
+                       char *one_share = talloc_strdup(mem_ctx, argv[0]);
+                       if (!strequal(netname, one_share))
+                               continue;
+               }
+
+               /* skip builtin shares */
+               /* FIXME: should print$ be added too ? */
+               if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$") || 
+                   strequal(netname,"global")) 
+                       continue;
+
+               /* only work with file-shares */
+               if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+                       d_printf("skipping [%s]. not a file share.\n", netname);
+                       continue;
+               }
+
+               if (!cli_tdis(cli)) 
+                       goto done;
+
+
+               /* finallly add the share on the dst server 
+                  please note that samba currently does not allow to 
+                  add a share without existing directory */
+
+               printf("migrating: [%s], path: %s, comment: %s, %s share-ACLs\n", 
+                       netname, path, remark, opt_acls ? "including" : "without" );
+
+               if (opt_verbose && opt_acls)
+                       display_sec_desc(share_sd);
+
+               result = cli_srvsvc_net_share_add(cli_dst, mem_ctx, netname, type,
+                                                 remark, perms, max_uses,
+                                                 num_uses, path, password, 
+                                                 level, share_sd);
+       
+                if (W_ERROR_V(result) == W_ERROR_V(WERR_ALREADY_EXISTS)) {
+                       printf("share does already exist\n");
+                       continue;
+                }
+       
+               if (!W_ERROR_IS_OK(result)) {
+                       printf("cannot add share: %s\n", dos_errstr(result));
+                       goto done;
+               }
+
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (got_dst_srvsvc_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+
+       return nt_status;
+
+}
+
+/** 
+ * Migrate shares from a rpc-server to another
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_share_migrate_shares(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_share_migrate_shares_internals,
+                              argc, argv);
+}
+
+typedef struct copy_clistate {
+       TALLOC_CTX *mem_ctx;
+       struct cli_state *cli_share_src;
+       struct cli_state *cli_share_dst;
+       const char *cwd;
+} copy_clistate;
+
+
+/**
+ * Copy a file/dir 
+ *
+ * @param f    file_info
+ * @param mask current search mask
+ * @param state        arg-pointer
+ *
+ **/
+static void copy_fn(file_info *f, const char *mask, void *state)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct copy_clistate *local_state = (struct copy_clistate *)state;
+       fstring filename, new_mask, dir;
+
+       if (strequal(f->name, ".") || strequal(f->name, "..")) 
+               return;
+
+       DEBUG(3,("got mask: %s, name: %s\n", mask, f->name));
+
+       /* DIRECTORY */
+       if (f->mode & aDIR) {
+
+               DEBUG(3,("got dir: %s\n", f->name));
+
+               fstrcpy(dir, local_state->cwd);
+               fstrcat(dir, "\\");
+               fstrcat(dir, f->name);
+
+               /* create that directory */
+               nt_status = net_copy_file(local_state->mem_ctx, 
+                                         local_state->cli_share_src, 
+                                         local_state->cli_share_dst, 
+                                         dir, dir, 
+                                         opt_acls? True : False, False);
+
+               if (!NT_STATUS_IS_OK(nt_status)) 
+                       printf("could not copy dir %s: %s\n", 
+                               dir, nt_errstr(nt_status));
+
+               /* search below that directory */
+               fstrcpy(new_mask, dir);
+               fstrcat(new_mask, "\\*");
+
+               if (!sync_files(local_state->mem_ctx, 
+                               local_state->cli_share_src, 
+                               local_state->cli_share_dst, 
+                               new_mask, dir))
+
+                       printf("could not sync files\n");
+                       
+               return;
+       }
+
+
+       /* FILE */
+       fstrcpy(filename, local_state->cwd);
+       fstrcat(filename, "\\");
+       fstrcat(filename, f->name);
+
+       DEBUG(3,("got file: %s\n", filename));
+
+       nt_status = net_copy_file(local_state->mem_ctx, 
+                                 local_state->cli_share_src, 
+                                 local_state->cli_share_dst, 
+                                 filename, filename, 
+                                 opt_acls? True : False, True);
+
+       if (!NT_STATUS_IS_OK(nt_status)) 
+               printf("could not copy file %s: %s\n", 
+                       filename, nt_errstr(nt_status));
+
+}
+
+/**
+ * sync files, can be called recursivly to list files 
+ * and then call copy_fn for each file 
+ *
+ * @param mem_ctx      TALLOC_CTX
+ * @param cli_share_src        a connected share on the originating server
+ * @param cli_share_dst        a connected share on the destination server
+ * @param mask         the current search mask
+ * @param cwd          the current path
+ *
+ * @return             Boolean result
+ **/
+BOOL sync_files(TALLOC_CTX *mem_ctx, 
+               struct cli_state *cli_share_src, 
+               struct cli_state *cli_share_dst,
+               pstring mask, fstring cwd)
+
+{
+
+       uint16 attribute = aSYSTEM | aHIDDEN | aDIR;
+       struct copy_clistate clistate;
+
+       clistate.mem_ctx        = mem_ctx;
+       clistate.cli_share_src  = cli_share_src;
+       clistate.cli_share_dst  = cli_share_dst;
+       clistate.cwd            = cwd;
+
+       DEBUG(3,("calling cli_list with mask: %s\n", mask));
+
+       if (cli_list(cli_share_src, mask, attribute, copy_fn, &clistate) == -1) {
+               d_printf("listing %s failed with error: %s\n", 
+                       mask, cli_errstr(cli_share_src));
+               return False;
+       }
+
+       return True;
+}
+
+
+/** 
+ * Sync all files inside a remote share to another share (over smb)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+rpc_share_migrate_files_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                 struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 int argc, const char **argv)
+{
+       WERROR result;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       SRV_SHARE_INFO_CTR ctr_src;
+       ENUM_HND hnd;
+       uint32 preferred_len = 0xffffffff, i;
+       uint32 level = 2;
+       struct cli_state *cli_share_src = NULL;
+       struct cli_state *cli_share_dst = NULL;
+       BOOL got_src_share = False;
+       BOOL got_dst_share = False;
+       pstring mask;
+       extern struct in_addr loopback_ip;
+
+       init_enum_hnd(&hnd, 0);
+
+       result = cli_srvsvc_net_share_enum(
+                       cli, mem_ctx, level, &ctr_src, preferred_len, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < ctr_src.num_entries; i++) {
+
+               fstring netname = "", remark = "", path = "";
+
+               rpcstr_pull_unistr2_fstring(
+                       netname, &ctr_src.share.info2[i].info_2_str.uni_netname);
+               rpcstr_pull_unistr2_fstring(
+                       remark, &ctr_src.share.info2[i].info_2_str.uni_remark);
+               rpcstr_pull_unistr2_fstring(
+                       path, &ctr_src.share.info2[i].info_2_str.uni_path);
+
+               /* since we do not have NetShareGetInfo implemented in samba3 we 
+                  only can skip inside the enum-ctr_src */
+               if (argc == 1) {
+                       char *one_share = talloc_strdup(mem_ctx, argv[0]);
+                       if (!strequal(netname, one_share))
+                               continue;
+               }
+
+               /* skip builtin and hidden shares 
+                  In particular, one might not want to mirror whole discs :) */
+               if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$"))
+                       continue;
+               
+               if (strequal(netname, "print$") || netname[1] == '$') {
+                       d_printf("skipping  [%s]: builtin/hidden share\n", netname);
+                       continue;
+               }
+
+               if (opt_exclude && in_list(netname, (char *)opt_exclude, False)) {
+                       printf("excluding [%s]\n", netname);
+                       continue;
+               } 
+
+               /* only work with file-shares */
+               if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+                       d_printf("skipping  [%s]: not a file share.\n", netname);
+                       continue;
+               }
+
+               if (!cli_tdis(cli))
+                       return NT_STATUS_UNSUCCESSFUL;
+
+               printf("syncing   [%s] files and directories %s ACLs\n", 
+                       netname, opt_acls ? "including" : "without");
+
+
+               /* open share source */
+               nt_status = connect_to_service(&cli_share_src, &cli->dest_ip, 
+                                              cli->desthost, netname, "A:");
+               if (!NT_STATUS_IS_OK(nt_status))
+                       goto done;
+
+               got_src_share = True;
+
+
+               /* open share destination */
+               nt_status = connect_to_service(&cli_share_dst, &loopback_ip, 
+                                              "127.0.0.1", netname, "A:");
+               if (!NT_STATUS_IS_OK(nt_status))
+                       goto done;
+
+               got_dst_share = True;
+
+
+               /* now call the filesync */
+               pstrcpy(mask, "\\*");
+
+               if (!sync_files(mem_ctx, cli_share_src, cli_share_dst, mask, NULL)) {
+                       d_printf("could not sync files for share: %s\n", netname);
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+               }
+               
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+
+       if (got_src_share)
+               cli_shutdown(cli_share_src);
+
+       if (got_dst_share)
+               cli_shutdown(cli_share_dst);
+               
+       return nt_status;
+
+}
+
+static int rpc_share_migrate_files(int argc, const char **argv)
+{
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_share_migrate_files_internals,
+                              argc, argv);
+}
+
+/** 
+ * Migrate shares (including share-definitions, share-acls and files with acls)
+ * from one server to another
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+static int rpc_share_migrate_all(int argc, const char **argv)
+{
+       int ret;
+
+       ret = run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_shares_internals, argc, argv);
+       if (ret)
+               return ret;
+#if 0
+       ret = run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_shares_security_internals, argc, argv);
+       if (ret)
+               return ret;
+#endif
+       return run_rpc_command(NULL, PI_SRVSVC, 0, rpc_share_migrate_files_internals, argc, argv);
+}
+
+
+/** 
+ * 'net rpc share migrate' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+static int rpc_share_migrate(int argc, const char **argv)
+{
+
+       struct functable func[] = {
+               {"all",         rpc_share_migrate_all},
+               {"files",       rpc_share_migrate_files},
+/*             {"security",    rpc_share_migrate_security},*/
+               {"shares",      rpc_share_migrate_shares},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, rpc_share_usage);
+}
+
 /** 
  * 'net rpc share' entrypoint.
  * @param argc  Standard main() style argc
@@ -2503,6 +2957,7 @@ int net_rpc_share(int argc, const char **argv)
        struct functable func[] = {
                {"add", rpc_share_add},
                {"delete", rpc_share_delete},
+               {"migrate", rpc_share_migrate},
                {NULL, NULL}
        };
 
@@ -3570,6 +4025,227 @@ static int rpc_vampire(int argc, const char **argv) {
        return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_vampire_internals,
                               argc, argv);
 }
+
+/** 
+ * Migrate everything from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ *
+ * The order is important !
+ * To successfully add drivers the print-queues have to exist !
+ * Applying ACLs should be the last step, because you're easily locked out
+ *
+ **/
+static int rpc_printer_migrate_all(int argc, const char **argv)
+{
+       int ret;
+
+       ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_printers_internals, argc, argv);
+       if (ret)
+               return ret;
+
+       ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_drivers_internals, argc, argv);
+       if (ret)
+               return ret;
+
+       ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_forms_internals, argc, argv);
+       if (ret)
+               return ret;
+
+       ret = run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_settings_internals, argc, argv);
+       if (ret)
+               return ret;
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, rpc_printer_migrate_security_internals, argc, argv);
+
+}
+
+/** 
+ * Migrate print-drivers from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_drivers(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_migrate_drivers_internals,
+                              argc, argv);
+}
+
+/** 
+ * Migrate print-forms from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_forms(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_migrate_forms_internals,
+                              argc, argv);
+}
+
+/** 
+ * Migrate printers from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_printers(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_migrate_printers_internals,
+                              argc, argv);
+}
+
+/** 
+ * Migrate printer-ACLs from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_security(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_migrate_security_internals,
+                              argc, argv);
+}
+
+/** 
+ * Migrate printer-settings from a print-server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_migrate_settings(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_migrate_settings_internals,
+                              argc, argv);
+}
+
+/** 
+ * 'net rpc printer' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int rpc_printer_migrate(int argc, const char **argv) 
+{
+
+       /* ouch: when addriver and setdriver are called from within
+          rpc_printer_migrate_drivers_internals, the printer-queue already
+          *has* to exist */
+
+       struct functable func[] = {
+               {"all",         rpc_printer_migrate_all},
+               {"drivers",     rpc_printer_migrate_drivers},
+               {"forms",       rpc_printer_migrate_forms},
+               {"help",        rpc_printer_usage},
+               {"printers",    rpc_printer_migrate_printers},
+               {"security",    rpc_printer_migrate_security},
+               {"settings",    rpc_printer_migrate_settings},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, rpc_printer_usage);
+}
+
+
+/** 
+ * List printers on a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_list(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_list_internals,
+                              argc, argv);
+}
+
+/** 
+ * List printer-drivers on a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_printer_driver_list(int argc, const char **argv)
+{
+
+       return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_driver_list_internals,
+                              argc, argv);
+}
+
+/** 
+ * Display rpc printer help page.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+int rpc_printer_usage(int argc, const char **argv)
+{
+        return net_help_printer(argc, argv);
+}
+
+/** 
+ * 'net rpc printer' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+int net_rpc_printer(int argc, const char **argv) 
+{
+       struct functable func[] = {
+               {"list", rpc_printer_list},
+               {"migrate", rpc_printer_migrate},
+               {"driver", rpc_printer_driver_list},
+               {NULL, NULL}
+       };
+
+       if (argc == 0)
+               return run_rpc_command(NULL, PI_SPOOLSS, 0, 
+                              rpc_printer_list_internals,
+                              argc, argv);
+
+       return net_run_function(argc, argv, func, rpc_printer_usage);
+}
+
 /****************************************************************************/
 
 
@@ -3590,6 +4266,7 @@ int net_rpc_usage(int argc, const char **argv)
         d_printf("  net rpc password <username> [<password>] -Uadmin_username%%admin_pass");
        d_printf("  net rpc group \t\tto list groups\n");
        d_printf("  net rpc share \t\tto add, delete, and list shares\n");
+       d_printf("  net rpc printer \t\tto list and migrate printers\n");
        d_printf("  net rpc file \t\t\tto list open files\n");
        d_printf("  net rpc changetrustpw \tto change the trust account password\n");
        d_printf("  net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n");
@@ -3659,6 +4336,7 @@ int net_rpc(int argc, const char **argv)
                {"group", net_rpc_group},
                {"share", net_rpc_share},
                {"file", net_rpc_file},
+               {"printer", net_rpc_printer},
                {"changetrustpw", net_rpc_changetrustpw},
                {"trustdom", rpc_trustdom},
                {"abortshutdown", rpc_shutdown_abort},
diff --git a/source/utils/net_rpc_printer.c b/source/utils/net_rpc_printer.c
new file mode 100644 (file)
index 0000000..b7d2438
--- /dev/null
@@ -0,0 +1,2185 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2004 Guenther Deschner (gd@samba.org)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+#include "includes.h"
+#include "../utils/net.h"
+
+struct table_node {
+       const char *long_archi;
+       const char *short_archi;
+       int version;
+};
+
+
+/* support itanium as well */
+static const struct table_node archi_table[]= {
+
+       {"Windows 4.0",          "WIN40",       0 },
+       {"Windows NT x86",       "W32X86",      2 },
+       {"Windows NT R4000",     "W32MIPS",     2 },
+       {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
+       {"Windows NT PowerPC",   "W32PPC",      2 },
+       {"Windows IA64",         "IA64",        3 },
+       {NULL,                   "",            -1 }
+};
+
+
+/**
+ * The display-functions for Security-Descriptors were taken from rpcclient
+ * 
+ * They reside here for debugging purpose and should 
+ * possibly be removed later on
+ *
+ **/
+/****************************************************************************
+convert a security permissions into a string
+****************************************************************************/
+char *get_sec_mask_str(uint32 type)
+{
+       static fstring typestr="";
+
+       typestr[0] = 0;
+
+       if (type & GENERIC_ALL_ACCESS)
+               fstrcat(typestr, "Generic all access ");
+       if (type & GENERIC_EXECUTE_ACCESS)
+               fstrcat(typestr, "Generic execute access ");
+       if (type & GENERIC_WRITE_ACCESS)
+               fstrcat(typestr, "Generic write access ");
+       if (type & GENERIC_READ_ACCESS)
+               fstrcat(typestr, "Generic read access ");
+       if (type & MAXIMUM_ALLOWED_ACCESS)
+               fstrcat(typestr, "MAXIMUM_ALLOWED_ACCESS ");
+       if (type & SYSTEM_SECURITY_ACCESS)
+               fstrcat(typestr, "SYSTEM_SECURITY_ACCESS ");
+       if (type & SYNCHRONIZE_ACCESS)
+               fstrcat(typestr, "SYNCHRONIZE_ACCESS ");
+       if (type & WRITE_OWNER_ACCESS)
+               fstrcat(typestr, "WRITE_OWNER_ACCESS ");
+       if (type & WRITE_DAC_ACCESS)
+               fstrcat(typestr, "WRITE_DAC_ACCESS ");
+       if (type & READ_CONTROL_ACCESS)
+               fstrcat(typestr, "READ_CONTROL_ACCESS ");
+       if (type & DELETE_ACCESS)
+               fstrcat(typestr, "DELETE_ACCESS ");
+
+       printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK);
+
+       return typestr;
+}
+
+
+/****************************************************************************
+ display sec_ace structure
+ ****************************************************************************/
+void display_sec_ace(SEC_ACE *ace)
+{
+       fstring sid_str;
+
+       printf("\tACE\n\t\ttype: ");
+       switch (ace->type) {
+               case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                       printf("ACCESS ALLOWED");
+                       break;
+               case SEC_ACE_TYPE_ACCESS_DENIED:
+                       printf("ACCESS DENIED");
+                       break;
+               case SEC_ACE_TYPE_SYSTEM_AUDIT:
+                       printf("SYSTEM AUDIT");
+                       break;
+               case SEC_ACE_TYPE_SYSTEM_ALARM:
+                       printf("SYSTEM ALARM");
+                       break;
+               default:
+                       printf("????");
+                       break;
+       }
+       printf(" (%d) flags: %d\n", ace->type, ace->flags);
+       printf("\t\tPermissions: 0x%x: %s\n", ace->info.mask, get_sec_mask_str(ace->info.mask));
+
+       sid_to_string(sid_str, &ace->trustee);
+       printf("\t\tSID: %s\n\n", sid_str);
+}
+
+
+/****************************************************************************
+ display sec_acl structure
+ ****************************************************************************/
+void display_sec_acl(SEC_ACL *sec_acl)
+{
+       int i;
+
+       printf("\tACL\tNum ACEs:\t%d\trevision:\t%x\n",
+                        sec_acl->num_aces, sec_acl->revision); 
+       printf("\t---\n");
+
+       if (sec_acl->size != 0 && sec_acl->num_aces != 0)
+               for (i = 0; i < sec_acl->num_aces; i++)
+                       display_sec_ace(&sec_acl->ace[i]);
+                               
+}
+
+/****************************************************************************
+ display sec_desc structure
+ ****************************************************************************/
+void display_sec_desc(SEC_DESC *sec)
+{
+       fstring sid_str;
+
+       if (sec == NULL)
+               return;
+
+       if (sec->sacl) {
+               printf("SACL\n");
+               display_sec_acl(sec->sacl);
+       }
+
+       if (sec->dacl) {
+               printf("DACL\n");
+               display_sec_acl(sec->dacl);
+       }
+
+       if (sec->owner_sid) {
+               sid_to_string(sid_str, sec->owner_sid);
+               printf("\tOwner SID:\t%s\n", sid_str);
+       }
+
+       if (sec->grp_sid) {
+               sid_to_string(sid_str, sec->grp_sid);
+               printf("\tParent SID:\t%s\n", sid_str);
+       }
+}
+
+
+/**
+ * This display-printdriver-functions was borrowed from rpcclient/cmd_spoolss.c.
+ * It is here for debugging purpose and should be removed later on.
+ **/
+
+/****************************************************************************
+printer info level 3 display function
+****************************************************************************/
+static void display_print_driver_3(DRIVER_INFO_3 *i1)
+{
+       fstring name = "";
+       fstring architecture = "";
+       fstring driverpath = "";
+       fstring datafile = "";
+       fstring configfile = "";
+       fstring helpfile = "";
+       fstring dependentfiles = "";
+       fstring monitorname = "";
+       fstring defaultdatatype = "";
+       
+       int length=0;
+       BOOL valid = True;
+       
+       if (i1 == NULL)
+               return;
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+       rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+       rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+       rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+       rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+       rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
+       rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
+
+       d_printf ("Printer Driver Info 3:\n");
+       d_printf ("\tVersion: [%x]\n", i1->version);
+       d_printf ("\tDriver Name: [%s]\n",name);
+       d_printf ("\tArchitecture: [%s]\n", architecture);
+       d_printf ("\tDriver Path: [%s]\n", driverpath);
+       d_printf ("\tDatafile: [%s]\n", datafile);
+       d_printf ("\tConfigfile: [%s]\n", configfile);
+       d_printf ("\tHelpfile: [%s]\n\n", helpfile);
+
+       while (valid) {
+               rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+               
+               length+=strlen(dependentfiles)+1;
+               
+               if (strlen(dependentfiles) > 0) {
+                       d_printf ("\tDependentfiles: [%s]\n", dependentfiles);
+               } else {
+                       valid = False;
+               }
+       }
+       
+       printf ("\n");
+
+       d_printf ("\tMonitorname: [%s]\n", monitorname);
+       d_printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
+
+       return; 
+}
+
+
+static void display_reg_value(pstring subkey, REGISTRY_VALUE value)
+{
+       pstring text;
+
+       switch(value.type) {
+       case REG_DWORD:
+               d_printf("\t[%s:%s]: REG_DWORD: 0x%08x\n", subkey, value.valuename, 
+                      *((uint32 *) value.data_p));
+               break;
+
+       case REG_SZ:
+               rpcstr_pull(text, value.data_p, sizeof(text), value.size,
+                           STR_TERMINATE);
+               d_printf("\t[%s:%s]: REG_SZ: %s\n", subkey, value.valuename, text);
+               break;
+
+       case REG_BINARY: 
+               d_printf("\t[%s:%s]: REG_BINARY: unknown length value not displayed\n", 
+                        subkey, value.valuename);
+               break;
+
+       case REG_MULTI_SZ: {
+               uint16 *curstr = (uint16 *) value.data_p;
+               uint8 *start = value.data_p;
+               d_printf("\t[%s:%s]: REG_MULTI_SZ:\n", subkey, value.valuename);
+               while ((*curstr != 0) && 
+                      ((uint8 *) curstr < start + value.size)) {
+                       rpcstr_pull(text, curstr, sizeof(text), -1, 
+                                   STR_TERMINATE);
+                       d_printf("%s\n", text);
+                       curstr += strlen(text) + 1;
+               }
+       }
+       break;
+
+       default:
+               d_printf("\t%s: unknown type %d\n", value.valuename, value.type);
+       }
+       
+}
+
+
+/**
+ * Copy a file or directory from a connected share to another connected share 
+ *
+ * @param mem_ctx              A talloc-context
+ * @param cli_share_src                A connected cli_state 
+ * @param cli_share_dst                A connected cli_state 
+ * @param src_file             The source file-name
+ * @param dst_file             The destination file-name
+ * @param copy_acls            Whether to copy acls
+ * @param is_file              Whether this file is a file or a dir
+ *
+ * @return Normal NTSTATUS return.
+ **/ 
+NTSTATUS net_copy_file(TALLOC_CTX *mem_ctx,
+                      struct cli_state *cli_share_src,
+                      struct cli_state *cli_share_dst, 
+                      char *src_name, char *dst_name,
+                      BOOL copy_acls, BOOL is_file)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       int fnum_src = 0;
+       int fnum_dst = 0;
+       static int io_bufsize = 64512;
+       int read_size = io_bufsize;
+       char *data = NULL;
+       off_t start = 0;
+       off_t nread = 0;
+       SEC_DESC *sd = NULL;
+
+
+       /* open on the originating server */
+       DEBUGADD(3,("opening %s %s on originating server\n", 
+               is_file ? "file":"dir", src_name));
+
+       if (is_file)
+               fnum_src = cli_open(cli_share_src, src_name, 
+                               O_RDONLY, DENY_NONE);
+       else
+               fnum_src = cli_nt_create(cli_share_src, src_name, 
+                               READ_CONTROL_ACCESS);
+
+       if (fnum_src == -1) {
+               DEBUGADD(0,("cannot open file %s on originating server %s\n", 
+                               src_name, cli_errstr(cli_share_src)));
+               nt_status = cli_nt_error(cli_share_src);
+               goto out;
+       }
+
+       /* open on the destination server */
+       if (is_file) {
+               DEBUGADD(3,("opening file %s on destination server\n", dst_name));
+               fnum_dst = cli_open(cli_share_dst, dst_name, 
+                               O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+       }
+
+        if (fnum_dst == -1) {
+                DEBUGADD(1,("cannot create file %s on destination server: %s\n", 
+                               dst_name, cli_errstr(cli_share_dst)));
+               nt_status = cli_nt_error(cli_share_dst);
+                goto out;
+        }
+
+
+       /* allocate memory */
+       if (!(data = (char *)malloc(read_size))) {
+               d_printf("malloc fail for size %d\n", read_size);
+               goto out;
+       }
+
+
+       if (copy_acls) {
+
+               /* get the security descriptor */
+               sd = cli_query_secdesc(cli_share_src, fnum_src, mem_ctx);
+               if (!sd) {
+                       DEBUG(0, ("failed to get security descriptor\n"));
+                       nt_status = cli_nt_error(cli_share_src);
+                       goto out;
+               }
+       }
+
+       /* copying file */
+       if (opt_verbose) {
+
+               d_printf("copying [\\\\%s\\%s%s] => [\\\\%s\\%s%s] %s acls.\n", 
+                       cli_share_src->desthost, cli_share_src->share, src_name, 
+                       cli_share_dst->desthost, cli_share_dst->share, dst_name,
+                       copy_acls ? "with" : "without" );
+
+               if (DEBUGLEVEL >= 3 && copy_acls)
+                       display_sec_desc(sd);
+       }
+
+       while (is_file) {
+
+               int n = cli_read(cli_share_src, fnum_src, data, nread + start, 
+                               read_size);
+
+               if (n <= 0)
+                       break;
+
+               int ret = cli_write(cli_share_dst, fnum_dst, 0, data, 
+                       nread + start, n);
+
+                if (n != ret) {
+                        d_printf("Error writing file: %s\n", 
+                               cli_errstr(cli_share_dst));
+                       nt_status = cli_nt_error(cli_share_dst);
+                        goto out;
+                }
+
+               nread += n;
+       }
+
+       /* creating dir */
+       if (!is_file && !cli_chkpath(cli_share_dst, dst_name)) {
+
+               DEBUGADD(1,("creating dir %s on the destination server\n", 
+                       dst_name));
+
+               if (!cli_mkdir(cli_share_dst, dst_name)) {
+                        DEBUG(0,("cannot create directory %s: %s\n",
+                                dst_name, cli_errstr(cli_share_dst)));
+                        nt_status = NT_STATUS_NO_SUCH_FILE;
+                }
+
+                if (!cli_chkpath(cli_share_dst, dst_name)) {
+                       d_printf("cannot check for directory %s: %s\n",
+                               dst_name, cli_errstr(cli_share_dst));
+                        goto out;
+                }
+       }
+
+
+       /* closing files */
+       if (!cli_close(cli_share_src, fnum_src)) {
+               d_printf("could not close file on originating server: %s\n", 
+                       cli_errstr(cli_share_src));
+               nt_status = cli_nt_error(cli_share_src);
+               goto out;
+       }
+
+       if (is_file && !cli_close(cli_share_dst, fnum_dst)) {
+               d_printf("could not close file on destinantion server: %s\n", 
+                       cli_errstr(cli_share_dst));
+               nt_status = cli_nt_error(cli_share_dst);
+               goto out;
+       }
+
+
+       /* finally set acls */
+       if (copy_acls) {
+
+               /* Open the file/dir a second time */ 
+               fnum_dst = cli_nt_create(cli_share_dst, dst_name, 
+                               WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
+
+               if (fnum_dst == -1) {
+                       DEBUG(0, ("failed to open file/dir again: %s: %s\n",
+                                 dst_name, cli_errstr(cli_share_dst)));
+                       nt_status = cli_nt_error(cli_share_dst);
+                       goto out;
+               }
+
+               if (!cli_set_secdesc(cli_share_dst, fnum_dst, sd)) {
+                       DEBUG(0, ("could not set secdesc on %s %s: %s\n", 
+                               is_file? "file":"dir", dst_name, 
+                               cli_errstr(cli_share_dst)));
+                       nt_status = cli_nt_error(cli_share_dst);
+                       goto out;
+               }
+       }
+
+       
+       nt_status = NT_STATUS_OK;
+
+out:
+
+       /* cleaning up */
+       if (fnum_src)
+               cli_close(cli_share_src, fnum_src);
+
+       if (fnum_dst)
+               cli_close(cli_share_dst, fnum_dst);
+
+       SAFE_FREE(data);
+
+       return nt_status;
+}
+
+
+/**
+ * Copy a driverfile from on connected share to another connected share 
+ * This silently assumes that a driver-file is picked up from 
+ *
+ *     \\src_server\print$\{arch}\{version}\file 
+ *
+ * and copied to
+ *
+ *     \\dst_server\print$\{arch}\file 
+ * 
+ * to be added via setdriver-calls later.
+ * @param mem_ctx              A talloc-context
+ * @param cli_share_src                A cli_state connected to source print$-share
+ * @param cli_share_dst                A cli_state connected to destination print$-share
+ * @param file                 The file-name to be copied 
+ * @param short_archi          The name of the driver-architecture (short form)
+ *
+ * @return Normal NTSTATUS return.
+ **/ 
+static NTSTATUS net_copy_driverfile(TALLOC_CTX *mem_ctx,
+                                   struct cli_state *cli_share_src,
+                                   struct cli_state *cli_share_dst, 
+                                   char *file, const char *short_archi) {
+
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       const char *p;
+       char *src_name;
+       char *dst_name;
+       fstring version;
+       fstring filename;
+       fstring tok;
+
+       /* scroll through the file until we have the part 
+          beyond archi_table.short_archi */
+       p = file;
+       while (next_token(&p, tok, "\\", sizeof(tok))) {
+               if (strequal(tok, short_archi)) {
+                       next_token(&p, version, "\\", sizeof(version));
+                       next_token(&p, filename, "\\", sizeof(filename));
+               }
+       }
+
+       /* build source file name */
+       if (asprintf(&src_name, "\\%s\\%s\\%s", short_archi, version, filename) < 0 ) 
+               return NT_STATUS_NO_MEMORY;
+
+
+       /* create destination file name */
+       if (asprintf(&dst_name, "\\%s\\%s", short_archi, filename) < 0 )
+                return NT_STATUS_NO_MEMORY;
+
+
+       /* finally copy the file */
+       nt_status = net_copy_file(mem_ctx, cli_share_src, cli_share_dst, 
+                                 src_name, dst_name, False, True);
+       if (!NT_STATUS_IS_OK(nt_status))
+               goto out;
+
+       nt_status = NT_STATUS_OK;
+
+out:
+       SAFE_FREE(src_name);
+       SAFE_FREE(dst_name);
+
+       return nt_status;
+}
+
+
+/**
+ * Check for existing Architecture directory on a given server
+ *
+ * @param cli_share            A cli_state connected to a print$-share
+ * @param short_archi          The Architecture for the print-driver
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+check_arch_dir(struct cli_state *cli_share, const char *short_archi)
+{
+
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       char *dir;
+
+       if (asprintf(&dir, "\\%s", short_archi) < 0) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       DEBUG(10,("creating print-driver dir for architecture: %s\n", 
+               short_archi));
+
+       if (!cli_mkdir(cli_share, dir)) {
+                DEBUG(1,("cannot create directory %s: %s\n",
+                         dir, cli_errstr(cli_share)));
+                nt_status = NT_STATUS_NO_SUCH_FILE;
+        }
+
+       if (!cli_chkpath(cli_share, dir)) {
+               d_printf("cannot check %s: %s\n", 
+                       dir, cli_errstr(cli_share));
+               goto out;
+       }
+
+       nt_status = NT_STATUS_OK;
+
+out:
+       SAFE_FREE(dir);
+       return nt_status;
+}
+
+
+/**
+ * Copy a print-driver (level 3) from one connected print$-share to another 
+ * connected print$-share
+ *
+ * @param mem_ctx              A talloc-context
+ * @param cli_share_src                A cli_state connected to a print$-share
+ * @param cli_share_dst                A cli_state connected to a print$-share
+ * @param short_archi          The Architecture for the print-driver
+ * @param i1                   The DRIVER_INFO_3-struct
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+copy_print_driver_3(TALLOC_CTX *mem_ctx,
+                   struct cli_state *cli_share_src, 
+                   struct cli_state *cli_share_dst, 
+                   const char *short_archi, DRIVER_INFO_3 *i1)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       int length = 0;
+       BOOL valid = True;
+       
+       fstring name = "";
+       fstring driverpath = "";
+       fstring datafile = "";
+       fstring configfile = "";
+       fstring helpfile = "";
+       fstring dependentfiles = "";
+       
+       if (i1 == NULL)
+               return nt_status;
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+       rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+       rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+       rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+
+
+       if (opt_verbose)
+               d_printf("copying driver: [%s], for architecture: [%s], version: [%d]\n", 
+                         name, short_archi, i1->version);
+       
+       nt_status = net_copy_driverfile(mem_ctx, cli_share_src, cli_share_dst, 
+               driverpath, short_archi);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+               
+       nt_status = net_copy_driverfile(mem_ctx, cli_share_src, cli_share_dst, 
+               datafile, short_archi);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+               
+       nt_status = net_copy_driverfile(mem_ctx, cli_share_src, cli_share_dst, 
+               configfile, short_archi);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+               
+       nt_status = net_copy_driverfile(mem_ctx, cli_share_src, cli_share_dst, 
+               helpfile, short_archi);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+       while (valid) {
+               rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+               length+=strlen(dependentfiles)+1;
+               if (strlen(dependentfiles) > 0) {
+
+                       nt_status = net_copy_driverfile(mem_ctx, 
+                                       cli_share_src, cli_share_dst, 
+                                       dependentfiles, short_archi);
+                       if (!NT_STATUS_IS_OK(nt_status))
+                               return nt_status;
+               } else {
+                       valid = False;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/**
+ * net_spoolss-functions
+ * =====================
+ *
+ * the net_spoolss-functions aim to simplify spoolss-client-functions
+ * required during the migration-process wrt buffer-sizes, returned
+ * error-codes, etc. 
+ *
+ * this greatly reduces the complexitiy of the migrate-functions.
+ *
+ **/
+
+static BOOL
+net_spoolss_enum_printers(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                         char *name, uint32 flags, uint32 level, 
+                         uint32 *num_printers, PRINTER_INFO_CTR *ctr)
+{
+
+       WERROR result;
+       uint32 needed;
+
+       /* enum printers */
+       result = cli_spoolss_enum_printers(
+               cli, mem_ctx, 0, &needed, name, flags,
+               level, num_printers, ctr);
+
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_INSUFFICIENT_BUFFER)) 
+               result = cli_spoolss_enum_printers(
+                       cli, mem_ctx, needed, NULL, name, flags,
+                       level, num_printers, ctr);
+       
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("cannot enum printers: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_open_printer_ex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                           const char *printername, const char *datatype, 
+                           uint32 access_required, const char *servername, 
+                           const char *username, POLICY_HND *hnd)
+{
+       WERROR result;
+
+       fstring servername2;
+       slprintf(servername2, sizeof(servername2)-1, "\\\\%s", cli->desthost);
+
+       DEBUG(10,("connecting to: %s as %s for %s and access: %x\n", 
+               servername2, username, printername, access_required));
+
+       /* open printer */
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+                       "", access_required,
+                       servername2, username, hnd);
+
+       /* be more verbose */
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) {
+               d_printf("no access to printer [%s] on [%s] for user [%s] granted\n", 
+                       printername, servername2, username);
+               return False;
+       }
+
+       if (!W_ERROR_IS_OK(result)) {
+               d_printf("cannot open printer %s on server %s: %s\n", 
+                       printername, servername2, dos_errstr(result));
+               return False;
+       }
+
+       DEBUG(2,("got printer handle for printer: %s, server: %s\n", 
+               printername, servername2));
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_getprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                      POLICY_HND *hnd, uint32 level, 
+                      PRINTER_INFO_CTR *ctr)
+{
+       WERROR result;
+       uint32 needed;
+
+       /* getprinter call */
+       result = cli_spoolss_getprinter(cli, 
+               mem_ctx, 0, &needed, hnd, level, ctr);
+
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_INSUFFICIENT_BUFFER)) 
+               result = cli_spoolss_getprinter(cli, 
+                       mem_ctx, needed, NULL, hnd, level, ctr);
+                       
+       if (!W_ERROR_IS_OK(result)) {
+               printf("cannot get printer-info: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_setprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                      POLICY_HND *hnd, uint32 level, 
+                      PRINTER_INFO_CTR *ctr)
+{
+       WERROR result;
+
+       /* setprinter call */
+       result = cli_spoolss_setprinter(cli, mem_ctx, hnd, level, ctr, 0);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("cannot set printer-info: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_setprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          POLICY_HND *hnd, REGISTRY_VALUE *value)
+{
+       WERROR result;
+       
+       /* setprinterdata call */
+       result = cli_spoolss_setprinterdata(cli, mem_ctx, hnd, value);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf ("unable to set printerdata: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_enumprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          POLICY_HND *hnd, const char *keyname,
+                          uint16 **keylist)
+{
+       WERROR result;
+       uint32 needed, len;
+
+       /* enumprinterkey call */
+       result = cli_spoolss_enumprinterkey(
+               cli, mem_ctx, 0, &needed, hnd, keyname, NULL, NULL);
+               
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_MORE_DATA)) 
+               result = cli_spoolss_enumprinterkey(
+                       cli, mem_ctx, needed, NULL, hnd, keyname, keylist,
+                       &len);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("enumprinterkey failed: %s\n", dos_errstr(result));
+               return False;
+       }
+       
+       return True;
+}
+
+
+static BOOL
+net_spoolss_enumprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             uint32 offered, 
+                             POLICY_HND *hnd, const char *keyname, 
+                             REGVAL_CTR *ctr) 
+{
+       WERROR result;
+       uint32 needed;
+
+       /* enumprinterdataex call */
+       result = cli_spoolss_enumprinterdataex(
+               cli, mem_ctx, 0, &needed, hnd, keyname, NULL);
+                       
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_MORE_DATA)) 
+               result = cli_spoolss_enumprinterdataex(
+                       cli, mem_ctx, needed, NULL, hnd, keyname, ctr);
+                                       
+       if (!W_ERROR_IS_OK(result)) {
+               printf("enumprinterdataex failed: %s\n", dos_errstr(result));
+               return False;
+       }
+       
+       return True;
+}
+
+
+static BOOL 
+net_spoolss_setprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                            POLICY_HND *hnd, char *keyname, 
+                            REGISTRY_VALUE *value)
+{
+       WERROR result;
+
+       /* setprinterdataex call */
+       result = cli_spoolss_setprinterdataex(cli, mem_ctx, hnd, 
+                                             keyname, value);
+       
+       if (!W_ERROR_IS_OK(result)) {
+               printf("could not set printerdataex: %s\n", dos_errstr(result));
+               return False;
+       }
+       
+       return True;
+}
+
+
+static BOOL
+net_spoolss_enumforms(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                     POLICY_HND *hnd, int level, uint32 *num_forms,
+                     FORM_1 **forms)
+                                                                                      
+{
+       WERROR result;
+       uint32 needed;
+
+       /* enumforms call */
+       result = cli_spoolss_enumforms(
+                       cli, mem_ctx, 0, &needed, hnd, level, num_forms, forms);
+
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_INSUFFICIENT_BUFFER)) 
+               result = cli_spoolss_enumforms(
+                               cli, mem_ctx, needed, NULL, hnd, level,
+                               num_forms, forms);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("could not enum forms: %s\n", dos_errstr(result));
+               return False;
+       }
+       
+       return True;
+}
+
+
+static BOOL
+net_spoolss_enumprinterdrivers (struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               uint32 level, const char *env,
+                               uint32 *num_drivers,
+                               PRINTER_DRIVER_CTR *ctr)
+{
+       WERROR result;
+       uint32 needed;
+
+       /* enumprinterdrivers call */
+       result = cli_spoolss_enumprinterdrivers(
+                       cli, mem_ctx, 0, &needed, level,
+                       env, num_drivers, ctr);
+
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_INSUFFICIENT_BUFFER))
+               result = cli_spoolss_enumprinterdrivers(
+                               cli, mem_ctx, needed, NULL, level,
+                               env, num_drivers, ctr);
+       
+       if (!W_ERROR_IS_OK(result)) {
+               printf("cannot enum drivers: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_getprinterdriver(struct cli_state *cli, 
+                            TALLOC_CTX *mem_ctx, 
+                            POLICY_HND *hnd, uint32 level, 
+                            const char *env, int version, 
+                            PRINTER_DRIVER_CTR *ctr)
+{
+       WERROR result;
+       uint32 needed;
+       
+       /* getprinterdriver call */
+       result = cli_spoolss_getprinterdriver(
+                       cli, mem_ctx, 0, &needed, hnd, level,
+                       env, version, ctr);
+
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_INSUFFICIENT_BUFFER))
+               result = cli_spoolss_getprinterdriver(
+                               cli, mem_ctx, needed, NULL, hnd, level,
+                               env, version, ctr);
+
+       if (!W_ERROR_IS_OK(result)) {
+               DEBUG(1,("cannot get driver (for architecture: %s): %s\n", 
+                       env, dos_errstr(result)));
+               if (W_ERROR_V(result) != W_ERROR_V(WERR_UNKNOWN_PRINTER_DRIVER)) {
+                       printf("cannot get driver: %s\n", dos_errstr(result));
+               }
+               return False;
+       }
+
+       return True;
+}
+
+
+static BOOL
+net_spoolss_addprinterdriver(struct cli_state *cli, 
+                            TALLOC_CTX *mem_ctx, uint32 level,
+                            PRINTER_DRIVER_CTR *ctr)
+{
+       WERROR result;
+
+       /* addprinterdriver call */
+       result = cli_spoolss_addprinterdriver(cli, mem_ctx, level, ctr);
+
+       /* be more verbose */
+       if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) {
+               printf("You are not allowed to add drivers\n");
+               return False;
+       }
+       if (!W_ERROR_IS_OK(result)) {
+               printf("cannot add driver: %s\n", dos_errstr(result));
+               return False;
+       }
+
+       return True;
+}
+
+/**
+ * abstraction function to get uint32 num_printers and PRINTER_INFO_CTR ctr 
+ * for a single printer or for all printers depending on argc/argv 
+ **/
+static BOOL
+get_printer_info(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                int level, int argc, const char **argv, 
+                uint32 *num_printers, PRINTER_INFO_CTR *ctr)
+{
+
+       char *sharename;
+       fstring servername;
+       POLICY_HND hnd;
+
+       /* no arguments given, enumerate all printers */
+       if (argc == 0) {
+
+               if (!net_spoolss_enum_printers(cli, mem_ctx, NULL, 
+                               PRINTER_ENUM_LOCAL|PRINTER_ENUM_SHARED, 
+                               level, num_printers, ctr)) 
+                       return False;
+
+               goto out;
+       }
+
+
+       /* argument given, get a single printer by name */
+       sharename = strdup(argv[0]);
+
+       if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, 
+                       cli->user_name, &hnd)) 
+               return False;
+
+       if (!net_spoolss_getprinter(cli, mem_ctx, &hnd, level, ctr)) {
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+               return False;
+       }
+
+       cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       *num_printers = 1;
+
+out:
+       DEBUG(3,("got %d printers\n", *num_printers));
+
+       return True;
+
+}
+
+
+/** 
+ * List print-queues (including local printers that are not shared)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_list_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                   struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                   int argc, const char **argv)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i, num_printers; 
+       uint32 level = 2;
+       pstring printername, sharename;
+       PRINTER_INFO_CTR ctr;
+
+       printf("listing printers\n");
+
+       if (!get_printer_info(cli, mem_ctx, level, argc, argv, &num_printers, &ctr))
+               return nt_status;
+
+       for (i = 0; i < num_printers; i++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, ctr.printers_2[i].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, ctr.printers_2[i].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               
+               d_printf("printer %d: %s, shared as: %s\n", 
+                       i+1, printername, sharename);
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/** 
+ * List printer-drivers from a server 
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_driver_list_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                          struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                          int argc, const char **argv)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i;
+       uint32 level = 3; 
+       PRINTER_DRIVER_CTR drv_ctr_enum;
+       int d;
+       
+       ZERO_STRUCT(drv_ctr_enum);
+
+
+       printf("listing printer-drivers\n");
+
+        for (i=0; archi_table[i].long_archi!=NULL; i++) {
+
+               int num_drivers;
+
+               /* enum remote drivers */
+               if (!net_spoolss_enumprinterdrivers(cli, mem_ctx, level,
+                               archi_table[i].long_archi, 
+                               &num_drivers, &drv_ctr_enum)) {
+                                                                               
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+               }
+
+               if (num_drivers == 0) {
+                       d_printf ("no drivers found on server for architecture: [%s].\n", 
+                               archi_table[i].long_archi);
+                       continue;
+               } 
+               
+               d_printf("got %d printer-drivers for architecture: [%s]\n", 
+                       num_drivers, archi_table[i].long_archi);
+
+
+               /* do something for all drivers for architecture */
+               for (d = 0; d < num_drivers; d++) {
+                       display_print_driver_3(&(drv_ctr_enum.info3[d]));
+               }
+       }
+       
+       nt_status = NT_STATUS_OK;
+
+done:
+       return nt_status;
+
+}
+
+
+/** 
+ * Migrate Printer-ACLs from a source server to the destination server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_migrate_security_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                               struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                               int argc, const char **argv)
+{
+       /* TODO: what now, info2 or info3 ? 
+          convince jerry that we should add clientside setacls level 3 at least
+       */
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i = 0;
+       uint32 num_printers;
+       uint32 level = 2;
+       fstring servername = "";
+       pstring printername = "", sharename = "";
+       BOOL got_hnd_src = False;
+       BOOL got_hnd_dst = False;
+       BOOL got_dst_spoolss_pipe = False;
+       POLICY_HND hnd_src, hnd_dst;
+       PRINTER_INFO_CTR ctr_src, ctr_dst, ctr_enum;
+       struct cli_state *cli_dst = NULL;
+
+       ZERO_STRUCT(ctr_src);
+
+       DEBUG(3,("copying printer ACLs\n"));
+
+       /* connect local PI_SPOOLSS */
+       nt_status = connect_local_pipe(&cli_dst, PI_SPOOLSS, &got_dst_spoolss_pipe);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+
+       /* enum remote printers */
+       if (!get_printer_info(cli, mem_ctx, level, argc, argv, &num_printers, &ctr_enum)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!num_printers) {
+               printf ("no printers found on server.\n");
+               nt_status = NT_STATUS_OK;
+               goto done;
+       } 
+       
+
+       /* do something for all printers */
+       for (i = 0; i < num_printers; i++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, ctr_enum.printers_2[i].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, ctr_enum.printers_2[i].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               /* we can reset NT_STATUS here because we do not 
+                  get any real NT_STATUS-codes anymore from now on */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               
+               d_printf("migrating printer ACLs for:     [%s] / [%s]\n", 
+                       printername, sharename);
+
+               /* according to msdn you have specify these access-rights 
+                  to see the security descriptor
+                       - READ_CONTROL (DACL)
+                       - ACCESS_SYSTEM_SECURITY (SACL)
+               */
+
+               /* open src printer handle */
+               if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, cli->user_name, &hnd_src)) 
+                       goto done;
+
+               got_hnd_src = True;
+
+
+               /* open dst printer handle */
+               if (!net_spoolss_open_printer_ex(cli_dst, mem_ctx, sharename, "", 
+                       PRINTER_ALL_ACCESS, servername, cli_dst->user_name, &hnd_dst)) 
+                       goto done;
+
+               got_hnd_dst = True;
+
+
+               /* check for existing dst printer */
+               if (!net_spoolss_getprinter(cli_dst, mem_ctx, &hnd_dst, level, &ctr_dst)) 
+                       goto done;
+
+               /* check for existing src printer */
+               if (!net_spoolss_getprinter(cli, mem_ctx, &hnd_src, 3, &ctr_src)) 
+                       goto done;
+
+
+               /* Copy Security Descriptor */
+
+               /* copy secdesc (info level 2) */
+               ctr_dst.printers_2->devmode = NULL; 
+               ctr_dst.printers_2->secdesc = dup_sec_desc(mem_ctx, ctr_src.printers_3->secdesc);
+
+               if (opt_verbose)
+                       display_sec_desc(ctr_dst.printers_2->secdesc);
+               
+               if (!net_spoolss_setprinter(cli_dst, mem_ctx, &hnd_dst, 2, &ctr_dst)) 
+                       goto done;
+               
+               DEBUGADD(1,("\tSetPrinter of SECDESC succeeded\n"));
+
+
+               /* close printer handles here */
+               if (got_hnd_src) {
+                       cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                       got_hnd_src = False;
+               }
+
+               if (got_hnd_dst) {
+                       cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                       got_hnd_dst = False;
+               }
+
+       }
+       
+       nt_status = NT_STATUS_OK;
+
+done:
+
+       if (got_hnd_src)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+
+       if (got_hnd_dst)
+               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+
+       if (got_dst_spoolss_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+       return nt_status;
+}
+
+
+/** 
+ * Migrate printer-forms from a src server to the dst server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_migrate_forms_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                            struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                            int argc, const char **argv)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       WERROR result;
+       uint32 i, f;
+       uint32 num_printers;
+       uint32 level = 1;
+       fstring servername = "";
+       pstring printername = "", sharename = "";
+       BOOL got_hnd_src = False;
+       BOOL got_hnd_dst = False;
+       BOOL got_dst_spoolss_pipe = False;
+       POLICY_HND hnd_src, hnd_dst;
+       PRINTER_INFO_CTR ctr_enum, ctr_dst;
+       uint32 num_forms;
+       FORM_1 *forms;
+       struct cli_state *cli_dst = NULL;
+       
+       ZERO_STRUCT(ctr_enum);
+
+       DEBUG(3,("copying forms\n"));
+       
+       /* connect local PI_SPOOLSS */
+       nt_status = connect_local_pipe(&cli_dst, PI_SPOOLSS, &got_dst_spoolss_pipe);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+       
+
+       /* enum src printers */
+       if (!get_printer_info(cli, mem_ctx, 2, argc, argv, &num_printers, &ctr_enum)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!num_printers) {
+               printf ("no printers found on server.\n");
+               nt_status = NT_STATUS_OK;
+               goto done;
+       } 
+       
+
+       /* do something for all printers */
+       for (i = 0; i < num_printers; i++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, ctr_enum.printers_2[i].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, ctr_enum.printers_2[i].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               /* we can reset NT_STATUS here because we do not 
+                  get any real NT_STATUS-codes anymore from now on */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               
+               d_printf("migrating printer forms for:    [%s] / [%s]\n", 
+                       printername, sharename);
+
+
+               /* open src printer handle */
+               if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, cli->user_name, &hnd_src)) 
+                       goto done;
+
+               got_hnd_src = True;
+
+
+               /* open dst printer handle */
+               if (!net_spoolss_open_printer_ex(cli_dst, mem_ctx, sharename, "", 
+                       PRINTER_ALL_ACCESS, servername, cli->user_name, &hnd_dst)) 
+                       goto done;
+
+               got_hnd_dst = True;
+
+
+               /* check for existing dst printer */
+               if (!net_spoolss_getprinter(cli_dst, mem_ctx, &hnd_dst, level, &ctr_dst)) 
+                       goto done;
+
+               /* finally migrate forms */
+               if (!net_spoolss_enumforms(cli, mem_ctx, &hnd_src, level, &num_forms, &forms))
+                       goto done;
+
+               DEBUG(1,("got %d forms for printer\n", num_forms));
+
+
+               for (f = 0; f < num_forms; f++) {
+
+                       FORM form;
+                       fstring form_name;
+                       
+                       /* only migrate FORM_PRINTER types, according to jerry 
+                          FORM_BUILTIN-types are hard-coded in samba */
+                       if (forms[f].flag != FORM_PRINTER)
+                               continue;
+
+                       if (forms[f].name.buffer)
+                               rpcstr_pull(form_name, forms[f].name.buffer,
+                                       sizeof(form_name), -1, STR_TERMINATE);
+
+                       if (opt_verbose)
+                               d_printf("\tmigrating form # %d [%s] of type [%d]\n", 
+                                       f, form_name, forms[f].flag);
+
+                       /* is there a more elegant way to do that ? */
+                       form.flags      = FORM_PRINTER;
+                       form.size_x     = forms[f].width;
+                       form.size_y     = forms[f].length;
+                       form.left       = forms[f].left;
+                       form.top        = forms[f].top;
+                       form.right      = forms[f].right;
+                       form.bottom     = forms[f].bottom;
+                       
+                       init_unistr2(&form.name, form_name, UNI_STR_TERMINATE);
+
+                       /* FIXME: there might be something wrong with samba's 
+                          builtin-forms */
+                       result = cli_spoolss_addform(cli_dst, mem_ctx, 
+                               &hnd_dst, 1, &form);
+                       if (!W_ERROR_IS_OK(result)) {
+                               d_printf("\tAddForm form %d: [%s] refused.\n", 
+                                       f, form_name);
+                               continue;
+                       }
+       
+                       DEBUGADD(1,("\tAddForm of [%s] succeeded\n", form_name));
+               }
+
+
+               /* close printer handles here */
+               if (got_hnd_src) {
+                       cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                       got_hnd_src = False;
+               }
+
+               if (got_hnd_dst) {
+                       cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                       got_hnd_dst = False;
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+
+       if (got_hnd_src)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+
+       if (got_hnd_dst)
+               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+
+       if (got_dst_spoolss_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+       return nt_status;
+
+}
+
+
+/** 
+ * Migrate printer-drivers from a src server to the dst server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_migrate_drivers_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                              struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                              int argc, const char **argv)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i, p;
+       uint32 num_printers;
+       uint32 level = 3; 
+       fstring servername = "";
+       pstring printername = "", sharename = "";
+       BOOL got_hnd_src = False;
+       BOOL got_hnd_dst = False;
+       BOOL got_dst_spoolss_pipe = False;
+       BOOL got_src_driver_share = False;
+       BOOL got_dst_driver_share = False;
+       POLICY_HND hnd_src, hnd_dst;
+       PRINTER_DRIVER_CTR drv_ctr_src, drv_ctr_dst;
+       PRINTER_INFO_CTR info_ctr_enum, info_ctr_dst;
+       struct cli_state *cli_dst = NULL;
+       struct cli_state *cli_share_src = NULL;
+       struct cli_state *cli_share_dst = NULL;
+       fstring drivername = "";
+       
+       ZERO_STRUCT(drv_ctr_src);
+       ZERO_STRUCT(drv_ctr_dst);
+       ZERO_STRUCT(info_ctr_enum);
+       ZERO_STRUCT(info_ctr_dst);
+
+
+       DEBUG(3,("copying printer-drivers\n"));
+
+       nt_status = connect_local_pipe(&cli_dst, PI_SPOOLSS, &got_dst_spoolss_pipe);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+       
+
+       /* open print$-share on the src server */
+       nt_status = connect_to_service(&cli_share_src, &cli->dest_ip, 
+                       cli->desthost, "print$", "A:");
+       if (!NT_STATUS_IS_OK(nt_status)) 
+               return nt_status;
+
+       got_src_driver_share = True;
+
+
+       /* open print$-share on the dst server */
+       nt_status = connect_to_service(&cli_share_dst, &cli_dst->dest_ip, 
+                       cli_dst->desthost, "print$", "A:");
+       if (!NT_STATUS_IS_OK(nt_status)) 
+               return nt_status;
+
+       got_dst_driver_share = True;
+
+
+       /* enum src printers */
+       if (!get_printer_info(cli, mem_ctx, 2, argc, argv, &num_printers, &info_ctr_enum)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!num_printers) {
+               printf ("no printers found on server.\n");
+               nt_status = NT_STATUS_OK;
+               goto done;
+       } 
+       
+
+       /* do something for all printers */
+       for (p = 0; p < num_printers; p++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, info_ctr_enum.printers_2[p].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, info_ctr_enum.printers_2[p].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               /* we can reset NT_STATUS here because we do not 
+                  get any real NT_STATUS-codes anymore from now on */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+
+               d_printf("migrating printer driver for:   [%s] / [%s]\n", 
+                       printername, sharename);
+
+               /* open dst printer handle */
+               if (!net_spoolss_open_printer_ex(cli_dst, mem_ctx, sharename, "", 
+                       PRINTER_ALL_ACCESS, servername, cli->user_name, &hnd_dst)) 
+                       goto done;
+                       
+               got_hnd_dst = True;
+
+               /* check for existing dst printer */
+               if (!net_spoolss_getprinter(cli_dst, mem_ctx, &hnd_dst, 2, &info_ctr_dst)) 
+                       goto done;
+
+
+               /* open src printer handle */
+               if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, cli->user_name, &hnd_src)) 
+                       goto done;
+
+               got_hnd_src = True;
+
+
+               /* in a first step call getdriver for each shared printer (per arch)
+                  to get a list of all files that have to be copied */
+                  
+               for (i=0; archi_table[i].long_archi!=NULL; i++) {
+
+                       /* getdriver src */
+                       if (!net_spoolss_getprinterdriver(cli, mem_ctx, &hnd_src, 
+                                       level, archi_table[i].long_archi, 
+                                       archi_table[i].version, &drv_ctr_src)) 
+                               continue;
+
+                       rpcstr_pull(drivername, drv_ctr_src.info3->name.buffer, 
+                                       sizeof(drivername), -1, STR_TERMINATE);
+
+                       if (opt_verbose)
+                               display_print_driver_3(drv_ctr_src.info3);
+
+
+                       /* check arch dir */
+                       nt_status = check_arch_dir(cli_share_dst, archi_table[i].short_archi);
+                       if (!NT_STATUS_IS_OK(nt_status))
+                               goto done;
+
+
+                       /* copy driver-files */
+                       nt_status = copy_print_driver_3(mem_ctx, cli_share_src, cli_share_dst, 
+                                                       archi_table[i].short_archi, 
+                                                       drv_ctr_src.info3);
+                       if (!NT_STATUS_IS_OK(nt_status))
+                               goto done;
+
+
+                       /* adddriver dst */
+                       if (!net_spoolss_addprinterdriver(cli_dst, mem_ctx, level, &drv_ctr_src)) { 
+                               nt_status = NT_STATUS_UNSUCCESSFUL;
+                               goto done;
+                       }
+                               
+                       DEBUGADD(1,("Sucessfully added driver [%s] for printer [%s]\n", 
+                               drivername, printername));
+
+               }
+
+               /* setdriver dst */
+               init_unistr(&info_ctr_dst.printers_2->drivername, drivername);
+               
+               if (!net_spoolss_setprinter(cli_dst, mem_ctx, &hnd_dst, 2, &info_ctr_dst)) { 
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+               }
+
+               DEBUGADD(1,("Sucessfully set driver %s for printer %s\n", 
+                       drivername, printername));
+
+               /* close dst */
+               if (got_hnd_dst) {
+                       cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                       got_hnd_dst = False;
+               }
+
+               /* close src */
+               if (got_hnd_src) {
+                       cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                       got_hnd_src = False;
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+
+       if (got_hnd_src)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+
+       if (got_hnd_dst)
+               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+
+       if (got_dst_spoolss_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+
+       if (got_src_driver_share)
+               cli_shutdown(cli_share_src);
+
+       if (got_dst_driver_share)
+               cli_shutdown(cli_share_dst);
+
+       return nt_status;
+
+}
+
+
+/** 
+ * Migrate printer-queues from a src to the dst server
+ * (requires a working "addprinter command" to be installed for the local smbd)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_migrate_printers_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                               struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                               int argc, const char **argv)
+{
+       WERROR result;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i = 0, num_printers;
+       uint32 level = 2;
+       PRINTER_INFO_CTR ctr_src, ctr_dst, ctr_enum;
+       struct cli_state *cli_dst = NULL;
+       POLICY_HND hnd_dst, hnd_src;
+       pstring printername, sharename;
+       fstring servername;
+       BOOL got_hnd_src = False;
+       BOOL got_hnd_dst = False;
+       BOOL got_dst_spoolss_pipe = False;
+
+       DEBUG(3,("copying printers\n"));
+
+       /* connect local PI_SPOOLSS */
+       nt_status = connect_local_pipe(&cli_dst, PI_SPOOLSS, &got_dst_spoolss_pipe);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+
+       /* enum printers */
+       if (!get_printer_info(cli, mem_ctx, 2, argc, argv, &num_printers, &ctr_enum)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!num_printers) {
+               printf ("no printers found on server.\n");
+               nt_status = NT_STATUS_OK;
+               goto done;
+       } 
+       
+
+       /* do something for all printers */
+       for (i = 0; i < num_printers; i++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, ctr_enum.printers_2[i].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, ctr_enum.printers_2[i].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               /* we can reset NT_STATUS here because we do not 
+                  get any real NT_STATUS-codes anymore from now on */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               
+               d_printf("migrating printer queue for:    [%s] / [%s]\n", 
+                       printername, sharename);
+
+
+               /* open dst printer handle */
+               if (!net_spoolss_open_printer_ex(cli_dst, mem_ctx, sharename, "", 
+                       PRINTER_ALL_ACCESS, servername, cli->user_name, &hnd_dst)) {
+                       
+                       DEBUG(1,("could not open printer: %s\n", sharename));
+               } else {
+                       got_hnd_dst = True;
+               }
+
+
+               /* check for existing dst printer */
+               if (!net_spoolss_getprinter(cli_dst, mem_ctx, &hnd_dst, level, &ctr_dst)) {
+                       printf ("could not get printer, creating printer.\n");
+               } else {
+                       DEBUG(1,("printer already exists: %s\n", sharename));
+                       /* close printer handles here */
+                       if (got_hnd_src) {
+                               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                               got_hnd_src = False;
+                       }
+
+                       if (got_hnd_dst) {
+                               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                               got_hnd_dst = False;
+                       }
+                       continue;
+               }
+
+
+               /* now get again src printer ctr via getprinter, 
+                  we first need a handle for that */
+
+               /* open src printer handle */
+               if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, cli->user_name, &hnd_src)) 
+                       goto done;
+
+               got_hnd_src = True;
+
+               /* getprinter on the src server */
+               if (!net_spoolss_getprinter(cli, mem_ctx, &hnd_src, level, &ctr_src)) 
+                       goto done;
+
+
+               /* copy each src printer to a dst printer 1:1, 
+                  maybe some values have to be changed though */
+               d_printf("creating printer: %s\n", printername);
+               result = cli_spoolss_addprinterex (cli_dst, mem_ctx, level, &ctr_src);
+
+               if (W_ERROR_IS_OK(result))
+                       d_printf ("printer [%s] successfully added.\n", printername);
+               else if (W_ERROR_V(result) == W_ERROR_V(WERR_PRINTER_ALREADY_EXISTS)) 
+                       d_printf ("printer [%s] already exists.\n", printername);
+               else {
+                       printf ("could not create printer\n");
+                       goto done;
+               }
+
+               /* close printer handles here */
+               if (got_hnd_src) {
+                       cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                       got_hnd_src = False;
+               }
+
+               if (got_hnd_dst) {
+                       cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                       got_hnd_dst = False;
+               }
+       }
+
+       nt_status = NT_STATUS_OK;
+
+done:
+       if (got_hnd_src)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+
+       if (got_hnd_dst)
+               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+
+       if (got_dst_spoolss_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+       return nt_status;
+}
+
+
+/** 
+ * Migrate Printer-Settings from a src server to the dst server
+ * (for this to work, printers and drivers already have to be migrated earlier)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS rpc_printer_migrate_settings_internals(const DOM_SID *domain_sid, const char *domain_name, 
+                                               struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                               int argc, const char **argv)
+{
+
+       /* FIXME: Here the nightmare begins */
+
+       WERROR result;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       uint32 i = 0, p = 0, j = 0;
+       uint32 num_printers, val_needed, data_needed;
+       uint32 level = 2;
+       fstring servername = "";
+       pstring printername = "", sharename = "";
+       BOOL got_hnd_src = False;
+       BOOL got_hnd_dst = False;
+       BOOL got_dst_spoolss_pipe = False;
+       POLICY_HND hnd_src, hnd_dst;
+       PRINTER_INFO_CTR ctr_enum, ctr_dst;
+       REGVAL_CTR reg_ctr;
+       struct cli_state *cli_dst = NULL;
+       char *devicename = NULL, *unc_name = NULL, *url = NULL;
+       fstring longname;
+
+       const char *keyname = NULL;
+       uint16 *keylist = NULL, *curkey;
+
+       ZERO_STRUCT(ctr_enum);
+
+       DEBUG(3,("copying printer settings\n"));
+
+       /* connect local PI_SPOOLSS */
+       nt_status = connect_local_pipe(&cli_dst, PI_SPOOLSS, &got_dst_spoolss_pipe);
+       if (!NT_STATUS_IS_OK(nt_status))
+               return nt_status;
+
+
+       /* enum src printers */
+       if (!get_printer_info(cli, mem_ctx, level, argc, argv, &num_printers, &ctr_enum)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!num_printers) {
+               printf ("no printers found on server.\n");
+               nt_status = NT_STATUS_OK;
+               goto done;
+       } 
+       
+
+       /* needed for dns-strings in regkeys */
+       get_mydnsfullname(longname);
+       
+       /* do something for all printers */
+       for (i = 0; i < num_printers; i++) {
+
+               /* do some initialization */
+               rpcstr_pull(printername, ctr_enum.printers_2[i].printername.buffer, 
+                       sizeof(printername), -1, STR_TERMINATE);
+               rpcstr_pull(sharename, ctr_enum.printers_2[i].sharename.buffer, 
+                       sizeof(sharename), -1, STR_TERMINATE);
+               keyname = "";
+               /* we can reset NT_STATUS here because we do not 
+                  get any real NT_STATUS-codes anymore from now on */
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               
+               d_printf("migrating printer settings for: [%s] / [%s]\n", 
+                       printername, sharename);
+
+
+               /* open src printer handle */
+               if (!net_spoolss_open_printer_ex(cli, mem_ctx, sharename, "", 
+                       MAXIMUM_ALLOWED_ACCESS, servername, cli->user_name, &hnd_src)) 
+                       goto done;
+
+               got_hnd_src = True;
+
+
+               /* open dst printer handle */
+               if (!net_spoolss_open_printer_ex(cli_dst, mem_ctx, sharename, "", 
+                       PRINTER_ALL_ACCESS, servername, cli_dst->user_name, &hnd_dst)) 
+                       goto done;
+
+               got_hnd_dst = True;
+
+
+               /* check for existing dst printer */
+               if (!net_spoolss_getprinter(cli_dst, mem_ctx, &hnd_dst, 
+                               level, &ctr_dst)) 
+                       goto done;
+
+
+               /* STEP 1: COPY DEVICE-MODE and other 
+                          PRINTER_INFO_2-attributes
+               */
+
+               ctr_dst.printers_2 = &ctr_enum.printers_2[i];
+
+               /* why is the port always disconnected when the printer 
+                  is correctly installed (incl. driver ???) */
+               init_unistr( &ctr_dst.printers_2->portname, SAMBA_PRINTER_PORT_NAME);
+
+               /* copy devmode (info level 2) */
+               ctr_dst.printers_2->devmode = talloc_memdup(mem_ctx, 
+                       ctr_enum.printers_2[i].devmode, sizeof(DEVICEMODE));
+
+               /* do not copy security descriptor (we have another command for that) */
+               ctr_dst.printers_2->secdesc = NULL;
+
+
+               /* devmode->devicename is possibly broken at the moment for all 
+                  strlen(longprinternames) > MAXDEVICENAME (that is 32 chars) 
+                  this fires up thousands of safe_strncpy-debug0-messages 
+                  on my test-servers
+                  TODO: tell jerry, jra, etc. again.
+               */
+
+#if 0
+               if (asprintf(&devicename, "\\\\%s\\%s", longname, printername) < 0) {
+                       nt_status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               init_unistr(&ctr_dst.printers_2->devmode->devicename, devicename); 
+#endif
+               if (!net_spoolss_setprinter(cli_dst, mem_ctx, &hnd_dst, 
+                                               level, &ctr_dst)) 
+                       goto done;
+               
+               DEBUGADD(1,("\tSetPrinter of DEVICEMODE succeeded\n"));
+
+
+
+               /* STEP 2: COPY REGISTRY VALUES */
+       
+               /* please keep in mind that samba parse_spools gives horribly 
+                  crippled results when used to cli_spoolss_enumprinterdataex 
+                  a win2k3-server.  
+                  FIXME: IIRC I've seen it too on a win2k-server 
+               */
+
+               /* enumerate data on src handle */
+               result = cli_spoolss_enumprinterdata(cli, mem_ctx, &hnd_src, p, 0, 0,
+                       &val_needed, &data_needed, NULL);
+
+               /* loop for all printerdata */
+               while (W_ERROR_IS_OK(result)) {
+                       
+                       REGISTRY_VALUE value;
+                       
+                       result = cli_spoolss_enumprinterdata(
+                               cli, mem_ctx, &hnd_src, p++, val_needed,
+                               data_needed, 0, 0, &value);
+
+                       /* loop for all reg_keys */
+                       if (W_ERROR_IS_OK(result)) {
+
+                               /* display_value */
+                               if (opt_verbose) 
+                                       display_reg_value(NULL, value);
+
+                               /* set_value */
+                               if (!net_spoolss_setprinterdata(cli_dst, mem_ctx, 
+                                                               &hnd_dst, &value)) 
+                                       goto done;
+
+                               DEBUGADD(1,("\tSetPrinterData of [%s] succeeded\n", 
+                                       value.valuename));
+                       }
+               }
+               
+               /* STEP 3: COPY SUBKEY VALUES */
+
+               /* here we need to enum all printer_keys and then work 
+                  on the result with enum_printer_key_ex. nt4 does not
+                  respond to enumprinterkey, win2k does, so continue 
+                  in case of an error */
+
+               if (!net_spoolss_enumprinterkey(cli, mem_ctx, &hnd_src, keyname, &keylist)) {
+                       printf("got no key-data\n");
+                       continue;
+               }
+
+
+               /* work on a list of printer keys 
+                  each key has to be enumerated to get all required
+                  information.  information is then set via setprinterdataex-calls */ 
+
+               if (keylist == NULL)
+                       continue;
+
+               curkey = keylist;
+               while (*curkey != 0) {
+
+                       pstring subkey;
+                       rpcstr_pull(subkey, curkey, sizeof(subkey), -1, STR_TERMINATE);
+
+                       curkey += strlen(subkey) + 1;
+
+                       /* enumerate all src subkeys */
+                       if (!net_spoolss_enumprinterdataex(cli, mem_ctx, 0, 
+                                                          &hnd_src, subkey, 
+                                                          &reg_ctr)) 
+                               goto done;
+
+                       for (j=0; j < reg_ctr.num_values; j++) {
+                       
+                               REGISTRY_VALUE value;
+                               UNISTR2 data;
+                       
+                               /* although samba replies with sane data in most cases we 
+                                  should try to avoid writing wrong registry data */
+       
+                               if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_PORTNAME) || 
+                                   strequal(reg_ctr.values[j]->valuename, SPOOL_REG_UNCNAME) ||
+                                   strequal(reg_ctr.values[j]->valuename, SPOOL_REG_URL) ||
+                                   strequal(reg_ctr.values[j]->valuename, SPOOL_REG_SHORTSERVERNAME) ||
+                                   strequal(reg_ctr.values[j]->valuename, SPOOL_REG_SERVERNAME)) {
+
+                                       if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_PORTNAME)) {
+                               
+                                               /* although windows uses a multi-sz, we use a sz */
+                                               init_unistr2(&data, SAMBA_PRINTER_PORT_NAME, UNI_STR_TERMINATE);
+                                               fstrcpy(value.valuename, SPOOL_REG_PORTNAME);
+                                       }
+                               
+                                       if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_UNCNAME)) {
+                                       
+                                               if (asprintf(&unc_name, "\\\\%s\\%s", longname, sharename) < 0) {
+                                                       nt_status = NT_STATUS_NO_MEMORY;
+                                                       goto done;
+                                               }
+                                               init_unistr2(&data, unc_name, UNI_STR_TERMINATE);
+                                               fstrcpy(value.valuename, SPOOL_REG_UNCNAME);
+                                       }
+
+                                       if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_URL)) {
+
+                                               continue;
+
+#if 0
+                                               /* FIXME: should we really do that ??? */
+                                               if (asprintf(&url, "http://%s:631/printers/%s", longname, sharename) < 0) {
+                                                       nt_status = NT_STATUS_NO_MEMORY;
+                                                       goto done;
+                                               }
+                                               init_unistr2(&data, url, UNI_STR_TERMINATE);
+                                               fstrcpy(value.valuename, SPOOL_REG_URL);
+#endif
+                                       }
+
+                                       if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_SERVERNAME)) {
+
+                                               init_unistr2(&data, longname, UNI_STR_TERMINATE);
+                                               fstrcpy(value.valuename, SPOOL_REG_SERVERNAME);
+                                       }
+
+                                       if (strequal(reg_ctr.values[j]->valuename, SPOOL_REG_SHORTSERVERNAME)) {
+
+                                               init_unistr2(&data, global_myname(), UNI_STR_TERMINATE);
+                                               fstrcpy(value.valuename, SPOOL_REG_SHORTSERVERNAME);
+                                       }
+
+                                       value.type = REG_SZ;
+                                       value.size = data.uni_str_len * 2;
+                                       value.data_p = talloc_memdup(mem_ctx, data.buffer, value.size);
+
+                                       if (opt_verbose) 
+                                               display_reg_value(subkey, value);
+
+                                       /* here we have to set all subkeys on the dst server */
+                                       if (!net_spoolss_setprinterdataex(cli_dst, mem_ctx, &hnd_dst, 
+                                                       subkey, &value)) 
+                                               goto done;
+                                                       
+                               } else {
+
+                                       if (opt_verbose) 
+                                               display_reg_value(subkey, *(reg_ctr.values[j]));
+
+                                       /* here we have to set all subkeys on the dst server */
+                                       if (!net_spoolss_setprinterdataex(cli_dst, mem_ctx, &hnd_dst, 
+                                                       subkey, reg_ctr.values[j])) 
+                                               goto done;
+
+                               }
+
+                               DEBUGADD(1,("\tSetPrinterDataEx of key [%s\\%s] succeeded\n", 
+                                               subkey, reg_ctr.values[j]->valuename));
+
+                       }
+                                               
+                       regval_ctr_destroy(&reg_ctr);
+               }
+
+               safe_free(keylist);
+
+               /* close printer handles here */
+               if (got_hnd_src) {
+                       cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+                       got_hnd_src = False;
+               }
+
+               if (got_hnd_dst) {
+                       cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+                       got_hnd_dst = False;
+               }
+
+       }
+       
+       nt_status = NT_STATUS_OK;
+
+done:
+       SAFE_FREE(devicename);
+       SAFE_FREE(url);
+       SAFE_FREE(unc_name);
+
+       if (got_hnd_src)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd_src);
+
+       if (got_hnd_dst)
+               cli_spoolss_close_printer(cli_dst, mem_ctx, &hnd_dst);
+
+       if (got_dst_spoolss_pipe) {
+               cli_nt_session_close(cli_dst);
+               cli_shutdown(cli_dst);
+       }
+       return nt_status;
+}