#include <stdio.h>
#include <stdlib.h>
#include <cups/cups.h>
#include <cups/language.h>
#include <cups/string.h>
#include <errno.h>
int Verbosity = 0;
const char *SAMBAUser,
*SAMBAServer;
int convert_ppd(const char *src, char *dst, int dstsize, ipp_t *info);
int do_samba_command(const char *command, const char *subcommand);
int export_dest(const char *dest);
char *ppd_gets(FILE *fp, char *buf, int buflen);
void usage(void);
int write_option(FILE *dstfp, int order, const char *name,
const char *text, const char *attrname,
ipp_attribute_t *suppattr, ipp_attribute_t *defattr,
int defval, int valcount);
int
main(int argc,
char *argv[])
{
int i, j;
int status;
int export_all;
int num_printers;
char **printers;
export_all = 0;
SAMBAUser = cupsUser();
SAMBAServer = NULL;
for (i = 1; i < argc; i ++)
if (strcmp(argv[i], "-a") == 0)
export_all = 1;
else if (strcmp(argv[i], "-U") == 0)
{
i ++;
if (i >= argc)
usage();
SAMBAUser = argv[i];
}
else if (strcmp(argv[i], "-H") == 0)
{
i ++;
if (i >= argc)
usage();
SAMBAServer = argv[i];
}
else if (strcmp(argv[i], "-h") == 0)
{
i ++;
if (i >= argc)
usage();
cupsSetServer(argv[i]);
}
else if (strcmp(argv[i], "-v") == 0)
Verbosity = 1;
else if (argv[i][0] != '-')
{
if (SAMBAServer == NULL)
SAMBAServer = cupsServer();
if ((status = export_dest(argv[i])) != 0)
return (status);
}
else
usage();
if (export_all)
{
if (SAMBAServer == NULL)
SAMBAServer = cupsServer();
num_printers = cupsGetPrinters(&printers);
for (j = 0, status = 0; j < num_printers; j ++)
if ((status = export_dest(printers[j])) != 0)
break;
for (j = 0; j < num_printers; j ++)
free(printers[j]);
if (num_printers)
free(printers);
if (status)
return (status);
}
return (0);
}
int
convert_ppd(const char *src,
char *dst,
int dstsize,
ipp_t *info)
{
FILE *srcfp,
*dstfp;
int dstfd;
ipp_attribute_t *suppattr,
*defattr;
char line[256],
junk[256],
*ptr,
option[41],
choice[41];
int jcloption,
linenum;
time_t curtime;
struct tm *curdate;
if ((srcfp = fopen(src, "rb")) == NULL)
return (1);
if ((dstfd = cupsTempFd(dst, dstsize)) < 0)
{
fclose(srcfp);
return (1);
}
if ((dstfp = fdopen(dstfd, "w")) == NULL)
{
close(dstfd);
fclose(srcfp);
return (1);
}
fputs("*PPD-Adobe: \"4.3\"\n", dstfp);
curtime = time(NULL);
curdate = gmtime(&curtime);
fprintf(dstfp, "*%% Modified on %04d%02d%02d%02d%02d%02d+0000 by cupsaddsmb\n",
curdate->tm_year + 1900, curdate->tm_mon + 1, curdate->tm_mday,
curdate->tm_hour, curdate->tm_min, curdate->tm_sec);
jcloption = 0;
linenum = 0;
while (ppd_gets(srcfp, line, sizeof(line)) != NULL)
{
linenum ++;
if (!strncmp(line, "*PPD-Adobe:", 11))
{
continue;
}
else if (!strncmp(line, "*JCLBegin:", 10) ||
!strncmp(line, "*JCLToPSInterpreter:", 20) ||
!strncmp(line, "*JCLEnd:", 8) ||
!strncmp(line, "*Protocols:", 11))
{
fprintf(dstfp, "*%% Commented out by cupsaddsmb...\n*%%%s", line + 1);
continue;
}
else if (!strncmp(line, "*JCLOpenUI", 10))
{
jcloption = 1;
fputs(line, dstfp);
}
else if (!strncmp(line, "*JCLCloseUI", 11))
{
jcloption = 0;
fputs(line, dstfp);
}
else if (jcloption &&
strncmp(line, "*End", 4) &&
strncmp(line, "*Default", 8) &&
strncmp(line, "*OrderDependency", 16))
{
if ((ptr = strchr(line, ':')) == NULL)
{
fprintf(stderr, "cupsaddsmb: Missing value on line %d!\n", linenum);
fclose(srcfp);
fclose(dstfp);
close(dstfd);
unlink(dst);
return (1);
}
if ((ptr = strchr(ptr, '\"')) == NULL)
{
fprintf(stderr, "cupsaddsmb: Missing double quote on line %d!\n",
linenum);
fclose(srcfp);
fclose(dstfp);
close(dstfd);
unlink(dst);
return (1);
}
if (sscanf(line, "*%40s%*[ \t]%40[^/]", option, choice) != 2)
{
fprintf(stderr, "cupsaddsmb: Bad option + choice on line %d!\n",
linenum);
fclose(srcfp);
fclose(dstfp);
close(dstfd);
unlink(dst);
return (1);
}
if (strchr(ptr + 1, '\"') == NULL)
{
while (ppd_gets(srcfp, junk, sizeof(junk)) != NULL)
{
linenum ++;
if (!strncmp(junk, "*End", 4))
break;
}
}
snprintf(ptr + 1, sizeof(line) - (ptr - line + 1),
"%%cupsJobTicket: %s=%s\n\"\n*End\n", option, choice);
fprintf(dstfp, "*%% Changed by cupsaddsmb...\n%s", line);
}
else
fputs(line, dstfp);
}
fclose(srcfp);
fputs("\n*% CUPS Job Ticket support and options...\n", dstfp);
fputs("*Protocols: PJL\n", dstfp);
fputs("*JCLBegin: \"%!PS-Adobe-3.0<0A>\"\n", dstfp);
fputs("*JCLToPSInterpreter: \"\"\n", dstfp);
fputs("*JCLEnd: \"\"\n", dstfp);
fputs("\n*OpenGroup: CUPS/CUPS Options\n\n", dstfp);
if ((defattr = ippFindAttribute(info, "job-hold-until-default",
IPP_TAG_ZERO)) != NULL &&
(suppattr = ippFindAttribute(info, "job-hold-until-supported",
IPP_TAG_ZERO)) != NULL)
write_option(dstfp, 10, "cupsJobHoldUntil", "Hold Until", "job-hold-until",
suppattr, defattr, 0, 1);
if ((defattr = ippFindAttribute(info, "job-priority-default",
IPP_TAG_INTEGER)) != NULL &&
(suppattr = ippFindAttribute(info, "job-priority-supported",
IPP_TAG_RANGE)) != NULL)
write_option(dstfp, 11, "cupsJobPriority", "Priority", "job-priority",
suppattr, defattr, 0, 1);
if ((defattr = ippFindAttribute(info, "job-sheets-default",
IPP_TAG_ZERO)) != NULL &&
(suppattr = ippFindAttribute(info, "job-sheets-supported",
IPP_TAG_ZERO)) != NULL)
{
write_option(dstfp, 20, "cupsJobSheetsStart", "Start Banner",
"job-sheets", suppattr, defattr, 0, 2);
write_option(dstfp, 21, "cupsJobSheetsEnd", "End Banner",
"job-sheets", suppattr, defattr, 1, 2);
}
fputs("*CloseGroup: CUPS\n", dstfp);
fclose(dstfp);
close(dstfd);
return (0);
}
int
do_samba_command(const char *command,
const char *subcmd)
{
int status;
char temp[4096];
static const char *p = NULL;
for (status = 1;;)
{
if (p == NULL)
{
snprintf(temp, sizeof(temp),
"Password for %s required to access %s via SAMBA: ",
SAMBAUser, SAMBAServer);
if ((p = cupsGetPassword(temp)) == NULL)
break;
}
snprintf(temp, sizeof(temp), "%s -N -U\'%s%%%s\' -c \'%s\'",
command, SAMBAUser, p, subcmd);
if (Verbosity)
printf("Running command: %s\n", temp);
else
strlcat(temp, " </dev/null >/dev/null 2>/dev/null", sizeof(temp));
if ((status = system(temp)) != 0)
{
if (Verbosity)
puts("");
if (p[0])
p = NULL;
else
break;
}
else
{
if (Verbosity)
puts("");
break;
}
}
return (status);
}
int
export_dest(const char *dest)
{
int status;
const char *ppdfile;
char newppd[1024],
file[1024],
command[1024],
subcmd[1024];
const char *datadir;
http_t *http;
cups_lang_t *language;
ipp_t *request,
*response;
static const char *pattrs[] =
{
"job-hold-until-supported",
"job-hold-until-default",
"job-sheets-supported",
"job-sheets-default",
"job-priority-supported",
"job-priority-default"
};
if ((datadir = getenv("CUPS_DATADIR")) == NULL)
datadir = CUPS_DATADIR;
if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL)
{
fprintf(stderr, "cupsaddsmb: Unable to connect to server \"%s\" for %s - %s\n",
cupsServer(), dest, strerror(errno));
return (1);
}
if ((ppdfile = cupsGetPPD2(http, dest)) == NULL)
{
fprintf(stderr, "cupsaddsmb: No PPD file for printer \"%s\" - skipping!\n",
dest);
httpClose(http);
return (0);
}
request = ippNew();
request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
request->request.op.request_id = 1;
language = cupsLangDefault();
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
snprintf(command, sizeof(command), "ipp://localhost/printers/%s", dest);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, command);
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
NULL, pattrs);
if ((response = cupsDoRequest(http, request, "/")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
fprintf(stderr, "cupsaddsmb: get-printer-attributes failed for \"%s\": %s\n",
dest, ippErrorString(response->request.status.status_code));
ippDelete(response);
cupsLangFree(language);
httpClose(http);
unlink(ppdfile);
return (2);
}
}
else
{
fprintf(stderr, "cupsaddsmb: get-printer-attributes failed for \"%s\": %s\n",
dest, ippErrorString(cupsLastError()));
cupsLangFree(language);
httpClose(http);
unlink(ppdfile);
return (2);
}
if (convert_ppd(ppdfile, newppd, sizeof(newppd), response))
{
fprintf(stderr, "cupsaddsmb: Unable to convert PPD file for %s - %s\n",
dest, strerror(errno));
ippDelete(response);
cupsLangFree(language);
httpClose(http);
unlink(ppdfile);
return (3);
}
ippDelete(response);
cupsLangFree(language);
httpClose(http);
unlink(ppdfile);
ppdfile = newppd;
snprintf(file, sizeof(file), "%s/drivers/pscript5.dll", datadir);
if (!access(file, 0))
{
snprintf(command, sizeof(command), "smbclient //%s/print\\$", SAMBAServer);
snprintf(subcmd, sizeof(subcmd),
"mkdir W32X86;"
"put %s W32X86/%s.ppd;"
"put %s/drivers/ps5ui.dll W32X86/ps5ui.dll;"
"put %s/drivers/pscript.hlp W32X86/pscript.hlp;"
"put %s/drivers/pscript.ntf W32X86/pscript.ntf;"
"put %s/drivers/pscript5.dll W32X86/pscript5.dll",
ppdfile, dest, datadir, datadir, datadir, datadir);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to copy Windows 2000 printer driver files (%d)!\n",
status);
unlink(ppdfile);
return (4);
}
snprintf(file, sizeof(file), "%s/drivers/cupsdrv6.dll", datadir);
if (!access(file, 0))
{
snprintf(subcmd, sizeof(subcmd),
"put %s/drivers/cupsdrv6.dll W32X86/cupsdrv6.dll;"
"put %s/drivers/cupsui6.dll W32X86/cupsui6.dll",
datadir, datadir);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to copy CUPS printer driver files (%d)!\n",
status);
unlink(ppdfile);
return (4);
}
snprintf(subcmd, sizeof(subcmd),
"adddriver \"Windows NT x86\" \"%s:"
"pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
"cupsdrv6.dll,cupsui6.dll,pscript.ntf\"",
dest, dest);
}
else
{
snprintf(subcmd, sizeof(subcmd),
"adddriver \"Windows NT x86\" \"%s:"
"pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:"
"pscript.ntf\"",
dest, dest);
}
snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to install Windows 2000 printer driver files (%d)!\n",
status);
unlink(ppdfile);
return (5);
}
}
snprintf(file, sizeof(file), "%s/drivers/ADOBEPS4.DRV", datadir);
if (!access(file, 0))
{
snprintf(command, sizeof(command), "smbclient //%s/print\\$", SAMBAServer);
snprintf(subcmd, sizeof(subcmd),
"mkdir WIN40;"
"put %s WIN40/%s.PPD;"
"put %s/drivers/ADFONTS.MFM WIN40/ADFONTS.MFM;"
"put %s/drivers/ADOBEPS4.DRV WIN40/ADOBEPS4.DRV;"
"put %s/drivers/ADOBEPS4.HLP WIN40/ADOBEPS4.HLP;"
"put %s/drivers/ICONLIB.DLL WIN40/ICONLIB.DLL;"
"put %s/drivers/PSMON.DLL WIN40/PSMON.DLL;",
ppdfile, dest, datadir, datadir, datadir, datadir, datadir);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to copy Windows 9x printer driver files (%d)!\n",
status);
unlink(ppdfile);
return (6);
}
snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);
snprintf(subcmd, sizeof(subcmd),
"adddriver \"Windows 4.0\" \"%s:ADOBEPS4.DRV:%s.PPD:NULL:"
"ADOBEPS4.HLP:PSMON.DLL:RAW:"
"ADOBEPS4.DRV,%s.PPD,ADOBEPS4.HLP,PSMON.DLL,ADFONTS.MFM,"
"ICONLIB.DLL\"",
dest, dest, dest);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to install Windows 9x printer driver files (%d)!\n",
status);
unlink(ppdfile);
return (7);
}
}
unlink(ppdfile);
snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);
snprintf(subcmd, sizeof(subcmd), "setdriver %s %s", dest, dest);
if ((status = do_samba_command(command, subcmd)) != 0)
{
fprintf(stderr, "cupsaddsmb: Unable to set Windows printer driver (%d)!\n",
status);
return (8);
}
return (0);
}
char *
ppd_gets(FILE *fp,
char *buf,
int buflen)
{
int ch;
char *ptr,
*end;
if (!fp || !buf || buflen < 2 || feof(fp))
return (NULL);
for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
{
if ((ch = getc(fp)) == EOF)
{
if (ptr == buf)
return (NULL);
else
break;
}
*ptr++ = ch;
if (ch == '\r')
{
if ((ch = getc(fp)) != '\n')
ungetc(ch, fp);
else if (ptr < end)
*ptr++ = ch;
break;
}
else if (ch == '\n')
{
break;
}
}
*ptr = '\0';
return (buf);
}
void
usage(void)
{
puts("Usage: cupsaddsmb [options] printer1 ... printerN");
puts(" cupsaddsmb [options] -a");
puts("");
puts("Options:");
puts(" -H samba-server Use the named SAMBA server");
puts(" -U samba-user Authenticate using the named SAMBA user");
puts(" -a Export all printers");
puts(" -h cups-server Use the named CUPS server");
puts(" -v Be verbose (show commands)");
exit(1);
}
int
write_option(FILE *dstfp,
int order,
const char *name,
const char *text,
const char *attrname,
ipp_attribute_t *suppattr,
ipp_attribute_t *defattr,
int defval,
int valcount)
{
int i;
if (!dstfp || !name || !text || !suppattr || !defattr)
return (1);
fprintf(dstfp, "*JCLOpenUI *%s/%s: PickOne\n"
"*OrderDependency: %d JCLSetup *%s\n",
name, text, order, name);
if (defattr->value_tag == IPP_TAG_INTEGER)
{
fprintf(dstfp, "*Default%s: %d\n", name, defattr->values[defval].integer);
if (suppattr->value_tag == IPP_TAG_RANGE)
{
for (i = suppattr->values[0].range.lower;
i <= suppattr->values[0].range.upper;
i ++)
{
fprintf(dstfp, "*%s %d: \"", name, i);
if (valcount == 1)
fprintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname, i);
else if (defval == 0)
fprintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname, i);
else if (defval < (valcount - 1))
fprintf(dstfp, ",%d\"\n", i);
else
fprintf(dstfp, ",%d\n\"\n*End\n", i);
}
}
else
{
for (i = 0; i < suppattr->num_values; i ++)
{
fprintf(dstfp, "*%s %d: \"", name, suppattr->values[i].integer);
if (valcount == 1)
fprintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname,
suppattr->values[i].integer);
else if (defval == 0)
fprintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname,
suppattr->values[i].integer);
else if (defval < (valcount - 1))
fprintf(dstfp, ",%d\"\n", suppattr->values[i].integer);
else
fprintf(dstfp, ",%d\n\"\n*End\n", suppattr->values[i].integer);
}
}
}
else
{
fprintf(dstfp, "*Default%s: %s\n", name,
defattr->values[defval].string.text);
for (i = 0; i < suppattr->num_values; i ++)
{
fprintf(dstfp, "*%s %s: \"", name, suppattr->values[i].string.text);
if (valcount == 1)
fprintf(dstfp, "%%cupsJobTicket: %s=%s\n\"\n*End\n", attrname,
suppattr->values[i].string.text);
else if (defval == 0)
fprintf(dstfp, "%%cupsJobTicket: %s=%s\"\n", attrname,
suppattr->values[i].string.text);
else if (defval < (valcount - 1))
fprintf(dstfp, ",%s\"\n", suppattr->values[i].string.text);
else
fprintf(dstfp, ",%s\n\"\n*End\n", suppattr->values[i].string.text);
}
}
fprintf(dstfp, "*JCLCloseUI: *%s\n\n", name);
return (0);
}