#!/usr/bin/perl
#
# Copyright (C) 2005 by Mandriva aginies _ateuh_ mandriva.com
#
# 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.

my $version = "0.1";

# i18n: IMPORTANT: to get correct namespace (draknfs instead of libDrakX)
BEGIN { unshift @::textdomains, 'draknfs' }

use strict;
use lib qw(/usr/lib/libDrakX);
use standalone;
use common;
use any;

use interactive;
# must come *after* definition of textdomains for proper initialisation
use ugtk2 qw(:ask :wrappers :create :dialogs);

my $in = 'interactive'->vnew('su');


use constant FALSE => 0;
use constant TRUE => 1;

sub test_root {
  unless ($> == 0) {
    err_dialog(N("Error!"), N("You are not root. Exiting..."));
    die unless $::testing;
  }
}

my $CONF = "/etc/exports";
my @listshare;
my @listuserid = qw(root_squash no_root_squash all_squash no_all_squash); push @listuserid, "";
my @listsecure = qw(secure insecure ); push @listsecure, "";
my @listsync = qw(async sync); push @listsync, "";
my @listr = qw(ro rw); push @listr, "";


use constant COLUMN_DIR => 0;
use constant COLUMN_ACCESS => 1;
use constant COLUMN_RIGHT => 2;
use constant COLUMN_OPTIONS => 3;
use constant NUM_COLUMNS => 4;

my %size_groups = map { $_ => Gtk2::SizeGroup->new('horizontal') } qw(label widget button);
my $label_and_widgets = sub {
  my ($label, $widget, $button) = @_;
  gtkpack_(Gtk2::HBox->new(0,5),
           0, gtkadd_widget($size_groups{label}, $label),
           1, gtkadd_widget($size_groups{widget}, $widget),
	   2, gtkadd_widget($size_groups{button}, $button),
          );
};

my $fdwidget = sub {
  my ($data, $label) = @_;
  my $fd = new Gtk2::FileSelection(N("Directory Selection"));
  $fd->set_modal(TRUE);
  $fd->signal_connect("destroy", sub { $fd->hide });
  $fd->ok_button->signal_connect(clicked => sub {
				   my $file = $fd->get_filename;
				   -d $file or err_dialog(N("Error!"), N("Should be a directory.")) and return;
				   $data->set_text($file);
				   $fd->hide;
				 }, $fd);
  $fd->cancel_button->signal_connect(clicked => sub { $fd->hide });
  return $fd;
};

sub get_nfs_data {
# /home/nis *(async,rw,no_root_squash)
# /home/nis/guibo/Build *(async,rw,no_root_squash)
  foreach (cat_($CONF)) {
    my ($dir, $access, $right, $options) = m/^(\/\S+)\s+(\S*)\((\S*)\)\s+(\S*)/;
    $dir and push @listshare, {
			       dir => $dir,
			       access => $access,
			       right => $right,
			       options => $options,
			     };
  }
}

sub write_conf {
  output($CONF, "# generated by drakhosts.pl\n");
  foreach my $a (@listshare) {
    append_to_file($CONF, "$a->{dir} $a->{access}($a->{right}) $a->{options}\n");
  }
}

my $help_access = N("NFS clients may be specified in a number of ways:
single host: You may specify a host either by an abbreviated name recognized be the resolver, the fully qualified domain name, or an IP address.

netgroups: NIS netgroups may be given as \@group.

wildcards: machine names may contain the wildcard characters * and ?. For instance: *.cs.foo.edu  matches  all  hosts  in the domain cs.foo.edu.

IP networks:  you can also export directories to all hosts  on  an  IP  (sub-)network simultaneously. for example, either `/255.255.252.0' or  `/22'  appended to the network base address result.
");

my $help_userid = N("");

my $help_global = N("");

sub help_b {
  my ($tittle, $help_data) = @_;
  gtksignal_connect(new Gtk2::Button(N("Help")), clicked => sub {
		      my $dialog = new Gtk2::Dialog();
		      $dialog->set_title("Help");
		      $dialog->set_modal(1);
		      gtkpack_($dialog->vbox,
			       0, gtkadd(Gtk2::Frame->new(N($tittle)),
					 gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
						  0, $help_data,
						 ),
					),
			      );
		      $dialog->show_all;
		    }
		   );
}

sub add_modify_entry {
  my ($widget, $treeview, $wanted) = @_;
  my $model = $treeview->get_model;
  my $selection = $treeview->get_selection;
  my $iter;
  my ($i, $dir, $access, $right, $all_right, $anonuid, $anongid, $options);
  my ($lr, $luserid, $lsecure, $lsync);
  undef $i;
  undef $iter;

  map { $_ = Gtk2::Entry->new } $dir, $access, $anongid, $anonuid, $options;
  map { $_ = Gtk2::OptionMenu->new() } $lr, $luserid, $lsecure, $lsync;
  my $luserid = new Gtk2::OptionMenu();

  $luserid->set_popdown_strings(@listuserid);
  $lr->set_popdown_strings(@listr);
  $lsync->set_popdown_strings(@listsync);
  $lsecure->set_popdown_strings(@listsecure);

  my $file_dialog = $fdwidget->($dir, "");
  my $button = Gtk2::Button->new(N("dir path"));
  $button->signal_connect(clicked => sub { $file_dialog->show });

# test if modify or add a nfs share
  my $dialog = new Gtk2::Dialog();
  $dialog->set_title("Draknfs $wanted entry");
  $dialog->set_modal(1);
  $dialog->set_resizable(1);

  if ($wanted =~ /modify/) {
    $iter = $selection->get_selected;
    my $path = $model->get_path($iter);
    $i = ($path->get_indices)[0];
    $dir->set_text($listshare[$i]{dir});
    $access->set_text($listshare[$i]{access});
    # list of all rigth in bracket
    #    $anongid, $anonuid, $lr, $luserid, $lsecure, $lsync;
    $right = $listshare[$i]{right};
    my @opts = split(/,/, $right);
    map { $_->set_text("") } $lr, $lsync, $anonuid, $anongid, $luserid, $lsecure;

    foreach my $opt (@opts) {
      if ($opt =~ m/(\bro\b|\brw\b)/) {
	$lr->set_text($opt);
      } elsif ($opt =~ m/\bsync\b|\basync\b/) {
	$lsync->set_text($opt);
      } elsif ($opt =~ m/anongid=(\d+)/) {
	$anongid->set_text($1);
      } elsif ($opt =~ m/anonuid=(\d+)/) {
	$anonuid->set_text($1);
      } elsif ($opt =~ m/(no_root_squash|root_squash|all_squash|no_all_squash)/) {
	$luserid->set_text($opt);
      } elsif ($opt =~ m/(\bsecure\b|\binsecure\b)/) {
	$lsecure->set_text($opt);
      } else { next }
    }
  }

  gtkpack_($dialog->vbox,
	   0, gtkadd(Gtk2::Frame->new(N("NFS directory")),
		     gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
			      0, $label_and_widgets->(N("Directory:"), $dir, $button),
			     ),
		     ),
	   0, gtkadd(Gtk2::Frame->new(N("Host access")),
                     gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
			      0, $label_and_widgets->(N("Access:"), $access, help_b("Help User ID", $help_access)),
			     ),
		    ),
	   0, gtkadd(Gtk2::Frame->new(N("User ID Mapping")),
                     gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
			      0, $label_and_widgets->(N("User ID:"), $luserid, help_b("Help User ID", $help_userid)),
			      0, $label_and_widgets->(N("anonuid:"), $anonuid, ""),
			      0, $label_and_widgets->(N("anongid:"), $anongid, ""),
			     ),
		    ),
	   0, gtkadd(Gtk2::Frame->new(N("General Options")),
                     gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
			      0, $label_and_widgets->(N("Force sync:"), $lsync, help_b("Help General options", $help_global)),
			      0, $label_and_widgets->(N("port below 1024:"), $lsecure, ""),
			      0, $label_and_widgets->(N("Read/Write request:"), $lr, ""),
			     ),
		    ),
	   0, gtkadd(Gtk2::Frame->new(N("More Options")),
                     gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
			      0, $label_and_widgets->(N("options:"), $options, ""),
			     ),
		    ),
	   0, create_okcancel({
			       cancel_clicked => sub { $dialog->destroy },
			       ok_clicked => sub {
				 my ($anonu, $anong);
				 $anonuid->get_text() and $anonu = "anonuid=" . $anonuid->get_text();
				 $anongid->get_text() and $anong = "anongid=" . $anongid->get_text();
				 map { $_ and $all_right = $_ . "," . $all_right;  } $luserid->get_text(), $anonu, $anong, $lsync->get_text(), $lsecure->get_text(), $lr->get_text();
				 if ($wanted =~ /add/) {
				   -d $dir->get_text() or err_dialog(N("Error!"), N("Please enter a directory to share.")) and return;
				   $iter = $model->append;
				   $i = "-1";
				   push @listshare, {
						     dir => $dir->get_text(),
						     access => $access->get_text(),
						     right => $all_right,
						     options => $options->get_text(),
						    };
				 }
				 $listshare[$i]{right} = $all_right;
				 $listshare[$i]{access} = $access->get_text();
				 $listshare[$i]{dir} = $dir->get_text();
				 $listshare[$i]{options} = $options->get_text();
				 $model->set($iter,
					     COLUMN_DIR, $listshare[$i]{dir},
					     COLUMN_ACCESS, $listshare[$i]{access},
					     COLUMN_RIGHT, $all_right,
					     COLUMN_OPTIONS, $listshare[$i]{options},
					    );
				 $dialog->destroy;
				 write_conf();
			       },
			      },
			     ),
	  );
  $dialog->show_all;
}

sub remove_entry {
  my ($widget, $treeview) = @_;
  my $model = $treeview->get_model;
  my $selection = $treeview->get_selection;
  my $iter = $selection->get_selected;
    if ($iter) {
      my $path = $model->get_path($iter);
      my $i = ($path->get_indices)[0];
      ask_okcancel("Remove entry ?", "Remove $listshare[$i]{dir}") or return;
      $model->remove($iter);
      splice @listshare, $i, 1;
    }
  write_conf();
}

sub create_model() {
  get_nfs_data();
  my $model = Gtk2::ListStore->new("Glib::String", "Glib::String",  "Glib::String", "Glib::String");
  foreach my $a (@listshare) {
    my $iter = $model->append;
    $model->set($iter,
		COLUMN_DIR, $a->{dir},
		COLUMN_ACCESS, $a->{access},
		COLUMN_RIGHT, $a->{right},
		COLUMN_OPTIONS, $a->{options},
	       ),
	     }
  return $model;
}

# add colum to model
sub add_columns {
  my $treeview = shift;
  my $model = $treeview->get_model;
  each_index {
    my $renderer = Gtk2::CellRendererText->new;
    $renderer->set(editable => 0);
    $renderer->signal_connect(edited => \&cell_edited, $model);
    $renderer->set_data(column => $::i);
    $treeview->insert_column_with_attributes(-1, $_, $renderer, 'text' => $::i);
  } N("Share Directory"), N("Hosts Wildcard"), N("General Options"), N("More options");
}


###############
# Main Program
###############
# create model
#test_root();

my $model = create_model();

my $window = ugtk2->new("Draknfs $version");
$window->{rwindow}->set_size_request(530, 300) unless $::isEmbedded;
my $W = $window->{window};
$W->signal_connect(delete_event => sub { ugtk2->exit });

my $treeview = Gtk2::TreeView->new_with_model($model);
$treeview->set_rules_hint(TRUE);
$treeview->get_selection->set_mode('single');
add_columns($treeview);

my $okcancel = create_okcancel({
				cancel_clicked => sub { ugtk2->exit },
				ok_clicked => \&write_conf,
			       },
			      );

# main interface
$W->add(gtkpack_(Gtk2::VBox->new(0,0),
		 1, create_scrolled_window($treeview),
		 0, gtkpack_(gtkset_border_width(Gtk2::HBox->new, 3),
                             0, gtksignal_connect(Gtk2::Button->new(N("Add an NFS share")), clicked => sub {
						    eval { add_modify_entry($model, $treeview, "add") };
						    my $err = $@;
						    if ($err) {
                                                      err_dialog(N("Error"), N("Failed to add NFS share.") . "\n\n" . $err);
                                                    }
                                                  }),
                             0, gtksignal_connect(Gtk2::Button->new(N("Modify an NFS share")), clicked => sub {
						    eval { add_modify_entry($model, $treeview, "modify") };
						    my $err = $@;
						    if ($err) {
                                                      err_dialog(N("Error"), N("Failed to Modify NFS share.") . "\n\n" . $err);
                                                    }
                                                  }),

                             0, gtksignal_connect(Gtk2::Button->new(N("Remove an NFS share")), clicked => sub {
						    eval { remove_entry($model, $treeview) };
						    my $err = $@;
						    if ($err) {
                                                      err_dialog(N("Error"), N("Failed to remove an NFS share.") . "\n\n" . $err);
                                                    }
                                                  }),
			     0, Gtk2::VSeparator->new,
                             0, gtksignal_connect(Gtk2::Button->new(N("Reload NFS server")), clicked => sub {
						    system("/sbin/service nfs reload");
                                                  }),

			    ),
		 if_($::isEmbedded, 0, $okcancel),
		),
       );

$W->show_all;
Gtk2->main;