s3-rpcclient: use one macro for all printername formats.
[jra/samba/.git] / source3 / rpcclient / cmd_spoolss.c
1 /*
2    Unix SMB/CIFS implementation.
3    RPC pipe client
4
5    Copyright (C) Gerald Carter                2001-2005
6    Copyright (C) Tim Potter                        2000
7    Copyright (C) Andrew Tridgell              1992-1999
8    Copyright (C) Luke Kenneth Casson Leighton 1996-1999
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "rpcclient.h"
26
27 #define RPCCLIENT_PRINTERNAME(_printername, _cli, _arg) \
28 { \
29         _printername = talloc_asprintf_strupper_m(mem_ctx, "%s\\%s", \
30                 _cli->srv_name_slash, _arg); \
31         W_ERROR_HAVE_NO_MEMORY(_printername); \
32 }
33
34 struct table_node {
35         const char      *long_archi;
36         const char      *short_archi;
37         int     version;
38 };
39
40 /* The version int is used by getdrivers.  Note that
41    all architecture strings that support mutliple
42    versions must be grouped together since enumdrivers
43    uses this property to prevent issuing multiple
44    enumdriver calls for the same arch */
45
46
47 static const struct table_node archi_table[]= {
48
49         {"Windows 4.0",          "WIN40",       0 },
50         {"Windows NT x86",       "W32X86",      2 },
51         {"Windows NT x86",       "W32X86",      3 },
52         {"Windows NT R4000",     "W32MIPS",     2 },
53         {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
54         {"Windows NT PowerPC",   "W32PPC",      2 },
55         {"Windows IA64",         "IA64",        3 },
56         {"Windows x64",          "x64",         3 },
57         {NULL,                   "",            -1 }
58 };
59
60 /**
61  * @file
62  *
63  * rpcclient module for SPOOLSS rpc pipe.
64  *
65  * This generally just parses and checks command lines, and then calls
66  * a cli_spoolss function.
67  **/
68
69 /****************************************************************************
70  function to do the mapping between the long architecture name and
71  the short one.
72 ****************************************************************************/
73
74 static const char *cmd_spoolss_get_short_archi(const char *long_archi)
75 {
76         int i=-1;
77
78         DEBUG(107,("Getting architecture dependant directory\n"));
79         do {
80                 i++;
81         } while ( (archi_table[i].long_archi!=NULL ) &&
82                   StrCaseCmp(long_archi, archi_table[i].long_archi) );
83
84         if (archi_table[i].long_archi==NULL) {
85                 DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
86                 return NULL;
87         }
88
89         /* this might be client code - but shouldn't this be an fstrcpy etc? */
90
91
92         DEBUGADD(108,("index: [%d]\n", i));
93         DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
94         DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
95
96         return archi_table[i].short_archi;
97 }
98
99 /****************************************************************************
100 ****************************************************************************/
101
102 static WERROR cmd_spoolss_open_printer_ex(struct rpc_pipe_client *cli,
103                                             TALLOC_CTX *mem_ctx,
104                                             int argc, const char **argv)
105 {
106         WERROR          werror;
107         POLICY_HND      hnd;
108
109         if (argc != 2) {
110                 printf("Usage: %s <printername>\n", argv[0]);
111                 return WERR_OK;
112         }
113
114         if (!cli)
115             return WERR_GENERAL_FAILURE;
116
117         /* Open the printer handle */
118
119         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
120                                                argv[1],
121                                                PRINTER_ALL_ACCESS,
122                                                &hnd);
123         if (W_ERROR_IS_OK(werror)) {
124                 printf("Printer %s opened successfully\n", argv[1]);
125                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, &werror);
126
127                 if (!W_ERROR_IS_OK(werror)) {
128                         printf("Error closing printer handle! (%s)\n",
129                                 get_dos_error_msg(werror));
130                 }
131         }
132
133         return werror;
134 }
135
136
137 /****************************************************************************
138 ****************************************************************************/
139
140 static void display_print_info_0(PRINTER_INFO_0 *i0)
141 {
142         fstring name = "";
143         fstring servername = "";
144
145         if (!i0)
146                 return;
147
148         rpcstr_pull(name, i0->printername.buffer, sizeof(name), -1, STR_TERMINATE);
149
150         rpcstr_pull(servername, i0->servername.buffer, sizeof(servername), -1,STR_TERMINATE);
151
152         printf("\tprintername:[%s]\n", name);
153         printf("\tservername:[%s]\n", servername);
154         printf("\tcjobs:[0x%x]\n", i0->cjobs);
155         printf("\ttotal_jobs:[0x%x]\n", i0->total_jobs);
156
157         printf("\t:date: [%d]-[%d]-[%d] (%d)\n", i0->year, i0->month,
158                i0->day, i0->dayofweek);
159         printf("\t:time: [%d]-[%d]-[%d]-[%d]\n", i0->hour, i0->minute,
160                i0->second, i0->milliseconds);
161
162         printf("\tglobal_counter:[0x%x]\n", i0->global_counter);
163         printf("\ttotal_pages:[0x%x]\n", i0->total_pages);
164
165         printf("\tmajorversion:[0x%x]\n", i0->major_version);
166         printf("\tbuildversion:[0x%x]\n", i0->build_version);
167
168         printf("\tunknown7:[0x%x]\n", i0->unknown7);
169         printf("\tunknown8:[0x%x]\n", i0->unknown8);
170         printf("\tunknown9:[0x%x]\n", i0->unknown9);
171         printf("\tsession_counter:[0x%x]\n", i0->session_counter);
172         printf("\tunknown11:[0x%x]\n", i0->unknown11);
173         printf("\tprinter_errors:[0x%x]\n", i0->printer_errors);
174         printf("\tunknown13:[0x%x]\n", i0->unknown13);
175         printf("\tunknown14:[0x%x]\n", i0->unknown14);
176         printf("\tunknown15:[0x%x]\n", i0->unknown15);
177         printf("\tunknown16:[0x%x]\n", i0->unknown16);
178         printf("\tchange_id:[0x%x]\n", i0->change_id);
179         printf("\tunknown18:[0x%x]\n", i0->unknown18);
180         printf("\tstatus:[0x%x]\n", i0->status);
181         printf("\tunknown20:[0x%x]\n", i0->unknown20);
182         printf("\tc_setprinter:[0x%x]\n", i0->c_setprinter);
183         printf("\tunknown22:[0x%x]\n", i0->unknown22);
184         printf("\tunknown23:[0x%x]\n", i0->unknown23);
185         printf("\tunknown24:[0x%x]\n", i0->unknown24);
186         printf("\tunknown25:[0x%x]\n", i0->unknown25);
187         printf("\tunknown26:[0x%x]\n", i0->unknown26);
188         printf("\tunknown27:[0x%x]\n", i0->unknown27);
189         printf("\tunknown28:[0x%x]\n", i0->unknown28);
190         printf("\tunknown29:[0x%x]\n", i0->unknown29);
191
192         printf("\n");
193 }
194
195 /****************************************************************************
196 ****************************************************************************/
197
198 static void display_print_info_1(PRINTER_INFO_1 *i1)
199 {
200         fstring desc = "";
201         fstring name = "";
202         fstring comm = "";
203
204         rpcstr_pull(desc, i1->description.buffer, sizeof(desc), -1,
205                     STR_TERMINATE);
206
207         rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
208         rpcstr_pull(comm, i1->comment.buffer, sizeof(comm), -1, STR_TERMINATE);
209
210         printf("\tflags:[0x%x]\n", i1->flags);
211         printf("\tname:[%s]\n", name);
212         printf("\tdescription:[%s]\n", desc);
213         printf("\tcomment:[%s]\n", comm);
214
215         printf("\n");
216 }
217
218 /****************************************************************************
219 ****************************************************************************/
220
221 static void display_print_info_2(PRINTER_INFO_2 *i2)
222 {
223         fstring servername = "";
224         fstring printername = "";
225         fstring sharename = "";
226         fstring portname = "";
227         fstring drivername = "";
228         fstring comment = "";
229         fstring location = "";
230         fstring sepfile = "";
231         fstring printprocessor = "";
232         fstring datatype = "";
233         fstring parameters = "";
234
235         rpcstr_pull(servername, i2->servername.buffer,sizeof(servername), -1, STR_TERMINATE);
236         rpcstr_pull(printername, i2->printername.buffer,sizeof(printername), -1, STR_TERMINATE);
237         rpcstr_pull(sharename, i2->sharename.buffer,sizeof(sharename), -1, STR_TERMINATE);
238         rpcstr_pull(portname, i2->portname.buffer,sizeof(portname), -1, STR_TERMINATE);
239         rpcstr_pull(drivername, i2->drivername.buffer,sizeof(drivername), -1, STR_TERMINATE);
240         rpcstr_pull(comment, i2->comment.buffer,sizeof(comment), -1, STR_TERMINATE);
241         rpcstr_pull(location, i2->location.buffer,sizeof(location), -1, STR_TERMINATE);
242         rpcstr_pull(sepfile, i2->sepfile.buffer,sizeof(sepfile), -1, STR_TERMINATE);
243         rpcstr_pull(printprocessor, i2->printprocessor.buffer,sizeof(printprocessor), -1, STR_TERMINATE);
244         rpcstr_pull(datatype, i2->datatype.buffer,sizeof(datatype), -1, STR_TERMINATE);
245         rpcstr_pull(parameters, i2->parameters.buffer,sizeof(parameters), -1, STR_TERMINATE);
246
247         printf("\tservername:[%s]\n", servername);
248         printf("\tprintername:[%s]\n", printername);
249         printf("\tsharename:[%s]\n", sharename);
250         printf("\tportname:[%s]\n", portname);
251         printf("\tdrivername:[%s]\n", drivername);
252         printf("\tcomment:[%s]\n", comment);
253         printf("\tlocation:[%s]\n", location);
254         printf("\tsepfile:[%s]\n", sepfile);
255         printf("\tprintprocessor:[%s]\n", printprocessor);
256         printf("\tdatatype:[%s]\n", datatype);
257         printf("\tparameters:[%s]\n", parameters);
258         printf("\tattributes:[0x%x]\n", i2->attributes);
259         printf("\tpriority:[0x%x]\n", i2->priority);
260         printf("\tdefaultpriority:[0x%x]\n", i2->defaultpriority);
261         printf("\tstarttime:[0x%x]\n", i2->starttime);
262         printf("\tuntiltime:[0x%x]\n", i2->untiltime);
263         printf("\tstatus:[0x%x]\n", i2->status);
264         printf("\tcjobs:[0x%x]\n", i2->cjobs);
265         printf("\taverageppm:[0x%x]\n", i2->averageppm);
266
267         if (i2->secdesc)
268                 display_sec_desc(i2->secdesc);
269
270         printf("\n");
271 }
272
273 /****************************************************************************
274 ****************************************************************************/
275
276 static void display_print_info_3(PRINTER_INFO_3 *i3)
277 {
278         display_sec_desc(i3->secdesc);
279
280         printf("\n");
281 }
282
283 /****************************************************************************
284 ****************************************************************************/
285
286 static void display_print_info_7(PRINTER_INFO_7 *i7)
287 {
288         fstring guid = "";
289         rpcstr_pull(guid, i7->guid.buffer,sizeof(guid), -1, STR_TERMINATE);
290         printf("\tguid:[%s]\n", guid);
291         printf("\taction:[0x%x]\n", i7->action);
292 }
293
294
295 /****************************************************************************
296 ****************************************************************************/
297
298 static WERROR cmd_spoolss_enum_printers(struct rpc_pipe_client *cli,
299                                           TALLOC_CTX *mem_ctx,
300                                           int argc, const char **argv)
301 {
302         WERROR                  result;
303         uint32                  info_level = 1;
304         PRINTER_INFO_CTR        ctr;
305         uint32                  i = 0, num_printers;
306         fstring name;
307
308         if (argc > 3)
309         {
310                 printf("Usage: %s [level] [name]\n", argv[0]);
311                 return WERR_OK;
312         }
313
314         if (argc >= 2)
315                 info_level = atoi(argv[1]);
316
317         if (argc == 3)
318                 fstrcpy(name, argv[2]);
319         else {
320                 slprintf(name, sizeof(name)-1, "\\\\%s", cli->desthost);
321                 strupper_m(name);
322         }
323
324         ZERO_STRUCT(ctr);
325
326         result = rpccli_spoolss_enum_printers(cli, mem_ctx, name, PRINTER_ENUM_LOCAL,
327                 info_level, &num_printers, &ctr);
328
329         if (W_ERROR_IS_OK(result)) {
330
331                 if (!num_printers) {
332                         printf ("No printers returned.\n");
333                         goto done;
334                 }
335
336                 for (i = 0; i < num_printers; i++) {
337                         switch(info_level) {
338                         case 0:
339                                 display_print_info_0(&ctr.printers_0[i]);
340                                 break;
341                         case 1:
342                                 display_print_info_1(&ctr.printers_1[i]);
343                                 break;
344                         case 2:
345                                 display_print_info_2(&ctr.printers_2[i]);
346                                 break;
347                         case 3:
348                                 display_print_info_3(&ctr.printers_3[i]);
349                                 break;
350                         default:
351                                 printf("unknown info level %d\n", info_level);
352                                 goto done;
353                         }
354                 }
355         }
356         done:
357
358         return result;
359 }
360
361 /****************************************************************************
362 ****************************************************************************/
363
364 static void display_port_info_1(PORT_INFO_1 *i1)
365 {
366         fstring buffer;
367
368         rpcstr_pull(buffer, i1->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
369         printf("\tPort Name:\t[%s]\n", buffer);
370 }
371
372 /****************************************************************************
373 ****************************************************************************/
374
375 static void display_port_info_2(PORT_INFO_2 *i2)
376 {
377         fstring buffer;
378
379         rpcstr_pull(buffer, i2->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
380         printf("\tPort Name:\t[%s]\n", buffer);
381         rpcstr_pull(buffer, i2->monitor_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
382
383         printf("\tMonitor Name:\t[%s]\n", buffer);
384         rpcstr_pull(buffer, i2->description.buffer, sizeof(buffer), -1, STR_TERMINATE);
385
386         printf("\tDescription:\t[%s]\n", buffer);
387         printf("\tPort Type:\t" );
388         if ( i2->port_type ) {
389                 int comma = 0; /* hack */
390                 printf( "[" );
391                 if ( i2->port_type & PORT_TYPE_READ ) {
392                         printf( "Read" );
393                         comma = 1;
394                 }
395                 if ( i2->port_type & PORT_TYPE_WRITE ) {
396                         printf( "%sWrite", comma ? ", " : "" );
397                         comma = 1;
398                 }
399                 /* These two have slightly different interpretations
400                  on 95/98/ME but I'm disregarding that for now */
401                 if ( i2->port_type & PORT_TYPE_REDIRECTED ) {
402                         printf( "%sRedirected", comma ? ", " : "" );
403                         comma = 1;
404                 }
405                 if ( i2->port_type & PORT_TYPE_NET_ATTACHED ) {
406                         printf( "%sNet-Attached", comma ? ", " : "" );
407                 }
408                 printf( "]\n" );
409         } else {
410                 printf( "[Unset]\n" );
411         }
412         printf("\tReserved:\t[%d]\n", i2->reserved);
413         printf("\n");
414 }
415
416 /****************************************************************************
417 ****************************************************************************/
418
419 static WERROR cmd_spoolss_enum_ports(struct rpc_pipe_client *cli,
420                                        TALLOC_CTX *mem_ctx, int argc,
421                                        const char **argv)
422 {
423         WERROR                  result;
424         uint32                  info_level = 1;
425         PORT_INFO_CTR           ctr;
426         uint32                  returned;
427
428         if (argc > 2) {
429                 printf("Usage: %s [level]\n", argv[0]);
430                 return WERR_OK;
431         }
432
433         if (argc == 2)
434                 info_level = atoi(argv[1]);
435
436         /* Enumerate ports */
437
438         ZERO_STRUCT(ctr);
439
440         result = rpccli_spoolss_enum_ports(cli, mem_ctx, info_level, &returned, &ctr);
441
442         if (W_ERROR_IS_OK(result)) {
443                 int i;
444
445                 for (i = 0; i < returned; i++) {
446                         switch (info_level) {
447                         case 1:
448                                 display_port_info_1(&ctr.port.info_1[i]);
449                                 break;
450                         case 2:
451                                 display_port_info_2(&ctr.port.info_2[i]);
452                                 break;
453                         default:
454                                 printf("unknown info level %d\n", info_level);
455                                 break;
456                         }
457                 }
458         }
459
460         return result;
461 }
462
463 /****************************************************************************
464 ****************************************************************************/
465
466 static WERROR cmd_spoolss_setprinter(struct rpc_pipe_client *cli,
467                                        TALLOC_CTX *mem_ctx,
468                                        int argc, const char **argv)
469 {
470         POLICY_HND      pol;
471         WERROR          result;
472         uint32          info_level = 2;
473         bool            opened_hnd = False;
474         PRINTER_INFO_CTR ctr;
475         const char      *printername, *comment = NULL;
476
477         if (argc == 1 || argc > 3) {
478                 printf("Usage: %s printername comment\n", argv[0]);
479
480                 return WERR_OK;
481         }
482
483         /* Open a printer handle */
484         if (argc == 3) {
485                 comment = argv[2];
486         }
487
488         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
489
490         /* get a printer handle */
491         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
492                                                printername,
493                                                PRINTER_ALL_ACCESS,
494                                                &pol);
495         if (!W_ERROR_IS_OK(result))
496                 goto done;
497
498         opened_hnd = True;
499
500         /* Get printer info */
501         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
502
503         if (!W_ERROR_IS_OK(result))
504                 goto done;
505
506
507         /* Modify the comment. */
508         init_unistr(&ctr.printers_2->comment, comment);
509         ctr.printers_2->devmode = NULL;
510         ctr.printers_2->secdesc = NULL;
511
512         result = rpccli_spoolss_setprinter(cli, mem_ctx, &pol, info_level, &ctr, 0);
513         if (W_ERROR_IS_OK(result))
514                 printf("Success in setting comment.\n");
515
516  done:
517         if (opened_hnd)
518                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
519
520         return result;
521 }
522
523 /****************************************************************************
524 ****************************************************************************/
525
526 static WERROR cmd_spoolss_setprintername(struct rpc_pipe_client *cli,
527                                        TALLOC_CTX *mem_ctx,
528                                        int argc, const char **argv)
529 {
530         POLICY_HND      pol;
531         WERROR          result;
532         uint32          info_level = 2;
533         bool            opened_hnd = False;
534         PRINTER_INFO_CTR ctr;
535         const char      *printername,
536                         *new_printername = NULL;
537
538         if (argc == 1 || argc > 3) {
539                 printf("Usage: %s printername new_printername\n", argv[0]);
540
541                 return WERR_OK;
542         }
543
544         /* Open a printer handle */
545         if (argc == 3) {
546                 new_printername = argv[2];
547         }
548
549         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
550
551         /* get a printer handle */
552         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
553                                                printername,
554                                                PRINTER_ALL_ACCESS,
555                                                &pol);
556         if (!W_ERROR_IS_OK(result))
557                 goto done;
558
559         opened_hnd = True;
560
561         /* Get printer info */
562         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
563
564         if (!W_ERROR_IS_OK(result))
565                 goto done;
566
567         /* Modify the printername. */
568         init_unistr(&ctr.printers_2->printername, new_printername);
569         ctr.printers_2->devmode = NULL;
570         ctr.printers_2->secdesc = NULL;
571
572         result = rpccli_spoolss_setprinter(cli, mem_ctx, &pol, info_level, &ctr, 0);
573         if (W_ERROR_IS_OK(result))
574                 printf("Success in setting printername.\n");
575
576  done:
577         if (opened_hnd)
578                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
579
580         return result;
581 }
582
583 /****************************************************************************
584 ****************************************************************************/
585
586 static WERROR cmd_spoolss_getprinter(struct rpc_pipe_client *cli,
587                                        TALLOC_CTX *mem_ctx,
588                                        int argc, const char **argv)
589 {
590         POLICY_HND      pol;
591         WERROR          result;
592         uint32          info_level = 1;
593         bool            opened_hnd = False;
594         PRINTER_INFO_CTR ctr;
595         const char      *printername;
596
597         if (argc == 1 || argc > 3) {
598                 printf("Usage: %s <printername> [level]\n", argv[0]);
599                 return WERR_OK;
600         }
601
602         /* Open a printer handle */
603         if (argc == 3) {
604                 info_level = atoi(argv[2]);
605         }
606
607         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
608
609         /* get a printer handle */
610
611         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
612                                                printername,
613                                                SEC_FLAG_MAXIMUM_ALLOWED,
614                                                &pol);
615         if (!W_ERROR_IS_OK(result))
616                 goto done;
617
618         opened_hnd = True;
619
620         /* Get printer info */
621
622         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
623
624         if (!W_ERROR_IS_OK(result))
625                 goto done;
626
627         /* Display printer info */
628
629         switch (info_level) {
630         case 0:
631                 display_print_info_0(ctr.printers_0);
632                 break;
633         case 1:
634                 display_print_info_1(ctr.printers_1);
635                 break;
636         case 2:
637                 display_print_info_2(ctr.printers_2);
638                 break;
639         case 3:
640                 display_print_info_3(ctr.printers_3);
641                 break;
642         case 7:
643                 display_print_info_7(ctr.printers_7);
644                 break;
645         default:
646                 printf("unknown info level %d\n", info_level);
647                 break;
648         }
649
650  done:
651         if (opened_hnd)
652                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
653
654         return result;
655 }
656
657 /****************************************************************************
658 ****************************************************************************/
659
660 static void display_reg_value(REGISTRY_VALUE value)
661 {
662         char *text = NULL;
663
664         switch(value.type) {
665         case REG_DWORD:
666                 printf("%s: REG_DWORD: 0x%08x\n", value.valuename,
667                        *((uint32 *) value.data_p));
668                 break;
669         case REG_SZ:
670                 rpcstr_pull_talloc(talloc_tos(),
671                                 &text,
672                                 value.data_p,
673                                 value.size,
674                                 STR_TERMINATE);
675                 printf("%s: REG_SZ: %s\n", value.valuename, text ? text : "");
676                 break;
677         case REG_BINARY: {
678                 char *hex = hex_encode_talloc(NULL, value.data_p, value.size);
679                 size_t i, len;
680                 printf("%s: REG_BINARY:", value.valuename);
681                 len = strlen(hex);
682                 for (i=0; i<len; i++) {
683                         if (hex[i] == '\0') {
684                                 break;
685                         }
686                         if (i%40 == 0) {
687                                 putchar('\n');
688                         }
689                         putchar(hex[i]);
690                 }
691                 TALLOC_FREE(hex);
692                 putchar('\n');
693                 break;
694         }
695         case REG_MULTI_SZ: {
696                 uint32 i, num_values;
697                 char **values;
698
699                 if (!W_ERROR_IS_OK(reg_pull_multi_sz(NULL, value.data_p,
700                                                      value.size, &num_values,
701                                                      &values))) {
702                         d_printf("reg_pull_multi_sz failed\n");
703                         break;
704                 }
705
706                 for (i=0; i<num_values; i++) {
707                         d_printf("%s\n", values[i]);
708                 }
709                 TALLOC_FREE(values);
710                 break;
711         }
712         default:
713                 printf("%s: unknown type %d\n", value.valuename, value.type);
714         }
715
716 }
717
718 /****************************************************************************
719 ****************************************************************************/
720
721 static WERROR cmd_spoolss_getprinterdata(struct rpc_pipe_client *cli,
722                                            TALLOC_CTX *mem_ctx,
723                                            int argc, const char **argv)
724 {
725         POLICY_HND      pol;
726         WERROR          result;
727         bool            opened_hnd = False;
728         fstring         printername;
729         const char *valuename;
730         REGISTRY_VALUE value;
731
732         if (argc != 3) {
733                 printf("Usage: %s <printername> <valuename>\n", argv[0]);
734                 printf("<printername> of . queries print server\n");
735                 return WERR_OK;
736         }
737         valuename = argv[2];
738
739         /* Open a printer handle */
740
741         if (strncmp(argv[1], ".", sizeof(".")) == 0)
742                 fstrcpy(printername, cli->srv_name_slash);
743         else
744                 slprintf(printername, sizeof(printername)-1, "%s\\%s",
745                           cli->srv_name_slash, argv[1]);
746
747         /* get a printer handle */
748
749         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
750                                                printername,
751                                                SEC_FLAG_MAXIMUM_ALLOWED,
752                                                &pol);
753         if (!W_ERROR_IS_OK(result))
754                 goto done;
755
756         opened_hnd = True;
757
758         /* Get printer info */
759
760         result = rpccli_spoolss_getprinterdata(cli, mem_ctx, &pol, valuename, &value);
761
762         if (!W_ERROR_IS_OK(result))
763                 goto done;
764
765         /* Display printer data */
766
767         fstrcpy(value.valuename, valuename);
768         display_reg_value(value);
769
770
771  done:
772         if (opened_hnd)
773                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
774
775         return result;
776 }
777
778 /****************************************************************************
779 ****************************************************************************/
780
781 static WERROR cmd_spoolss_getprinterdataex(struct rpc_pipe_client *cli,
782                                              TALLOC_CTX *mem_ctx,
783                                              int argc, const char **argv)
784 {
785         POLICY_HND      pol;
786         WERROR          result;
787         bool            opened_hnd = False;
788         fstring         printername;
789         const char *valuename, *keyname;
790         REGISTRY_VALUE value;
791
792         if (argc != 4) {
793                 printf("Usage: %s <printername> <keyname> <valuename>\n",
794                        argv[0]);
795                 printf("<printername> of . queries print server\n");
796                 return WERR_OK;
797         }
798         valuename = argv[3];
799         keyname = argv[2];
800
801         /* Open a printer handle */
802
803         if (strncmp(argv[1], ".", sizeof(".")) == 0)
804                 fstrcpy(printername, cli->srv_name_slash);
805         else
806                 slprintf(printername, sizeof(printername)-1, "%s\\%s",
807                           cli->srv_name_slash, argv[1]);
808
809         /* get a printer handle */
810
811         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
812                                                printername,
813                                                SEC_FLAG_MAXIMUM_ALLOWED,
814                                                &pol);
815         if (!W_ERROR_IS_OK(result))
816                 goto done;
817
818         opened_hnd = True;
819
820         /* Get printer info */
821
822         result = rpccli_spoolss_getprinterdataex(cli, mem_ctx, &pol, keyname,
823                 valuename, &value);
824
825         if (!W_ERROR_IS_OK(result))
826                 goto done;
827
828         /* Display printer data */
829
830         fstrcpy(value.valuename, valuename);
831         display_reg_value(value);
832
833
834  done:
835         if (opened_hnd)
836                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
837
838         return result;
839 }
840
841 /****************************************************************************
842 ****************************************************************************/
843
844 static void display_print_driver_1(DRIVER_INFO_1 *i1)
845 {
846         fstring name;
847         if (i1 == NULL)
848                 return;
849
850         rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
851
852         printf ("Printer Driver Info 1:\n");
853         printf ("\tDriver Name: [%s]\n\n", name);
854
855         return;
856 }
857
858 /****************************************************************************
859 ****************************************************************************/
860
861 static void display_print_driver_2(DRIVER_INFO_2 *i1)
862 {
863         fstring name;
864         fstring architecture;
865         fstring driverpath;
866         fstring datafile;
867         fstring configfile;
868         if (i1 == NULL)
869                 return;
870
871         rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
872         rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
873         rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
874         rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
875         rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
876
877         printf ("Printer Driver Info 2:\n");
878         printf ("\tVersion: [%x]\n", i1->version);
879         printf ("\tDriver Name: [%s]\n", name);
880         printf ("\tArchitecture: [%s]\n", architecture);
881         printf ("\tDriver Path: [%s]\n", driverpath);
882         printf ("\tDatafile: [%s]\n", datafile);
883         printf ("\tConfigfile: [%s]\n\n", configfile);
884
885         return;
886 }
887
888 /****************************************************************************
889 ****************************************************************************/
890
891 static void display_print_driver_3(DRIVER_INFO_3 *i1)
892 {
893         fstring name = "";
894         fstring architecture = "";
895         fstring driverpath = "";
896         fstring datafile = "";
897         fstring configfile = "";
898         fstring helpfile = "";
899         fstring dependentfiles = "";
900         fstring monitorname = "";
901         fstring defaultdatatype = "";
902
903         int length=0;
904         bool valid = True;
905
906         if (i1 == NULL)
907                 return;
908
909         rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
910         rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
911         rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
912         rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
913         rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
914         rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
915         rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
916         rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
917
918         printf ("Printer Driver Info 3:\n");
919         printf ("\tVersion: [%x]\n", i1->version);
920         printf ("\tDriver Name: [%s]\n",name);
921         printf ("\tArchitecture: [%s]\n", architecture);
922         printf ("\tDriver Path: [%s]\n", driverpath);
923         printf ("\tDatafile: [%s]\n", datafile);
924         printf ("\tConfigfile: [%s]\n", configfile);
925         printf ("\tHelpfile: [%s]\n\n", helpfile);
926
927         while (valid)
928         {
929                 rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
930
931                 length+=strlen(dependentfiles)+1;
932
933                 if (strlen(dependentfiles) > 0)
934                 {
935                         printf ("\tDependentfiles: [%s]\n", dependentfiles);
936                 }
937                 else
938                 {
939                         valid = False;
940                 }
941         }
942
943         printf ("\n");
944
945         printf ("\tMonitorname: [%s]\n", monitorname);
946         printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
947
948         return;
949 }
950
951 /****************************************************************************
952 ****************************************************************************/
953
954 static WERROR cmd_spoolss_getdriver(struct rpc_pipe_client *cli,
955                                       TALLOC_CTX *mem_ctx,
956                                       int argc, const char **argv)
957 {
958         POLICY_HND      pol;
959         WERROR          werror;
960         uint32          info_level = 3;
961         bool            opened_hnd = False;
962         PRINTER_DRIVER_CTR      ctr;
963         const char      *printername;
964         uint32          i;
965         bool            success = False;
966
967         if ((argc == 1) || (argc > 3))
968         {
969                 printf("Usage: %s <printername> [level]\n", argv[0]);
970                 return WERR_OK;
971         }
972
973         /* get the arguments need to open the printer handle */
974
975         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
976
977         if (argc == 3)
978                 info_level = atoi(argv[2]);
979
980         /* Open a printer handle */
981
982         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
983                                                printername,
984                                                PRINTER_ACCESS_USE,
985                                                &pol);
986         if (!W_ERROR_IS_OK(werror)) {
987                 printf("Error opening printer handle for %s!\n", printername);
988                 return werror;
989         }
990
991         opened_hnd = True;
992
993         /* loop through and print driver info level for each architecture */
994
995         for (i=0; archi_table[i].long_archi!=NULL; i++) {
996
997                 werror = rpccli_spoolss_getprinterdriver( cli, mem_ctx, &pol, info_level,
998                         archi_table[i].long_archi, archi_table[i].version,
999                         &ctr);
1000
1001                 if (!W_ERROR_IS_OK(werror))
1002                         continue;
1003
1004                 /* need at least one success */
1005
1006                 success = True;
1007
1008                 printf ("\n[%s]\n", archi_table[i].long_archi);
1009
1010                 switch (info_level) {
1011                 case 1:
1012                         display_print_driver_1 (ctr.info1);
1013                         break;
1014                 case 2:
1015                         display_print_driver_2 (ctr.info2);
1016                         break;
1017                 case 3:
1018                         display_print_driver_3 (ctr.info3);
1019                         break;
1020                 default:
1021                         printf("unknown info level %d\n", info_level);
1022                         break;
1023                 }
1024         }
1025
1026         /* Cleanup */
1027
1028         if (opened_hnd)
1029                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
1030
1031         if ( success )
1032                 werror = WERR_OK;
1033
1034         return werror;
1035 }
1036
1037 /****************************************************************************
1038 ****************************************************************************/
1039
1040 static WERROR cmd_spoolss_enum_drivers(struct rpc_pipe_client *cli,
1041                                          TALLOC_CTX *mem_ctx,
1042                                          int argc, const char **argv)
1043 {
1044         WERROR werror = WERR_OK;
1045         uint32          info_level = 1;
1046         PRINTER_DRIVER_CTR      ctr;
1047         uint32          i, j,
1048                         returned;
1049
1050         if (argc > 2) {
1051                 printf("Usage: enumdrivers [level]\n");
1052                 return WERR_OK;
1053         }
1054
1055         if (argc == 2)
1056                 info_level = atoi(argv[1]);
1057
1058
1059         /* loop through and print driver info level for each architecture */
1060         for (i=0; archi_table[i].long_archi!=NULL; i++) {
1061                 /* check to see if we already asked for this architecture string */
1062
1063                 if ( i>0 && strequal(archi_table[i].long_archi, archi_table[i-1].long_archi) )
1064                         continue;
1065
1066                 werror = rpccli_spoolss_enumprinterdrivers(
1067                         cli, mem_ctx, info_level,
1068                         archi_table[i].long_archi, &returned, &ctr);
1069
1070                 if (W_ERROR_V(werror) == W_ERROR_V(WERR_INVALID_ENVIRONMENT)) {
1071                         printf ("Server does not support environment [%s]\n",
1072                                 archi_table[i].long_archi);
1073                         werror = WERR_OK;
1074                         continue;
1075                 }
1076
1077                 if (returned == 0)
1078                         continue;
1079
1080                 if (!W_ERROR_IS_OK(werror)) {
1081                         printf ("Error getting driver for environment [%s] - %d\n",
1082                                 archi_table[i].long_archi, W_ERROR_V(werror));
1083                         continue;
1084                 }
1085
1086                 printf ("\n[%s]\n", archi_table[i].long_archi);
1087                 switch (info_level)
1088                 {
1089
1090                 case 1:
1091                         for (j=0; j < returned; j++) {
1092                                 display_print_driver_1 (&ctr.info1[j]);
1093                         }
1094                         break;
1095                 case 2:
1096                         for (j=0; j < returned; j++) {
1097                                 display_print_driver_2 (&ctr.info2[j]);
1098                         }
1099                         break;
1100                 case 3:
1101                         for (j=0; j < returned; j++) {
1102                                 display_print_driver_3 (&ctr.info3[j]);
1103                         }
1104                         break;
1105                 default:
1106                         printf("unknown info level %d\n", info_level);
1107                         return WERR_UNKNOWN_LEVEL;
1108                 }
1109         }
1110
1111         return werror;
1112 }
1113
1114 /****************************************************************************
1115 ****************************************************************************/
1116
1117 static void display_printdriverdir_1(DRIVER_DIRECTORY_1 *i1)
1118 {
1119         fstring name;
1120         if (i1 == NULL)
1121                 return;
1122
1123         rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
1124
1125         printf ("\tDirectory Name:[%s]\n", name);
1126 }
1127
1128 /****************************************************************************
1129 ****************************************************************************/
1130
1131 static WERROR cmd_spoolss_getdriverdir(struct rpc_pipe_client *cli,
1132                                          TALLOC_CTX *mem_ctx,
1133                                          int argc, const char **argv)
1134 {
1135         WERROR result;
1136         fstring                 env;
1137         DRIVER_DIRECTORY_CTR    ctr;
1138
1139         if (argc > 2) {
1140                 printf("Usage: %s [environment]\n", argv[0]);
1141                 return WERR_OK;
1142         }
1143
1144         /* Get the arguments need to open the printer handle */
1145
1146         if (argc == 2)
1147                 fstrcpy (env, argv[1]);
1148         else
1149                 fstrcpy (env, "Windows NT x86");
1150
1151         /* Get the directory.  Only use Info level 1 */
1152
1153         result = rpccli_spoolss_getprinterdriverdir(cli, mem_ctx, 1, env, &ctr);
1154
1155         if (W_ERROR_IS_OK(result))
1156                 display_printdriverdir_1(ctr.info1);
1157
1158         return result;
1159 }
1160
1161 /****************************************************************************
1162 ****************************************************************************/
1163
1164 void set_drv_info_3_env (DRIVER_INFO_3 *info, const char *arch)
1165 {
1166
1167         int i;
1168
1169         for (i=0; archi_table[i].long_archi != NULL; i++)
1170         {
1171                 if (strcmp(arch, archi_table[i].short_archi) == 0)
1172                 {
1173                         info->version = archi_table[i].version;
1174                         init_unistr (&info->architecture, archi_table[i].long_archi);
1175                         break;
1176                 }
1177         }
1178
1179         if (archi_table[i].long_archi == NULL)
1180         {
1181                 DEBUG(0, ("set_drv_info_3_env: Unknown arch [%s]\n", arch));
1182         }
1183
1184         return;
1185 }
1186
1187
1188 /**************************************************************************
1189  wrapper for strtok to get the next parameter from a delimited list.
1190  Needed to handle the empty parameter string denoted by "NULL"
1191  *************************************************************************/
1192
1193 static char* get_driver_3_param (char* str, const char* delim, UNISTR* dest,
1194                                  char **saveptr)
1195 {
1196         char    *ptr;
1197
1198         /* get the next token */
1199         ptr = strtok_r(str, delim, saveptr);
1200
1201         /* a string of 'NULL' is used to represent an empty
1202            parameter because two consecutive delimiters
1203            will not return an empty string.  See man strtok(3)
1204            for details */
1205         if (ptr && (StrCaseCmp(ptr, "NULL") == 0))
1206                 ptr = NULL;
1207
1208         if (dest != NULL)
1209                 init_unistr(dest, ptr);
1210
1211         return ptr;
1212 }
1213
1214 /********************************************************************************
1215  fill in the members of a DRIVER_INFO_3 struct using a character
1216  string in the form of
1217          <Long Printer Name>:<Driver File Name>:<Data File Name>:\
1218              <Config File Name>:<Help File Name>:<Language Monitor Name>:\
1219              <Default Data Type>:<Comma Separated list of Files>
1220  *******************************************************************************/
1221 static bool init_drv_info_3_members ( TALLOC_CTX *mem_ctx, DRIVER_INFO_3 *info,
1222                                       char *args )
1223 {
1224         char    *str, *str2;
1225         uint32  len, i;
1226         char *saveptr = NULL;
1227
1228         /* fill in the UNISTR fields */
1229         str = get_driver_3_param (args, ":", &info->name, &saveptr);
1230         str = get_driver_3_param (NULL, ":", &info->driverpath, &saveptr);
1231         str = get_driver_3_param (NULL, ":", &info->datafile, &saveptr);
1232         str = get_driver_3_param (NULL, ":", &info->configfile, &saveptr);
1233         str = get_driver_3_param (NULL, ":", &info->helpfile, &saveptr);
1234         str = get_driver_3_param (NULL, ":", &info->monitorname, &saveptr);
1235         str = get_driver_3_param (NULL, ":", &info->defaultdatatype, &saveptr);
1236
1237         /* <Comma Separated List of Dependent Files> */
1238         /* save the beginning of the string */
1239         str2 = get_driver_3_param (NULL, ":", NULL, &saveptr);
1240         str = str2;
1241
1242         /* begin to strip out each filename */
1243         str = strtok_r(str, ",", &saveptr);
1244         len = 0;
1245         while (str != NULL)
1246         {
1247                 /* keep a cumlative count of the str lengths */
1248                 len += strlen(str)+1;
1249                 str = strtok_r(NULL, ",", &saveptr);
1250         }
1251
1252         /* allocate the space; add one extra slot for a terminating NULL.
1253            Each filename is NULL terminated and the end contains a double
1254            NULL */
1255         if ((info->dependentfiles=TALLOC_ARRAY(mem_ctx, uint16, len+1)) == NULL)
1256         {
1257                 DEBUG(0,("init_drv_info_3_members: Unable to malloc memory for dependenfiles\n"));
1258                 return False;
1259         }
1260         for (i=0; i<len; i++)
1261         {
1262                 SSVAL(&info->dependentfiles[i], 0, str2[i]);
1263         }
1264         info->dependentfiles[len] = '\0';
1265
1266         return True;
1267 }
1268
1269
1270 /****************************************************************************
1271 ****************************************************************************/
1272
1273 static WERROR cmd_spoolss_addprinterdriver(struct rpc_pipe_client *cli,
1274                                              TALLOC_CTX *mem_ctx,
1275                                              int argc, const char **argv)
1276 {
1277         WERROR result;
1278         uint32                  level = 3;
1279         PRINTER_DRIVER_CTR      ctr;
1280         DRIVER_INFO_3           info3;
1281         const char              *arch;
1282         fstring                 driver_name;
1283         char                    *driver_args;
1284
1285         /* parse the command arguments */
1286         if (argc != 3 && argc != 4)
1287         {
1288                 printf ("Usage: %s <Environment> \\\n", argv[0]);
1289                 printf ("\t<Long Printer Name>:<Driver File Name>:<Data File Name>:\\\n");
1290                 printf ("\t<Config File Name>:<Help File Name>:<Language Monitor Name>:\\\n");
1291                 printf ("\t<Default Data Type>:<Comma Separated list of Files> \\\n");
1292                 printf ("\t[version]\n");
1293
1294             return WERR_OK;
1295         }
1296
1297         /* Fill in the DRIVER_INFO_3 struct */
1298         ZERO_STRUCT(info3);
1299         if (!(arch = cmd_spoolss_get_short_archi(argv[1])))
1300         {
1301                 printf ("Error Unknown architechture [%s]\n", argv[1]);
1302                 return WERR_INVALID_PARAM;
1303         }
1304         else
1305                 set_drv_info_3_env(&info3, arch);
1306
1307         driver_args = talloc_strdup( mem_ctx, argv[2] );
1308         if (!init_drv_info_3_members(mem_ctx, &info3, driver_args ))
1309         {
1310                 printf ("Error Invalid parameter list - %s.\n", argv[2]);
1311                 return WERR_INVALID_PARAM;
1312         }
1313
1314         /* if printer driver version specified, override the default version
1315          * used by the architecture.  This allows installation of Windows
1316          * 2000 (version 3) printer drivers. */
1317         if (argc == 4)
1318         {
1319                 info3.version = atoi(argv[3]);
1320         }
1321
1322
1323         ctr.info3 = &info3;
1324         result = rpccli_spoolss_addprinterdriver (cli, mem_ctx, level, &ctr);
1325
1326         if (W_ERROR_IS_OK(result)) {
1327                 rpcstr_pull(driver_name, info3.name.buffer,
1328                             sizeof(driver_name), -1, STR_TERMINATE);
1329                 printf ("Printer Driver %s successfully installed.\n",
1330                         driver_name);
1331         }
1332
1333         return result;
1334 }
1335
1336
1337 /****************************************************************************
1338 ****************************************************************************/
1339
1340 static WERROR cmd_spoolss_addprinterex(struct rpc_pipe_client *cli,
1341                                          TALLOC_CTX *mem_ctx,
1342                                          int argc, const char **argv)
1343 {
1344         WERROR result;
1345         uint32                  level = 2;
1346         PRINTER_INFO_CTR        ctr;
1347         PRINTER_INFO_2          info2;
1348
1349         /* parse the command arguments */
1350         if (argc != 5)
1351         {
1352                 printf ("Usage: %s <name> <shared name> <driver> <port>\n", argv[0]);
1353                 return WERR_OK;
1354         }
1355
1356         /* Fill in the DRIVER_INFO_2 struct */
1357         ZERO_STRUCT(info2);
1358
1359         init_unistr( &info2.printername,        argv[1]);
1360         init_unistr( &info2.sharename,          argv[2]);
1361         init_unistr( &info2.drivername,         argv[3]);
1362         init_unistr( &info2.portname,           argv[4]);
1363         init_unistr( &info2.comment,            "Created by rpcclient");
1364         init_unistr( &info2.printprocessor,     "winprint");
1365         init_unistr( &info2.datatype,           "RAW");
1366         info2.devmode =         NULL;
1367         info2.secdesc =         NULL;
1368         info2.attributes        = PRINTER_ATTRIBUTE_SHARED;
1369         info2.priority          = 0;
1370         info2.defaultpriority   = 0;
1371         info2.starttime         = 0;
1372         info2.untiltime         = 0;
1373
1374         /* These three fields must not be used by AddPrinter()
1375            as defined in the MS Platform SDK documentation..
1376            --jerry
1377         info2.status            = 0;
1378         info2.cjobs             = 0;
1379         info2.averageppm        = 0;
1380         */
1381
1382         ctr.printers_2 = &info2;
1383         result = rpccli_spoolss_addprinterex (cli, mem_ctx, level, &ctr);
1384
1385         if (W_ERROR_IS_OK(result))
1386                 printf ("Printer %s successfully installed.\n", argv[1]);
1387
1388         return result;
1389 }
1390
1391 /****************************************************************************
1392 ****************************************************************************/
1393
1394 static WERROR cmd_spoolss_setdriver(struct rpc_pipe_client *cli,
1395                                       TALLOC_CTX *mem_ctx,
1396                                       int argc, const char **argv)
1397 {
1398         POLICY_HND              pol;
1399         WERROR                  result;
1400         uint32                  level = 2;
1401         bool                    opened_hnd = False;
1402         PRINTER_INFO_CTR        ctr;
1403         PRINTER_INFO_2          info2;
1404         const char              *printername;
1405
1406         /* parse the command arguments */
1407         if (argc != 3)
1408         {
1409                 printf ("Usage: %s <printer> <driver>\n", argv[0]);
1410                 return WERR_OK;
1411         }
1412
1413         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1414
1415         /* Get a printer handle */
1416
1417         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1418                                                printername,
1419                                                PRINTER_ALL_ACCESS,
1420                                                &pol);
1421         if (!W_ERROR_IS_OK(result))
1422                 goto done;
1423
1424         opened_hnd = True;
1425
1426         /* Get printer info */
1427
1428         ZERO_STRUCT (info2);
1429         ctr.printers_2 = &info2;
1430
1431         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, level, &ctr);
1432
1433         if (!W_ERROR_IS_OK(result)) {
1434                 printf ("Unable to retrieve printer information!\n");
1435                 goto done;
1436         }
1437
1438         /* Set the printer driver */
1439
1440         init_unistr(&ctr.printers_2->drivername, argv[2]);
1441
1442         result = rpccli_spoolss_setprinter(cli, mem_ctx, &pol, level, &ctr, 0);
1443
1444         if (!W_ERROR_IS_OK(result)) {
1445                 printf("SetPrinter call failed!\n");
1446                 goto done;;
1447         }
1448
1449         printf("Successfully set %s to driver %s.\n", argv[1], argv[2]);
1450
1451 done:
1452         /* Cleanup */
1453
1454         if (opened_hnd)
1455                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
1456
1457         return result;
1458 }
1459
1460
1461 /****************************************************************************
1462 ****************************************************************************/
1463
1464 static WERROR cmd_spoolss_deletedriverex(struct rpc_pipe_client *cli,
1465                                          TALLOC_CTX *mem_ctx,
1466                                          int argc, const char **argv)
1467 {
1468         WERROR result, ret = WERR_UNKNOWN_PRINTER_DRIVER;
1469         NTSTATUS status;
1470
1471         int   i;
1472         int vers = -1;
1473
1474         const char *arch = NULL;
1475         uint32_t delete_flags = 0;
1476
1477         /* parse the command arguments */
1478         if (argc < 2 || argc > 4) {
1479                 printf ("Usage: %s <driver> [arch] [version]\n", argv[0]);
1480                 return WERR_OK;
1481         }
1482
1483         if (argc >= 3)
1484                 arch = argv[2];
1485         if (argc == 4)
1486                 vers = atoi (argv[3]);
1487
1488         if (vers >= 0) {
1489                 delete_flags |= DPD_DELETE_SPECIFIC_VERSION;
1490         }
1491
1492         /* delete the driver for all architectures */
1493         for (i=0; archi_table[i].long_archi; i++) {
1494
1495                 if (arch &&  !strequal( archi_table[i].long_archi, arch))
1496                         continue;
1497
1498                 if (vers >= 0 && archi_table[i].version != vers)
1499                         continue;
1500
1501                 /* make the call to remove the driver */
1502                 status = rpccli_spoolss_DeletePrinterDriverEx(cli, mem_ctx,
1503                                                               cli->srv_name_slash,
1504                                                               archi_table[i].long_archi,
1505                                                               argv[1],
1506                                                               delete_flags,
1507                                                               archi_table[i].version,
1508                                                               &result);
1509
1510                 if ( !W_ERROR_IS_OK(result) )
1511                 {
1512                         if ( !W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ) {
1513                                 printf ("Failed to remove driver %s for arch [%s] (version: %d): %s\n",
1514                                         argv[1], archi_table[i].long_archi, archi_table[i].version, win_errstr(result));
1515                         }
1516                 }
1517                 else
1518                 {
1519                         printf ("Driver %s and files removed for arch [%s] (version: %d).\n", argv[1],
1520                         archi_table[i].long_archi, archi_table[i].version);
1521                         ret = WERR_OK;
1522                 }
1523         }
1524
1525         return ret;
1526 }
1527
1528
1529 /****************************************************************************
1530 ****************************************************************************/
1531
1532 static WERROR cmd_spoolss_deletedriver(struct rpc_pipe_client *cli,
1533                                          TALLOC_CTX *mem_ctx,
1534                                          int argc, const char **argv)
1535 {
1536         WERROR result = WERR_OK;
1537         NTSTATUS status;
1538         int                     i;
1539
1540         /* parse the command arguments */
1541         if (argc != 2) {
1542                 printf ("Usage: %s <driver>\n", argv[0]);
1543                 return WERR_OK;
1544         }
1545
1546         /* delete the driver for all architectures */
1547         for (i=0; archi_table[i].long_archi; i++) {
1548                 /* make the call to remove the driver */
1549                 status = rpccli_spoolss_DeletePrinterDriver(cli, mem_ctx,
1550                                                             cli->srv_name_slash,
1551                                                             archi_table[i].long_archi,
1552                                                             argv[1],
1553                                                             &result);
1554                 if (!NT_STATUS_IS_OK(status)) {
1555                         return result;
1556                 }
1557                 if ( !W_ERROR_IS_OK(result) ) {
1558                         if ( !W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ) {
1559                                 printf ("Failed to remove driver %s for arch [%s] - error 0x%x!\n",
1560                                         argv[1], archi_table[i].long_archi,
1561                                         W_ERROR_V(result));
1562                         }
1563                 } else {
1564                         printf ("Driver %s removed for arch [%s].\n", argv[1],
1565                                 archi_table[i].long_archi);
1566                 }
1567         }
1568
1569         return result;
1570 }
1571
1572 /****************************************************************************
1573 ****************************************************************************/
1574
1575 static WERROR cmd_spoolss_getprintprocdir(struct rpc_pipe_client *cli,
1576                                             TALLOC_CTX *mem_ctx,
1577                                             int argc, const char **argv)
1578 {
1579         WERROR result;
1580         char *environment = NULL;
1581         fstring procdir;
1582
1583         /* parse the command arguments */
1584         if (argc > 2) {
1585                 printf ("Usage: %s [environment]\n", argv[0]);
1586                 return WERR_OK;
1587         }
1588
1589         if (asprintf(&environment, "%s", (argc == 2) ? argv[1] :
1590                      PRINTER_DRIVER_ARCHITECTURE) < 0) {
1591                 return WERR_NOMEM;
1592         }
1593
1594         result = rpccli_spoolss_getprintprocessordirectory(
1595                 cli, mem_ctx, cli->srv_name_slash, environment, procdir);
1596
1597         if (W_ERROR_IS_OK(result))
1598                 printf("%s\n", procdir);
1599
1600         SAFE_FREE(environment);
1601
1602         return result;
1603 }
1604
1605 /****************************************************************************
1606 ****************************************************************************/
1607
1608 static WERROR cmd_spoolss_addform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1609                                     int argc, const char **argv)
1610 {
1611         POLICY_HND handle;
1612         WERROR werror;
1613         NTSTATUS status;
1614         const char *printername;
1615         bool got_handle = False;
1616         union spoolss_AddFormInfo info;
1617         struct spoolss_AddFormInfo1 info1;
1618
1619         /* Parse the command arguments */
1620
1621         if (argc != 3) {
1622                 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1623                 return WERR_OK;
1624         }
1625
1626         /* Get a printer handle */
1627
1628         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1629
1630         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1631                                                printername,
1632                                                PRINTER_ALL_ACCESS,
1633                                                &handle);
1634         if (!W_ERROR_IS_OK(werror))
1635                 goto done;
1636
1637         got_handle = True;
1638
1639         /* Dummy up some values for the form data */
1640
1641         info1.flags             = FORM_USER;
1642         info1.form_name         = argv[2];
1643         info1.size.width        = 100;
1644         info1.size.height       = 100;
1645         info1.area.left         = 0;
1646         info1.area.top          = 10;
1647         info1.area.right        = 20;
1648         info1.area.bottom       = 30;
1649
1650         info.info1 = &info1;
1651
1652         /* Add the form */
1653
1654
1655         status = rpccli_spoolss_AddForm(cli, mem_ctx,
1656                                         &handle,
1657                                         1,
1658                                         info,
1659                                         &werror);
1660
1661  done:
1662         if (got_handle)
1663                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1664
1665         return werror;
1666 }
1667
1668 /****************************************************************************
1669 ****************************************************************************/
1670
1671 static WERROR cmd_spoolss_setform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1672                                     int argc, const char **argv)
1673 {
1674         POLICY_HND handle;
1675         WERROR werror;
1676         NTSTATUS status;
1677         const char *printername;
1678         bool got_handle = False;
1679         union spoolss_AddFormInfo info;
1680         struct spoolss_AddFormInfo1 info1;
1681
1682         /* Parse the command arguments */
1683
1684         if (argc != 3) {
1685                 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1686                 return WERR_OK;
1687         }
1688
1689         /* Get a printer handle */
1690
1691         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1692
1693         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1694                                                printername,
1695                                                SEC_FLAG_MAXIMUM_ALLOWED,
1696                                                &handle);
1697         if (!W_ERROR_IS_OK(werror))
1698                 goto done;
1699
1700         got_handle = True;
1701
1702         /* Dummy up some values for the form data */
1703
1704         info1.flags             = FORM_PRINTER;
1705         info1.size.width        = 100;
1706         info1.size.height       = 100;
1707         info1.area.left         = 0;
1708         info1.area.top          = 1000;
1709         info1.area.right        = 2000;
1710         info1.area.bottom       = 3000;
1711         info1.form_name         = argv[2];
1712
1713         info.info1 = &info1;
1714
1715         /* Set the form */
1716
1717         status = rpccli_spoolss_SetForm(cli, mem_ctx,
1718                                         &handle,
1719                                         argv[2],
1720                                         1,
1721                                         info,
1722                                         &werror);
1723
1724  done:
1725         if (got_handle)
1726                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1727
1728         return werror;
1729 }
1730
1731 /****************************************************************************
1732 ****************************************************************************/
1733
1734 static const char *get_form_flag(int form_flag)
1735 {
1736         switch (form_flag) {
1737         case FORM_USER:
1738                 return "FORM_USER";
1739         case FORM_BUILTIN:
1740                 return "FORM_BUILTIN";
1741         case FORM_PRINTER:
1742                 return "FORM_PRINTER";
1743         default:
1744                 return "unknown";
1745         }
1746 }
1747
1748 /****************************************************************************
1749 ****************************************************************************/
1750
1751 static void display_form(FORM_1 *form)
1752 {
1753         fstring form_name = "";
1754
1755         if (form->name.buffer)
1756                 rpcstr_pull(form_name, form->name.buffer,
1757                             sizeof(form_name), -1, STR_TERMINATE);
1758
1759         printf("%s\n" \
1760                 "\tflag: %s (%d)\n" \
1761                 "\twidth: %d, length: %d\n" \
1762                 "\tleft: %d, right: %d, top: %d, bottom: %d\n\n",
1763                 form_name, get_form_flag(form->flag), form->flag,
1764                 form->width, form->length,
1765                 form->left, form->right,
1766                 form->top, form->bottom);
1767 }
1768
1769 /****************************************************************************
1770 ****************************************************************************/
1771
1772 static void display_form_info1(struct spoolss_FormInfo1 *r)
1773 {
1774         printf("%s\n" \
1775                 "\tflag: %s (%d)\n" \
1776                 "\twidth: %d, length: %d\n" \
1777                 "\tleft: %d, right: %d, top: %d, bottom: %d\n\n",
1778                 r->form_name, get_form_flag(r->flags), r->flags,
1779                 r->size.width, r->size.height,
1780                 r->area.left, r->area.right,
1781                 r->area.top, r->area.bottom);
1782 }
1783
1784 /****************************************************************************
1785 ****************************************************************************/
1786
1787 static WERROR cmd_spoolss_getform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1788                                     int argc, const char **argv)
1789 {
1790         POLICY_HND handle;
1791         WERROR werror;
1792         NTSTATUS status;
1793         const char *printername;
1794         bool got_handle = False;
1795         DATA_BLOB buffer;
1796         uint32_t offered = 0;
1797         union spoolss_FormInfo info;
1798         uint32_t needed;
1799
1800         /* Parse the command arguments */
1801
1802         if (argc != 3) {
1803                 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1804                 return WERR_OK;
1805         }
1806
1807         /* Get a printer handle */
1808
1809         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1810
1811         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1812                                                printername,
1813                                                SEC_FLAG_MAXIMUM_ALLOWED,
1814                                                &handle);
1815         if (!W_ERROR_IS_OK(werror))
1816                 goto done;
1817
1818         got_handle = True;
1819
1820         /* Get the form */
1821
1822         status = rpccli_spoolss_GetForm(cli, mem_ctx,
1823                                         &handle,
1824                                         argv[2],
1825                                         1,
1826                                         NULL,
1827                                         offered,
1828                                         &info,
1829                                         &needed,
1830                                         &werror);
1831         if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
1832                 buffer = data_blob_talloc(mem_ctx, NULL, needed);
1833                 offered = needed;
1834                 status = rpccli_spoolss_GetForm(cli, mem_ctx,
1835                                                 &handle,
1836                                                 argv[2],
1837                                                 1,
1838                                                 &buffer,
1839                                                 offered,
1840                                                 &info,
1841                                                 &needed,
1842                                                 &werror);
1843         }
1844
1845         if (!NT_STATUS_IS_OK(status)) {
1846                 return werror;
1847         }
1848
1849         display_form_info1(&info.info1);
1850  done:
1851         if (got_handle)
1852                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1853
1854         return werror;
1855 }
1856
1857 /****************************************************************************
1858 ****************************************************************************/
1859
1860 static WERROR cmd_spoolss_deleteform(struct rpc_pipe_client *cli,
1861                                        TALLOC_CTX *mem_ctx, int argc,
1862                                        const char **argv)
1863 {
1864         POLICY_HND handle;
1865         WERROR werror;
1866         NTSTATUS status;
1867         const char *printername;
1868         bool got_handle = False;
1869
1870         /* Parse the command arguments */
1871
1872         if (argc != 3) {
1873                 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1874                 return WERR_OK;
1875         }
1876
1877         /* Get a printer handle */
1878
1879         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1880
1881         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1882                                                printername,
1883                                                SEC_FLAG_MAXIMUM_ALLOWED,
1884                                                &handle);
1885         if (!W_ERROR_IS_OK(werror))
1886                 goto done;
1887
1888         got_handle = True;
1889
1890         /* Delete the form */
1891
1892         status = rpccli_spoolss_DeleteForm(cli, mem_ctx,
1893                                            &handle,
1894                                            argv[2],
1895                                            &werror);
1896         if (!NT_STATUS_IS_OK(status)) {
1897                 return ntstatus_to_werror(status);
1898         }
1899
1900  done:
1901         if (got_handle)
1902                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1903
1904         return werror;
1905 }
1906
1907 /****************************************************************************
1908 ****************************************************************************/
1909
1910 static WERROR cmd_spoolss_enum_forms(struct rpc_pipe_client *cli,
1911                                        TALLOC_CTX *mem_ctx, int argc,
1912                                        const char **argv)
1913 {
1914         POLICY_HND handle;
1915         WERROR werror;
1916         const char *printername;
1917         bool got_handle = False;
1918         uint32 num_forms, level = 1, i;
1919         FORM_1 *forms;
1920
1921         /* Parse the command arguments */
1922
1923         if (argc != 2) {
1924                 printf ("Usage: %s <printer>\n", argv[0]);
1925                 return WERR_OK;
1926         }
1927
1928         /* Get a printer handle */
1929
1930         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1931
1932         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1933                                                printername,
1934                                                SEC_FLAG_MAXIMUM_ALLOWED,
1935                                                &handle);
1936         if (!W_ERROR_IS_OK(werror))
1937                 goto done;
1938
1939         got_handle = True;
1940
1941         /* Enumerate forms */
1942
1943         werror = rpccli_spoolss_enumforms(cli, mem_ctx, &handle, level, &num_forms, &forms);
1944
1945         if (!W_ERROR_IS_OK(werror))
1946                 goto done;
1947
1948         /* Display output */
1949
1950         for (i = 0; i < num_forms; i++) {
1951
1952                 display_form(&forms[i]);
1953
1954         }
1955
1956  done:
1957         if (got_handle)
1958                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1959
1960         return werror;
1961 }
1962
1963 /****************************************************************************
1964 ****************************************************************************/
1965
1966 static WERROR cmd_spoolss_setprinterdata(struct rpc_pipe_client *cli,
1967                                             TALLOC_CTX *mem_ctx,
1968                                             int argc, const char **argv)
1969 {
1970         WERROR result;
1971         const char *printername;
1972         POLICY_HND pol;
1973         bool opened_hnd = False;
1974         PRINTER_INFO_CTR ctr;
1975         PRINTER_INFO_0 info;
1976         REGISTRY_VALUE value;
1977         TALLOC_CTX *tmp_ctx = talloc_stackframe();
1978
1979         /* parse the command arguments */
1980         if (argc < 5) {
1981                 printf ("Usage: %s <printer> <string|binary|dword|multistring>"
1982                         " <value> <data>\n",
1983                         argv[0]);
1984                 result = WERR_INVALID_PARAM;
1985                 goto done;
1986         }
1987
1988         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1989
1990         value.type = REG_NONE;
1991
1992         if (strequal(argv[2], "string")) {
1993                 value.type = REG_SZ;
1994         }
1995
1996         if (strequal(argv[2], "binary")) {
1997                 value.type = REG_BINARY;
1998         }
1999
2000         if (strequal(argv[2], "dword")) {
2001                 value.type = REG_DWORD;
2002         }
2003
2004         if (strequal(argv[2], "multistring")) {
2005                 value.type = REG_MULTI_SZ;
2006         }
2007
2008         if (value.type == REG_NONE) {
2009                 printf("Unknown data type: %s\n", argv[2]);
2010                 result =  WERR_INVALID_PARAM;
2011                 goto done;
2012         }
2013
2014         /* get a printer handle */
2015
2016         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2017                                                printername,
2018                                                SEC_FLAG_MAXIMUM_ALLOWED,
2019                                                &pol);
2020         if (!W_ERROR_IS_OK(result))
2021                 goto done;
2022
2023         opened_hnd = True;
2024
2025         ctr.printers_0 = &info;
2026
2027         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, 0, &ctr);
2028
2029         if (!W_ERROR_IS_OK(result))
2030                 goto done;
2031
2032         printf("%s\n", current_timestring(tmp_ctx, True));
2033         printf("\tchange_id (before set)\t:[0x%x]\n", info.change_id);
2034
2035         /* Set the printer data */
2036
2037         fstrcpy(value.valuename, argv[3]);
2038
2039         switch (value.type) {
2040         case REG_SZ: {
2041                 UNISTR2 data;
2042                 init_unistr2(&data, argv[4], UNI_STR_TERMINATE);
2043                 value.size = data.uni_str_len * 2;
2044                 if (value.size) {
2045                         value.data_p = (uint8 *)TALLOC_MEMDUP(mem_ctx, data.buffer,
2046                                                       value.size);
2047                 } else {
2048                         value.data_p = NULL;
2049                 }
2050                 break;
2051         }
2052         case REG_DWORD: {
2053                 uint32 data = strtoul(argv[4], NULL, 10);
2054                 value.size = sizeof(data);
2055                 if (sizeof(data)) {
2056                         value.data_p = (uint8 *)TALLOC_MEMDUP(mem_ctx, &data,
2057                                                       sizeof(data));
2058                 } else {
2059                         value.data_p = NULL;
2060                 }
2061                 break;
2062         }
2063         case REG_BINARY: {
2064                 DATA_BLOB data = strhex_to_data_blob(mem_ctx, argv[4]);
2065                 value.data_p = data.data;
2066                 value.size = data.length;
2067                 break;
2068         }
2069         case REG_MULTI_SZ: {
2070                 int i;
2071                 size_t len = 0;
2072                 char *p;
2073
2074                 for (i=4; i<argc; i++) {
2075                         if (strcmp(argv[i], "NULL") == 0) {
2076                                 argv[i] = "";
2077                         }
2078                         len += strlen(argv[i])+1;
2079                 }
2080
2081                 value.size = len*2;
2082                 value.data_p = TALLOC_ARRAY(mem_ctx, unsigned char, value.size);
2083                 if (value.data_p == NULL) {
2084                         result = WERR_NOMEM;
2085                         goto done;
2086                 }
2087
2088                 p = (char *)value.data_p;
2089                 len = value.size;
2090                 for (i=4; i<argc; i++) {
2091                         size_t l = (strlen(argv[i])+1)*2;
2092                         rpcstr_push(p, argv[i], len, STR_TERMINATE);
2093                         p += l;
2094                         len -= l;
2095                 }
2096                 SMB_ASSERT(len == 0);
2097                 break;
2098         }
2099         default:
2100                 printf("Unknown data type: %s\n", argv[2]);
2101                 result = WERR_INVALID_PARAM;
2102                 goto done;
2103         }
2104
2105         result = rpccli_spoolss_setprinterdata(cli, mem_ctx, &pol, &value);
2106
2107         if (!W_ERROR_IS_OK(result)) {
2108                 printf ("Unable to set [%s=%s]!\n", argv[3], argv[4]);
2109                 goto done;
2110         }
2111         printf("\tSetPrinterData succeeded [%s: %s]\n", argv[3], argv[4]);
2112
2113         result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, 0, &ctr);
2114
2115         if (!W_ERROR_IS_OK(result))
2116                 goto done;
2117
2118         printf("%s\n", current_timestring(tmp_ctx, True));
2119         printf("\tchange_id (after set)\t:[0x%x]\n", info.change_id);
2120
2121 done:
2122         /* cleanup */
2123         TALLOC_FREE(tmp_ctx);
2124         if (opened_hnd)
2125                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
2126
2127         return result;
2128 }
2129
2130 /****************************************************************************
2131 ****************************************************************************/
2132
2133 static void display_job_info_1(JOB_INFO_1 *job)
2134 {
2135         fstring username = "", document = "", text_status = "";
2136
2137         rpcstr_pull(username, job->username.buffer,
2138                     sizeof(username), -1, STR_TERMINATE);
2139
2140         rpcstr_pull(document, job->document.buffer,
2141                     sizeof(document), -1, STR_TERMINATE);
2142
2143         rpcstr_pull(text_status, job->text_status.buffer,
2144                     sizeof(text_status), -1, STR_TERMINATE);
2145
2146         printf("%d: jobid[%d]: %s %s %s %d/%d pages\n", job->position, job->jobid,
2147                username, document, text_status, job->pagesprinted,
2148                job->totalpages);
2149 }
2150
2151 /****************************************************************************
2152 ****************************************************************************/
2153
2154 static void display_job_info_2(JOB_INFO_2 *job)
2155 {
2156         fstring username = "", document = "", text_status = "";
2157
2158         rpcstr_pull(username, job->username.buffer,
2159                     sizeof(username), -1, STR_TERMINATE);
2160
2161         rpcstr_pull(document, job->document.buffer,
2162                     sizeof(document), -1, STR_TERMINATE);
2163
2164         rpcstr_pull(text_status, job->text_status.buffer,
2165                     sizeof(text_status), -1, STR_TERMINATE);
2166
2167         printf("%d: jobid[%d]: %s %s %s %d/%d pages, %d bytes\n", job->position, job->jobid,
2168                username, document, text_status, job->pagesprinted,
2169                job->totalpages, job->size);
2170 }
2171
2172 /****************************************************************************
2173 ****************************************************************************/
2174
2175 static WERROR cmd_spoolss_enum_jobs(struct rpc_pipe_client *cli,
2176                                       TALLOC_CTX *mem_ctx, int argc,
2177                                       const char **argv)
2178 {
2179         WERROR result;
2180         uint32 level = 1, num_jobs, i;
2181         bool got_hnd = False;
2182         const char *printername;
2183         POLICY_HND hnd;
2184         JOB_INFO_CTR ctr;
2185
2186         if (argc < 2 || argc > 3) {
2187                 printf("Usage: %s printername [level]\n", argv[0]);
2188                 return WERR_OK;
2189         }
2190
2191         if (argc == 3)
2192                 level = atoi(argv[2]);
2193
2194         /* Open printer handle */
2195
2196         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2197
2198         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2199                                                printername,
2200                                                SEC_FLAG_MAXIMUM_ALLOWED,
2201                                                &hnd);
2202         if (!W_ERROR_IS_OK(result))
2203                 goto done;
2204
2205         got_hnd = True;
2206
2207         /* Enumerate ports */
2208
2209         result = rpccli_spoolss_enumjobs(cli, mem_ctx, &hnd, level, 0, 1000,
2210                 &num_jobs, &ctr);
2211
2212         if (!W_ERROR_IS_OK(result))
2213                 goto done;
2214
2215         for (i = 0; i < num_jobs; i++) {
2216                 switch(level) {
2217                 case 1:
2218                         display_job_info_1(&ctr.job.job_info_1[i]);
2219                         break;
2220                 case 2:
2221                         display_job_info_2(&ctr.job.job_info_2[i]);
2222                         break;
2223                 default:
2224                         d_printf("unknown info level %d\n", level);
2225                         break;
2226                 }
2227         }
2228
2229 done:
2230         if (got_hnd)
2231                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2232
2233         return result;
2234 }
2235
2236 /****************************************************************************
2237 ****************************************************************************/
2238
2239 static WERROR cmd_spoolss_enum_data( struct rpc_pipe_client *cli,
2240                                        TALLOC_CTX *mem_ctx, int argc,
2241                                        const char **argv)
2242 {
2243         WERROR result;
2244         uint32 i=0, val_needed, data_needed;
2245         bool got_hnd = False;
2246         const char *printername;
2247         POLICY_HND hnd;
2248
2249         if (argc != 2) {
2250                 printf("Usage: %s printername\n", argv[0]);
2251                 return WERR_OK;
2252         }
2253
2254         /* Open printer handle */
2255
2256         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2257
2258         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2259                                                printername,
2260                                                SEC_FLAG_MAXIMUM_ALLOWED,
2261                                                &hnd);
2262         if (!W_ERROR_IS_OK(result))
2263                 goto done;
2264
2265         got_hnd = True;
2266
2267         /* Enumerate data */
2268
2269         result = rpccli_spoolss_enumprinterdata(cli, mem_ctx, &hnd, i, 0, 0,
2270                                              &val_needed, &data_needed,
2271                                              NULL);
2272         while (W_ERROR_IS_OK(result)) {
2273                 REGISTRY_VALUE value;
2274                 result = rpccli_spoolss_enumprinterdata(
2275                         cli, mem_ctx, &hnd, i++, val_needed,
2276                         data_needed, 0, 0, &value);
2277                 if (W_ERROR_IS_OK(result))
2278                         display_reg_value(value);
2279         }
2280         if (W_ERROR_V(result) == ERRnomoreitems)
2281                 result = W_ERROR(ERRsuccess);
2282
2283 done:
2284         if (got_hnd)
2285                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2286
2287         return result;
2288 }
2289
2290 /****************************************************************************
2291 ****************************************************************************/
2292
2293 static WERROR cmd_spoolss_enum_data_ex( struct rpc_pipe_client *cli,
2294                                           TALLOC_CTX *mem_ctx, int argc,
2295                                           const char **argv)
2296 {
2297         WERROR result;
2298         uint32 i;
2299         bool got_hnd = False;
2300         const char *printername;
2301         const char *keyname = NULL;
2302         POLICY_HND hnd;
2303         REGVAL_CTR *ctr = NULL;
2304
2305         if (argc != 3) {
2306                 printf("Usage: %s printername <keyname>\n", argv[0]);
2307                 return WERR_OK;
2308         }
2309
2310         keyname = argv[2];
2311
2312         /* Open printer handle */
2313
2314         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2315
2316         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2317                                                printername,
2318                                                SEC_FLAG_MAXIMUM_ALLOWED,
2319                                                &hnd);
2320         if (!W_ERROR_IS_OK(result))
2321                 goto done;
2322
2323         got_hnd = True;
2324
2325         /* Enumerate subkeys */
2326
2327         if ( !(ctr = TALLOC_ZERO_P( mem_ctx, REGVAL_CTR )) )
2328                 return WERR_NOMEM;
2329
2330         result = rpccli_spoolss_enumprinterdataex(cli, mem_ctx, &hnd, keyname, ctr);
2331
2332         if (!W_ERROR_IS_OK(result))
2333                 goto done;
2334
2335         for (i=0; i < ctr->num_values; i++) {
2336                 display_reg_value(*(ctr->values[i]));
2337         }
2338
2339         TALLOC_FREE( ctr );
2340
2341 done:
2342         if (got_hnd)
2343                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2344
2345         return result;
2346 }
2347
2348 /****************************************************************************
2349 ****************************************************************************/
2350
2351 static WERROR cmd_spoolss_enum_printerkey( struct rpc_pipe_client *cli,
2352                                              TALLOC_CTX *mem_ctx, int argc,
2353                                              const char **argv)
2354 {
2355         WERROR result;
2356         bool got_hnd = False;
2357         const char *printername;
2358         const char *keyname = NULL;
2359         POLICY_HND hnd;
2360         uint16 *keylist = NULL, *curkey;
2361
2362         if (argc < 2 || argc > 3) {
2363                 printf("Usage: %s printername [keyname]\n", argv[0]);
2364                 return WERR_OK;
2365         }
2366
2367         if (argc == 3)
2368                 keyname = argv[2];
2369         else
2370                 keyname = "";
2371
2372         /* Open printer handle */
2373
2374         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2375
2376         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2377                                                printername,
2378                                                SEC_FLAG_MAXIMUM_ALLOWED,
2379                                                &hnd);
2380         if (!W_ERROR_IS_OK(result))
2381                 goto done;
2382
2383         got_hnd = True;
2384
2385         /* Enumerate subkeys */
2386
2387         result = rpccli_spoolss_enumprinterkey(cli, mem_ctx, &hnd, keyname, &keylist, NULL);
2388
2389         if (!W_ERROR_IS_OK(result))
2390                 goto done;
2391
2392         curkey = keylist;
2393         while (*curkey != 0) {
2394                 char *subkey = NULL;
2395                 rpcstr_pull_talloc(mem_ctx, &subkey, curkey, -1,
2396                             STR_TERMINATE);
2397                 if (!subkey) {
2398                         break;
2399                 }
2400                 printf("%s\n", subkey);
2401                 curkey += strlen(subkey) + 1;
2402         }
2403
2404 done:
2405
2406         SAFE_FREE(keylist);
2407
2408         if (got_hnd)
2409                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2410
2411         return result;
2412 }
2413
2414 /****************************************************************************
2415 ****************************************************************************/
2416
2417 static WERROR cmd_spoolss_rffpcnex(struct rpc_pipe_client *cli,
2418                                      TALLOC_CTX *mem_ctx, int argc,
2419                                      const char **argv)
2420 {
2421         const char *printername;
2422         POLICY_HND hnd;
2423         bool got_hnd = False;
2424         WERROR result;
2425         SPOOL_NOTIFY_OPTION option;
2426
2427         if (argc != 2) {
2428                 printf("Usage: %s printername\n", argv[0]);
2429                 result = WERR_OK;
2430                 goto done;
2431         }
2432
2433         /* Open printer */
2434
2435         RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2436
2437         result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2438                                                printername,
2439                                                SEC_FLAG_MAXIMUM_ALLOWED,
2440                                                &hnd);
2441         if (!W_ERROR_IS_OK(result)) {
2442                 printf("Error opening %s\n", argv[1]);
2443                 goto done;
2444         }
2445
2446         got_hnd = True;
2447
2448         /* Create spool options */
2449
2450         ZERO_STRUCT(option);
2451
2452         option.version = 2;
2453         option.option_type_ptr = 1;
2454         option.count = option.ctr.count = 2;
2455
2456         option.ctr.type = TALLOC_ARRAY(mem_ctx, SPOOL_NOTIFY_OPTION_TYPE, 2);
2457         if (option.ctr.type == NULL) {
2458                 result = WERR_NOMEM;
2459                 goto done;
2460         }
2461
2462         ZERO_STRUCT(option.ctr.type[0]);
2463         option.ctr.type[0].type = PRINTER_NOTIFY_TYPE;
2464         option.ctr.type[0].count = option.ctr.type[0].count2 = 1;
2465         option.ctr.type[0].fields_ptr = 1;
2466         option.ctr.type[0].fields[0] = PRINTER_NOTIFY_SERVER_NAME;
2467
2468         ZERO_STRUCT(option.ctr.type[1]);
2469         option.ctr.type[1].type = JOB_NOTIFY_TYPE;
2470         option.ctr.type[1].count = option.ctr.type[1].count2 = 1;
2471         option.ctr.type[1].fields_ptr = 1;
2472         option.ctr.type[1].fields[0] = JOB_NOTIFY_PRINTER_NAME;
2473
2474         /* Send rffpcnex */
2475
2476         result = rpccli_spoolss_rffpcnex(
2477                 cli, mem_ctx, &hnd, 0, 0, cli->srv_name_slash, 123, &option);
2478
2479         if (!W_ERROR_IS_OK(result)) {
2480                 printf("Error rffpcnex %s\n", argv[1]);
2481                 goto done;
2482         }
2483
2484 done:
2485         if (got_hnd)
2486                 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2487
2488         return result;
2489 }
2490
2491 /****************************************************************************
2492 ****************************************************************************/
2493
2494 static bool compare_printer( struct rpc_pipe_client *cli1, POLICY_HND *hnd1,
2495                              struct rpc_pipe_client *cli2, POLICY_HND *hnd2 )
2496 {
2497         PRINTER_INFO_CTR ctr1, ctr2;
2498         WERROR werror;
2499         TALLOC_CTX *mem_ctx = talloc_init("compare_printer");
2500
2501         printf("Retrieving printer propertiesfor %s...", cli1->desthost);
2502         werror = rpccli_spoolss_getprinter( cli1, mem_ctx, hnd1, 2, &ctr1);
2503         if ( !W_ERROR_IS_OK(werror) ) {
2504                 printf("failed (%s)\n", win_errstr(werror));
2505                 talloc_destroy(mem_ctx);
2506                 return False;
2507         }
2508         printf("ok\n");
2509
2510         printf("Retrieving printer properties for %s...", cli2->desthost);
2511         werror = rpccli_spoolss_getprinter( cli2, mem_ctx, hnd2, 2, &ctr2);
2512         if ( !W_ERROR_IS_OK(werror) ) {
2513                 printf("failed (%s)\n", win_errstr(werror));
2514                 talloc_destroy(mem_ctx);
2515                 return False;
2516         }
2517         printf("ok\n");
2518
2519         talloc_destroy(mem_ctx);
2520
2521         return True;
2522 }
2523
2524 /****************************************************************************
2525 ****************************************************************************/
2526
2527 static bool compare_printer_secdesc( struct rpc_pipe_client *cli1, POLICY_HND *hnd1,
2528                                      struct rpc_pipe_client *cli2, POLICY_HND *hnd2 )
2529 {
2530         PRINTER_INFO_CTR ctr1, ctr2;
2531         WERROR werror;
2532         TALLOC_CTX *mem_ctx = talloc_init("compare_printer_secdesc");
2533         SEC_DESC *sd1, *sd2;
2534         bool result = True;
2535
2536
2537         printf("Retrieving printer security for %s...", cli1->desthost);
2538         werror = rpccli_spoolss_getprinter( cli1, mem_ctx, hnd1, 3, &ctr1);
2539         if ( !W_ERROR_IS_OK(werror) ) {
2540                 printf("failed (%s)\n", win_errstr(werror));
2541                 result = False;
2542                 goto done;
2543         }
2544         printf("ok\n");
2545
2546         printf("Retrieving printer security for %s...", cli2->desthost);
2547         werror = rpccli_spoolss_getprinter( cli2, mem_ctx, hnd2, 3, &ctr2);
2548         if ( !W_ERROR_IS_OK(werror) ) {
2549                 printf("failed (%s)\n", win_errstr(werror));
2550                 result = False;
2551                 goto done;
2552         }
2553         printf("ok\n");
2554
2555
2556         printf("++ ");
2557
2558         if ( (ctr1.printers_3 != ctr2.printers_3) && (!ctr1.printers_3 || !ctr2.printers_3) ) {
2559                 printf("NULL PRINTER_INFO_3!\n");
2560                 result = False;
2561                 goto done;
2562         }
2563
2564         sd1 = ctr1.printers_3->secdesc;
2565         sd2 = ctr2.printers_3->secdesc;
2566
2567         if ( (sd1 != sd2) && ( !sd1 || !sd2 ) ) {
2568                 printf("NULL secdesc!\n");
2569                 result = False;
2570                 goto done;
2571         }
2572
2573         if (!sec_desc_equal( sd1, sd2 ) ) {
2574                 printf("Security Descriptors *not* equal!\n");
2575                 result = False;
2576                 goto done;
2577         }
2578
2579         printf("Security descriptors match\n");
2580
2581 done:
2582         talloc_destroy(mem_ctx);
2583         return result;
2584 }
2585
2586
2587 /****************************************************************************
2588 ****************************************************************************/
2589
2590 extern struct user_auth_info *rpcclient_auth_info;
2591
2592 static WERROR cmd_spoolss_printercmp(struct rpc_pipe_client *cli,
2593                                      TALLOC_CTX *mem_ctx, int argc,
2594                                      const char **argv)
2595 {
2596         const char *printername;
2597         char *printername_path = NULL;
2598         struct cli_state *cli_server2 = NULL;
2599         struct rpc_pipe_client *cli2 = NULL;
2600         POLICY_HND hPrinter1, hPrinter2;
2601         NTSTATUS nt_status;
2602         WERROR werror;
2603
2604         if ( argc != 3 )  {
2605                 printf("Usage: %s <printer> <server>\n", argv[0]);
2606                 return WERR_OK;
2607         }
2608
2609         /* first get the connection to the remote server */
2610
2611         nt_status = cli_full_connection(&cli_server2, global_myname(), argv[2],
2612                                         NULL, 0,
2613                                         "IPC$", "IPC",
2614                                         get_cmdline_auth_info_username(rpcclient_auth_info),
2615                                         lp_workgroup(),
2616                                         get_cmdline_auth_info_password(rpcclient_auth_info),
2617                                         get_cmdline_auth_info_use_kerberos(rpcclient_auth_info) ? CLI_FULL_CONNECTION_USE_KERBEROS : 0,
2618                                         get_cmdline_auth_info_signing_state(rpcclient_auth_info), NULL);
2619
2620         if ( !NT_STATUS_IS_OK(nt_status) )
2621                 return WERR_GENERAL_FAILURE;
2622
2623         nt_status = cli_rpc_pipe_open_noauth(cli_server2, &syntax_spoolss,
2624                                              &cli2);
2625         if (!NT_STATUS_IS_OK(nt_status)) {
2626                 printf("failed to open spoolss pipe on server %s (%s)\n",
2627                         argv[2], nt_errstr(nt_status));
2628                 return WERR_GENERAL_FAILURE;
2629         }
2630
2631         /* now open up both printers */
2632
2633         RPCCLIENT_PRINTERNAME(printername_path, cli, printername);
2634
2635         printf("Opening %s...", printername_path);
2636
2637         werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2638                                                printername_path,
2639                                                PRINTER_ALL_ACCESS,
2640                                                &hPrinter1);
2641         if ( !W_ERROR_IS_OK(werror) ) {
2642                 printf("failed (%s)\n", win_errstr(werror));
2643                 goto done;
2644         }
2645         printf("ok\n");
2646
2647         RPCCLIENT_PRINTERNAME(printername_path, cli2, printername);
2648
2649         printf("Opening %s...", printername_path);
2650         werror = rpccli_spoolss_openprinter_ex(cli2, mem_ctx,
2651                                                printername_path,
2652                                                PRINTER_ALL_ACCESS,
2653                                                &hPrinter2);
2654         if ( !W_ERROR_IS_OK(werror) ) {
2655                  printf("failed (%s)\n", win_errstr(werror));
2656                 goto done;
2657         }
2658         printf("ok\n");
2659
2660         compare_printer( cli, &hPrinter1, cli2, &hPrinter2 );
2661         compare_printer_secdesc( cli, &hPrinter1, cli2, &hPrinter2 );
2662 #if 0
2663         compare_printerdata( cli_server1, &hPrinter1, cli_server2, &hPrinter2 );
2664 #endif
2665
2666
2667 done:
2668         /* cleanup */
2669
2670         printf("Closing printers...");
2671         rpccli_spoolss_ClosePrinter( cli, mem_ctx, &hPrinter1, NULL );
2672         rpccli_spoolss_ClosePrinter( cli2, mem_ctx, &hPrinter2, NULL );
2673         printf("ok\n");
2674
2675         /* close the second remote connection */
2676
2677         cli_shutdown( cli_server2 );
2678         return WERR_OK;
2679 }
2680
2681 /* List of commands exported by this module */
2682 struct cmd_set spoolss_commands[] = {
2683
2684         { "SPOOLSS"  },
2685
2686         { "adddriver",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterdriver,   &syntax_spoolss, NULL, "Add a print driver",                  "" },
2687         { "addprinter",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterex,       &syntax_spoolss, NULL, "Add a printer",                       "" },
2688         { "deldriver",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_deletedriver,       &syntax_spoolss, NULL, "Delete a printer driver",             "" },
2689         { "deldriverex",        RPC_RTYPE_WERROR, NULL, cmd_spoolss_deletedriverex,     &syntax_spoolss, NULL, "Delete a printer driver with files",  "" },
2690         { "enumdata",           RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data,          &syntax_spoolss, NULL, "Enumerate printer data",              "" },
2691         { "enumdataex",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data_ex,       &syntax_spoolss, NULL, "Enumerate printer data for a key",    "" },
2692         { "enumkey",            RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printerkey,    &syntax_spoolss, NULL, "Enumerate printer keys",              "" },
2693         { "enumjobs",           RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_jobs,          &syntax_spoolss, NULL, "Enumerate print jobs",                "" },
2694         { "enumports",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_ports,         &syntax_spoolss, NULL, "Enumerate printer ports",             "" },
2695         { "enumdrivers",        RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_drivers,       &syntax_spoolss, NULL, "Enumerate installed printer drivers", "" },
2696         { "enumprinters",       RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printers,      &syntax_spoolss, NULL, "Enumerate printers",                  "" },
2697         { "getdata",            RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdata,     &syntax_spoolss, NULL, "Get print driver data",               "" },
2698         { "getdataex",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdataex,   &syntax_spoolss, NULL, "Get printer driver data with keyname", ""},
2699         { "getdriver",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriver,          &syntax_spoolss, NULL, "Get print driver information",        "" },
2700         { "getdriverdir",       RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriverdir,       &syntax_spoolss, NULL, "Get print driver upload directory",   "" },
2701         { "getprinter",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinter,         &syntax_spoolss, NULL, "Get printer info",                    "" },
2702         { "openprinter",        RPC_RTYPE_WERROR, NULL, cmd_spoolss_open_printer_ex,    &syntax_spoolss, NULL, "Open printer handle",                 "" },
2703         { "setdriver",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_setdriver,          &syntax_spoolss, NULL, "Set printer driver",                  "" },
2704         { "getprintprocdir",    RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprintprocdir,    &syntax_spoolss, NULL, "Get print processor directory",       "" },
2705         { "addform",            RPC_RTYPE_WERROR, NULL, cmd_spoolss_addform,            &syntax_spoolss, NULL, "Add form",                            "" },
2706         { "setform",            RPC_RTYPE_WERROR, NULL, cmd_spoolss_setform,            &syntax_spoolss, NULL, "Set form",                            "" },
2707         { "getform",            RPC_RTYPE_WERROR, NULL, cmd_spoolss_getform,            &syntax_spoolss, NULL, "Get form",                            "" },
2708         { "deleteform",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_deleteform,         &syntax_spoolss, NULL, "Delete form",                         "" },
2709         { "enumforms",          RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_forms,         &syntax_spoolss, NULL, "Enumerate forms",                     "" },
2710         { "setprinter",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinter,         &syntax_spoolss, NULL, "Set printer comment",                 "" },
2711         { "setprintername",     RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprintername,     &syntax_spoolss, NULL, "Set printername",                 "" },
2712         { "setprinterdata",     RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinterdata,     &syntax_spoolss, NULL, "Set REG_SZ printer data",             "" },
2713         { "rffpcnex",           RPC_RTYPE_WERROR, NULL, cmd_spoolss_rffpcnex,           &syntax_spoolss, NULL, "Rffpcnex test", "" },
2714         { "printercmp",         RPC_RTYPE_WERROR, NULL, cmd_spoolss_printercmp,         &syntax_spoolss, NULL, "Printer comparison test", "" },
2715
2716         { NULL }
2717 };