4 * "cupsaddsmb" command for the Common UNIX Printing System (CUPS).
6 * Copyright 2001-2005 by Easy Software Products.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * main() - Export printers on the command-line.
27 * convert_ppd() - Convert a PPD file to a form usable by any of the
28 * Windows PostScript printer drivers.
29 * do_samba_command() - Do a SAMBA command, asking for a password as needed.
30 * export_dest() - Export a destination to SAMBA.
31 * ppd_gets() - Get a CR and/or LF-terminated line.
32 * usage() - Show program usage and exit...
33 * write_option() - Write a CUPS option to a PPD file.
37 * Include necessary headers...
42 #include <cups/cups.h>
43 #include <cups/language.h>
44 #include <cups/string.h>
53 const char *SAMBAUser
,
61 int convert_ppd(const char *src
, char *dst
, int dstsize
, ipp_t
*info
);
62 int do_samba_command(const char *command
, const char *subcommand
);
63 int export_dest(const char *dest
);
64 char *ppd_gets(FILE *fp
, char *buf
, int buflen
);
66 int write_option(FILE *dstfp
, int order
, const char *name
,
67 const char *text
, const char *attrname
,
68 ipp_attribute_t
*suppattr
, ipp_attribute_t
*defattr
,
69 int defval
, int valcount
);
73 * 'main()' - Export printers on the command-line.
76 int /* O - Exit status */
77 main(int argc
, /* I - Number of command-line arguments */
78 char *argv
[]) /* I - Command-line arguments */
80 int i
, j
; /* Looping vars */
81 int status
; /* Status from export_dest() */
82 int export_all
; /* Export all printers? */
83 int num_printers
; /* Number of printers */
84 char **printers
; /* Printers */
88 * Parse command-line arguments...
93 SAMBAUser
= cupsUser();
96 for (i
= 1; i
< argc
; i
++)
97 if (strcmp(argv
[i
], "-a") == 0)
99 else if (strcmp(argv
[i
], "-U") == 0)
107 else if (strcmp(argv
[i
], "-H") == 0)
113 SAMBAServer
= argv
[i
];
115 else if (strcmp(argv
[i
], "-h") == 0)
121 cupsSetServer(argv
[i
]);
123 else if (strcmp(argv
[i
], "-v") == 0)
125 else if (argv
[i
][0] != '-')
127 if (SAMBAServer
== NULL
)
128 SAMBAServer
= cupsServer();
130 if ((status
= export_dest(argv
[i
])) != 0)
137 * See if the user specified "-a"...
143 * Export all printers...
146 if (SAMBAServer
== NULL
)
147 SAMBAServer
= cupsServer();
149 num_printers
= cupsGetPrinters(&printers
);
151 for (j
= 0, status
= 0; j
< num_printers
; j
++)
152 if ((status
= export_dest(printers
[j
])) != 0)
155 for (j
= 0; j
< num_printers
; j
++)
170 * 'convert_ppd()' - Convert a PPD file to a form usable by any of the
171 * Windows PostScript printer drivers.
174 int /* O - 0 on success, 1 on failure */
175 convert_ppd(const char *src
, /* I - Source (original) PPD */
176 char *dst
, /* O - Destination PPD */
177 int dstsize
, /* I - Size of destination buffer */
178 ipp_t
*info
) /* I - Printer attributes */
180 FILE *srcfp
, /* Source file */
181 *dstfp
; /* Destination file */
182 int dstfd
; /* Destination file descriptor */
183 ipp_attribute_t
*suppattr
, /* IPP -supported attribute */
184 *defattr
; /* IPP -default attribute */
185 char line
[256], /* Line from PPD file */
186 junk
[256], /* Extra junk to throw away */
187 *ptr
, /* Pointer into line */
188 option
[41], /* Option */
189 choice
[41]; /* Choice */
190 int jcloption
, /* In a JCL option? */
191 linenum
; /* Current line number */
192 time_t curtime
; /* Current time */
193 struct tm
*curdate
; /* Current date */
197 * Open the original PPD file...
200 if ((srcfp
= fopen(src
, "rb")) == NULL
)
204 * Create a temporary output file using the destination buffer...
207 if ((dstfd
= cupsTempFd(dst
, dstsize
)) < 0)
214 if ((dstfp
= fdopen(dstfd
, "w")) == NULL
)
217 * Unable to convert to FILE *...
228 * Write a new header explaining that this isn't the original PPD...
231 fputs("*PPD-Adobe: \"4.3\"\n", dstfp
);
233 curtime
= time(NULL
);
234 curdate
= gmtime(&curtime
);
236 fprintf(dstfp
, "*%% Modified on %04d%02d%02d%02d%02d%02d+0000 by cupsaddsmb\n",
237 curdate
->tm_year
+ 1900, curdate
->tm_mon
+ 1, curdate
->tm_mday
,
238 curdate
->tm_hour
, curdate
->tm_min
, curdate
->tm_sec
);
241 * Read the existing PPD file, converting all PJL commands to CUPS
242 * job ticket comments...
248 while (ppd_gets(srcfp
, line
, sizeof(line
)) != NULL
)
252 if (!strncmp(line
, "*PPD-Adobe:", 11))
255 * Already wrote the PPD header...
260 else if (!strncmp(line
, "*JCLBegin:", 10) ||
261 !strncmp(line
, "*JCLToPSInterpreter:", 20) ||
262 !strncmp(line
, "*JCLEnd:", 8) ||
263 !strncmp(line
, "*Protocols:", 11))
266 * Don't use existing JCL keywords; we'll create our own, below...
269 fprintf(dstfp
, "*%% Commented out by cupsaddsmb...\n*%%%s", line
+ 1);
272 else if (!strncmp(line
, "*JCLOpenUI", 10))
277 else if (!strncmp(line
, "*JCLCloseUI", 11))
282 else if (jcloption
&&
283 strncmp(line
, "*End", 4) &&
284 strncmp(line
, "*Default", 8) &&
285 strncmp(line
, "*OrderDependency", 16))
287 if ((ptr
= strchr(line
, ':')) == NULL
)
289 fprintf(stderr
, "cupsaddsmb: Missing value on line %d!\n", linenum
);
297 if ((ptr
= strchr(ptr
, '\"')) == NULL
)
299 fprintf(stderr
, "cupsaddsmb: Missing double quote on line %d!\n",
308 if (sscanf(line
, "*%40s%*[ \t]%40[^/]", option
, choice
) != 2)
310 fprintf(stderr
, "cupsaddsmb: Bad option + choice on line %d!\n",
319 if (strchr(ptr
+ 1, '\"') == NULL
)
325 while (ppd_gets(srcfp
, junk
, sizeof(junk
)) != NULL
)
329 if (!strncmp(junk
, "*End", 4))
334 snprintf(ptr
+ 1, sizeof(line
) - (ptr
- line
+ 1),
335 "%%cupsJobTicket: %s=%s\n\"\n*End\n", option
, choice
);
337 fprintf(dstfp
, "*%% Changed by cupsaddsmb...\n%s", line
);
346 * Now add the CUPS-specific attributes and options...
349 fputs("\n*% CUPS Job Ticket support and options...\n", dstfp
);
350 fputs("*Protocols: PJL\n", dstfp
);
351 fputs("*JCLBegin: \"%!PS-Adobe-3.0<0A>\"\n", dstfp
);
352 fputs("*JCLToPSInterpreter: \"\"\n", dstfp
);
353 fputs("*JCLEnd: \"\"\n", dstfp
);
355 fputs("\n*OpenGroup: CUPS/CUPS Options\n\n", dstfp
);
357 if ((defattr
= ippFindAttribute(info
, "job-hold-until-default",
358 IPP_TAG_ZERO
)) != NULL
&&
359 (suppattr
= ippFindAttribute(info
, "job-hold-until-supported",
360 IPP_TAG_ZERO
)) != NULL
)
361 write_option(dstfp
, 10, "cupsJobHoldUntil", "Hold Until", "job-hold-until",
362 suppattr
, defattr
, 0, 1);
364 if ((defattr
= ippFindAttribute(info
, "job-priority-default",
365 IPP_TAG_INTEGER
)) != NULL
&&
366 (suppattr
= ippFindAttribute(info
, "job-priority-supported",
367 IPP_TAG_RANGE
)) != NULL
)
368 write_option(dstfp
, 11, "cupsJobPriority", "Priority", "job-priority",
369 suppattr
, defattr
, 0, 1);
371 if ((defattr
= ippFindAttribute(info
, "job-sheets-default",
372 IPP_TAG_ZERO
)) != NULL
&&
373 (suppattr
= ippFindAttribute(info
, "job-sheets-supported",
374 IPP_TAG_ZERO
)) != NULL
)
376 write_option(dstfp
, 20, "cupsJobSheetsStart", "Start Banner",
377 "job-sheets", suppattr
, defattr
, 0, 2);
378 write_option(dstfp
, 21, "cupsJobSheetsEnd", "End Banner",
379 "job-sheets", suppattr
, defattr
, 1, 2);
382 fputs("*CloseGroup: CUPS\n", dstfp
);
392 * 'do_samba_command()' - Do a SAMBA command, asking for
393 * a password as needed.
396 int /* O - Status of command */
397 do_samba_command(const char *command
, /* I - Command to run */
398 const char *subcmd
) /* I - Sub-command */
400 int status
; /* Status of command */
401 char temp
[4096]; /* Command/prompt string */
402 static const char *p
= NULL
; /* Password data */
409 snprintf(temp
, sizeof(temp
),
410 "Password for %s required to access %s via SAMBA: ",
411 SAMBAUser
, SAMBAServer
);
413 if ((p
= cupsGetPassword(temp
)) == NULL
)
417 snprintf(temp
, sizeof(temp
), "%s -N -U\'%s%%%s\' -c \'%s\'",
418 command
, SAMBAUser
, p
, subcmd
);
421 printf("Running command: %s\n", temp
);
423 strlcat(temp
, " </dev/null >/dev/null 2>/dev/null", sizeof(temp
));
425 if ((status
= system(temp
)) != 0)
449 * 'export_dest()' - Export a destination to SAMBA.
452 int /* O - 0 on success, non-zero on error */
453 export_dest(const char *dest
) /* I - Destination to export */
455 int status
; /* Status of smbclient/rpcclient commands */
456 const char *ppdfile
; /* PPD file for printer drivers */
457 char newppd
[1024], /* New PPD file for printer drivers */
458 file
[1024], /* File to test for */
459 command
[1024], /* Command to run */
460 subcmd
[1024]; /* Sub-command */
461 const char *datadir
; /* CUPS_DATADIR */
462 http_t
*http
; /* Connection to server */
463 cups_lang_t
*language
; /* Default language */
464 ipp_t
*request
, /* IPP request */
465 *response
; /* IPP response */
466 static const char *pattrs
[] = /* Printer attributes we want */
468 "job-hold-until-supported",
469 "job-hold-until-default",
470 "job-sheets-supported",
471 "job-sheets-default",
472 "job-priority-supported",
473 "job-priority-default"
478 * Get the location of the printer driver files...
481 if ((datadir
= getenv("CUPS_DATADIR")) == NULL
)
482 datadir
= CUPS_DATADIR
;
485 * Open a connection to the scheduler...
488 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL
)
490 fprintf(stderr
, "cupsaddsmb: Unable to connect to server \"%s\" for %s - %s\n",
491 cupsServer(), dest
, strerror(errno
));
496 * Get the PPD file...
499 if ((ppdfile
= cupsGetPPD2(http
, dest
)) == NULL
)
501 fprintf(stderr
, "cupsaddsmb: No PPD file for printer \"%s\" - skipping!\n",
508 * Append the supported banner pages to the PPD file...
512 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
513 request
->request
.op
.request_id
= 1;
515 language
= cupsLangDefault();
517 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
518 "attributes-charset", NULL
, cupsLangEncoding(language
));
520 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
521 "attributes-natural-language", NULL
, language
->language
);
523 snprintf(command
, sizeof(command
), "ipp://localhost/printers/%s", dest
);
524 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
525 "printer-uri", NULL
, command
);
527 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
528 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
532 * Do the request and get back a response...
535 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
537 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
539 fprintf(stderr
, "cupsaddsmb: get-printer-attributes failed for \"%s\": %s\n",
540 dest
, ippErrorString(response
->request
.status
.status_code
));
542 cupsLangFree(language
);
550 fprintf(stderr
, "cupsaddsmb: get-printer-attributes failed for \"%s\": %s\n",
551 dest
, ippErrorString(cupsLastError()));
552 cupsLangFree(language
);
559 * Convert the PPD file to the Windows driver format...
562 if (convert_ppd(ppdfile
, newppd
, sizeof(newppd
), response
))
564 fprintf(stderr
, "cupsaddsmb: Unable to convert PPD file for %s - %s\n",
565 dest
, strerror(errno
));
567 cupsLangFree(language
);
574 cupsLangFree(language
);
578 * Remove the old PPD and point to the new one...
586 * See which drivers are available; the new CUPS v6 and Adobe drivers
587 * depend on the Windows 2k PS driver, so copy that driver first:
597 snprintf(file
, sizeof(file
), "%s/drivers/pscript5.dll", datadir
);
598 if (!access(file
, 0))
601 * Windows 2k driver is installed; do the smbclient commands needed
602 * to copy the Win2k drivers over...
605 snprintf(command
, sizeof(command
), "smbclient //%s/print\\$", SAMBAServer
);
607 snprintf(subcmd
, sizeof(subcmd
),
609 "put %s W32X86/%s.ppd;"
610 "put %s/drivers/ps5ui.dll W32X86/ps5ui.dll;"
611 "put %s/drivers/pscript.hlp W32X86/pscript.hlp;"
612 "put %s/drivers/pscript.ntf W32X86/pscript.ntf;"
613 "put %s/drivers/pscript5.dll W32X86/pscript5.dll",
614 ppdfile
, dest
, datadir
, datadir
, datadir
, datadir
);
616 if ((status
= do_samba_command(command
, subcmd
)) != 0)
618 fprintf(stderr
, "cupsaddsmb: Unable to copy Windows 2000 printer driver files (%d)!\n",
625 * See if we also have the CUPS driver files; if so, use them!
628 snprintf(file
, sizeof(file
), "%s/drivers/cupsdrv6.dll", datadir
);
629 if (!access(file
, 0))
632 * Copy the CUPS driver files over...
635 snprintf(subcmd
, sizeof(subcmd
),
636 "put %s/drivers/cupsdrv6.dll W32X86/cupsdrv6.dll;"
637 "put %s/drivers/cupsui6.dll W32X86/cupsui6.dll",
640 if ((status
= do_samba_command(command
, subcmd
)) != 0)
642 fprintf(stderr
, "cupsaddsmb: Unable to copy CUPS printer driver files (%d)!\n",
649 * Do the rpcclient command needed for the CUPS drivers...
652 snprintf(subcmd
, sizeof(subcmd
),
653 "adddriver \"Windows NT x86\" \"%s:"
654 "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
655 "cupsdrv6.dll,cupsui6.dll,pscript.ntf\"",
661 * Don't have the CUPS drivers, so just use the standard Windows
665 snprintf(subcmd
, sizeof(subcmd
),
666 "adddriver \"Windows NT x86\" \"%s:"
667 "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
672 snprintf(command
, sizeof(command
), "rpcclient %s", SAMBAServer
);
674 if ((status
= do_samba_command(command
, subcmd
)) != 0)
676 fprintf(stderr
, "cupsaddsmb: Unable to install Windows 2000 printer driver files (%d)!\n",
683 snprintf(file
, sizeof(file
), "%s/drivers/ADOBEPS4.DRV", datadir
);
684 if (!access(file
, 0))
687 * Do the smbclient commands needed for the Adobe Win9x drivers...
690 snprintf(command
, sizeof(command
), "smbclient //%s/print\\$", SAMBAServer
);
692 snprintf(subcmd
, sizeof(subcmd
),
694 "put %s WIN40/%s.PPD;"
695 "put %s/drivers/ADFONTS.MFM WIN40/ADFONTS.MFM;"
696 "put %s/drivers/ADOBEPS4.DRV WIN40/ADOBEPS4.DRV;"
697 "put %s/drivers/ADOBEPS4.HLP WIN40/ADOBEPS4.HLP;"
698 "put %s/drivers/ICONLIB.DLL WIN40/ICONLIB.DLL;"
699 "put %s/drivers/PSMON.DLL WIN40/PSMON.DLL;",
700 ppdfile
, dest
, datadir
, datadir
, datadir
, datadir
, datadir
);
702 if ((status
= do_samba_command(command
, subcmd
)) != 0)
704 fprintf(stderr
, "cupsaddsmb: Unable to copy Windows 9x printer driver files (%d)!\n",
711 * Do the rpcclient commands needed for the Adobe Win9x drivers...
714 snprintf(command
, sizeof(command
), "rpcclient %s", SAMBAServer
);
716 snprintf(subcmd
, sizeof(subcmd
),
717 "adddriver \"Windows 4.0\" \"%s:ADOBEPS4.DRV:%s.PPD:NULL:"
718 "ADOBEPS4.HLP:PSMON.DLL:RAW:"
719 "ADOBEPS4.DRV,%s.PPD,ADOBEPS4.HLP,PSMON.DLL,ADFONTS.MFM,"
723 if ((status
= do_samba_command(command
, subcmd
)) != 0)
725 fprintf(stderr
, "cupsaddsmb: Unable to install Windows 9x printer driver files (%d)!\n",
735 * Finally, associate the drivers we just added with the queue...
738 snprintf(command
, sizeof(command
), "rpcclient %s", SAMBAServer
);
740 snprintf(subcmd
, sizeof(subcmd
), "setdriver %s %s", dest
, dest
);
742 if ((status
= do_samba_command(command
, subcmd
)) != 0)
744 fprintf(stderr
, "cupsaddsmb: Unable to set Windows printer driver (%d)!\n",
754 * 'ppd_gets()' - Get a CR and/or LF-terminated line.
757 char * /* O - Line read or NULL on eof/error */
758 ppd_gets(FILE *fp
, /* I - File to read from*/
759 char *buf
, /* O - String buffer */
760 int buflen
) /* I - Size of string buffer */
762 int ch
; /* Character from file */
763 char *ptr
, /* Current position in line buffer */
764 *end
; /* End of line buffer */
768 * Range check input...
771 if (!fp
|| !buf
|| buflen
< 2 || feof(fp
))
775 * Now loop until we have a valid line...
778 for (ptr
= buf
, end
= buf
+ buflen
- 1; ptr
< end
;)
780 if ((ch
= getc(fp
)) == EOF
)
796 if ((ch
= getc(fp
)) != '\n')
806 * Line feed ends a line...
820 * 'usage()' - Show program usage and exit...
826 puts("Usage: cupsaddsmb [options] printer1 ... printerN");
827 puts(" cupsaddsmb [options] -a");
830 puts(" -H samba-server Use the named SAMBA server");
831 puts(" -U samba-user Authenticate using the named SAMBA user");
832 puts(" -a Export all printers");
833 puts(" -h cups-server Use the named CUPS server");
834 puts(" -v Be verbose (show commands)");
840 * 'write_option()' - Write a CUPS option to a PPD file.
843 int /* O - 0 on success, 1 on failure */
844 write_option(FILE *dstfp
, /* I - PPD file */
845 int order
, /* I - Order dependency */
846 const char *name
, /* I - Option name */
847 const char *text
, /* I - Option text */
848 const char *attrname
, /* I - Attribute name */
849 ipp_attribute_t
*suppattr
, /* I - IPP -supported attribute */
850 ipp_attribute_t
*defattr
, /* I - IPP -default attribute */
851 int defval
, /* I - Default value number */
852 int valcount
) /* I - Number of values */
854 int i
; /* Looping var */
857 if (!dstfp
|| !name
|| !text
|| !suppattr
|| !defattr
)
860 fprintf(dstfp
, "*JCLOpenUI *%s/%s: PickOne\n"
861 "*OrderDependency: %d JCLSetup *%s\n",
862 name
, text
, order
, name
);
864 if (defattr
->value_tag
== IPP_TAG_INTEGER
)
867 * Do numeric options with a range or list...
870 fprintf(dstfp
, "*Default%s: %d\n", name
, defattr
->values
[defval
].integer
);
872 if (suppattr
->value_tag
== IPP_TAG_RANGE
)
875 * List each number in the range...
878 for (i
= suppattr
->values
[0].range
.lower
;
879 i
<= suppattr
->values
[0].range
.upper
;
882 fprintf(dstfp
, "*%s %d: \"", name
, i
);
885 fprintf(dstfp
, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname
, i
);
886 else if (defval
== 0)
887 fprintf(dstfp
, "%%cupsJobTicket: %s=%d\"\n", attrname
, i
);
888 else if (defval
< (valcount
- 1))
889 fprintf(dstfp
, ",%d\"\n", i
);
891 fprintf(dstfp
, ",%d\n\"\n*End\n", i
);
897 * List explicit numbers...
900 for (i
= 0; i
< suppattr
->num_values
; i
++)
902 fprintf(dstfp
, "*%s %d: \"", name
, suppattr
->values
[i
].integer
);
905 fprintf(dstfp
, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname
,
906 suppattr
->values
[i
].integer
);
907 else if (defval
== 0)
908 fprintf(dstfp
, "%%cupsJobTicket: %s=%d\"\n", attrname
,
909 suppattr
->values
[i
].integer
);
910 else if (defval
< (valcount
- 1))
911 fprintf(dstfp
, ",%d\"\n", suppattr
->values
[i
].integer
);
913 fprintf(dstfp
, ",%d\n\"\n*End\n", suppattr
->values
[i
].integer
);
920 * Do text options with a list...
923 fprintf(dstfp
, "*Default%s: %s\n", name
,
924 defattr
->values
[defval
].string
.text
);
926 for (i
= 0; i
< suppattr
->num_values
; i
++)
928 fprintf(dstfp
, "*%s %s: \"", name
, suppattr
->values
[i
].string
.text
);
931 fprintf(dstfp
, "%%cupsJobTicket: %s=%s\n\"\n*End\n", attrname
,
932 suppattr
->values
[i
].string
.text
);
933 else if (defval
== 0)
934 fprintf(dstfp
, "%%cupsJobTicket: %s=%s\"\n", attrname
,
935 suppattr
->values
[i
].string
.text
);
936 else if (defval
< (valcount
- 1))
937 fprintf(dstfp
, ",%s\"\n", suppattr
->values
[i
].string
.text
);
939 fprintf(dstfp
, ",%s\n\"\n*End\n", suppattr
->values
[i
].string
.text
);
943 fprintf(dstfp
, "*JCLCloseUI: *%s\n\n", name
);