2 Unix SMB/CIFS implementation.
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
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.
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.
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/>.
25 #include "rpcclient.h"
27 #define RPCCLIENT_PRINTERNAME(_printername, _cli, _arg) \
29 _printername = talloc_asprintf_strupper_m(mem_ctx, "%s\\%s", \
30 _cli->srv_name_slash, _arg); \
31 W_ERROR_HAVE_NO_MEMORY(_printername); \
35 const char *long_archi;
36 const char *short_archi;
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 */
47 static const struct table_node archi_table[]= {
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 },
63 * rpcclient module for SPOOLSS rpc pipe.
65 * This generally just parses and checks command lines, and then calls
66 * a cli_spoolss function.
69 /****************************************************************************
70 function to do the mapping between the long architecture name and
72 ****************************************************************************/
74 static const char *cmd_spoolss_get_short_archi(const char *long_archi)
78 DEBUG(107,("Getting architecture dependant directory\n"));
81 } while ( (archi_table[i].long_archi!=NULL ) &&
82 StrCaseCmp(long_archi, archi_table[i].long_archi) );
84 if (archi_table[i].long_archi==NULL) {
85 DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
89 /* this might be client code - but shouldn't this be an fstrcpy etc? */
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));
96 return archi_table[i].short_archi;
99 /****************************************************************************
100 ****************************************************************************/
102 static WERROR cmd_spoolss_open_printer_ex(struct rpc_pipe_client *cli,
104 int argc, const char **argv)
110 printf("Usage: %s <printername>\n", argv[0]);
115 return WERR_GENERAL_FAILURE;
117 /* Open the printer handle */
119 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
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);
127 if (!W_ERROR_IS_OK(werror)) {
128 printf("Error closing printer handle! (%s)\n",
129 get_dos_error_msg(werror));
137 /****************************************************************************
138 ****************************************************************************/
140 static void display_print_info_0(PRINTER_INFO_0 *i0)
143 fstring servername = "";
148 rpcstr_pull(name, i0->printername.buffer, sizeof(name), -1, STR_TERMINATE);
150 rpcstr_pull(servername, i0->servername.buffer, sizeof(servername), -1,STR_TERMINATE);
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);
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);
162 printf("\tglobal_counter:[0x%x]\n", i0->global_counter);
163 printf("\ttotal_pages:[0x%x]\n", i0->total_pages);
165 printf("\tmajorversion:[0x%x]\n", i0->major_version);
166 printf("\tbuildversion:[0x%x]\n", i0->build_version);
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);
195 /****************************************************************************
196 ****************************************************************************/
198 static void display_print_info_1(PRINTER_INFO_1 *i1)
204 rpcstr_pull(desc, i1->description.buffer, sizeof(desc), -1,
207 rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
208 rpcstr_pull(comm, i1->comment.buffer, sizeof(comm), -1, STR_TERMINATE);
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);
218 /****************************************************************************
219 ****************************************************************************/
221 static void display_print_info_2(PRINTER_INFO_2 *i2)
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 = "";
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);
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);
268 display_sec_desc(i2->secdesc);
273 /****************************************************************************
274 ****************************************************************************/
276 static void display_print_info_3(PRINTER_INFO_3 *i3)
278 display_sec_desc(i3->secdesc);
283 /****************************************************************************
284 ****************************************************************************/
286 static void display_print_info_7(PRINTER_INFO_7 *i7)
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);
295 /****************************************************************************
296 ****************************************************************************/
298 static WERROR cmd_spoolss_enum_printers(struct rpc_pipe_client *cli,
300 int argc, const char **argv)
303 uint32 info_level = 1;
304 PRINTER_INFO_CTR ctr;
305 uint32 i = 0, num_printers;
310 printf("Usage: %s [level] [name]\n", argv[0]);
315 info_level = atoi(argv[1]);
318 fstrcpy(name, argv[2]);
320 slprintf(name, sizeof(name)-1, "\\\\%s", cli->desthost);
326 result = rpccli_spoolss_enum_printers(cli, mem_ctx, name, PRINTER_ENUM_LOCAL,
327 info_level, &num_printers, &ctr);
329 if (W_ERROR_IS_OK(result)) {
332 printf ("No printers returned.\n");
336 for (i = 0; i < num_printers; i++) {
339 display_print_info_0(&ctr.printers_0[i]);
342 display_print_info_1(&ctr.printers_1[i]);
345 display_print_info_2(&ctr.printers_2[i]);
348 display_print_info_3(&ctr.printers_3[i]);
351 printf("unknown info level %d\n", info_level);
361 /****************************************************************************
362 ****************************************************************************/
364 static void display_port_info_1(PORT_INFO_1 *i1)
368 rpcstr_pull(buffer, i1->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
369 printf("\tPort Name:\t[%s]\n", buffer);
372 /****************************************************************************
373 ****************************************************************************/
375 static void display_port_info_2(PORT_INFO_2 *i2)
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);
383 printf("\tMonitor Name:\t[%s]\n", buffer);
384 rpcstr_pull(buffer, i2->description.buffer, sizeof(buffer), -1, STR_TERMINATE);
386 printf("\tDescription:\t[%s]\n", buffer);
387 printf("\tPort Type:\t" );
388 if ( i2->port_type ) {
389 int comma = 0; /* hack */
391 if ( i2->port_type & PORT_TYPE_READ ) {
395 if ( i2->port_type & PORT_TYPE_WRITE ) {
396 printf( "%sWrite", comma ? ", " : "" );
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 ? ", " : "" );
405 if ( i2->port_type & PORT_TYPE_NET_ATTACHED ) {
406 printf( "%sNet-Attached", comma ? ", " : "" );
410 printf( "[Unset]\n" );
412 printf("\tReserved:\t[%d]\n", i2->reserved);
416 /****************************************************************************
417 ****************************************************************************/
419 static WERROR cmd_spoolss_enum_ports(struct rpc_pipe_client *cli,
420 TALLOC_CTX *mem_ctx, int argc,
424 uint32 info_level = 1;
429 printf("Usage: %s [level]\n", argv[0]);
434 info_level = atoi(argv[1]);
436 /* Enumerate ports */
440 result = rpccli_spoolss_enum_ports(cli, mem_ctx, info_level, &returned, &ctr);
442 if (W_ERROR_IS_OK(result)) {
445 for (i = 0; i < returned; i++) {
446 switch (info_level) {
448 display_port_info_1(&ctr.port.info_1[i]);
451 display_port_info_2(&ctr.port.info_2[i]);
454 printf("unknown info level %d\n", info_level);
463 /****************************************************************************
464 ****************************************************************************/
466 static WERROR cmd_spoolss_setprinter(struct rpc_pipe_client *cli,
468 int argc, const char **argv)
472 uint32 info_level = 2;
473 bool opened_hnd = False;
474 PRINTER_INFO_CTR ctr;
475 const char *printername, *comment = NULL;
477 if (argc == 1 || argc > 3) {
478 printf("Usage: %s printername comment\n", argv[0]);
483 /* Open a printer handle */
488 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
490 /* get a printer handle */
491 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
495 if (!W_ERROR_IS_OK(result))
500 /* Get printer info */
501 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
503 if (!W_ERROR_IS_OK(result))
507 /* Modify the comment. */
508 init_unistr(&ctr.printers_2->comment, comment);
509 ctr.printers_2->devmode = NULL;
510 ctr.printers_2->secdesc = NULL;
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");
518 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
523 /****************************************************************************
524 ****************************************************************************/
526 static WERROR cmd_spoolss_setprintername(struct rpc_pipe_client *cli,
528 int argc, const char **argv)
532 uint32 info_level = 2;
533 bool opened_hnd = False;
534 PRINTER_INFO_CTR ctr;
535 const char *printername,
536 *new_printername = NULL;
538 if (argc == 1 || argc > 3) {
539 printf("Usage: %s printername new_printername\n", argv[0]);
544 /* Open a printer handle */
546 new_printername = argv[2];
549 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
551 /* get a printer handle */
552 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
556 if (!W_ERROR_IS_OK(result))
561 /* Get printer info */
562 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
564 if (!W_ERROR_IS_OK(result))
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;
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");
578 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
583 /****************************************************************************
584 ****************************************************************************/
586 static WERROR cmd_spoolss_getprinter(struct rpc_pipe_client *cli,
588 int argc, const char **argv)
592 uint32 info_level = 1;
593 bool opened_hnd = False;
594 PRINTER_INFO_CTR ctr;
595 const char *printername;
597 if (argc == 1 || argc > 3) {
598 printf("Usage: %s <printername> [level]\n", argv[0]);
602 /* Open a printer handle */
604 info_level = atoi(argv[2]);
607 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
609 /* get a printer handle */
611 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
613 SEC_FLAG_MAXIMUM_ALLOWED,
615 if (!W_ERROR_IS_OK(result))
620 /* Get printer info */
622 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, info_level, &ctr);
624 if (!W_ERROR_IS_OK(result))
627 /* Display printer info */
629 switch (info_level) {
631 display_print_info_0(ctr.printers_0);
634 display_print_info_1(ctr.printers_1);
637 display_print_info_2(ctr.printers_2);
640 display_print_info_3(ctr.printers_3);
643 display_print_info_7(ctr.printers_7);
646 printf("unknown info level %d\n", info_level);
652 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
657 /****************************************************************************
658 ****************************************************************************/
660 static void display_reg_value(REGISTRY_VALUE value)
666 printf("%s: REG_DWORD: 0x%08x\n", value.valuename,
667 *((uint32 *) value.data_p));
670 rpcstr_pull_talloc(talloc_tos(),
675 printf("%s: REG_SZ: %s\n", value.valuename, text ? text : "");
678 char *hex = hex_encode_talloc(NULL, value.data_p, value.size);
680 printf("%s: REG_BINARY:", value.valuename);
682 for (i=0; i<len; i++) {
683 if (hex[i] == '\0') {
696 uint32 i, num_values;
699 if (!W_ERROR_IS_OK(reg_pull_multi_sz(NULL, value.data_p,
700 value.size, &num_values,
702 d_printf("reg_pull_multi_sz failed\n");
706 for (i=0; i<num_values; i++) {
707 d_printf("%s\n", values[i]);
713 printf("%s: unknown type %d\n", value.valuename, value.type);
718 /****************************************************************************
719 ****************************************************************************/
721 static WERROR cmd_spoolss_getprinterdata(struct rpc_pipe_client *cli,
723 int argc, const char **argv)
727 bool opened_hnd = False;
729 const char *valuename;
730 REGISTRY_VALUE value;
733 printf("Usage: %s <printername> <valuename>\n", argv[0]);
734 printf("<printername> of . queries print server\n");
739 /* Open a printer handle */
741 if (strncmp(argv[1], ".", sizeof(".")) == 0)
742 fstrcpy(printername, cli->srv_name_slash);
744 slprintf(printername, sizeof(printername)-1, "%s\\%s",
745 cli->srv_name_slash, argv[1]);
747 /* get a printer handle */
749 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
751 SEC_FLAG_MAXIMUM_ALLOWED,
753 if (!W_ERROR_IS_OK(result))
758 /* Get printer info */
760 result = rpccli_spoolss_getprinterdata(cli, mem_ctx, &pol, valuename, &value);
762 if (!W_ERROR_IS_OK(result))
765 /* Display printer data */
767 fstrcpy(value.valuename, valuename);
768 display_reg_value(value);
773 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
778 /****************************************************************************
779 ****************************************************************************/
781 static WERROR cmd_spoolss_getprinterdataex(struct rpc_pipe_client *cli,
783 int argc, const char **argv)
787 bool opened_hnd = False;
789 const char *valuename, *keyname;
790 REGISTRY_VALUE value;
793 printf("Usage: %s <printername> <keyname> <valuename>\n",
795 printf("<printername> of . queries print server\n");
801 /* Open a printer handle */
803 if (strncmp(argv[1], ".", sizeof(".")) == 0)
804 fstrcpy(printername, cli->srv_name_slash);
806 slprintf(printername, sizeof(printername)-1, "%s\\%s",
807 cli->srv_name_slash, argv[1]);
809 /* get a printer handle */
811 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
813 SEC_FLAG_MAXIMUM_ALLOWED,
815 if (!W_ERROR_IS_OK(result))
820 /* Get printer info */
822 result = rpccli_spoolss_getprinterdataex(cli, mem_ctx, &pol, keyname,
825 if (!W_ERROR_IS_OK(result))
828 /* Display printer data */
830 fstrcpy(value.valuename, valuename);
831 display_reg_value(value);
836 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
841 /****************************************************************************
842 ****************************************************************************/
844 static void display_print_driver_1(DRIVER_INFO_1 *i1)
850 rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
852 printf ("Printer Driver Info 1:\n");
853 printf ("\tDriver Name: [%s]\n\n", name);
858 /****************************************************************************
859 ****************************************************************************/
861 static void display_print_driver_2(DRIVER_INFO_2 *i1)
864 fstring architecture;
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);
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);
888 /****************************************************************************
889 ****************************************************************************/
891 static void display_print_driver_3(DRIVER_INFO_3 *i1)
894 fstring architecture = "";
895 fstring driverpath = "";
896 fstring datafile = "";
897 fstring configfile = "";
898 fstring helpfile = "";
899 fstring dependentfiles = "";
900 fstring monitorname = "";
901 fstring defaultdatatype = "";
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);
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);
929 rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
931 length+=strlen(dependentfiles)+1;
933 if (strlen(dependentfiles) > 0)
935 printf ("\tDependentfiles: [%s]\n", dependentfiles);
945 printf ("\tMonitorname: [%s]\n", monitorname);
946 printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
951 /****************************************************************************
952 ****************************************************************************/
954 static WERROR cmd_spoolss_getdriver(struct rpc_pipe_client *cli,
956 int argc, const char **argv)
960 uint32 info_level = 3;
961 bool opened_hnd = False;
962 PRINTER_DRIVER_CTR ctr;
963 const char *printername;
965 bool success = False;
967 if ((argc == 1) || (argc > 3))
969 printf("Usage: %s <printername> [level]\n", argv[0]);
973 /* get the arguments need to open the printer handle */
975 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
978 info_level = atoi(argv[2]);
980 /* Open a printer handle */
982 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
986 if (!W_ERROR_IS_OK(werror)) {
987 printf("Error opening printer handle for %s!\n", printername);
993 /* loop through and print driver info level for each architecture */
995 for (i=0; archi_table[i].long_archi!=NULL; i++) {
997 werror = rpccli_spoolss_getprinterdriver( cli, mem_ctx, &pol, info_level,
998 archi_table[i].long_archi, archi_table[i].version,
1001 if (!W_ERROR_IS_OK(werror))
1004 /* need at least one success */
1008 printf ("\n[%s]\n", archi_table[i].long_archi);
1010 switch (info_level) {
1012 display_print_driver_1 (ctr.info1);
1015 display_print_driver_2 (ctr.info2);
1018 display_print_driver_3 (ctr.info3);
1021 printf("unknown info level %d\n", info_level);
1029 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
1037 /****************************************************************************
1038 ****************************************************************************/
1040 static WERROR cmd_spoolss_enum_drivers(struct rpc_pipe_client *cli,
1041 TALLOC_CTX *mem_ctx,
1042 int argc, const char **argv)
1044 WERROR werror = WERR_OK;
1045 uint32 info_level = 1;
1046 PRINTER_DRIVER_CTR ctr;
1051 printf("Usage: enumdrivers [level]\n");
1056 info_level = atoi(argv[1]);
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 */
1063 if ( i>0 && strequal(archi_table[i].long_archi, archi_table[i-1].long_archi) )
1066 werror = rpccli_spoolss_enumprinterdrivers(
1067 cli, mem_ctx, info_level,
1068 archi_table[i].long_archi, &returned, &ctr);
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);
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));
1086 printf ("\n[%s]\n", archi_table[i].long_archi);
1091 for (j=0; j < returned; j++) {
1092 display_print_driver_1 (&ctr.info1[j]);
1096 for (j=0; j < returned; j++) {
1097 display_print_driver_2 (&ctr.info2[j]);
1101 for (j=0; j < returned; j++) {
1102 display_print_driver_3 (&ctr.info3[j]);
1106 printf("unknown info level %d\n", info_level);
1107 return WERR_UNKNOWN_LEVEL;
1114 /****************************************************************************
1115 ****************************************************************************/
1117 static void display_printdriverdir_1(DRIVER_DIRECTORY_1 *i1)
1123 rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
1125 printf ("\tDirectory Name:[%s]\n", name);
1128 /****************************************************************************
1129 ****************************************************************************/
1131 static WERROR cmd_spoolss_getdriverdir(struct rpc_pipe_client *cli,
1132 TALLOC_CTX *mem_ctx,
1133 int argc, const char **argv)
1137 DRIVER_DIRECTORY_CTR ctr;
1140 printf("Usage: %s [environment]\n", argv[0]);
1144 /* Get the arguments need to open the printer handle */
1147 fstrcpy (env, argv[1]);
1149 fstrcpy (env, "Windows NT x86");
1151 /* Get the directory. Only use Info level 1 */
1153 result = rpccli_spoolss_getprinterdriverdir(cli, mem_ctx, 1, env, &ctr);
1155 if (W_ERROR_IS_OK(result))
1156 display_printdriverdir_1(ctr.info1);
1161 /****************************************************************************
1162 ****************************************************************************/
1164 void set_drv_info_3_env (DRIVER_INFO_3 *info, const char *arch)
1169 for (i=0; archi_table[i].long_archi != NULL; i++)
1171 if (strcmp(arch, archi_table[i].short_archi) == 0)
1173 info->version = archi_table[i].version;
1174 init_unistr (&info->architecture, archi_table[i].long_archi);
1179 if (archi_table[i].long_archi == NULL)
1181 DEBUG(0, ("set_drv_info_3_env: Unknown arch [%s]\n", arch));
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 *************************************************************************/
1193 static char* get_driver_3_param (char* str, const char* delim, UNISTR* dest,
1198 /* get the next token */
1199 ptr = strtok_r(str, delim, saveptr);
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)
1205 if (ptr && (StrCaseCmp(ptr, "NULL") == 0))
1209 init_unistr(dest, ptr);
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,
1226 char *saveptr = NULL;
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);
1237 /* <Comma Separated List of Dependent Files> */
1238 /* save the beginning of the string */
1239 str2 = get_driver_3_param (NULL, ":", NULL, &saveptr);
1242 /* begin to strip out each filename */
1243 str = strtok_r(str, ",", &saveptr);
1247 /* keep a cumlative count of the str lengths */
1248 len += strlen(str)+1;
1249 str = strtok_r(NULL, ",", &saveptr);
1252 /* allocate the space; add one extra slot for a terminating NULL.
1253 Each filename is NULL terminated and the end contains a double
1255 if ((info->dependentfiles=TALLOC_ARRAY(mem_ctx, uint16, len+1)) == NULL)
1257 DEBUG(0,("init_drv_info_3_members: Unable to malloc memory for dependenfiles\n"));
1260 for (i=0; i<len; i++)
1262 SSVAL(&info->dependentfiles[i], 0, str2[i]);
1264 info->dependentfiles[len] = '\0';
1270 /****************************************************************************
1271 ****************************************************************************/
1273 static WERROR cmd_spoolss_addprinterdriver(struct rpc_pipe_client *cli,
1274 TALLOC_CTX *mem_ctx,
1275 int argc, const char **argv)
1279 PRINTER_DRIVER_CTR ctr;
1280 DRIVER_INFO_3 info3;
1282 fstring driver_name;
1285 /* parse the command arguments */
1286 if (argc != 3 && argc != 4)
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");
1297 /* Fill in the DRIVER_INFO_3 struct */
1299 if (!(arch = cmd_spoolss_get_short_archi(argv[1])))
1301 printf ("Error Unknown architechture [%s]\n", argv[1]);
1302 return WERR_INVALID_PARAM;
1305 set_drv_info_3_env(&info3, arch);
1307 driver_args = talloc_strdup( mem_ctx, argv[2] );
1308 if (!init_drv_info_3_members(mem_ctx, &info3, driver_args ))
1310 printf ("Error Invalid parameter list - %s.\n", argv[2]);
1311 return WERR_INVALID_PARAM;
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. */
1319 info3.version = atoi(argv[3]);
1324 result = rpccli_spoolss_addprinterdriver (cli, mem_ctx, level, &ctr);
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",
1337 /****************************************************************************
1338 ****************************************************************************/
1340 static WERROR cmd_spoolss_addprinterex(struct rpc_pipe_client *cli,
1341 TALLOC_CTX *mem_ctx,
1342 int argc, const char **argv)
1346 PRINTER_INFO_CTR ctr;
1347 PRINTER_INFO_2 info2;
1349 /* parse the command arguments */
1352 printf ("Usage: %s <name> <shared name> <driver> <port>\n", argv[0]);
1356 /* Fill in the DRIVER_INFO_2 struct */
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;
1370 info2.defaultpriority = 0;
1371 info2.starttime = 0;
1372 info2.untiltime = 0;
1374 /* These three fields must not be used by AddPrinter()
1375 as defined in the MS Platform SDK documentation..
1379 info2.averageppm = 0;
1382 ctr.printers_2 = &info2;
1383 result = rpccli_spoolss_addprinterex (cli, mem_ctx, level, &ctr);
1385 if (W_ERROR_IS_OK(result))
1386 printf ("Printer %s successfully installed.\n", argv[1]);
1391 /****************************************************************************
1392 ****************************************************************************/
1394 static WERROR cmd_spoolss_setdriver(struct rpc_pipe_client *cli,
1395 TALLOC_CTX *mem_ctx,
1396 int argc, const char **argv)
1401 bool opened_hnd = False;
1402 PRINTER_INFO_CTR ctr;
1403 PRINTER_INFO_2 info2;
1404 const char *printername;
1406 /* parse the command arguments */
1409 printf ("Usage: %s <printer> <driver>\n", argv[0]);
1413 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1415 /* Get a printer handle */
1417 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1421 if (!W_ERROR_IS_OK(result))
1426 /* Get printer info */
1428 ZERO_STRUCT (info2);
1429 ctr.printers_2 = &info2;
1431 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, level, &ctr);
1433 if (!W_ERROR_IS_OK(result)) {
1434 printf ("Unable to retrieve printer information!\n");
1438 /* Set the printer driver */
1440 init_unistr(&ctr.printers_2->drivername, argv[2]);
1442 result = rpccli_spoolss_setprinter(cli, mem_ctx, &pol, level, &ctr, 0);
1444 if (!W_ERROR_IS_OK(result)) {
1445 printf("SetPrinter call failed!\n");
1449 printf("Successfully set %s to driver %s.\n", argv[1], argv[2]);
1455 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
1461 /****************************************************************************
1462 ****************************************************************************/
1464 static WERROR cmd_spoolss_deletedriverex(struct rpc_pipe_client *cli,
1465 TALLOC_CTX *mem_ctx,
1466 int argc, const char **argv)
1468 WERROR result, ret = WERR_UNKNOWN_PRINTER_DRIVER;
1474 const char *arch = NULL;
1475 uint32_t delete_flags = 0;
1477 /* parse the command arguments */
1478 if (argc < 2 || argc > 4) {
1479 printf ("Usage: %s <driver> [arch] [version]\n", argv[0]);
1486 vers = atoi (argv[3]);
1489 delete_flags |= DPD_DELETE_SPECIFIC_VERSION;
1492 /* delete the driver for all architectures */
1493 for (i=0; archi_table[i].long_archi; i++) {
1495 if (arch && !strequal( archi_table[i].long_archi, arch))
1498 if (vers >= 0 && archi_table[i].version != vers)
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,
1507 archi_table[i].version,
1510 if ( !W_ERROR_IS_OK(result) )
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));
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);
1529 /****************************************************************************
1530 ****************************************************************************/
1532 static WERROR cmd_spoolss_deletedriver(struct rpc_pipe_client *cli,
1533 TALLOC_CTX *mem_ctx,
1534 int argc, const char **argv)
1536 WERROR result = WERR_OK;
1540 /* parse the command arguments */
1542 printf ("Usage: %s <driver>\n", argv[0]);
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,
1554 if (!NT_STATUS_IS_OK(status)) {
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,
1564 printf ("Driver %s removed for arch [%s].\n", argv[1],
1565 archi_table[i].long_archi);
1572 /****************************************************************************
1573 ****************************************************************************/
1575 static WERROR cmd_spoolss_getprintprocdir(struct rpc_pipe_client *cli,
1576 TALLOC_CTX *mem_ctx,
1577 int argc, const char **argv)
1580 char *environment = NULL;
1583 /* parse the command arguments */
1585 printf ("Usage: %s [environment]\n", argv[0]);
1589 if (asprintf(&environment, "%s", (argc == 2) ? argv[1] :
1590 PRINTER_DRIVER_ARCHITECTURE) < 0) {
1594 result = rpccli_spoolss_getprintprocessordirectory(
1595 cli, mem_ctx, cli->srv_name_slash, environment, procdir);
1597 if (W_ERROR_IS_OK(result))
1598 printf("%s\n", procdir);
1600 SAFE_FREE(environment);
1605 /****************************************************************************
1606 ****************************************************************************/
1608 static WERROR cmd_spoolss_addform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1609 int argc, const char **argv)
1614 const char *printername;
1615 bool got_handle = False;
1616 union spoolss_AddFormInfo info;
1617 struct spoolss_AddFormInfo1 info1;
1619 /* Parse the command arguments */
1622 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1626 /* Get a printer handle */
1628 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1630 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1634 if (!W_ERROR_IS_OK(werror))
1639 /* Dummy up some values for the form data */
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;
1650 info.info1 = &info1;
1655 status = rpccli_spoolss_AddForm(cli, mem_ctx,
1663 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1668 /****************************************************************************
1669 ****************************************************************************/
1671 static WERROR cmd_spoolss_setform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1672 int argc, const char **argv)
1677 const char *printername;
1678 bool got_handle = False;
1679 union spoolss_AddFormInfo info;
1680 struct spoolss_AddFormInfo1 info1;
1682 /* Parse the command arguments */
1685 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1689 /* Get a printer handle */
1691 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1693 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1695 SEC_FLAG_MAXIMUM_ALLOWED,
1697 if (!W_ERROR_IS_OK(werror))
1702 /* Dummy up some values for the form data */
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];
1713 info.info1 = &info1;
1717 status = rpccli_spoolss_SetForm(cli, mem_ctx,
1726 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1731 /****************************************************************************
1732 ****************************************************************************/
1734 static const char *get_form_flag(int form_flag)
1736 switch (form_flag) {
1740 return "FORM_BUILTIN";
1742 return "FORM_PRINTER";
1748 /****************************************************************************
1749 ****************************************************************************/
1751 static void display_form(FORM_1 *form)
1753 fstring form_name = "";
1755 if (form->name.buffer)
1756 rpcstr_pull(form_name, form->name.buffer,
1757 sizeof(form_name), -1, STR_TERMINATE);
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);
1769 /****************************************************************************
1770 ****************************************************************************/
1772 static void display_form_info1(struct spoolss_FormInfo1 *r)
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);
1784 /****************************************************************************
1785 ****************************************************************************/
1787 static WERROR cmd_spoolss_getform(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
1788 int argc, const char **argv)
1793 const char *printername;
1794 bool got_handle = False;
1796 uint32_t offered = 0;
1797 union spoolss_FormInfo info;
1800 /* Parse the command arguments */
1803 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1807 /* Get a printer handle */
1809 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1811 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1813 SEC_FLAG_MAXIMUM_ALLOWED,
1815 if (!W_ERROR_IS_OK(werror))
1822 status = rpccli_spoolss_GetForm(cli, mem_ctx,
1831 if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
1832 buffer = data_blob_talloc(mem_ctx, NULL, needed);
1834 status = rpccli_spoolss_GetForm(cli, mem_ctx,
1845 if (!NT_STATUS_IS_OK(status)) {
1849 display_form_info1(&info.info1);
1852 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1857 /****************************************************************************
1858 ****************************************************************************/
1860 static WERROR cmd_spoolss_deleteform(struct rpc_pipe_client *cli,
1861 TALLOC_CTX *mem_ctx, int argc,
1867 const char *printername;
1868 bool got_handle = False;
1870 /* Parse the command arguments */
1873 printf ("Usage: %s <printer> <formname>\n", argv[0]);
1877 /* Get a printer handle */
1879 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1881 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1883 SEC_FLAG_MAXIMUM_ALLOWED,
1885 if (!W_ERROR_IS_OK(werror))
1890 /* Delete the form */
1892 status = rpccli_spoolss_DeleteForm(cli, mem_ctx,
1896 if (!NT_STATUS_IS_OK(status)) {
1897 return ntstatus_to_werror(status);
1902 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1907 /****************************************************************************
1908 ****************************************************************************/
1910 static WERROR cmd_spoolss_enum_forms(struct rpc_pipe_client *cli,
1911 TALLOC_CTX *mem_ctx, int argc,
1916 const char *printername;
1917 bool got_handle = False;
1918 uint32 num_forms, level = 1, i;
1921 /* Parse the command arguments */
1924 printf ("Usage: %s <printer>\n", argv[0]);
1928 /* Get a printer handle */
1930 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1932 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
1934 SEC_FLAG_MAXIMUM_ALLOWED,
1936 if (!W_ERROR_IS_OK(werror))
1941 /* Enumerate forms */
1943 werror = rpccli_spoolss_enumforms(cli, mem_ctx, &handle, level, &num_forms, &forms);
1945 if (!W_ERROR_IS_OK(werror))
1948 /* Display output */
1950 for (i = 0; i < num_forms; i++) {
1952 display_form(&forms[i]);
1958 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &handle, NULL);
1963 /****************************************************************************
1964 ****************************************************************************/
1966 static WERROR cmd_spoolss_setprinterdata(struct rpc_pipe_client *cli,
1967 TALLOC_CTX *mem_ctx,
1968 int argc, const char **argv)
1971 const char *printername;
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();
1979 /* parse the command arguments */
1981 printf ("Usage: %s <printer> <string|binary|dword|multistring>"
1982 " <value> <data>\n",
1984 result = WERR_INVALID_PARAM;
1988 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
1990 value.type = REG_NONE;
1992 if (strequal(argv[2], "string")) {
1993 value.type = REG_SZ;
1996 if (strequal(argv[2], "binary")) {
1997 value.type = REG_BINARY;
2000 if (strequal(argv[2], "dword")) {
2001 value.type = REG_DWORD;
2004 if (strequal(argv[2], "multistring")) {
2005 value.type = REG_MULTI_SZ;
2008 if (value.type == REG_NONE) {
2009 printf("Unknown data type: %s\n", argv[2]);
2010 result = WERR_INVALID_PARAM;
2014 /* get a printer handle */
2016 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2018 SEC_FLAG_MAXIMUM_ALLOWED,
2020 if (!W_ERROR_IS_OK(result))
2025 ctr.printers_0 = &info;
2027 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, 0, &ctr);
2029 if (!W_ERROR_IS_OK(result))
2032 printf("%s\n", current_timestring(tmp_ctx, True));
2033 printf("\tchange_id (before set)\t:[0x%x]\n", info.change_id);
2035 /* Set the printer data */
2037 fstrcpy(value.valuename, argv[3]);
2039 switch (value.type) {
2042 init_unistr2(&data, argv[4], UNI_STR_TERMINATE);
2043 value.size = data.uni_str_len * 2;
2045 value.data_p = (uint8 *)TALLOC_MEMDUP(mem_ctx, data.buffer,
2048 value.data_p = NULL;
2053 uint32 data = strtoul(argv[4], NULL, 10);
2054 value.size = sizeof(data);
2056 value.data_p = (uint8 *)TALLOC_MEMDUP(mem_ctx, &data,
2059 value.data_p = NULL;
2064 DATA_BLOB data = strhex_to_data_blob(mem_ctx, argv[4]);
2065 value.data_p = data.data;
2066 value.size = data.length;
2069 case REG_MULTI_SZ: {
2074 for (i=4; i<argc; i++) {
2075 if (strcmp(argv[i], "NULL") == 0) {
2078 len += strlen(argv[i])+1;
2082 value.data_p = TALLOC_ARRAY(mem_ctx, unsigned char, value.size);
2083 if (value.data_p == NULL) {
2084 result = WERR_NOMEM;
2088 p = (char *)value.data_p;
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);
2096 SMB_ASSERT(len == 0);
2100 printf("Unknown data type: %s\n", argv[2]);
2101 result = WERR_INVALID_PARAM;
2105 result = rpccli_spoolss_setprinterdata(cli, mem_ctx, &pol, &value);
2107 if (!W_ERROR_IS_OK(result)) {
2108 printf ("Unable to set [%s=%s]!\n", argv[3], argv[4]);
2111 printf("\tSetPrinterData succeeded [%s: %s]\n", argv[3], argv[4]);
2113 result = rpccli_spoolss_getprinter(cli, mem_ctx, &pol, 0, &ctr);
2115 if (!W_ERROR_IS_OK(result))
2118 printf("%s\n", current_timestring(tmp_ctx, True));
2119 printf("\tchange_id (after set)\t:[0x%x]\n", info.change_id);
2123 TALLOC_FREE(tmp_ctx);
2125 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &pol, NULL);
2130 /****************************************************************************
2131 ****************************************************************************/
2133 static void display_job_info_1(JOB_INFO_1 *job)
2135 fstring username = "", document = "", text_status = "";
2137 rpcstr_pull(username, job->username.buffer,
2138 sizeof(username), -1, STR_TERMINATE);
2140 rpcstr_pull(document, job->document.buffer,
2141 sizeof(document), -1, STR_TERMINATE);
2143 rpcstr_pull(text_status, job->text_status.buffer,
2144 sizeof(text_status), -1, STR_TERMINATE);
2146 printf("%d: jobid[%d]: %s %s %s %d/%d pages\n", job->position, job->jobid,
2147 username, document, text_status, job->pagesprinted,
2151 /****************************************************************************
2152 ****************************************************************************/
2154 static void display_job_info_2(JOB_INFO_2 *job)
2156 fstring username = "", document = "", text_status = "";
2158 rpcstr_pull(username, job->username.buffer,
2159 sizeof(username), -1, STR_TERMINATE);
2161 rpcstr_pull(document, job->document.buffer,
2162 sizeof(document), -1, STR_TERMINATE);
2164 rpcstr_pull(text_status, job->text_status.buffer,
2165 sizeof(text_status), -1, STR_TERMINATE);
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);
2172 /****************************************************************************
2173 ****************************************************************************/
2175 static WERROR cmd_spoolss_enum_jobs(struct rpc_pipe_client *cli,
2176 TALLOC_CTX *mem_ctx, int argc,
2180 uint32 level = 1, num_jobs, i;
2181 bool got_hnd = False;
2182 const char *printername;
2186 if (argc < 2 || argc > 3) {
2187 printf("Usage: %s printername [level]\n", argv[0]);
2192 level = atoi(argv[2]);
2194 /* Open printer handle */
2196 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2198 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2200 SEC_FLAG_MAXIMUM_ALLOWED,
2202 if (!W_ERROR_IS_OK(result))
2207 /* Enumerate ports */
2209 result = rpccli_spoolss_enumjobs(cli, mem_ctx, &hnd, level, 0, 1000,
2212 if (!W_ERROR_IS_OK(result))
2215 for (i = 0; i < num_jobs; i++) {
2218 display_job_info_1(&ctr.job.job_info_1[i]);
2221 display_job_info_2(&ctr.job.job_info_2[i]);
2224 d_printf("unknown info level %d\n", level);
2231 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2236 /****************************************************************************
2237 ****************************************************************************/
2239 static WERROR cmd_spoolss_enum_data( struct rpc_pipe_client *cli,
2240 TALLOC_CTX *mem_ctx, int argc,
2244 uint32 i=0, val_needed, data_needed;
2245 bool got_hnd = False;
2246 const char *printername;
2250 printf("Usage: %s printername\n", argv[0]);
2254 /* Open printer handle */
2256 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2258 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2260 SEC_FLAG_MAXIMUM_ALLOWED,
2262 if (!W_ERROR_IS_OK(result))
2267 /* Enumerate data */
2269 result = rpccli_spoolss_enumprinterdata(cli, mem_ctx, &hnd, i, 0, 0,
2270 &val_needed, &data_needed,
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);
2280 if (W_ERROR_V(result) == ERRnomoreitems)
2281 result = W_ERROR(ERRsuccess);
2285 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2290 /****************************************************************************
2291 ****************************************************************************/
2293 static WERROR cmd_spoolss_enum_data_ex( struct rpc_pipe_client *cli,
2294 TALLOC_CTX *mem_ctx, int argc,
2299 bool got_hnd = False;
2300 const char *printername;
2301 const char *keyname = NULL;
2303 REGVAL_CTR *ctr = NULL;
2306 printf("Usage: %s printername <keyname>\n", argv[0]);
2312 /* Open printer handle */
2314 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2316 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2318 SEC_FLAG_MAXIMUM_ALLOWED,
2320 if (!W_ERROR_IS_OK(result))
2325 /* Enumerate subkeys */
2327 if ( !(ctr = TALLOC_ZERO_P( mem_ctx, REGVAL_CTR )) )
2330 result = rpccli_spoolss_enumprinterdataex(cli, mem_ctx, &hnd, keyname, ctr);
2332 if (!W_ERROR_IS_OK(result))
2335 for (i=0; i < ctr->num_values; i++) {
2336 display_reg_value(*(ctr->values[i]));
2343 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2348 /****************************************************************************
2349 ****************************************************************************/
2351 static WERROR cmd_spoolss_enum_printerkey( struct rpc_pipe_client *cli,
2352 TALLOC_CTX *mem_ctx, int argc,
2356 bool got_hnd = False;
2357 const char *printername;
2358 const char *keyname = NULL;
2360 uint16 *keylist = NULL, *curkey;
2362 if (argc < 2 || argc > 3) {
2363 printf("Usage: %s printername [keyname]\n", argv[0]);
2372 /* Open printer handle */
2374 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2376 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2378 SEC_FLAG_MAXIMUM_ALLOWED,
2380 if (!W_ERROR_IS_OK(result))
2385 /* Enumerate subkeys */
2387 result = rpccli_spoolss_enumprinterkey(cli, mem_ctx, &hnd, keyname, &keylist, NULL);
2389 if (!W_ERROR_IS_OK(result))
2393 while (*curkey != 0) {
2394 char *subkey = NULL;
2395 rpcstr_pull_talloc(mem_ctx, &subkey, curkey, -1,
2400 printf("%s\n", subkey);
2401 curkey += strlen(subkey) + 1;
2409 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2414 /****************************************************************************
2415 ****************************************************************************/
2417 static WERROR cmd_spoolss_rffpcnex(struct rpc_pipe_client *cli,
2418 TALLOC_CTX *mem_ctx, int argc,
2421 const char *printername;
2423 bool got_hnd = False;
2425 SPOOL_NOTIFY_OPTION option;
2428 printf("Usage: %s printername\n", argv[0]);
2435 RPCCLIENT_PRINTERNAME(printername, cli, argv[1]);
2437 result = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2439 SEC_FLAG_MAXIMUM_ALLOWED,
2441 if (!W_ERROR_IS_OK(result)) {
2442 printf("Error opening %s\n", argv[1]);
2448 /* Create spool options */
2450 ZERO_STRUCT(option);
2453 option.option_type_ptr = 1;
2454 option.count = option.ctr.count = 2;
2456 option.ctr.type = TALLOC_ARRAY(mem_ctx, SPOOL_NOTIFY_OPTION_TYPE, 2);
2457 if (option.ctr.type == NULL) {
2458 result = WERR_NOMEM;
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;
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;
2476 result = rpccli_spoolss_rffpcnex(
2477 cli, mem_ctx, &hnd, 0, 0, cli->srv_name_slash, 123, &option);
2479 if (!W_ERROR_IS_OK(result)) {
2480 printf("Error rffpcnex %s\n", argv[1]);
2486 rpccli_spoolss_ClosePrinter(cli, mem_ctx, &hnd, NULL);
2491 /****************************************************************************
2492 ****************************************************************************/
2494 static bool compare_printer( struct rpc_pipe_client *cli1, POLICY_HND *hnd1,
2495 struct rpc_pipe_client *cli2, POLICY_HND *hnd2 )
2497 PRINTER_INFO_CTR ctr1, ctr2;
2499 TALLOC_CTX *mem_ctx = talloc_init("compare_printer");
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);
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);
2519 talloc_destroy(mem_ctx);
2524 /****************************************************************************
2525 ****************************************************************************/
2527 static bool compare_printer_secdesc( struct rpc_pipe_client *cli1, POLICY_HND *hnd1,
2528 struct rpc_pipe_client *cli2, POLICY_HND *hnd2 )
2530 PRINTER_INFO_CTR ctr1, ctr2;
2532 TALLOC_CTX *mem_ctx = talloc_init("compare_printer_secdesc");
2533 SEC_DESC *sd1, *sd2;
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));
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));
2558 if ( (ctr1.printers_3 != ctr2.printers_3) && (!ctr1.printers_3 || !ctr2.printers_3) ) {
2559 printf("NULL PRINTER_INFO_3!\n");
2564 sd1 = ctr1.printers_3->secdesc;
2565 sd2 = ctr2.printers_3->secdesc;
2567 if ( (sd1 != sd2) && ( !sd1 || !sd2 ) ) {
2568 printf("NULL secdesc!\n");
2573 if (!sec_desc_equal( sd1, sd2 ) ) {
2574 printf("Security Descriptors *not* equal!\n");
2579 printf("Security descriptors match\n");
2582 talloc_destroy(mem_ctx);
2587 /****************************************************************************
2588 ****************************************************************************/
2590 extern struct user_auth_info *rpcclient_auth_info;
2592 static WERROR cmd_spoolss_printercmp(struct rpc_pipe_client *cli,
2593 TALLOC_CTX *mem_ctx, int argc,
2596 const char *printername = argv[1];
2597 char *printername_path = NULL;
2598 struct cli_state *cli_server2 = NULL;
2599 struct rpc_pipe_client *cli2 = NULL;
2600 POLICY_HND hPrinter1, hPrinter2;
2605 printf("Usage: %s <printer> <server>\n", argv[0]);
2609 /* first get the connection to the remote server */
2611 nt_status = cli_full_connection(&cli_server2, global_myname(), argv[2],
2614 get_cmdline_auth_info_username(rpcclient_auth_info),
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);
2620 if ( !NT_STATUS_IS_OK(nt_status) )
2621 return WERR_GENERAL_FAILURE;
2623 nt_status = cli_rpc_pipe_open_noauth(cli_server2, &syntax_spoolss,
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;
2631 /* now open up both printers */
2633 RPCCLIENT_PRINTERNAME(printername_path, cli, printername);
2635 printf("Opening %s...", printername_path);
2637 werror = rpccli_spoolss_openprinter_ex(cli, mem_ctx,
2641 if ( !W_ERROR_IS_OK(werror) ) {
2642 printf("failed (%s)\n", win_errstr(werror));
2647 RPCCLIENT_PRINTERNAME(printername_path, cli2, printername);
2649 printf("Opening %s...", printername_path);
2650 werror = rpccli_spoolss_openprinter_ex(cli2, mem_ctx,
2654 if ( !W_ERROR_IS_OK(werror) ) {
2655 printf("failed (%s)\n", win_errstr(werror));
2660 compare_printer( cli, &hPrinter1, cli2, &hPrinter2 );
2661 compare_printer_secdesc( cli, &hPrinter1, cli2, &hPrinter2 );
2663 compare_printerdata( cli_server1, &hPrinter1, cli_server2, &hPrinter2 );
2670 printf("Closing printers...");
2671 rpccli_spoolss_ClosePrinter( cli, mem_ctx, &hPrinter1, NULL );
2672 rpccli_spoolss_ClosePrinter( cli2, mem_ctx, &hPrinter2, NULL );
2675 /* close the second remote connection */
2677 cli_shutdown( cli_server2 );
2681 /* List of commands exported by this module */
2682 struct cmd_set spoolss_commands[] = {
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", "" },