
/** -*- Mode: C++; tab-width: 4 -*-
 * vim: sw=4 ts=4:
 *
 * GDeb standalone main executable
 *
 * 	(C) 1998 Lalo Martins <lalo@debian.org>
 * 	    2002-2004 Filip Van Raemdonck <mechanix@debian.org>
 *
 * 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
 *
 * 	$Id$
 *
 **/

#include <getopt.h>
#include <config.h>
#include <gtk/gtk.h>
#include <gksuui.h>
#include <gksu.h>
#include <sys/stat.h>

#include "gdeb.h"

#include <apt-pkg/debfile.h>
#include <apt-pkg/error.h>
#include <apt-pkg/extracttar.h>

#if ENABLE_NLS
#define _(x) gettext(x)
#else
#define _(x) (x)
#endif

#define GDEB_NAME "gdeb"
#define GDEB_RESPONSE_INSTALL 1

class DebPackage : public GDeb::Package
{
	string filename;
	FileFd* fd;
	debDebFile* df;
	debDebFile::MemControlExtract ctrl;
public:
	DebPackage (const string&);
  virtual ~DebPackage() {}

  // Stuff to display
	virtual string Name (void);
	virtual string Version (void);
	virtual string Description_Short (void);
	virtual string Description_Long (void);
	virtual string Section (void);
	virtual string Priority (void);
	virtual string Size (void);
	virtual string Installed_Size (void);
	virtual string Maintainer (void);
	virtual string Status (void) { return _("Unknown"); }
  virtual string Current_Version()  { return "1.0"; }
  virtual string Architecture()  { return "i386"; }
  virtual string Source()  { return "source?"; }
	virtual string Filename (void) { return filename; }
	virtual string Filelist (void);
	virtual string Author (void);
	virtual string Homepage (void);
  virtual bool   Essential() { return true; }
  virtual bool   Downloadable() { return true; }
  virtual bool   Automatic()  { return false; }
  virtual vector<string> Provides() { return vector<string>(0); }
  virtual vector<string> PreDepends()  { return vector<string>(0); }
  virtual vector<string> Depends()  { return vector<string>(0); }
  virtual vector<string> Recommends()  { return vector<string>(0); }
  virtual vector<string> Suggests()  { return vector<string>(0); }
  virtual vector<string> Conflicts()  { return vector<string>(0); }
  virtual vector<string> Replaces()  { return vector<string>(0); }
  
  // Actions
  virtual void Install() { g_warning(__FUNCTION__); }
  virtual void Delete() { g_warning(__FUNCTION__); }
  virtual void Keep() { g_warning(__FUNCTION__); }
};

class ListStream : public pkgDirStream {
	string content;
public:
	virtual bool DoItem (Item &item, int &itfd) {
		if (content.size() > 0) content += "\n";
		content += item.Name;
		return true;
	}
	string getContent (void) { return content; }
};

class gdebError {};

DebPackage::DebPackage (const string& name) : filename (name), fd (0), df (0) {
	fd = new FileFd (name, FileFd::ReadOnly);
	if (_error->PendingError()) {
		_error->DumpErrors();
		_error->Error (_("Failure opening package `%s' for read."), name.c_str());
		throw gdebError();
	}

	df = new debDebFile (*fd);
	if (!ctrl.Read (*df)) {
		_error->DumpErrors();
		_error->Error (_("Failure reading the package's contents."));
		throw gdebError();
	}

	if (!ctrl.Control) {
		_error->DumpErrors();
		_error->Error (_("Could not extract the package's control record."));
		throw gdebError();
	}
}

string
DebPackage::Name (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Package");
	}
	return filename;
}

string
DebPackage::Version (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Version");
	}
	return _("(no version)");
}

string
DebPackage::Description_Short (void) {
	if (df && ctrl.Control) {
		string desc = ctrl.Section.FindS ("Description");
		string::size_type pos = desc.find ('\n');
		if (pos == string::npos) return desc;
		return string (desc, 0, pos);
	}
	return _("No description available");
}

string
DebPackage::Description_Long (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Description");
	}
	return _("No description available");
}

string
DebPackage::Section (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Section");
	}
	return _("Unknown");
}

string
DebPackage::Priority (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Priority");
	}
	return _("No priority available");
}

string
DebPackage::Size (void) {
	if (fd) {
		struct stat st;
		if (fstat (fd->Fd(), &st) == 0) {
			char size[64], suffix = 0;
			int fsize = st.st_size;

/* FIXME: get a decent mechanism */
			if (fsize > 4096) {
				fsize /= 1024;
				suffix = 'k';
			}
			if (fsize > 8096) {
				fsize /= 1024;
				suffix = 'M';
			}

			snprintf (size, 63, "%d%c", fsize, suffix);
			size[63] = 0;
			return size;
		}
		_error->Errno ("fstat", "Stat failed for %s", filename.c_str());
	}
	return _("N/A");
}

string
DebPackage::Installed_Size (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Installed-Size") + "k";
	}
	return _("Unknown");
}

string
DebPackage::Maintainer (void) {
	if (df && ctrl.Control) {
		return ctrl.Section.FindS ("Maintainer");
	}
	return _("Unknown");
}

string
DebPackage::Filelist (void) {
	if (df) {
		const ARArchive::Member* data = df->GotoMember ("data.tar.gz");
		if (!data) return "";

		ExtractTar tpart (df->GetFile(), data->Size);

		ListStream list;
		if (!tpart.Go (list)) return "";

		return list.getContent();
	}
	return "";
}

string
DebPackage::Author (void) {
	if (df && ctrl.Control) {
		string s = ctrl.Section.FindS ("Author");
		if (s.size()) return s;
	}
	return _("Unknown");
}

string
DebPackage::Homepage (void) {
	if (df && ctrl.Control) {
		string s = ctrl.Section.FindS ("Homepage");
		if (s.size()) return s;
	}
	return _("Unknown");
}

static void
gdeb_response_cb (GtkWidget* wdg, gint responseid, gpointer data) {
	if (responseid == GTK_RESPONSE_CLOSE) {
		gtk_main_quit();
	} else if (responseid == GDEB_RESPONSE_INSTALL) {
	  GtkWidget *dialog;
	  GksuContext *context;
	  int retvalue = 0;
	  gchar *password = NULL;
	  GError *error = NULL;

	  gchar* msg = g_strdup_printf (_("Gdeb needs superuser priviledges to install %s."),
									((DebPackage*) data)->Name().c_str());

	  dialog = gksuui_dialog_new ();
	  context = gksu_context_new ();
	  
	  gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), msg);
	  g_free (msg);

	  gtk_window_set_title (GTK_WINDOW(dialog), _("Enter root password"));
	  retvalue = gtk_dialog_run (GTK_DIALOG(dialog));
	  gtk_widget_hide (dialog);
  
	  msg = g_strdup_printf (_("x-terminal-emulator -e /bin/sh -c " \
							   "\\\"/usr/bin/dpkg -i %s && echo Installation done || echo Installation failed;" \
							   "echo Press enter to continue; read DISCARDED\\\""),
							 ((DebPackage*) data)->Filename().c_str());
	  if (retvalue != GTK_RESPONSE_OK)
		return;

      password = gksuui_dialog_get_password (GKSUUI_DIALOG(dialog));
	  gksu_context_set_command (context, msg);
	  gksu_context_set_password (context, password);
	  gksu_context_run (context, &error);
	  if(error) {
		printf("error:\n command: %s\n user: %s\n",
				 gksu_context_get_command (context),
				 gksu_context_get_user (context));
	  }
	}
}

static void
gdeb_printerr (const gchar* format, ...) {
	va_list args;
	va_start (args, format);
	gchar* msg = g_strdup_vprintf (format, args);
	va_end (args);

	GtkWidget* w = gtk_message_dialog_new (NULL, (GtkDialogFlags) 0,
	      GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, msg);
	gtk_dialog_set_has_separator (GTK_DIALOG (w), FALSE);
	gtk_dialog_run (GTK_DIALOG (w));

	g_free (msg);
	exit (EXIT_FAILURE);
}

static void
gdeb_usage (GtkMessageType msgtype = GTK_MESSAGE_INFO) {
	GtkWidget* w = gtk_message_dialog_new (NULL, (GtkDialogFlags) 0,
	      msgtype, GTK_BUTTONS_CLOSE,
	      _("Usage:\n"
			"  gdeb -I | --info <deb>\tShow information about package\n"
	        "  gdeb -h | --help\tDisplay this help and exit"));
	gtk_dialog_set_has_separator (GTK_DIALOG (w), FALSE);
	gtk_dialog_run (GTK_DIALOG (w));

}

int main ( int argc, char ** argv )
{
	bindtextdomain (GDEB_NAME, GNOMELOCALEDIR);
	bind_textdomain_codeset (GDEB_NAME, "UTF-8");
	textdomain (GDEB_NAME);

	gtk_init (&argc, &argv);

	if (argc <= 1) {
		gdeb_usage (GTK_MESSAGE_ERROR);
		exit (EXIT_FAILURE);
	}

	gchar* filename = NULL;

	gint optc;
	struct option lopts[] = { {"info", 1, 0, 'I'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} };
	while ((optc = getopt_long (argc, argv, ":I:h", lopts, NULL)) != -1) {
		if (optc == 'h') {
			gdeb_usage();
			exit (EXIT_SUCCESS);
		} else if (optc == '?') {
			gdeb_printerr (_("gdeb: unknown option -%c"), optopt);
		} else if (optc == ':') {
			gdeb_printerr (_("gdeb: missing option argument"));
		} else if (optc != 'I') {
			gdeb_printerr (_("The option parser is acting up!?"));
		}
		filename = optarg;
	}

	if (optind < argc) {
		gdeb_printerr (_("No action option was provided."));
	}

	try {
		DebPackage dp (filename);

  GDeb gd(_("GNOME .deb viewer"));

  gd.set_package(&dp);

	GtkWidget* window = gtk_dialog_new_with_buttons (
	      GDEB_NAME, NULL, GTK_DIALOG_NO_SEPARATOR,
	      _("Install"), GDEB_RESPONSE_INSTALL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_CLOSE);
	g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
	g_signal_connect (GTK_OBJECT (window), "response", G_CALLBACK (gdeb_response_cb), &dp);
	gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
	gtk_window_set_title (GTK_WINDOW (window), basename (filename));

	gtk_container_set_border_width (GTK_CONTAINER (gd.widget()), 6);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG (window)->vbox), gd.widget());

  gtk_widget_show_all(window);

  gtk_main();

		exit (EXIT_SUCCESS);
	} catch (gdebError) {
		string str;
		_error->PopMessage (str);
		gdeb_printerr (str.c_str());
	}
}
