/*
 *  This file is part of the KDE libraries
 *  Copyright (c) 2001 Michael Goffioul <tdeprint@swing.be>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License version 2 as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 **/

#include <config.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <cups/cups.h>
#include <cups/ipp.h>

#define BUFSIZE		1024
#define BUFSIZE2	32

#define	USE_LOG		0

/* global variables */
char	passwd[BUFSIZE2] = {0};
int	pwd_asked = 0;
#if USE_LOG
FILE	*debugF = NULL;
#endif

/* utility functions */
static void error(const char* msg)
{
	fprintf(stderr, "%s\n", msg);
#if USE_LOG
	if (debugF != NULL) fclose(debugF);
#endif
	exit(-1);
}

static void usage()
{
	error("usage: cupsdoprint [-H host[:port]][-P dest][-J name][-o opt=value[,...]][-U login[:password]] files...");
}

static char * shell_quote(const char *s)
{
   char *result;
   char *p;
   p = result = malloc(strlen(s)*4+3);
   *p++ = '\'';
   while(*s)
   {
     if (*s == '\'')
     {
        *p++ = '\'';
        *p++ = '\\';
        *p++ = '\'';
        *p++ = '\'';
	s++;
     }
     else
     {
        *p++ = *s++;
     }
   }
   *p++ = '\'';
   *p = '\0';
   return result;
}

static const char* getPasswordCB(const char* prompt)
{
	char buf[ 256 ] = {0}, *c;
	char *_user = shell_quote( cupsUser() ), *_passwd = NULL;
	FILE *output;

	snprintf( buf, sizeof( buf )-1, "dcop kded tdeprintd requestPassword %s %s %d %d",
			_user,
			cupsServer(),
			ippPort(),
			pwd_asked );
	free( _user );
	_user = NULL;
	output = popen( buf, "r" );
	if ( output != NULL )
	{
		while ( fgets( buf, sizeof( buf )-1, output ) )
		{
			_user = _passwd = NULL;
			if ( strcmp( buf, "::" ) != 0 )
			{
				c = strchr( buf, ':' );
				if ( c != NULL )
				{
					*c = '\0';
					_user = buf;
					_passwd = ++c;
					c = strchr( c, ':' );
					if ( c != NULL )
					{
						*c = '\0';
						/* retrieve password sequence number */
						pwd_asked = atoi( ++c );
						/* update CUPS with current username */
						cupsSetUser( _user );
						/* copy password to a non temporary location */
						strlcpy( passwd, _passwd, BUFSIZE2 );
						_passwd = passwd;
					}
					else
						_passwd = NULL;
				}
			}
		}
		pclose( output );
	}
	else
		return NULL;

	/* erase buffer containing unencrypted password, for security */
	memset( buf, 0, 256 );

	/* if OK, _passwd should point to global passwd variable, otherwise it should be NULL */
	return _passwd;
}

/* main function */
int main(int argc, char* argv[])
{
	int	c, port = -1;
	char	printer[BUFSIZE] = {0}, jobname[BUFSIZE] = {0}, host[BUFSIZE] = {0};
	char	login[BUFSIZE2] = {0};
	char	*a;
	cups_option_t	*options = NULL;
	int		num_options = 0;
	const char*	files[100] = {0};
	int	num_files = 0;
	int	jobID = 0;

#if USE_LOG
	debugF = fopen("/tmp/cupsdoprint.debug","w");
	if (debugF == NULL)
		error("unable to open log file");
#endif

	while ((c=getopt(argc, argv, "P:J:H:o:U:?")) != -1)
	{
#if USE_LOG
		fprintf(debugF,"%c: %s\n",c,optarg);
#endif
		switch (c)
		{
			case 'P':
				strlcpy(printer, optarg, BUFSIZE);
				if ((a=strchr(printer, '/')) != NULL)
					error("This utility doesn't support printer instances");
				break;
			case 'J':
				strlcpy(jobname, optarg, BUFSIZE);
				break;
			case 'H':
				strlcpy(host, optarg, BUFSIZE);
				if ((a=strchr(host, ':')) != NULL)
				{
					*a = 0;
					port = atoi(++a);
					if (port == 0)
						error("Wrong port value");
				}
				break;
			case 'U':
				strlcpy(login, optarg, BUFSIZE2);
				if ((a=strchr(login, ':')) != NULL)
				{
					*a = 0;
					strlcpy(passwd, ++a, BUFSIZE2);
				}
				break;
			case 'o':
#if USE_LOG
				fprintf(debugF,"Parsing options (n=%d)\n",num_options);
#endif
				num_options = cupsParseOptions(optarg, num_options, &options);
#if USE_LOG
				fprintf(debugF,"Options parsed (n=%d)\n",num_options);
#endif
				break;
			case '?':
			default:
				usage();
				break;
		}
	}

	/* check the printer */
	if (!*printer)
	{
		printer[BUFSIZE-1] = '\0';
		if (getenv("PRINTER") != NULL)
			strlcpy(printer, getenv("PRINTER"), BUFSIZE-1);
		else
			error("No printer specified (and PRINTER variable is empty)");
	}

	/* CUPS settings */
	if (host[0] != 0) cupsSetServer(host);
	if (port > 0) ippSetPort(port);
	if (login[0] != 0) cupsSetUser(login);
	if (jobname[0] == 0) strcpy(jobname,"TDE Print System");
	cupsSetPasswordCB(getPasswordCB);

	/* check for files */
	if (optind < 1 || optind >= argc)
		error("This utility doesn't support printing from STDIN");
	else
		for (c=optind; c<argc; c++)
		{
			if (access(argv[c], R_OK) != 0)
			{
				fprintf(stderr, "%s: ", argv[c]);
				error("file not found or not readable");
			}
			else
				files[num_files++] = strdup(argv[c]);
		}

#if USE_LOG
	fprintf(debugF,"Processed options:\n");
	for (c=0; c<num_options; c++)
		fprintf(debugF,"%s = %s\n",options[c].name,options[c].value);
#endif
	/* print files */
	jobID = cupsPrintFiles(printer, num_files, files, jobname, num_options, options);
	/* erase unemcrypted password for security */
	memset( passwd, 0, BUFSIZE2 );
	/* check job creation status */
	if (jobID <= 0)
		error(ippErrorString(cupsLastError()));

#if USE_LOG
	fclose(debugF);
#endif
	return 0;
}
