#!/usr/bin/perl -w

#
# gtkgrepmail v.0.5 - (c) 2002 Terminus Network Services
#
# $Id: gtkgrepmail,v 1.11 2002/02/02 01:15:16 terminus Exp $
#
# 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, 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.

# Define modules
use Gnome;
use Date::Manip;
use File::Basename;
use URI::Escape;

# Define variables
$defaultMailpath="$ENV{HOME}/Mail";
$defaultMailbox="search-results";
$defaultGrepmail="/usr/bin/grepmail";
$defaultIMAP="/usr/bin/X11/xterm -e /usr/bin/elm -f";
$defaultSSH="/usr/bin/ssh";
$defaultHost="localhost";
$defaultUser="$ENV{USER}";
$package = 'gtkgrepmail';
$VERSION = '0.50';
$debug = 0;
$configpath = $ENV{HOME} . "/.gtkgrepmailrc";
    
# Main program routine
Gnome->init("$package", "$VERSION");
LoadOptions();
dlgMain_new();
refreshfiles();
# Now let Gtk handle signals
Gtk->main;

sub msgError_new {
    $errortext = shift;
    #
    # Construct a GnomeMessageBox 'msgError'
    $msgError_form = new Gnome::MessageBox("$errortext", 'error');
    $msgError_form->close_hides(0 );
    $msgError_form->set_close(1 );
    $msgError_form->position('none' );
    $msgError_form->set_policy(0, 0, 0 );
    $msgError_form->set_modal(1 );
    $msgError_form->realize;
    $accelgroup = new Gtk::AccelGroup;
    $accelgroup->attach($msgError_form);
    $msgError = $msgError_form;
	#
	# Construct a GtkVBox 'dialog_vbox1'
	$dialog_vbox1 = $msgError->vbox;
	$dialog_vbox1->show;
	    #
	    # Construct a GtkHButtonBox 'dialog_action_area1'
	    $dialog_action_area1 = new Gtk::HButtonBox;
	    $dialog_vbox1->add($dialog_action_area1);
	    $dialog_action_area1->show;
	    $dialog_action_area1->set_layout('end' );
	    $dialog_action_area1->set_spacing(8 );
	    $dialog_action_area1->set_child_size(85, 27 );
	    $dialog_action_area1->set_child_ipadding(7, 0 );
		#
		# Construct a GtkButton 'btnOK'
		$btnOK = Gnome::Stock->button('Button_Ok' );
		$dialog_action_area1->add($btnOK);
		$btnOK->show;
		$btnOK->can_default(1 );
		$btnOK->can_focus(1 );
	    $dialog_vbox1->set_child_packing($dialog_action_area1, 0, 1, 0, 'end' );
    $btnOK->signal_connect( 'clicked', "on_btnOK_clicked" );

    $msgError_form->show;
}

sub on_btnOK_clicked {
    $msgError_form->destroy()
};

sub dlgOptions_new {
    #
    # Construct a GnomeDialog 'dlgOptions'
    $dlgOptions_form = new Gnome::Dialog('gtkgrepmail');
    $dlgOptions_form->close_hides(0 );
    $dlgOptions_form->set_close(0 );
    $dlgOptions_form->position('center' );
    $dlgOptions_form->set_policy(0, 0, 0 );
    $dlgOptions_form->set_modal(1 );
    $dlgOptions_form->realize;
    $accelgroup = new Gtk::AccelGroup;
    $accelgroup->attach($dlgOptions_form );
    $dlgOptions = $dlgOptions_form;
    $dlgOptions->border_width(10 );
	#
	# Construct a GtkVBox 'dvbMain'
	$dvbMain = $dlgOptions->vbox;
	$dvbMain->show;
	    #
	    # Construct a GtkVBox 'vboFields'
	    $vboFields = new Gtk::VBox(0, 0 );
	    $dvbMain->add($vboFields);
	    $vboFields->show;
		#
		# Construct a GtkLabel 'lblMailpath'
		$lblMailpath = new Gtk::Label('Path (local or remote) to mail folders');
		$lblMailpath->set_justify('left' );
		$lblMailpath->set_line_wrap(0 );
		$vboFields->add($lblMailpath);
		$lblMailpath->show;
		$lblMailpath->set_alignment(0, 0 );
		$vboFields->set_child_packing($lblMailpath, 0, 0, 0, 'start' );
		#
		# Construct a GnomeFileEntry 'fileMailpath'
		$fileMailpath = new Gnome::FileEntry('', '');
		$fileMailpath->gnome_entry->set_max_saved(10 );
		$fileMailpath->set_directory(0 );
		$fileMailpath->set_modal(0);
		$vboFields->add($fileMailpath);
		$fileMailpath->show;
		    #
		    # Construct a GtkEntry 'cmbMailpath'
		    $cmbMailpath = $fileMailpath->gtk_entry;
		    $cmbMailpath->show;
		    $cmbMailpath->can_focus(1 );
		    $cmbMailpath->set_max_length(0 );
		    $cmbMailpath->set_visibility(1 );
		    $cmbMailpath->set_editable(1 );
		$vboFields->set_child_packing($fileMailpath, 0, 0, 0, 'start' );
		#
		# Construct a GtkLabel 'lblMailbox'
		$lblMailbox = new Gtk::Label('Relative path to temporary mailbox');
		$lblMailbox->set_justify('left' );
		$lblMailbox->set_line_wrap(0 );
		$vboFields->add($lblMailbox);
		$lblMailbox->show;
		$lblMailbox->set_alignment(0, 0 );
		$vboFields->set_child_packing($lblMailbox, 0, 0, 0, 'start' );
		#
		# Construct a GnomeFileEntry 'fileMailbox'
		$fileMailbox = new Gnome::FileEntry('', '');
		$fileMailbox->gnome_entry->set_max_saved(10 );
		$fileMailbox->set_directory(0 );
		$fileMailbox->set_modal(0);
		$vboFields->add($fileMailbox);
		$fileMailbox->show;
		    #
		    # Construct a GtkEntry 'cmbMailbox'
		    $cmbMailbox = $fileMailbox->gtk_entry;
		    $cmbMailbox->show;
		    $cmbMailbox->can_focus(1 );
		    $cmbMailbox->set_max_length(0 );
		    $cmbMailbox->set_visibility(1 );
		    $cmbMailbox->set_editable(1 );
		$vboFields->set_child_packing($fileMailbox, 0, 0, 0, 'start' );
		#
		# Construct a GtkLabel 'lblGrepmail'
		$lblGrepmail = new Gtk::Label('Path (local or remote) to grepmail');
		$lblGrepmail->set_justify('left' );
		$lblGrepmail->set_line_wrap(0 );
		$vboFields->add($lblGrepmail);
		$lblGrepmail->show;
		$lblGrepmail->set_alignment(0, 0 );
		$vboFields->set_child_packing($lblGrepmail, 0, 0, 0, 'start' );
		#
		# Construct a GnomeFileEntry 'fileGrepmail'
		$fileGrepmail = new Gnome::FileEntry('', '');
		$fileGrepmail->gnome_entry->set_max_saved(10 );
		$fileGrepmail->set_directory(0 );
		$fileGrepmail->set_modal(0);
		$vboFields->add($fileGrepmail);
		$fileGrepmail->show;
		    #
		    # Construct a GtkEntry 'cmbGrepmail'
		    $cmbGrepmail = $fileGrepmail->gtk_entry;
		    $cmbGrepmail->show;
		    $cmbGrepmail->can_focus(1 );
		    $cmbGrepmail->set_max_length(0 );
		    $cmbGrepmail->set_visibility(1 );
		    $cmbGrepmail->set_editable(1 );
		#
		# Construct a GtkLabel 'lblIMAP'
		$lblIMAP = new Gtk::Label('Path to IMAP or mailbox browser');
		$lblIMAP->set_justify('left' );
		$lblIMAP->set_line_wrap(0 );
		$vboFields->add($lblIMAP);
		$lblIMAP->show;
		$lblIMAP->set_alignment(0, 0 );
		$vboFields->set_child_packing($lblIMAP, 0, 0, 0, 'start' );
		#
		# Construct a GnomeFileEntry 'fileIMAP'
		$fileIMAP = new Gnome::FileEntry('', '');
		$fileIMAP->gnome_entry->set_max_saved(10 );
		$fileIMAP->set_directory(0 );
		$fileIMAP->set_modal(0);
		$vboFields->add($fileIMAP);
		$fileIMAP->show;
		    #
		    # Construct a GtkEntry 'cmbIMAP'
		    $cmbIMAP = $fileIMAP->gtk_entry;
		    $cmbIMAP->show;
		    $cmbIMAP->can_focus(1 );
		    $cmbIMAP->set_max_length(0 );
		    $cmbIMAP->set_visibility(1 );
		    $cmbIMAP->set_editable(1 );
		$vboFields->set_child_packing($fileIMAP, 0, 0, 0, 'start' );
		#
		# Construct a GtkLabel 'lblSSH'
		$lblSSH = new Gtk::Label('Path to ssh or rsh');
		$lblSSH->set_justify('left' );
		$lblSSH->set_line_wrap(0 );
		$vboFields->add($lblSSH);
		$lblSSH->show;
		$lblSSH->set_alignment(0, 0 );
		$vboFields->set_child_packing($lblSSH, 0, 0, 0, 'start' );
		#
		# Construct a GnomeFileEntry 'fileSSH'
		$fileSSH = new Gnome::FileEntry('', '');
		$fileSSH->gnome_entry->set_max_saved(10 );
		$fileSSH->set_directory(0 );
		$fileSSH->set_modal(0);
		$vboFields->add($fileSSH);
		$fileSSH->show;
		    #
		    # Construct a GtkEntry 'cmbSSH'
		    $cmbSSH = $fileSSH->gtk_entry;
		    $cmbSSH->show;
		    $cmbSSH->can_focus(1 );
		    $cmbSSH->set_max_length(0 );
		    $cmbSSH->set_visibility(1 );
		    $cmbSSH->set_editable(1 );
		$vboFields->set_child_packing($fileSSH, 0, 0, 0, 'start' );
		#
		# Construct a GtkTable 'tblLogin'
		$tblLogin = new Gtk::Table(4, 2, 0 );
		$tblLogin->set_row_spacings(0 );
		$tblLogin->set_col_spacings(0 );
		$vboFields->add($tblLogin);
		$tblLogin->show;
		    #
		    # Construct a GtkLabel 'lblHost'
		    $lblHost = new Gtk::Label('Host');
		    $lblHost->set_justify('left' );
		    $lblHost->set_line_wrap(0 );
		    $tblLogin->attach($lblHost, 0, 1, 0, 1, ['fill'], [], 0, 0 );
		    $lblHost->show;
		    $lblHost->set_alignment(0, 0 );
		    #
		    # Construct a GtkEntry 'entHost'
		    $entHost = new Gtk::Entry;
		    $tblLogin->attach($entHost, 0, 1, 1, 2, ['expand', 'fill'], [], 0, 0 );
		    $entHost->show;
		    $entHost->can_focus(1 );
		    $entHost->set_max_length(50 );
		    $entHost->set_visibility(1 );
		    $entHost->set_editable(1 );
		    #
		    # Construct a GtkLabel 'lblPort'
#		    $lblPort = new Gtk::Label('SSH port');
#		    $lblPort->set_justify('left' );
#		    $lblPort->set_line_wrap(0 );
#		    $tblLogin->attach($lblPort, 1, 2, 0, 1, ['fill'], [], 0, 0 );
#		    $lblPort->show;
#		    $lblPort->set_alignment(0, 0 );
		    #
		    # Construct a GtkEntry 'entPort'
#		    $entPort = new Gtk::Entry;
#		    $tblLogin->attach($entPort, 1, 2, 1, 2, ['expand', 'fill'], [], 0, 0 );
#		    $entPort->show;
#		    $entPort->can_focus(1 );
#		    $entPort->set_max_length(5 );
#		    $entPort->set_editable(1 );
		    #
		    # Construct a GtkLabel 'lblUser'
		    $lblUser = new Gtk::Label('User');
		    $lblUser->set_justify('left' );
		    $lblUser->set_line_wrap(0 );
		    $tblLogin->attach($lblUser, 0, 1, 2, 3, ['fill'], [], 0, 0 );
		    $lblUser->show;
		    $lblUser->set_alignment(0, 0 );
		    #
		    # Construct a GtkLabel 'lblPassword'
#		    $lblPassword = new Gtk::Label('Password');
#		    $lblPassword->set_justify('left' );
#		    $lblPassword->set_line_wrap(0 );
#		    $tblLogin->attach($lblPassword, 1, 2, 2, 3, ['fill'], [], 0, 0 );
#		    $lblPassword->show0
#		    $lblPassword->set_alignment(0, 0 );
		    #
		    # Construct a GtkEntry 'entUser'
		    $entUser = new Gtk::Entry;
		    $tblLogin->attach($entUser, 0, 1, 3, 4, ['expand', 'fill'], [], 0, 0 );
		    $entUser->show;
		    $entUser->can_focus(1 );
		    $entUser->set_text('');
		    $entUser->set_max_length(30 );
		    $entUser->set_visibility(1 );
		    $entUser->set_editable(1 );
		    #
		    # Construct a GtkEntry 'entPassword'
#		    $entPassword = new Gtk::Entry;
#		    $tblLogin->attach($entPassword, 1, 2, 3, 4, ['expand', 'fill'], [], 0, 0 );
#		    $entPassword->show;
#		    $entPassword->can_focus(1 );
#		    $entPassword->set_text('');
#		    $entPassword->set_max_length(30 );
#		    $entPassword->set_visibility(0 );
#		    $entPassword->set_editable(1 );
#		$vboFields->set_child_packing($tblLogin, 1, 1, 0, 'start' );
#	    $dvbMain->set_child_packing($vboFields, 1, 1, 0, 'start' );
	    #
	    # Construct a GtkHButtonBox 'dacButton'
	    $dacButton = new Gtk::HButtonBox;
	    $dvbMain->add($dacButton);
	    $dacButton->show;
	    $dacButton->set_layout('end' );
	    $dacButton->set_spacing(8 );
	    $dacButton->set_child_size(85, 27 );
	    $dacButton->set_child_ipadding(7, 0 );
		#
		# Construct a GtkButton 'pbSave'
		$pbSave = new Gtk::Button('_Save');
		$dacButton->add($pbSave);
		$pbSave->show;
		$pbSave->can_default(1 );
		$pbSave->can_focus(1 );
		$pbSave_key = $pbSave->child->parse_uline('_Save');
		$pbSave->add_accelerator('clicked', $accelgroup, $pbSave_key, 'mod1_mask', ['visible', 'locked'] );
		#
		# Construct a GtkButton 'pbCancel'
		$pbCancel = new Gtk::Button('_Cancel');
		$dacButton->add($pbCancel);
		$pbCancel->show;
		$pbCancel->can_default(1 );
		$pbCancel->can_focus(1 );
		$pbCancel_key = $pbCancel->child->parse_uline('_Cancel');
		$pbCancel->add_accelerator('clicked', $accelgroup, $pbCancel_key, 'mod1_mask', ['visible', 'locked'] );
	    $dvbMain->set_child_packing($dacButton, 0, 1, 0, 'end' );
    #
    # Connect all signals now that dlgOptions_form are constructed
    $dlgOptions->signal_connect( 'destroy', "on_dlgOptions_destroy" );
    $dlgOptions->signal_connect( 'show', "on_dlgOptions_show" );
    $pbSave->signal_connect( 'clicked', "on_pbSave_clicked" );
    $pbCancel->signal_connect( 'clicked', "on_pbCancel_clicked" );
    $cmbMailbox->signal_connect( 'activate', "on_cmbMailbox_activate" );

    $dlgOptions_form->show;
}

sub on_dlgOptions_destroy {
    on_pbCancel_clicked();
}

sub on_cmbMailbox_activate {
    $usrMailbox = $cmbMailbox->get_text();
    $cmbMailbox->set_text(basename($usrMailbox));
}

sub on_dlgOptions_show {
    LoadOptions();
    $cmbMailpath->set_text($usrMailpath);
    $cmbMailbox->set_text($usrMailbox);
    $cmbGrepmail->set_text($usrGrepmail);
    $cmbIMAP->set_text($usrIMAP);
    $cmbSSH->set_text($usrSSH);
    $entHost->set_text($usrHost);
#    $entPort->set_text($usrPort);
    $entUser->set_text($usrUser);
#    $entPassword->set_text($usrPassword);
}

sub LoadOptions {
    load_config();
    $usrMailpath = $cfg{Mailpath} || $defaultMailpath;
    $usrMailbox = $cfg{Mailbox} || $defaultMailbox;
    $usrGrepmail = $cfg{Grepmail} || $defaultGrepmail;
    $usrIMAP = $cfg{IMAP} || $defaultIMAP;
    $usrSSH = $cfg{SSH} || $defaultSSH;
    $usrHost = $cfg{Host} || $defaultHost;
#    $usrPort = $cfg{Port} || $defaultPort;
    $usrUser = $cfg{User} || $defaultUser;
#    $usrPassword = $cfg{Password} || $defaultPassword;
    @sshcommand = split(/ /, $usrSSH);
    mailpath_create();
}

sub mailpath_create {
    # Remove trailing slash, if any
    $len = length($usrMailpath);
    $pos = rindex($usrMailpath, '/');
    if ($pos == $len - 1) {
      $usrMailpath = substr($usrMailpath, 0, $len - 1)
    };
    # Escape shell characters
    $valMailpath = $usrMailpath;
    $valMailpath =~ s/([\&;\`'! \\\|"*?<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    $valMailbox = $usrMailbox;
    $valMailbox =~ s/([\&;\`'! \\\|\/"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    $valGrepmail = $usrGrepmail;
    $valGrepmail =~ s/([\&;\`'! \\\|"*?<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
}

sub on_pbCancel_clicked {
    $dlgOptions_form->hide(); 
}

sub on_pbSave_clicked {
    $usrMailpath = $cmbMailpath->get_text();
    $usrMailbox = $cmbMailbox->get_text();
    $usrGrepmail = $cmbGrepmail->get_text();
    $usrIMAP = $cmbIMAP->get_text();
    $usrSSH = $cmbSSH->get_text();
    $usrHost = $entHost->get_text();
#    $usrPort = $entPort->get_text();
    $usrUser = $entUser->get_text();
#    $usrPassword = $entPassword->get_text();
    # Do some rudimentary sanity checking
    if (! -x $usrIMAP ) {
      push @errorlist, "* Cannot execute IMAP browser (use /bin/true if you are sure you do not want one)\n"
    };
    unless ( $usrHost ) {
      $usrHost = "localhost"
    };
    if ( ( $usrHost ne "localhost" ) &! -x $usrSSH ) {
      push @errorlist, "* Cannot execute ssh or rsh\n"
    };
    if ( ( $usrHost ne "localhost" ) &! $usrUser ) {
      push @errorlist, "* You must specify a username\n"
    };
    if ( ( $usrHost eq "localhost" ) &! -d $usrMailpath ) {
      push @errorlist, "* Mailbox path does not exist\n"
    };
    if ( ( $usrHost eq "localhost" ) &! -x $usrGrepmail ) {
      push @errorlist, "* Cannot execute grepmail\n"
    };
    unless ( $usrMailbox ) {
      push @errorlist, "* You must specify a mailbox for search results\n"
    };
    unless ( $usrMailpath ) {
      push @errorlist, "* You must specify a fully-qualified mailbox path\n"
    };
    if ( $usrHost =~ /([\&;\`'! \\\|\/"*?~<>^\(\)\[\]\{\}\$\n\r])/ ) {
      push @errorlist, "* Illegal characters in hostname\n"
    };
    if ( $usrUser =~ /([\&;\`'! \\\|\/"*?~<>^\(\)\[\]\{\}\$\n\r])/ ) {
      push @errorlist, "* Illegal characters in username\n"
    };
    if ( @errorlist ) {
      chomp $errorlist[$#errorlist];
      msgError_new("Please correct the following errors:\n@errorlist");
      undef (@errorlist);
    } else {
      open (CONFIG, ">$configpath") || msgError_new("Cannot write to configuration file $configpath");
      save_config('Mailpath', $usrMailpath);
      save_config('Mailbox', $usrMailbox);
      save_config('Grepmail', $usrGrepmail);
      save_config('IMAP', $usrIMAP);
      save_config('SSH', $usrSSH);
      save_config('Host', $usrHost);
#      save_config('Port', $usrPort);
      # We don't want to allow the port to be set as this doesn't work with rsh
      save_config('User', $usrUser);
#      save_config('Password', $usrPassword);
      # Don't offer to save password, use ssh-agent and ssh-add instead
      close CONFIG;
      mailpath_create;
      refreshfiles();
      $dlgOptions_form->hide(); 
    }
}

sub dlgMain_new {
    #
    # Construct a GnomeApp 'dlgMain'
    $dlgMain_form = new Gnome::App('gtkgrepmail', 'gtkgrepmail');
    $dlgMain_form->enable_layout_config(1 );
    $dlgMain_form->position('none' );
    $dlgMain_form->set_policy(0, 1, 0 );
    $dlgMain_form->set_modal(0 );
    $dlgMain_form->realize;
    $accelgroup = new Gtk::AccelGroup;
    $accelgroup->attach($dlgMain_form );
    $dlgMain = $dlgMain_form;
	#
	# Construct a GnomeDock 'dock1'
	$dock1 = $dlgMain->get_dock;
	$dock1->allow_floating_items(1 );
	$dock1->show;
	    #
	    # Construct a GnomeDockItem 'dockitem1'
	    $dockitem1 = new Gnome::DockItem('dockitem1', ['exclusive', 'never_vertical'] );
	    $dockitem1->set_shadow_type('out' );
	    $dock1->add_item($dockitem1, 'top', 0, 0, 0, 0 );
	    $dockitem1->show;
	    $dockitem1->border_width(2 );
		#
		# Construct a GtkMenuBar 'menubar'
		$menubar = new Gtk::MenuBar;
		$menubar->set_shadow_type('none' );
		$dockitem1->add($menubar);
		$menubar->show;
		    #
		    # Construct a GtkMenuItem 'mnuFile'
		    $mnuFile = new Gtk::MenuItem('_File');
		    $menubar->append($mnuFile);
		    $mnuFile->show;
		    $mnuFile_key = $mnuFile->child->parse_uline('_File');
		    $mnuFile->add_accelerator('activate_item', $accelgroup, $mnuFile_key, 'mod1_mask', ['visible', 'locked'] );
			#
			# Construct a GtkMenu 'mnuFile_menu'
			$mnuFile_menu = new Gtk::Menu;
			$mnuFile->set_submenu($mnuFile_menu);
			    #
			    # Construct a GtkPixmapMenuItem 'mnuExit'
			    $mnuExit = Gnome::Stock->menu_item('Menu_Quit', 'Exit');
			    $accelgroup->add(81, ['control_mask'], ['visible', 'locked'], $mnuExit, 'activate');
			    $mnuFile_menu->append($mnuExit);
			    $mnuExit->show;
		    #
		    # Construct a GtkMenuItem 'mnuSettings'
		    $mnuSettings = new Gtk::MenuItem('_Settings');
		    $menubar->append($mnuSettings);
		    $mnuSettings->show;
		    $mnuSettings_key = $mnuSettings->child->parse_uline('_Settings');
		    $mnuSettings->add_accelerator('activate_item', $accelgroup, $mnuSettings_key, 'mod1_mask', ['visible', 'locked'] );
			#
			# Construct a GtkMenu 'mnuSettings_menu'
			$mnuSettings_menu = new Gtk::Menu;
			$mnuSettings->set_submenu($mnuSettings_menu);
			    #
			    # Construct a GtkPixmapMenuItem 'mnuPreferences'
			    $mnuPreferences = Gnome::Stock->menu_item('Menu_Preferences', 'Preferences');
			    $mnuSettings_menu->append($mnuPreferences);
			    $mnuPreferences->show;
		    #
		    # Construct a GtkMenuItem 'mnuHelp'
		    $mnuHelp = new Gtk::MenuItem('_Help');
		    $menubar->append($mnuHelp);
		    $mnuHelp->show;
		    $mnuHelp_key = $mnuHelp->child->parse_uline('_Help');
		    $mnuHelp->add_accelerator('activate_item', $accelgroup, $mnuHelp_key, 'mod1_mask', ['visible', 'locked'] );
			#
			# Construct a GtkMenu 'mnuHelp_menu'
			$mnuHelp_menu = new Gtk::Menu;
			$mnuHelp->set_submenu($mnuHelp_menu);
			    #
			    # Construct a GtkPixmapMenuItem 'mnuManual'
			    $mnuManual = new Gtk::MenuItem('Manual');
			    $mnuHelp_menu->append($mnuManual);
			    $mnuManual->show;
			    #
			    # Let's have a separator here
			    $mnuSeparator = new Gtk::MenuItem();
			    $mnuHelp_menu->append($mnuSeparator);
			    $mnuSeparator->show;
			    #
			    # Construct a GtkPixmapMenuItem 'mnuAbout'
			    $mnuAbout = Gnome::Stock->menu_item('Menu_About', 'About');
			    $mnuHelp_menu->append($mnuAbout);
			    $mnuAbout->show;
	    #
	    # Construct a GtkVBox 'vbox1'
	    $vbox1 = new Gtk::VBox(0, 0 );
	    $dlgMain->set_contents($vbox1);
	    $vbox1->show;
		#
		# Construct a GtkNotebook 'notebook'
		$notebook = new Gtk::Notebook;
		$vbox1->add($notebook);
		$notebook->show;
		$notebook->can_focus(1 );
		$notebook->set_tab_pos('top' );
		$notebook->set_show_tabs(1 );
		$notebook->set_show_border(1 );
		$notebook->set_scrollable(0 );
		$notebook->set_tab_hborder(2 );
		$notebook->set_tab_vborder(2 );
		    #
		    # Construct a GtkTable 'tableSearch'
		    $tableSearch = new Gtk::Table(6, 2, 0 );
		    $tableSearch->set_row_spacings(0 );
		    $tableSearch->set_col_spacings(0 );
		    $tableSearch->show;
			#
			# Construct a GtkLabel 'lblSearchText'
			$lblSearchText = new Gtk::Label('');
			$lblSearchText->parse_uline('_Expression');
			$lblSearchText->set_justify('left' );
			$lblSearchText->set_line_wrap(0 );
			$tableSearch->attach($lblSearchText, 0, 1, 0, 1, [], [], 0, 0 );
			$lblSearchText->show;
			#
			# Construct a GtkEntry 'entSearch'
			$entSearch = new Gtk::Entry;
			$tableSearch->attach($entSearch, 1, 2, 0, 1, ['expand', 'fill'], [], 0, 0 );
			$entSearch->show;
			$entSearch->can_focus(1 );
			$entSearch->set_text('');
			$entSearch->set_max_length(0 );
			$entSearch->set_visibility(1 );
			$entSearch->set_editable(1 );
			#
			# Construct a GtkLabel 'lblContain'
			$lblContain = new Gtk::Label('contained in');
			$lblContain->set_justify('left' );
			$lblContain->set_line_wrap(0 );
			$tableSearch->attach($lblContain, 0, 1, 1, 2, [], [], 0, 0 );
			$lblContain->show;
			#
			# Construct a GtkHBox 'hbox1'
			$hbox1 = new Gtk::HBox(0, 0 );
			$tableSearch->attach($hbox1, 1, 2, 1, 2, ['fill'], ['fill'], 0, 0 );
			$hbox1->show;
			    #
			    # Construct a GtkCheckButton 'chkBody'
			    $chkBody = new Gtk::CheckButton('Body');
			    $chkBody->set_active(1 );
			    $hbox1->add($chkBody);
			    $chkBody->show;
			    $chkBody->can_focus(1 );
			    $hbox1->set_child_packing($chkBody, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkCheckButton 'chkHeaders'
			    $chkHeaders = new Gtk::CheckButton('Headers');
			    $chkHeaders->set_active(1 );
			    $hbox1->add($chkHeaders);
			    $chkHeaders->show;
			    $chkHeaders->can_focus(1 );
			    $hbox1->set_child_packing($chkHeaders, 0, 0, 0, 'start' );
			#
			# Construct a GtkLabel 'lblContain2'
			$lblContain2 = new Gtk::Label('contained in');
			$lblContain2->set_justify('center' );
			$lblContain2->set_line_wrap(0 );
			$tableSearch->attach($lblContain2, 0, 1, 3, 4, [], [], 0, 0 );
			$lblContain2->show;
			$lblContain2->set_alignment(0.5, 0.5 );
			#
			# Construct a GtkHBox 'hbox2'
			$hbox2 = new Gtk::HBox(0, 0 );
			$tableSearch->attach($hbox2, 1, 2, 3, 4, ['fill'], ['fill'], 0, 0 );
			$hbox2->show;
			    #
			    # Construct a GtkCheckButton 'chkBody2'
			    $chkBody2 = new Gtk::CheckButton('Body');
			    $chkBody2->set_active(1 );
			    $hbox2->add($chkBody2);
			    $chkBody2->show;
			    $chkBody2->can_focus(1 );
			    $hbox2->set_child_packing($chkBody2, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkCheckButton 'chkHeaders2'
			    $chkHeaders2 = new Gtk::CheckButton('Headers');
			    $chkHeaders2->set_active(1 );
			    $hbox2->add($chkHeaders2);
			    $chkHeaders2->show;
			    $chkHeaders2->can_focus(1 );
			    $hbox2->set_child_packing($chkHeaders2, 0, 0, 0, 'start' );
			#
			# Construct a GtkHBox 'hbox3'
			$hbox3 = new Gtk::HBox(0, 0 );
			$tableSearch->attach($hbox3, 0, 1, 2, 3, ['fill'], ['fill'], 0, 0 );
			$hbox3->show;
			    #
			    # Construct a GtkRadioButton 'rdbAnd'
			    $rdbAnd = new Gtk::RadioButton('and');
			    $hbox3->add($rdbAnd);
			    $rdbAnd->show;
			    $rdbAnd->can_focus(1 );
			    $rdbAnd->set_active(0 );
			    $hbox3->set_child_packing($rdbAnd, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkRadioButton 'rdbOr'
			    $rdbOr = new Gtk::RadioButton('or', $rdbAnd);
			    $hbox3->add($rdbOr);
			    $rdbOr->show;
			    $rdbOr->can_focus(1 );
			    $rdbOr->set_active(0 );
			    $hbox3->set_child_packing($rdbOr, 0, 0, 0, 'start' );
			#
			# Construct a GtkEntry 'entAnd'
			$entAnd = new Gtk::Entry;
			$tableSearch->attach($entAnd, 1, 2, 2, 3, ['expand', 'fill'], [], 0, 0 );
			$entAnd->show;
			$entAnd->can_focus(1 );
			$entAnd->set_text('');
			$entAnd->set_max_length(0 );
			$entAnd->set_visibility(1 );
			$entAnd->set_editable(1 );
			#
			# Construct a GtkLabel 'lblNot'
			$lblNot = new Gtk::Label('and not');
			$lblNot->set_justify('center' );
			$lblNot->set_line_wrap(0 );
			$tableSearch->attach($lblNot, 0, 1, 4, 5, [], [], 0, 0 );
			$lblNot->show;
			$lblNot->set_alignment(0.5, 0.5 );
			#
			# Construct a GtkEntry 'entNot'
			$entNot = new Gtk::Entry;
			$tableSearch->attach($entNot, 1, 2, 4, 5, ['expand', 'fill'], [], 0, 0 );
			$entNot->show;
			$entNot->can_focus(1 );
			$entNot->set_text('');
			$entNot->set_max_length(0 );
			$entNot->set_visibility(1 );
			$entNot->set_editable(1 );
			#
			# Construct a GtkLabel 'lblContain3'
			$lblContain3 = new Gtk::Label('contained in');
			$lblContain3->set_justify('center' );
			$lblContain3->set_line_wrap(0 );
			$tableSearch->attach($lblContain3, 0, 1, 5, 6, [], [], 0, 0 );
			$lblContain3->show;
			$lblContain3->set_alignment(0.5, 0.5 );
			#
			# Construct a GtkHBox 'hbox6'
			$hbox6 = new Gtk::HBox(0, 0 );
			$tableSearch->attach($hbox6, 1, 2, 5, 6, ['fill'], ['fill'], 0, 0 );
			$hbox6->show;
			    #
			    # Construct a GtkCheckButton 'chkBody3'
			    $chkBody3 = new Gtk::CheckButton('Body');
			    $chkBody3->set_active(1 );
			    $hbox6->add($chkBody3);
			    $chkBody3->show;
			    $chkBody3->can_focus(1 );
			    $hbox6->set_child_packing($chkBody3, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkCheckButton 'chkHeaders3'
			    $chkHeaders3 = new Gtk::CheckButton('Headers');
			    $chkHeaders3->set_active(1 );
			    $hbox6->add($chkHeaders3);
			    $chkHeaders3->show;
			    $chkHeaders3->can_focus(1 );
			    $hbox6->set_child_packing($chkHeaders3, 0, 0, 0, 'start' );
		    #
		    # Construct a GtkLabel 'lblSearchfor'
		    $lblSearchfor = new Gtk::Label('');
		    $lblSearchfor->parse_uline('Sea_rch for');
		    $lblSearchfor->set_justify('center' );
		    $lblSearchfor->set_line_wrap(0 );
		    $notebook->append_page($tableSearch, $lblSearchfor);
		    $lblSearchfor->show;
		    $lblSearchfor->set_alignment(0.5, 0.5 );
		    #
		    # Construct a GtkVBox 'vbox4'
		    $vbox4 = new Gtk::VBox(0, 0 );
		    $vbox4->show;
			#
			# Construct a GtkScrolledWindow 'scrFiles'
			$scrFiles = new Gtk::ScrolledWindow( undef, undef);
			$scrFiles->set_policy('always', 'always' );
			$scrFiles->border_width(0 );
			$scrFiles->hscrollbar->set_update_policy('continuous' );
			$scrFiles->vscrollbar->set_update_policy('continuous' );
			$vbox4->add($scrFiles);
			$scrFiles->show;
			    #
			    # Construct a GtkViewport 'vptFiles'
			    $vptFiles = new Gtk::Viewport(new Gtk::Adjustment( 0.0, 0.0, 101.0, 0.1, 1.0, 1.0), new Gtk::Adjustment( 0.0, 0.0, 101.0, 0.1, 1.0, 1.0) );
			    $vptFiles->set_shadow_type('in' );
			    $scrFiles->add_with_viewport($vptFiles);
			    $vptFiles->show;
				#
				# Construct a GtkList 'lstFiles'
				$lstFiles = new Gtk::CList(1);
				$lstFiles->set_selection_mode('multiple' );
				$lstFiles->set_shadow_type('none');
				$vptFiles->add($lstFiles);
				$lstFiles->show;
			$vbox4->set_child_packing($scrFiles, 1, 1, 0, 'start' );
			#
			# Construct a GtkHBox 'hbox4'
			$hbox4 = new Gtk::HBox(0, 0 );
			$vbox4->add($hbox4);
			$hbox4->show;
			    #
			    # Construct a GtkButton 'pbRefresh'
			    $pbRefresh = new Gtk::Button('_Refresh');
			    $hbox4->add($pbRefresh);
			    $pbRefresh->show;
			    $pbRefresh->can_focus(1 );
			    $pbRefresh_key = $pbRefresh->child->parse_uline('_Refresh');
			    $pbRefresh->add_accelerator('clicked', $accelgroup, $pbRefresh_key, 'mod1_mask', ['visible', 'locked'] );
			    $hbox4->set_child_packing($pbRefresh, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkCheckButton 'chkRecurse'
			    $chkRecurse = new Gtk::CheckButton('Recurse _directories');
			    $chkRecurse->set_active(0 );
			    $hbox4->add($chkRecurse);
			    $chkRecurse->show;
			    $chkRecurse->can_focus(1 );
			    $chkRecurse_key = $chkRecurse->child->parse_uline('Recurse _directories');
			    $chkRecurse->add_accelerator('clicked', $accelgroup, $chkRecurse_key, 'mod1_mask', ['visible', 'locked'] );
			    $hbox4->set_child_packing($chkRecurse, 0, 0, 0, 'start' );
			$vbox4->set_child_packing($hbox4, 0, 0, 0, 'start' );
		    #
		    # Construct a GtkLabel 'lblSearchin'
		    $lblSearchin = new Gtk::Label('');
		    $lblSearchin->parse_uline('Search _in');
		    $lblSearchin->set_justify('center' );
		    $lblSearchin->set_line_wrap(0 );
		    $notebook->append_page($vbox4, $lblSearchin);
		    $lblSearchin->show;
		    $lblSearchin->set_alignment(0.5, 0.5 );
		    #
		    # Construct a GtkVBox 'vbox2'
		    $vbox2 = new Gtk::VBox(0, 0 );
		    $vbox2->show;
			#
			# Construct a GtkFrame 'frmDate'
			$frmDate = new Gtk::Frame('Search by date');
			$frmDate->set_label_align(0, 0 );
			$frmDate->set_shadow_type('etched_in' );
			$vbox2->add($frmDate);
			$frmDate->show;
			    #
			    # Construct a GtkVBox 'vbox3'
			    $vbox3 = new Gtk::VBox(0, 0 );
			    $frmDate->add($vbox3);
			    $vbox3->show;
				#
				# Construct a GtkTable 'tblSearch'
				$tblSearch = new Gtk::Table(4, 2, 0 );
				$tblSearch->set_row_spacings(0 );
				$tblSearch->set_col_spacings(0 );
				$vbox3->add($tblSearch);
				$tblSearch->show;
				    #
				    # Construct a GtkRadioButton 'rdbBetween'
				    $rdbBetween = new Gtk::RadioButton('_Between');
				    $tblSearch->attach($rdbBetween, 0, 1, 1, 2, [], [], 0, 0 );
				    $rdbBetween->show;
				    $rdbBetween->can_focus(1 );
				    $rdbBetween_key = $rdbBetween->child->parse_uline('_Between');
				    $rdbBetween->add_accelerator('clicked', $accelgroup, $rdbBetween_key, 'mod1_mask', ['visible', 'locked'] );
				    $rdbBetween->set_active(0 );
				    #
				    # Construct a GtkLabel 'lblAnd'
				    $lblAnd = new Gtk::Label('and');
				    $lblAnd->set_justify('right' );
				    $lblAnd->set_line_wrap(0 );
				    $tblSearch->attach($lblAnd, 0, 1, 2, 3, [], [], 0, 0 );
				    $lblAnd->show;
				    $lblAnd->set_alignment(0.5, 0.5 );
				    #
				    # Construct a GtkRadioButton 'rdbExpression'
				    $rdbExpression = new Gtk::RadioButton('_Expression', $rdbBetween);
				    $tblSearch->attach($rdbExpression, 0, 1, 3, 4, [], [], 0, 0 );
				    $rdbExpression->show;
				    $rdbExpression->can_focus(1 );
				    $rdbExpression_key = $rdbExpression->child->parse_uline('_Expression');
				    $rdbExpression->add_accelerator('clicked', $accelgroup, $rdbExpression_key, 'mod1_mask', ['visible', 'locked'] );
				    $rdbExpression->set_active(0 );
				    #
				    # Construct a GtkEntry 'dateExpression'
				    $dateExpression = new Gtk::Entry;
				    $tblSearch->attach($dateExpression, 1, 2, 3, 4, ['expand', 'fill'], [], 0, 0 );
				    $dateExpression->show;
				    $dateExpression->can_focus(1 );
				    $dateExpression->set_text('');
				    $dateExpression->set_max_length(0 );
				    $dateExpression->set_visibility(1 );
				    $dateExpression->set_editable(0 );
				    #
				    # Construct a GnomeDateEdit 'dateStart'
				    $dateStart = new Gnome::DateEdit(0, 0, 1);
				    $dateStart->set_popup_range(7, 19 );
				    $tblSearch->attach($dateStart, 1, 2, 1, 2, ['expand', 'fill'], [], 0, 0 );
				    $dateStart->show;
				    #
				    # Construct a GnomeDateEdit 'dateEnd'
				    $dateEnd = new Gnome::DateEdit(0, 0, 1);
				    $dateEnd->set_popup_range(7, 19 );
				    $tblSearch->attach($dateEnd, 1, 2, 2, 3, ['expand', 'fill'], [], 0, 0 );
				    $dateEnd->show;
				    #
				    # Construct a GtkRadioButton 'rdbAny'
				    $rdbAny = new Gtk::RadioButton('A_ny', $rdbBetween);
				    $tblSearch->attach($rdbAny, 0, 2, 0, 1, [], [], 0, 0 );
				    $rdbAny->show;
				    $rdbAny->can_focus(1 );
				    $rdbAny_key = $rdbAny->child->parse_uline('A_ny');
				    $rdbAny->add_accelerator('clicked', $accelgroup, $rdbAny_key, 'mod1_mask', ['visible', 'locked'] );
				    $rdbAny->set_active(1 );
				$vbox3->set_child_packing($tblSearch, 1, 1, 0, 'start' );
			$vbox2->set_child_packing($frmDate, 1, 1, 0, 'start' );
			#
			# Construct a GtkHBox 'hbox5'
			$hbox5 = new Gtk::HBox(0, 0 );
			$vbox2->add($hbox5);
			$hbox5->show;
			    #
			    # Construct a GtkCheckButton 'chkCase'
			    $chkCase = new Gtk::CheckButton('Case insensi_tive');
			    $chkCase->set_active(1 );
			    $hbox5->add($chkCase);
			    $chkCase->show;
			    $chkCase->can_focus(1 );
			    $chkCase_key = $chkCase->child->parse_uline('Case insensi_tive');
			    $chkCase->add_accelerator('clicked', $accelgroup, $chkCase_key, 'mod1_mask', ['visible', 'locked'] );
			    $hbox5->set_child_packing($chkCase, 0, 0, 0, 'start' );
			    #
			    # Construct a GtkCheckButton 'chkInverse'
			    $chkInverse = new Gtk::CheckButton('Inverse _match');
			    $chkInverse->set_active(0 );
			    $hbox5->add($chkInverse);
			    $chkInverse->show;
			    $chkInverse->can_focus(1 );
			    $chkInverse_key = $chkInverse->child->parse_uline('Inverse _match');
			    $chkInverse->add_accelerator('clicked', $accelgroup, $chkInverse_key, 'mod1_mask', ['visible', 'locked'] );
			    $hbox5->set_child_packing($chkInverse, 0, 0, 0, 'start' );
			$vbox2->set_child_packing($hbox5, 1, 1, 0, 'start' );
			#
			# Construct a GtkCheckButton 'chkSkipattach'
			$chkSkipattach = new Gtk::CheckButton('Skip non-te_xt attachments');
			$chkSkipattach->set_active(1 );
			$vbox2->add($chkSkipattach);
			$chkSkipattach->show;
			$chkSkipattach->can_focus(1 );
			$chkSkipattach_key = $chkSkipattach->child->parse_uline('Skip non-te_xt attachments');
			$chkSkipattach->add_accelerator('clicked', $accelgroup, $chkSkipattach_key, 'mod1_mask', ['visible', 'locked'] );
			$vbox2->set_child_packing($chkSkipattach, 0, 0, 0, 'start' );
		    #
		    # Construct a GtkLabel 'lblOptions'
		    $lblOptions = new Gtk::Label('');
		    $lblOptions->parse_uline('Search _options');
		    $lblOptions->set_justify('center' );
		    $lblOptions->set_line_wrap(0 );
		    $notebook->append_page($vbox2, $lblOptions);
		    $lblOptions->show;
		    $lblOptions->set_alignment(0.5, 0.5 );
		$vbox1->set_child_packing($notebook, 1, 1, 0, 'start' );
		#
		# Construct a GtkHButtonBox 'hbuttonbox1'
		$hbuttonbox1 = new Gtk::HButtonBox;
		$vbox1->add($hbuttonbox1);
		$hbuttonbox1->show;
		$hbuttonbox1->set_layout('default_style' );
		$hbuttonbox1->set_spacing(30 );
		$hbuttonbox1->set_child_size(85, 27 );
		$hbuttonbox1->set_child_ipadding(7, 0 );
		    #
		    # Construct a GtkButton 'pbClear'
		    $pbClear = new Gtk::Button('_Clear');
		    $hbuttonbox1->add($pbClear);
		    $pbClear->show;
		    $pbClear->can_default(1 );
		    $pbClear->can_focus(1 );
		    $pbClear_key = $pbClear->child->parse_uline('_Clear');
		    $pbClear->add_accelerator('clicked', $accelgroup, $pbClear_key, 'mod1_mask', ['visible', 'locked'] );
		    #
		    # Construct a GtkButton 'pbSearch'
		    $pbSearch = new Gtk::Button('Se_arch');
		    $hbuttonbox1->add($pbSearch);
		    $pbSearch->show;
		    $pbSearch->can_default(1 );
		    $pbSearch->can_focus(1 );
		    $pbSearch_key = $pbSearch->child->parse_uline('Se_arch');
		    $pbSearch->add_accelerator('clicked', $accelgroup, $pbSearch_key, 'mod1_mask', ['visible', 'locked'] );
		    #
		    # Construct a GtkButton 'pbView'
		    $pbView = new Gtk::Button('_View');
		    $hbuttonbox1->add($pbView);
		    $pbView->show;
		    $pbView->can_default(1 );
		    $pbView->can_focus(1 );
		    $pbView_key = $pbView->child->parse_uline('_View');
		    $pbView->add_accelerator('clicked', $accelgroup, $pbView_key, 'mod1_mask', ['visible', 'locked'] );
		$vbox1->set_child_packing($hbuttonbox1, 1, 1, 0, 'start' );
	#
	# Construct a GnomeAppBar 'appbar1'
	$appbar1 = new Gtk::Statusbar();
	$dlgMain->set_statusbar($appbar1);
	$appbar1->show;
	$context_id = $appbar1->get_context_id( "gtkgrepmail" );
    #
    # Connect all signals now that dlgMain_form are constructed
    $mnuExit->signal_connect( 'activate', "on_mnuExit_activate" );
    $mnuPreferences->signal_connect( 'activate', "on_mnuPreferences_activate" );
    $mnuManual->signal_connect( 'activate', "on_mnuManual_activate" );
    $mnuAbout->signal_connect( 'activate', "on_mnuAbout_activate" );
    $pbRefresh->signal_connect( 'clicked', "on_pbRefresh_clicked" );
    $pbClear->signal_connect( 'clicked', "on_pbClear_clicked" );
    $pbSearch->signal_connect( 'clicked', "on_pbSearch_clicked" );
    $pbView->signal_connect( 'clicked', "on_pbView_clicked" );
    $lstFiles->signal_connect( 'select_row', "on_lstFiles_select_row" );
    $lstFiles->signal_connect( 'unselect_row', "on_lstFiles_unselect_row" );
    $rdbExpression->signal_connect( 'toggled', "on_rdbExpression_toggled" );

    $dlgMain_form->show;
}

sub on_rdbExpression_toggled {
    $valrdbExpression = $rdbExpression->get_active;
    if ( $valrdbExpression ) {
      $dateExpression->set_editable(1)
    } else {
      $dateExpression->set_editable(0)
    };
}

sub on_pbClear_clicked {
    $appbar1->push($context_id,"Form cleared");
    $entSearch->set_text("");
    $entAnd->set_text("");
    $entNot->set_text("");
    $chkBody->set_active(1);
    $chkHeaders->set_active(1);
    $chkBody2->set_active(1);
    $chkHeaders2->set_active(1);
    $chkBody3->set_active(1);
    $chkHeaders3->set_active(1);
    $rdbAnd->set_active(1);
    $chkRecurse->set_active(0);
    $row = 0;
    while ( $row <= $#filelist ) {
      $lstFiles->unselect_item($row,0);
      $row++;
    }
    @valfiles = "";
    $rdbAny->set_active(1);
    $dateExpression->set_text("");
    $chkCase->set_active(1);
    $chkInverse->set_active(0);
    $chkSkipattach->set_active(1);
    $dateStart->set_time(0);
    $dateEnd->set_time(0);
}

sub on_pbRefresh_clicked {
    refreshfiles()
}

sub on_lstFiles_select_row {
    ( $clist, $row, $column, undef, undef ) = @_;
    $text = $clist->get_text( $row, $column );
    push @valfiles, "$valMailpath/$text";
}

sub on_lstFiles_unselect_row {
    ( $clist, $row, $column, undef, undef ) = @_;
    $text = $clist->get_text( $row, $column );
    $file = pop(@valfiles);
    if ( $text ) {
      push @valfiles, $file unless ( $file eq "$valMailpath/$text" )
    }
}

sub refreshfiles {
    $appbar1->push($context_id,"Creating file list, please wait...");
    $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 150 ) );
    # Cursor does not redraw itself unless we do this
    $dlgMain_form->draw(undef);
    $lstFiles->clear;
    @valfiles = "";
    if ( $usrHost eq "localhost" ) {
      @filelist = <$valMailpath/*>;
    }
    else {
      $filelist = `@sshcommand $usrHost -l $usrUser ls $valMailpath` || msgError_new("Error: could not list files on remote host");
      @filelist = split(/\s/, $filelist);
    }
    foreach $file ( @filelist ) {
      chomp $file ;
      if ( basename($file) eq "search-results" ) { next };
      $lstFiles->append(basename($file))
    };
    $appbar1->pop($context_id);
    $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 68 ) );
}
    
sub on_pbSearch_clicked {
    $appbar1->pop($context_id);
    # Reset old values
    $andspec = "";
    $orspec = "";
    $notspec = "";
    $datespec = "";
    $scopeflag = "";
    $scopeflag2 = "";
    $scopeflag3 = "";
    $valRecurse = "";
    $valInverse = "";
    $valCase = "";
    $valSkipattach = "";
    # Grab new values
    $valSearch = $entSearch->get_text;
    $valAnd = $entAnd->get_text;
    $valNot = $entNot->get_text;
    # Now let's escape all the shell characters
    $valSearch =~ s/([\&;\`'!\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    $valAnd =~ s/([\&;\`'!\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    $valNot =~ s/([\&;\`'!\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    # Now all the body and header checkboxes
    $valBody = $chkBody->get_active;
    $valHeaders = $chkHeaders->get_active;
    if ( $valBody &! $valHeaders ) { $scopeflag = "-b" };
    if ( $valHeaders &! $valBody ) { $scopeflag = "-h" };
    $valBody2 = $chkBody2->get_active;
    $valHeaders2 = $chkHeaders2->get_active;
    if ( $valBody2 &! $valHeaders2 ) { $scopeflag2 = "-b" };
    if ( $valHeaders2 &! $valBody2 ) { $scopeflag2 = "-h" };
    $valBody3 = $chkBody3->get_active;
    $valHeaders3 = $chkHeaders3->get_active;
    if ( $valBody3 &! $valHeaders3 ) { $scopeflag3 = "-b" };
    if ( $valHeaders3 &! $valBody3 ) { $scopeflag3 = "-h" };
    # And the rest
    $valrdbAnd = $rdbAnd->get_active;
    if ( $chkRecurse->get_active ) { $valRecurse = "-R" };
    $valBetween = $rdbBetween->get_active;
    $valExpression = $dateExpression->get_text;
    print $valExpression;
    if ($valExpression) {
      $valExpression =~ s/([\&;\`'!\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g;
    };
    print $valExpression;
    $valrdbExpression = $rdbExpression->get_active;
    if ( $chkCase->get_active ) { $valCase = "-i" };
    if ( $chkInverse->get_active ) { $valInverse = "-v" };
    if ( $chkSkipattach->get_active ) { $valSkipattach  = "-M" };
    $valStart = $dateStart->get_date;
    $valEnd = $dateEnd->get_date;
    $datestart = &ParseDateString("epoch $valStart");
    $dateend = &ParseDateString("epoch $valEnd");
    if ( $valrdbExpression && $valExpression ) { $datespec = "-d \\\"$valExpression\\\"" }
    elsif ( $valBetween ) { $datespec = "-d \\\"between $datestart and $dateend\\\"" };
    if ( $valrdbAnd && $valAnd ) { $andspec = "| grepmail $scopeflag2 $valInverse $valCase -e \\\"$valAnd\\\"" }
    elsif ( $valAnd ) { $orspec = "\\|\\\"$valAnd\\\"" };
    if ( $valNot ) { $notspec = "| grepmail $scopeflag3 $valCase -v -e \\\"$valNot\\\"" };
    $grepmailcmd = "$valGrepmail -e \\(\\\"$valSearch\\\"$orspec\\) $scopeflag $valInverse $valCase $valRecurse $valSkipattach $datespec @valfiles $andspec $notspec 2>&1 1>$valMailpath/$valMailbox";
    unless ( errorhandler() ) {
      $appbar1->push($context_id,"Initiating search...");
      $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 150 ) );
      # Cursor does not redraw itself unless we do this
      $dlgMain_form->draw(undef);
      if ( $usrHost ne "localhost" ) {
        $grepmailcmd = "@sshcommand $usrHost -l $usrUser \"$grepmailcmd\"";
      }
      if ( $debug ) { print $grepmailcmd . "\n" };
      $results = `$grepmailcmd`;
      $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 68 ) );
      if ( $results ) {
        chomp $results;
        msgError_new("The following warnings or errors were returned:\n$results");
        $appbar1->pop($context_id)
      } else {
        $appbar1->push($context_id,"Search complete")
      }
    }
}

sub on_pbView_clicked {
  @imapcommand = split(/ /, $usrIMAP);
  $appbar1->push($context_id,"Attempting to launch browser...");
  $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 150 ) );
  # Cursor does not redraw itself unless we do this
  $dlgMain_form->draw(undef);
  if ( $usrHost eq "localhost" ) {
    $returncode = system(@imapcommand, "$valMailpath/$valMailbox");
  } else {
    $imappath = $usrMailpath;
    # We are assuming here that if something like "~/Mail" appears at the
    # start of the path, this is not part of the IMAP tree.  Should work.
    $imappath =~ s/^(($ENV{HOME}|~))*(\/)*(Mail)*(\/)*//ig;
    $imappath = "IMAP://$usrHost/" . uri_escape($imappath) . "/" . uri_escape($usrMailbox);
    $returncode = system(@imapcommand, $imappath)
  }
  $appbar1->pop($context_id);
  if ( $returncode ) {
    msgError_new("Could not launch browser")
  } else {
    $appbar1->push($context_id,"Browser launched");
    $dlgMain_form->window->set_cursor( new Gtk::Gdk::Cursor( 68 ) );
  };
}

sub on_mnuManual_activate {
    unless ( $child = fork ) {
      exec "gnome-help-browser 'man:gtkgrepmail'";
    }
}

sub on_mnuAbout_activate {
    dlgAbout_new();
}

sub on_mnuExit_activate {
    Gtk->exit( 0 );
    return 0;
}

sub on_mnuPreferences_activate {
    dlgOptions_new();
}

sub errorhandler {
    if ( $valBetween ) {
      if ( $datestart eq "1970010108:00:01" ) {
        msgError_new("Error in start date");
        return 1
      } elsif ( $dateend eq "1970010108:00:01" ) {
        msgError_new("Error in end date");
        return 1
      } elsif ( $datestart gt $dateend ) {
        msgError_new("Start date is after end date");
	return 1
      }
    };
    if ( $valSearch ) {
      unless ( $valBody || $valHeaders ) {
        msgError_new("You must select at least Body or Headers to search");
	return 1
      }
    }
    if ( $valAnd ) {
      unless ( $valBody2 || $valHeaders2 ) {
        msgError_new("You must select at least Body or Headers to search");
	return 1
      }
    }
    if ( $valNot ) {
      unless ( $valBody3 || $valHeaders3 ) {
        msgError_new("You must select at least Body or Headers to search");
	return 1
      }
    }
    unless ( $#valfiles ) {
      msgError_new("Please select at least one mailbox to search");
      return 1
    };
    unless ( $valBetween || $valrdbExpression || $valSearch ) {
      msgError_new("Please specify a search term");
      return 1
    };
    if ( $valrdbExpression &! $valExpression ) {
      msgError_new("Please specify a date expression");
      return 1
    };
}

sub dlgAbout_new {
    #
    # Construct a GnomeAbout 'dlgAbout'
    $dlgAbout_form = new Gnome::About('gtkgrepmail', "$VERSION", '(C) 2002 Terminus Network Services', 'Jeremy Malcolm', 'A gtk front end to grepmail which will allow you a GUI interface to search through your sent and saved mail.');
    $dlgAbout_form->set_title('About');
    $dlgAbout_form->position('mouse' );
    $dlgAbout_form->set_policy(1, 1, 0 );
    $dlgAbout_form->set_modal(1 );
    $dlgAbout_form->realize;
    $accelgroup = new Gtk::AccelGroup;
    $accelgroup->attach($dlgAbout_form );

    $dlgAbout_form->show;
}

sub load_config {
    if ( open (CONFIG, "$configpath") ) {
      while ( <CONFIG> ) {
        m/^(#|\n)/ && next;
        chomp;
        /^([^=]+)=([^=]+)/;
        $cfg{$1} = $2;
      };
      close CONFIG
    }
}

sub save_config {
    ($key, $value) = @_;
    print CONFIG "$key=$value\n";
}
1;

__END__

=pod

=head1 NAME

gtkgrepmail - a gtk front end to grepmail

=head1 SYNOPSIS

  gtkgrepmail

=head1 DESCRIPTION

A gtk front end to grepmail which will allow you a GUI interface to search
through your sent and saved mail.  Support is provided for both local
mailboxes and remote mailboxes that are accessible using IMAP and ssh/rsh.

=head1 OPTIONS

Preferences are set by clicking the I<Settings> menu and selecting
I<Preferences>.  The options set out there are as follows:

=over 8

=item B<Path (local or remote) to mail folders>

Here you must specify the I<full> path to the directory that contains your
mail folders.  These must be in standard UNIX mailbox format, but may be
compressed by gzip, tzip or bzip2 if those utilities are present on the
same system.  You do not need to prepend a hostname to the path here,
this will automatically be assumed if a host other than C<localhost> is
specified below.

=item B<Relative path to temporary mailbox>

gtkgrepmail stores your search results in a temporary mailbox.  Use the
default (search-results) unless you have some particular reason to choose
differently.  Do not prepend a path.  Do not use spaces in the filename.

=item B<Path (local or remote) to grepmail>

Here you must specify the I<full> path to grepmail, on whichever system
contains your mailboxes.  You do not need to prepend a hostname to the
path here, this will automatically be assumed if a host other than
C<localhost> is specified below.

=item B<Path to IMAP or mailbox browser>

If your mailboxes are on the local computer, you can specify any program
(even a text editor such as vi) that can read the mailboxes.  The default
is "C</usr/bin/X11/xterm -e /usr/bin/elm -f>".

If your mailboxes are on a remote computer, specify a program that can
accept an IMAP URI such as C<IMAP://servername/path/to/mailbox>.  One
such program is Netscape Navigator.

=item B<Path to ssh or rsh>

If you intend to access remote mailboxes, gtkgrepmail calls grepmail on
the remote computer using ssh (recommended) or rsh.  It is important to
note that gtkgrepmail I<requires> that you can log into the remote
computer interactively entering a password.  It is recommended that you
achieve this through the use of the ssh-agent(1) facility.

=item B<Host>

If you intend to access your mailboxes on your local computer, leave
this set as "C<localhost>".  Otherwise, enter a name by which your
remote computer can be reached.

=item B<User>

If accessing your mailboxes remotely, enter your username here.  This is
used both for rsh/ssh and for IMAP.

=back

=head1 USAGE

The main window of the gtkgrepmail application has three tabbed panes:

=over 8

=item B<Search for>

Type the expression you wish to search for in the first textbox field.
In the second field you may type another expression which you wish to
combine with the first in order to return results which only match both
expressions (toggle the "and" radio button) or which match either
expression (toggle the "or" radio button).  If you wish to ensure that
a certain expression does not exist in the search results, enter that
expression in the third field.  In all three cases, you may choose to
restrict your search on that expression to the body or the header of
emails by untoggling the Header or Body checkboxes respectively.

=item B<Search in>

Here will be listed all of the mailboxes that are available for you to
search.  Select one or more mailboxes by clicking them.  Press "Refresh"
if the available mailboxes have changed since the list was drawn.  If
any of your selections are a directory and you want to search all of
the files underneath it, click the "Recurse directories" checkbox.

=item B<Search options>

Here you can restrict your search by date: click the "Between" radio
button if you wish to search for emails between two dates, or click
the "Expression" radio button and describe the period in which you
are interested.  You can use expressions such as "today", "yesterday",
"5 days ago", "5 weeks ago", "before July 2001", "12/10/00" and so
on: for complete details see Date::Manip(3pm).

As for the other options found on this pane, "Case insensitive" should
be self-explanatory, "Inverse match" will find emails that do I<not>
match your first two search expressions.  The state of the "and/or"
toggle will not make any difference in this case; if "apple" and "pear"
are your first two expressions, the results will be all emails that
contain neither of those terms.  The "Skip non-text attachments" option
will ignore MIME attachments to your emails that are not in a plain
text format (this is generally a good idea when searching for short
words).

=item B<Buttons>

The buttons at the bottom of the main window control the main functions
of the program.  "Clear" will reset the data in all three tabs of the
form to their default values.  "Search" will commence the search that
you have specified.  "View" will launch your IMAP or mailbox browser so
that you can see the search results.

=back

=head1 AUTHOR

Jeremy Malcolm <Jeremy@Malcolm.wattle.id.au>

=head1 SEE ALSO

grepmail(1), grep(1), perl(1), ssh(1), ssh-agent(3), rsh(1),
Date::Manip(3pm)

=head1 BUGS

Does not offer complete feature-compatibility with grepmail.  For example
by clicking "Header" and "Body" checkboxes you are specifying that you
want the text to appear I<either> in the header or body, not that you
want it to appear in both.  There is no way of specifying that.

Has no provision to prompt you for your SSH password or to save or
cache that password.  The latter might be construed as as a feature,
rather than a bug.

The application will appear to hang while a search is in progress.
How noticeable that is will depend on how large your mailboxes are and
how busy your network connection is (if applicable).

The status bar does not update before a search is made or a directory
listing is compiled.

The "Inverse match" option does not do what you might expect.  The
state of the "and/or" radio buttons is ignored and the contents of
the "Not" field remains uninverted.  At the moment, I am disinclined
fix this bug, as it would only make the meaning of "Inverse match"
more confusing than it already is.

=cut

