/*
 * "$Id: cupsaddsmb.c,v 1.1.1.14 2004/06/05 02:42:35 jlovell Exp $"
 *
 *   "cupsaddsmb" command for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 2001-2004 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products and are protected by Federal
 *   copyright law.  Distribution and use rights are outlined in the file
 *   "LICENSE.txt" which should have been included with this file.  If this
 *   file is missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636-3111 USA
 *
 *       Voice: (301) 373-9603
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org
 *
 * Contents:
 *
 *   main()             - Export printers on the command-line.
 *   do_samba_command() - Do a SAMBA command, asking for a password as needed.
 *   export_dest()      - Export a destination to SAMBA.
 *   usage()            - Show program usage and exit...
 */

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <cups/cups.h>
#include <cups/language.h>
#include <cups/string.h>
#include <errno.h>


/*
 * Local globals...
 */

int		Verbosity = 0;
const char	*SAMBAUser,
		*SAMBAServer;


/*
 * Local functions...
 */

int	do_samba_command(const char *, const char *);
int	export_dest(const char *);
void	usage();


/*
 * 'main()' - Export printers on the command-line.
 */

int			/* O - Exit status */
main(int  argc,		/* I - Number of command-line arguments */
     char *argv[])	/* I - Command-line arguments */
{
  int	i, j;		/* Looping vars */
  int	status;		/* Status from export_dest() */
  int	export_all;	/* Export all printers? */
  int	num_printers;	/* Number of printers */
  char	**printers;	/* Printers */


 /*
  * Parse command-line arguments...
  */

  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();

 /*
  * See if the user specified "-a"...
  */

  if (export_all)
  {
   /*
    * Export all printers...
    */

    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);
}


/*
 * 'do_samba_command()' - Do a SAMBA command, asking for
 *                        a password as needed.
 */

int					/* O - Status of command */
do_samba_command(const char *command,	/* I - Command to run */
                 const char *subcmd)	/* I - Sub-command */
{
  int		status;			/* Status of command */
  char		temp[4096];		/* Command/prompt string */
  static const char *p = NULL;		/* Password data */


  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);
}


/*
 * 'export_dest()' - Export a destination to SAMBA.
 */

int					/* O - 0 on success, non-zero on error */
export_dest(const char *dest)		/* I - Destination to export */
{
  int			i;		/* Looping var */
  int			status;		/* Status of smbclient/rpcclient commands */
  const char		*ppdfile;	/* PPD file for printer drivers */
  char			command[1024],	/* Command to run */
			subcmd[1024];	/* Sub-command */
  const char		*datadir;	/* CUPS_DATADIR */
  FILE			*fp;		/* PPD file */
  http_t		*http;		/* Connection to server */
  cups_lang_t		*language;	/* Default language */
  ipp_t			*request,	/* IPP request */
			*response;	/* IPP response */
  ipp_attribute_t	*attr;		/* IPP attribute */
  static const char	*pattrs[] =	/* Printer attributes we want */
			{
			  "job-sheets-supported",
			  "job-sheets-default",
			  "job-hold-until-supported",
			  "job-hold-until-default"
			};


  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
    datadir = CUPS_DATADIR;

 /*
  * Get the PPD file...
  */

  if ((ppdfile = cupsGetPPD(dest)) == NULL)
  {
    fprintf(stderr, "Warning: No PPD file for printer \"%s\" - skipping!\n", dest);
    return (0);
  }

 /*
  * Append the supported banner pages to the PPD file...
  */

  if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL)
  {
    fprintf(stderr, "Unable to connect to server \"%s\" for %s - %s\n",
            cupsServer(), dest, strerror(errno));
    unlink(ppdfile);
    return (1);
  }

  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);

 /*
  * Do the request and get back a response...
  */

  if ((response = cupsDoRequest(http, request, "/")) != NULL)
  {
    if (response->request.status.status_code > IPP_OK_CONFLICT)
    {
      fprintf(stderr, "ERROR: get-printer-attributes failed for %s: %s\n",
              dest, ippErrorString(response->request.status.status_code));
      ippDelete(response);
      unlink(ppdfile);
      return (2);
    }
  }
  else
  {
    fprintf(stderr, "ERROR: get-printer-attributes failed for %s: %s\n",
            dest, ippErrorString(cupsLastError()));
    unlink(ppdfile);
    return (2);
  }

 /*
  * Append the banner attributes to the end of the PPD file...
  */

  if ((fp = fopen(ppdfile, "a")) == NULL)
  {
    fprintf(stderr, "ERROR: Unable to append banner attributes to PPD file for %s - %s\n",
            dest, strerror(errno));
    ippDelete(response);
    unlink(ppdfile);
    return (3);
  }

  if ((attr = ippFindAttribute(response, "job-sheets-supported", IPP_TAG_NAME)) != NULL)
  {
    fprintf(fp, "*cupsJobSheetsSupported: \"%s", attr->values[0].string.text);

    for (i = 1; i < attr->num_values; i ++)
      fprintf(fp, ",%s", attr->values[i].string.text);

    fputs("\"\n", fp);
  }

  if ((attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_NAME)) != NULL)
  {
    fprintf(fp, "*cupsJobSheetsDefault: \"%s", attr->values[0].string.text);

    if (attr->num_values > 1)
      fprintf(fp, ",%s", attr->values[1].string.text);

    fputs("\"\n", fp);
  }

  if ((attr = ippFindAttribute(response, "job-hold-until-supported",
                               IPP_TAG_KEYWORD)) != NULL)
  {
    fprintf(fp, "*cupsJobHoldUntilSupported: \"%s", attr->values[0].string.text);

    for (i = 1; i < attr->num_values; i ++)
      fprintf(fp, ",%s", attr->values[i].string.text);

    fputs("\"\n", fp);
  }

  if ((attr = ippFindAttribute(response, "job-hold-until-default", IPP_TAG_KEYWORD)) != NULL)
    fprintf(fp, "*cupsJobHoldUntilDefault: \"%s\"\n", attr->values[0].string.text);

  fclose(fp);

  ippDelete(response);
  cupsLangFree(language);
  httpClose(http);

 /*
  * See which drivers are available - old CUPS, new CUPS, old Adobe, or
  * new Adobe?
  */

  snprintf(command, sizeof(command), "%s/drivers/cupsdrv5.dll", datadir);
  if (access(command, 0) == 0)
  {
   /*
    * Do the smbclient commands needed for the new CUPS WinNT drivers...
    */

    snprintf(command, sizeof(command), "smbclient //%s/print\\$", SAMBAServer);

    snprintf(subcmd, sizeof(subcmd),
             "mkdir W32X86;"
	     "put %s W32X86/%s.ppd;"
	     "put %s/drivers/cupsdrv5.dll W32X86/cupsdrv5.dll;"
	     "put %s/drivers/cupsui5.dll W32X86/cupsui5.dll;"
	     "put %s/drivers/cups5.hlp W32X86/cups5.hlp",
	     ppdfile, dest, datadir, datadir, datadir);

    if ((status = do_samba_command(command, subcmd)) != 0)
    {
      fprintf(stderr, "ERROR: Unable to copy Windows printer driver files (%d)!\n",
              status);
      unlink(ppdfile);
      return (4);
    }

   /*
    * Do the rpcclient commands needed for the CUPS WinNT drivers...
    */

    snprintf(subcmd, sizeof(subcmd),
             "adddriver \"Windows NT x86\" \"%s:cupsdrv5.dll:%s.ppd:cupsui5.dll:cups5.hlp:NULL:RAW:NULL\"",
	     dest, dest);

    snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);

    if ((status = do_samba_command(command, subcmd)) != 0)
    {
      fprintf(stderr, "ERROR: Unable to install Windows printer driver files (%d)!\n",
              status);
      unlink(ppdfile);
      return (5);
    }
  }
  else
  {
    snprintf(command, sizeof(command), "%s/drivers/cupsdrvr.dll", datadir);
    if (access(command, 0) == 0)
    {
     /*
      * Do the smbclient commands needed for the old CUPS WinNT drivers...
      */

      snprintf(command, sizeof(command), "smbclient //%s/print\\$", SAMBAServer);

      snprintf(subcmd, sizeof(subcmd),
               "mkdir W32X86;"
	       "put %s W32X86/%s.ppd;"
	       "put %s/drivers/cupsdrvr.dll W32X86/cupsdrvr.dll;"
	       "put %s/drivers/cupsui.dll W32X86/cupsui.dll;"
	       "put %s/drivers/cups.hlp W32X86/cups.hlp",
	       ppdfile, dest, datadir, datadir, datadir);

      if ((status = do_samba_command(command, subcmd)) != 0)
      {
	fprintf(stderr, "ERROR: Unable to copy Windows printer driver files (%d)!\n",
        	status);
	unlink(ppdfile);
	return (4);
      }

     /*
      * Do the rpcclient commands needed for the CUPS WinNT drivers...
      */

      snprintf(subcmd, sizeof(subcmd),
               "adddriver \"Windows NT x86\" \"%s:cupsdrvr.dll:%s.ppd:cupsui.dll:cups.hlp:NULL:RAW:NULL\"",
	       dest, dest);

      snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);

      if ((status = do_samba_command(command, subcmd)) != 0)
      {
	fprintf(stderr, "ERROR: Unable to install Windows printer driver files (%d)!\n",
        	status);
	unlink(ppdfile);
	return (5);
      }
    }
    else
    {
     /*
      * Do the smbclient commands needed for the old Adobe WinNT drivers...
      */

      snprintf(command, sizeof(command), "smbclient //%s/print\\$", SAMBAServer);

      snprintf(subcmd, sizeof(subcmd),
               "mkdir W32X86;"
	       "put %s W32X86/%s.PPD;"
	       "put %s/drivers/ADOBEPS5.DLL W32X86/ADOBEPS5.DLL;"
	       "put %s/drivers/ADOBEPSU.DLL W32X86/ADOBEPSU.DLL;"
	       "put %s/drivers/ADOBEPSU.HLP W32X86/ADOBEPSU.HLP",
	       ppdfile, dest, datadir, datadir, datadir);

      if ((status = do_samba_command(command, subcmd)) != 0)
      {
	fprintf(stderr, "ERROR: Unable to copy Windows printer driver files (%d)!\n",
        	status);
	unlink(ppdfile);
	return (4);
      }

     /*
      * Do the rpcclient commands needed for the Adobe WinNT drivers...
      */

      snprintf(subcmd, sizeof(subcmd),
               "adddriver \"Windows NT x86\" \"%s:ADOBEPS5.DLL:%s.PPD:ADOBEPSU.DLL:ADOBEPSU.HLP:NULL:RAW:NULL\"",
	       dest, dest);

      snprintf(command, sizeof(command), "rpcclient %s", SAMBAServer);

      if ((status = do_samba_command(command, subcmd)) != 0)
      {
	fprintf(stderr, "ERROR: Unable to install Windows printer driver files (%d)!\n",
        	status);
	unlink(ppdfile);
	return (5);
      }
    }
  }

  snprintf(command, sizeof(command), "%s/drivers/ADOBEPS4.DRV", datadir);
  if (access(command, 0) == 0)
  {
   /*
    * Do the smbclient commands needed for the Adobe Win9x drivers...
    */

    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/DEFPRTR2.PPD WIN40/DEFPRTR2.PPD;"
	     "put %s/drivers/ICONLIB.DLL WIN40/ICONLIB.DLL;"
	     "put %s/drivers/PSMON.DLL WIN40/PSMON.DLL;",
	     ppdfile, dest, datadir, datadir, datadir,
	     datadir, datadir, datadir);

    if ((status = do_samba_command(command, subcmd)) != 0)
    {
      fprintf(stderr, "ERROR: Unable to copy Windows printer driver files (%d)!\n",
              status);
      unlink(ppdfile);
      return (6);
    }

   /*
    * Do the rpcclient commands needed for the Adobe Win9x drivers...
    */

    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,DEFPRTR2.PPD,ICONLIB.DLL\"",
	     dest, dest, dest);

    if ((status = do_samba_command(command, subcmd)) != 0)
    {
      fprintf(stderr, "ERROR: Unable to install Windows printer driver files (%d)!\n",
              status);
      unlink(ppdfile);
      return (7);
    }
  }

  unlink(ppdfile);

 /*
  * Finally, associate the drivers we just added with the queue...
  */

  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, "ERROR: Unable to install Windows printer driver files (%d)!\n",
            status);
    return (8);
  }

  return (0);
}


/*
 * 'usage()' - Show program usage and exit...
 */

void
usage()
{
  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);
}


/*
 * End of "$Id: cupsaddsmb.c,v 1.1.1.14 2004/06/05 02:42:35 jlovell Exp $".
 */
