/*  GFAX - Gnome fax application
 *  Copyright (C) 1999 George A. Farris
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#include <config.h>
#include <gnome.h>
#include <glade/glade.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>

#include "gfax.h"
#include "setup.h"

#include "ftplib.h"

#include "hylafax_sendfax.h"

/* memory that holds the command output */
static GString  *command_output = NULL;

static gboolean stop_transmit_flag;

struct transmitGUI {
	GnomeDialog *dialog;
	GtkProgressBar *transmitProgressBar;
};

void show_command_result(int exitcode, char *output);
void stop_transmit(GnomeDialog *dialog, gint arg1, gpointer user_data);
gint stop_transmit2(GnomeDialog *dialog, gpointer user_data);

static void hylafax_log(char *str) 
{
	g_string_append(command_output, str);
}

char *hylafax_getlog(void) 
{
	return command_output->str;
}

/***********************************************************************
 * Ask the user for a hylafax password and return this password.
 **********************************************************************/
static char *askpassword(void) 
{
	char *password;

	GladeXML *window;	/* The main window */
	GnomeDialog *dialog;
	GtkEntry  *passwordEntry;

	window = glade_xml_new(GLADEDIR "/gfax.glade", "passwordDialog");
	dialog = (GnomeDialog *)glade_xml_get_widget(window, "passwordDialog");
	passwordEntry  = (GtkEntry *)glade_xml_get_widget(window, "passwordEntry");

	/* connect the signals */		
	glade_xml_signal_autoconnect(window);
	gnome_dialog_run(dialog);
	password = g_strdup_printf("\"%s\"", gtk_entry_get_text(passwordEntry));
	gtk_object_destroy((GtkObject *)dialog);
	return password;
}

/************************************************************************
 * Callback function called by "Cancel" button of the transmit
 * dialog. Sets the flag "stop_transmit_flag" which makes the main
 * loop stop with the transfer.
 **********************************************************************/
void stop_transmit(GnomeDialog *dialog,
		   gint arg1,
		   gpointer user_data) 
{
	stop_transmit_flag = 1;
}

/************************************************************************
 * Callback function called by "CLOSE" button of the window
 * manager. Sets the flag "stop_transmit_flag" which makes the main
 * loop stop with the transfer.
 **********************************************************************/
gint stop_transmit2(GnomeDialog *dialog,
		   gpointer user_data)
{
	stop_transmit_flag = 1;
	return 0;
}

/**********************************************************************
 * Builds a "Please wait, transmitting" dialog containing a
 * ProgressBar and a Cancel button. This dialog is used during upload
 * of the fax file to the hylafax server. All necessary data will be
 * stored in the parameter "*gui" which must be allocated by the
 * caller!
 **********************************************************************/
static void showTransmitDialog(struct transmitGUI *gui)
{
	/* widgets for ui */
	GtkWidget *label;
	GtkWidget *pixmap = NULL, *hbox, *vbox;
	gchar *s;

	s = gnome_unconditional_pixmap_file("gnome-modem.png");
	if (s) {
		pixmap = gnome_pixmap_new_from_file(s);
		g_free(s);
	}
   
	gui->dialog = GNOME_DIALOG(gnome_dialog_new(_("Please wait..."), 
				      GNOME_STOCK_BUTTON_CANCEL, NULL));
	label = gtk_label_new(_("Transmitting fax to server..."));
	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
	gui->transmitProgressBar = GTK_PROGRESS_BAR(gtk_progress_bar_new());
	gtk_widget_set_usize(GTK_WIDGET(gui->transmitProgressBar),
			     300,0);
	hbox = gtk_hbox_new (FALSE, 0);
	vbox = gtk_vbox_new (FALSE, 0);
	gtk_box_pack_start (GTK_BOX(GNOME_DIALOG(gui->dialog)->vbox),
			    hbox, FALSE, FALSE, 0);

	if (pixmap) {
		gtk_box_pack_start (GTK_BOX(hbox),
				    pixmap, FALSE, TRUE, 0);
		gtk_widget_show (pixmap);
	}
	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gui->transmitProgressBar), FALSE, FALSE, 0);
   
	gtk_widget_show (hbox);
	gtk_widget_show (vbox);
	gtk_widget_show(GTK_WIDGET(label));
	gtk_widget_show(GTK_WIDGET(gui->transmitProgressBar));
	gtk_signal_connect(GTK_OBJECT(gui->dialog), "clicked",
			   GTK_SIGNAL_FUNC(stop_transmit), 
			   NULL);
	gtk_signal_connect(GTK_OBJECT(gui->dialog), "close",
			   GTK_SIGNAL_FUNC(stop_transmit2), 
			   NULL);
   
	gtk_progress_set_show_text(GTK_PROGRESS(gui->transmitProgressBar), TRUE);
	gtk_progress_set_format_string(GTK_PROGRESS(gui->transmitProgressBar), _("%v of %u (%p%%)"));
	gtk_window_set_modal(GTK_WINDOW(gui->dialog), FALSE);
	gtk_widget_show(GTK_WIDGET (gui->dialog));

	/*
	 * GNOME is event triggered but we are already in an event
	 * routine. So no further events during the time of the upload
	 * will be processed. Make GNOME take all pending actions
	 * (otherwise the GUI will not be updated and e.g. the
	 * ProgressBar will never show.
	 */
	while (gtk_events_pending())
		gtk_main_iteration();
}

static void showTransmitStatus(struct transmitGUI *gui, 
			       long pos, long filesize)
{
	gtk_progress_configure(GTK_PROGRESS(gui->transmitProgressBar), 
			       (gfloat)pos, (gfloat)0, (gfloat)filesize);
	/*
	 * GNOME is event triggered but we are already in an event
	 * routine. So no further events during the time of the upload
	 * will be processed. Make GNOME take all pending actions
	 * (otherwise the GUI will not be updated and e.g. the
	 * ProgressBar will never show.
	 */
	while (gtk_events_pending())
		gtk_main_iteration();
}


/**********************************************************************
 * Open a control connection to the hylafax server and return this
 * connection as a reference parameter. This connection could be used
 * by some other hylafax commands in this "library".
 **********************************************************************/
enum hylafax_errno hylafax_connect(netbuf **con) 
{
	int status;
	char *user;
	char *password;
	char *host;
	struct passwd *pw;
	
	/* get name of current user */
	pw = getpwuid(getuid());
	user = pw->pw_name;

	/* allocate memory for command output: */
 	command_output	   = g_string_new(NULL);

	/* build host:port address */
	host = g_strdup_printf("%s:%d", hylafaxServerName,
			                hylafaxServerPort);

	FtpSetLogger(hylafax_log);
	ftplib_debug = 3;
	FtpInit();

	status = FtpConnect(host, con);
	if ( status != 1 ) return ENOSERVER;
	FtpOptions(FTPLIB_CONNMODE, FTPLIB_PORT, *con);

	/* try password "anonymous" */
	status = FtpLogin(user, "anonymous", *con);
	if ( status != 1 ) {
		/* Login incorrect, ask user for password */
		password = askpassword();
		status = FtpLogin(user, password, *con);
		/* delete password */
		memset(password, strlen(password), 1);
		free(password);

		if ( status != 1 ) {
			FtpQuit(*con);
			return ELOGININCORRECT;
		}
	}

	return EOK;
}

/**********************************************************************
 * Send a fax to the hylafax server for further processing. This
 * function includes hylafax_connect and hylafax_disconnect and is
 * therefore "standalone". It could not be used in conjunction with
 * the other functions in this file.
 **********************************************************************/
enum hylafax_errno hylafax_sendfax(Destination *dest) 
{
	netbuf *con;
	netbuf *data;
	char *doc_filename;
	char *doc_filename_start;
	char *doc_filename_end;
	int doc_filename_len;
	char *user;
	char *faxNumber;
	FILE *local = NULL;
	char buf[8192];
	int l,c;
	enum hylafax_errno herrno;
	struct passwd *pw;
	long filesize;
	long pos;
	struct transmitGUI gui;

	/* get name of current user */
	pw = getpwuid(getuid());
	user = pw->pw_name;

	faxNumber = g_strdup_printf("%s%s", phonePrefix, dest->faxNumber);

	/* connect to hylafax server */
	herrno = hylafax_connect(&con);
	if ( herrno != EOK ) {
		return herrno;
	}
	
	/* check for postscript: */
	local = fopen(dest->fileName, "rb");
	if (local == NULL) {
		FtpLog(_("File \"%s\" could not be read (%s)"), dest->fileName,
		       strerror(errno));
		FtpClose(data);
		FtpQuit(con);
		return ENOLOCALFILE;
	}
	l = fread(buf, 1, 4, local);
	if ( l != 4 || strncmp(buf, "%!PS", l) != 0 ) {
		FtpLog(_("Document \"%s\" contains no POSTSCRIPT"), dest->fileName);
		FtpClose(data);
		FtpQuit(con);
		return ENOLOCALFILE;
	}
	fclose(local);

	if (!FtpSendCmd("TYPE I", '2', con)) {
		return EERROR;
	}

	if (FtpOpenPort(con, &data, FTPLIB_BINARY, FTPLIB_WRITE) == -1) {
		FtpQuit(con);
		return EERROR;
	}

	if (!FtpSendCmd("MODE S", '2', con)) {
		FtpClose(data);
		FtpQuit(con);
		return EERROR;
	}

	if ( !FtpSendCmd("STOT", '1', con)) {
		FtpQuit(con);
		return EERROR;
	}
	/* get name of server document from response: */
	doc_filename_start = strstr(FtpLastResponse(con), "FILE: ");
	doc_filename = NULL;
	if ( doc_filename_start != NULL ) {
		doc_filename_start += 6;        /* skip "FILE: " */
		doc_filename_end = strstr(doc_filename_start, " (Opening new data connection");
		if ( doc_filename_end != NULL ) {
			doc_filename_len = doc_filename_end - doc_filename_start;
			doc_filename = malloc(doc_filename_len + 1);
			strncpy(doc_filename, doc_filename_start, doc_filename_len);
			doc_filename[doc_filename_len] = 0;
		}
	}
	if ( doc_filename == NULL ) {
		FtpClose(data);
		FtpQuit(con);
		return ENOSERVERFILE;
	}

	/* send document through data connection... */
	FtpAcceptConnection(data, con);

	local = fopen(dest->fileName, "rb");
	if (local == NULL) {
		FtpClose(data);
		FtpQuit(con);
		return ENOLOCALFILE;
	}
	fseek(local, 0L, SEEK_END);
	filesize = ftell(local);
	rewind(local);

	pos = 0;
	stop_transmit_flag = FALSE;
	showTransmitDialog(&gui);
	while ((l = fread(buf, 1, sizeof(buf), local)) > 0) {
		if ( stop_transmit_flag ) {
			break;
		}
		if ((c = FtpWrite(buf, l, data)) < l) {
			gtk_widget_hide(GTK_WIDGET(gui.dialog));
			FtpClose(data);
			FtpQuit(con);
			return ENOSTORE;
		}
		pos += l;
		showTransmitStatus(&gui, pos, filesize);
	}
	gtk_widget_hide(GTK_WIDGET(gui.dialog));
	if (!FtpClose(data)) {
		FtpLog(_("Could not send document to hylafax server"));
		FtpQuit(con);
		return ENOSTORE;
	}

	if (!FtpSendCmd("JNEW", '2', con) ||
	    !FtpSendVarCmd("JPARM FROMUSER %s", '2', con, user) ||
	    !FtpSendCmd("JPARM LASTTIME 000259", '2', con) ||
	    !FtpSendCmd("JPARM MAXDIALS 12", '2', con) ||
	    !FtpSendCmd("JPARM MAXTRIES 3", '2', con) ||
	    !FtpSendCmd("JPARM SCHEDPRI 127", '2', con) ||
	    !FtpSendVarCmd("JPARM DIALSTRING \"%s\"", '2', con, faxNumber) ||
	    !FtpSendVarCmd("JPARM NOTIFYADDR \"%s\"", '2', con, emailAddress)||
	    !FtpSendCmd("JPARM VRES 196", '2', con) ||
	    !FtpSendCmd("JPARM PAGEWIDTH 209", '2', con) ||
	    !FtpSendCmd("JPARM PAGELENGTH 296", '2', con) ||
	    !FtpSendCmd("JPARM NOTIFY \"none\"", '2', con) ||
	    !FtpSendCmd("JPARM PAGECHOP \"default\"", '2', con) ||
	    !FtpSendCmd("JPARM CHOPTHRESHOLD 3", '2', con) ||
	    !FtpSendVarCmd("JPARM DOCUMENT \"%s\"", '2', con, doc_filename) ||
	    !FtpSendCmd("JPARM PAGECHOP \"default\"", '2', con) ||
	    !FtpSendCmd("JPARM PAGECHOP \"default\"", '2', con) ||
	    !FtpSendCmd("JPARM PAGECHOP \"default\"", '2', con)) {
		FtpQuit(con);
		return EERROR;
	}

	/* no errors -> submit */
	if ( !stop_transmit_flag ) {
		if (!FtpSendCmd("JSUBM", '2', con) ) {
			FtpQuit(con);
			return ENOSTORE;
		}
	} else {
		FtpLog(_("ABORTED ON USER REQUEST."));
		FtpQuit(con);
		return EERROR;
	}
	FtpQuit(con);
	return EOK;
}

/**********************************************************************
 * Ask hylafax for the status of a given queue. This function connects
 * through the data connection "con" to a hylafax server and asks for
 * the contents of the queue named "queueName". The output sent back
 * by hylafax will be in the form of the given "jobfmt" format.
 *
 * This routine will tokenize the response at every ";" character and
 * return an array of pointers in "queue". This pointer array contains
 * a entry for every job returned by hylafax. Each entry in this array
 * is itself an array of character pointers pointing to the tokenized
 * elements of "jobfmt".
 *
 * So if you have the following jobs scheduled in the queue, the
 * output pointer array "queue" will contain:
 *
 * [root@localhost src]# faxstat -s
 * HylaFAX scheduler on localhost.localdomain: Running
 * 
 * JID  Pri S  Owner Number       Pages Dials     TTS Status
 * 97   127 W   root 463742        0:0   0:12         
 * 98   127 W   root 431631        0:0   0:12         
 *
 * queue[0] -> { "97", "127", "W", "root", "463742", "0:0", "0:12", NULL }
 * queue[1] -> { "98", "127", "W", "root", "431632", "0:0", "0:12", NULL }
 *
 * Hylafax offers only three possible queue names:
 *   (a) "sendq"   (b) "recvq"    (c) "doneq"
 *
 * "jobfmt" could include a printf-like notation of hylafax jobfmt as 
 * described in the hfaxd(1) manual page.
 *
 **********************************************************************/
enum hylafax_errno hylafax_faxstat(netbuf *con, 
				   gchar *queueName, gchar *jobfmt, 
				   GPtrArray *queue) 
{
	netbuf *data;
	char buf[8192];
	int l;
	gchar **tokens;
	int i;

	// See "man hfaxd" for a description of flags
	if (!FtpSendVarCmd("JOBFMT \"%s\"", '2', con, jobfmt)) {
		return EERROR;
	}

	if (FtpOpenPort(con, &data, FTPLIB_ASCII, FTPLIB_READ) == -1) {
		return EERROR;
	}

	if ( !FtpSendVarCmd("LIST %s", '1', con, queueName)) {
		return EERROR;
	}

	FtpAcceptConnection(data, con);

	while ((l = FtpRead(buf, sizeof(buf), data)) > 0) {
		/* parse elements */
		buf[l] = 0;
		tokens = g_strsplit(buf, ";", -1);
		
		/* strip every entry */
		for(i = 0; tokens[i] != NULL; i++) {
			g_strstrip(tokens[i]);
		}

		g_ptr_array_add(queue, tokens);
	}
	if (!FtpClose(data)) {
		FtpLog(_("Could not receive queue from hylafax server"));
		return ENOSTORE;
	}

	return EOK;
}

/**********************************************************************
 * Ask hylafax for the status of the server itself.  This response is
 * returned in the parameter string "output". It is normally a single
 * line of the form 
 * "HylaFAX scheduler on localhost.localdomain: Running".
 **********************************************************************/
enum hylafax_errno hylafax_server_state(netbuf *con, GString *output) 
{
	netbuf *data;
	char buf[8192];
	int l;
	enum hylafax_errno herrno;

	if (FtpOpenPort(con, &data, FTPLIB_ASCII, FTPLIB_READ) == -1) {
		return EERROR;
	}

	if ( !FtpSendCmd("LIST status", '1', con)) {
		return EERROR;
	}

	FtpAcceptConnection(data, con);

	l = FtpRead(buf, sizeof(buf), data);
	if ( l >= 0 ) {
		buf[l] = 0;
		g_strstrip(buf);
		herrno = EOK;
	} else {
		buf[0] = 0;
		herrno = EERROR;
	}
	g_string_assign(output, buf);

	if (!FtpClose(data)) {
		FtpLog(_("Could not receive status from hylafax server"));
		return ENOSTORE;
	}

	return herrno;
}

/**********************************************************************
 * Kill a job on the hylafax server by sending the corresponding kill
 * commands together with the job id.
 ***********************************************************************/
enum hylafax_errno hylafax_faxrm(netbuf *con, gchar *id) 
{
	if ( !FtpSendVarCmd("JKILL %s", '2', con, id)) {
		// 504 Job 93 not killed; already done.
		if ( strncmp(FtpLastResponse(con), "504", 3) != 0 ) {
			return EERROR;
		}
	}
	FtpSendVarCmd("JSUSP %s", '2', con, id);
	if ( !FtpSendVarCmd("JDELE %s", '2', con, id)) {
		return EERROR;
	}
	return EOK;
}

/**********************************************************************
 * Disconnect the control connection from the hylafax server and show
 * the hylafax log.
 * 
 * @param con the control connection to the hylafax server.
 * @param showResult if true, then show the hylafax log file in
 *                   a dialog box.
 * @param herrno     if == EOK then the dialog box will contain the 
 *                   message "everything worked fine". Otherwise:
 *                   "an error occured".
 ***********************************************************************/
void hylafax_disconnect(netbuf *con, gboolean showResult, 
			enum hylafax_errno herrno) 
{
	FtpQuit(con);
	
	if ( showResult ) {
		if ( herrno == EOK ) {
			show_command_result(0, hylafax_getlog());
		} else {
			show_command_result(1, hylafax_getlog());
		}
	}

	g_string_free(command_output, TRUE);
}

/* These are Emacs variables to use a common "Gfax" coding style:
 * ;;; Local Variables: ***
 * ;;; mode:C ***
 * ;;; c-basic-offset:8 ***
 * ;;; indent-tabs-mode:t ***
 * ;;; End: *** */
