Use param instead of global for entry
[clawsker.git] / clawsker
index 5644412e3549fcaf71b52a3c2d7a52dc82391e51..1903211138bf9686c540fc380d06bf0f34909b72 100755 (executable)
--- a/clawsker
+++ b/clawsker
@@ -1,22 +1,34 @@
 #!/usr/bin/perl -w
 #
 # Clawsker :: A Claws Mail Tweaker
-# Copyright 2007-2014 Ricardo Mones <ricardo@mones.org>
+# Copyright 2007-2016 Ricardo Mones <ricardo@mones.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 3 of the License, or
+# (at your option) any later version.
+#
 # See COPYING file for license details.
 # See AUTHORS file for a complete list of contributors.
 #
+
+binmode STDOUT, ":encoding(utf8)";
+
+use 5.010_000;
 use strict;
 use utf8;
 use Glib qw(TRUE FALSE);
-use Gtk2 -init;
+use Gtk2;
 use POSIX qw(setlocale);
 use Locale::gettext;
 use Encode;
 use Digest::MD5 qw(md5_hex);
+use Getopt::Long;
 
 my $NAME = 'clawsker';
 my $PREFIX = '@PREFIX@';
 my $LIBDIR = '@LIBDIR@';
+my $DATADIR = '@DATADIR@';
 my $VERSION = '@VERSION@';
 my $VERBOSE = FALSE;
 my $CLAWSV = undef;
@@ -72,6 +84,7 @@ sub _ {
     compo_frame => _('Compose window'),
     netm_frame => _('NetworkManager'),
     diff_frame => _('Viewing patches'),
+    mpass_frame => _('Master passphrase'),
 
     l_oth_use_dlg => _('Use detached address book edit dialogue'),
     h_oth_use_dlg => _('If true use a separate dialogue to edit a person\'s details. Otherwise will use a form embedded in the address book\'s main window.'),
@@ -81,10 +94,12 @@ sub _ {
     h_oth_min_time => _('The minimum time in minutes to keep a cache in memory. Caches more recent than this time will not be freed, even if the memory usage is too high.'),
     l_oth_use_netm => _('Use NetworkManager'),
     h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
+    l_oth_mp_rounds => _('Rounds for PBKDF2 function'),
+    h_oth_mp_rounds => _('Specify the number of iterations the key derivation function will be applied on master passphrase computation. Does not modify currently stored passphrase, only master passphrases computed after changing this value are affected.'),
     
     l_gui_b_unread => _('Show unread messages with bold font'),
     h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
-    l_gui_no_markup => _('Don\'t use markup in compose window'),
+    l_gui_no_markup => _('Don\'t use markup'),
     h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
     l_gui_dot_lines => _('Use dotted lines in tree view components'),
     h_gui_dot_lines => _('Use the old dotted line look in the main window tree views (Folder, Message and other lists) instead of the modern lineless look.'),
@@ -98,7 +113,7 @@ sub _ {
     l_gui_v_scroll_auto => _('Automatic'),
     l_gui_v_scroll_hide => _('Hide always'),
     l_gui_from_show => _('From column displays'),
-    h_gui_from_show => _('Selects the data displayed in the From column of the Message List: name, address or both'),
+    h_gui_from_show => _('Selects the data displayed in the From column of the Message List: name, address or both.'),
     l_gui_from_show_name => _('Name only'),
     l_gui_from_show_addr => _('Address only'),
     l_gui_from_show_both => _('Both name and address'),
@@ -114,12 +129,16 @@ sub _ {
     h_gui_strip_sum => _('Enable alternately coloured lines in Message List and Folder List.'),
     l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
     h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
-    l_gui_margin_co => _('Show compose margin'),
+    l_gui_margin_co => _('Show margin'),
     h_gui_margin_co => _('Shows a small margin in the Compose View.'),
     l_gui_mview_date => _('Don\'t display localized date'),
-    h_gui_mview_date => _('Toggles localization of date format in Message View'),
+    h_gui_mview_date => _('Toggles localization of date format in Message View.'),
     l_gui_zero_char => _('Zero replacement character'),
-    h_gui_zero_char => _('Replaces \'0\' with the given character in Folder List'),
+    h_gui_zero_char => _('Replaces \'0\' with the given character in Folder List.'),
+    l_gui_type_any => _('Editable headers'),
+    h_gui_type_any => _('Allows to manually type any value in Compose Window header entries or just select from the available choices in the associated dropdown list.'),
+    l_gui_next_del => _('Select next message on delete'),
+    h_gui_next_del => _('When deleting a message, toggles between selecting the next one (newer message) or the previous one (older message).'),
 
     l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
     h_beh_hover_t => _('Time in milliseconds that will cause a folder tree to expand when the mouse cursor is held over it during drag and drop.'),
@@ -146,11 +165,11 @@ sub _ {
     l_beh_pp_unsel => _('Primary paste unselects selection'),
     h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
     l_beh_inline_at => _('Show inline attachments'),
-    h_beh_inline_at => _('Allows hiding inline attachments already shown in mail structure view'),
+    h_beh_inline_at => _('Allows hiding inline attachments already shown in mail structure view.'),
     l_beh_addr_swc => _('Address search in compose window matches any'),
-    h_beh_addr_swc => _('On Tab-key completion, address text will match any part of the string or only the from the start'),
+    h_beh_addr_swc => _('On Tab-key completion, address text will match any part of the string or only from the start.'),
     l_beh_fold_swc => _('Folder search in folder selector matches any'),
-    h_beh_fold_swc => _('On folder name completion text will match any part of the string or only from the start'),
+    h_beh_fold_swc => _('On folder name completion text will match any part of the string or only from the start.'),
 
     l_col_emphasis => _('X-Mailer header'),
     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
@@ -189,10 +208,6 @@ sub _ {
     e_error => _('Error: '),
     e_noclawsrc => _('resource file for Claws Mail was not found.'),
     e_running => _('seems Claws Mail is currently running, close it first.'),
-    e_requireddir => _('option requires a directory name.'),
-    e_requiredfile => _('option requires a file name.'),
-    e_notadir => _('specified name is not a directory or does not exist.'),
-    e_notafile => _('specified name is not a file or does not exist.'),
 );
 
 # all preferences read by load_preferences
@@ -209,7 +224,7 @@ use constant NAME  => 0; # the name on the rc file
 use constant LABEL => 1; # the label on the GUI
 use constant DESC  => 2; # the description for the hint/help
 use constant TYPE  => 3; # data type: bool, int, float, string, color
-use constant CMVER => 4; # lowest Claws Mail version the feature exists
+use constant CMVER => 4; # lowest[,highest] Claws Mail version(s) the feature exists
 use constant CMDEF => 5; # default value for the preference in Claws Mail
 use constant GUI   => 6; # GUI element
 
@@ -255,7 +270,7 @@ sub get_claws_version {
     my @ver = split (/\./, $fver[3]);
     $res .= "$ver[0].";
     $res .= "$ver[1].";
-    if ($ver[2] =~ /(\d+)cvs(\d+)/) {
+    if ($ver[2] =~ /(\d+)git(\d+)/) {
         $res .= "$1.$2";
     }
     else {
@@ -356,7 +371,7 @@ sub error_dialog {
     my ($emsg) = @_;
     my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
     my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
-    $errordlg->set_title (_('Error message'));
+    $errordlg->set_title (_('Clawsker error'));
     $errordlg->run;
     $errordlg->destroy;
 }
@@ -369,16 +384,14 @@ sub claws_is_running {
 }
 
 sub check_claws_not_running {
-    my $socket = (not $ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
-    $socket .= "claws-mail-$<";
-    -S $socket and return claws_is_running ();
-    # since 3.9.0cvs36
-    my $lockdir = "/tmp/claws-mail-$<";
+    my $tmpdir = (defined $ENV{TMPDIR})? $ENV{TMPDIR}: '/tmp';
+    $tmpdir = '/tmp' if ($tmpdir eq '');
+    my $lockdir = "$tmpdir/claws-mail-$<";
     -d $lockdir and do { 
-       $_ = $CONFIGDIR;
-       s/\/$//;
-        $socket = "$lockdir/" . md5_hex ($_);
-       -S $socket and return claws_is_running ();
+        $_ = $CONFIGDIR;
+        s/\/$//;
+        my $socket = "$lockdir/" . md5_hex ($_);
+        -S $socket and return claws_is_running ();
     };
     return TRUE;
 }
@@ -386,7 +399,7 @@ sub check_claws_not_running {
 sub check_rc_file {
     my ($rcfile) = @_;
     (defined($rcfile) && -f $rcfile) or do {
-       my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
+        my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
         log_message ($emsg);
         error_dialog ($emsg);
         return FALSE;
@@ -425,9 +438,9 @@ sub new_check_button_for {
     my $hbox = Gtk2::HBox->new (FALSE, 5);
     my $cb = Gtk2::CheckButton->new ($label);
     $$hash{$key}[GUI] = $cb;
-    if (defined ($HPVALUE{$name})) {
-        $cb->set_active ($HPVALUE{$name} eq '1');
-    }
+    my $value = $HPVALUE{$name};
+    $value //= $$hash{$key}[CMDEF];
+    $cb->set_active ($value eq '1');
     $cb->signal_connect (clicked => sub {
             my ($w, $e) = @_;
             handle_bool_value ($w, $e, \$HPVALUE{$name});
@@ -450,8 +463,10 @@ sub new_text_box_for_int {
     my $glabel = Gtk2::Label->new ($label);
     my $pagei = int (($type[2] - $type[1]) / 10);
     my $gentry = Gtk2::SpinButton->new_with_range ($type[1], $type[2], $pagei);
+    my $value = $HPVALUE{$name};
+    $value //= $$hash{$key}[CMDEF];
     $gentry->set_numeric (TRUE);
-    $gentry->set_value ($HPVALUE{$name});
+    $gentry->set_value ($value);
     $$hash{$key}[GUI] = $gentry;
     $gentry->signal_connect('value-changed' => sub {
             my ($w, $e) = @_;
@@ -466,8 +481,8 @@ sub new_text_box_for_int {
     return $hbox;
 }
 
-sub new_text_box_for_nchar {
-    my ($hash, $key) = @_;
+sub new_text_box_for_nchar($$$) {
+    my ($hash, $key, $vhash) = @_;
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     my @type = split (/,/, $$hash{$key}[TYPE]); # char,minlen,maxlen
@@ -476,11 +491,13 @@ sub new_text_box_for_nchar {
     my $gentry = Gtk2::Entry->new ();
     $gentry->set_max_length($type[2]) if defined ($type[2]);
     $gentry->set_width_chars(int ($type[2]) + 2) if defined ($type[2]);
-    $gentry->set_text ($HPVALUE{$name});
+    my $value = $$vhash{$name};
+    $value //= $$hash{$key}[CMDEF];
+    $gentry->set_text ($value);
     $$hash{$key}[GUI] = $gentry;
     $gentry->signal_connect('key-release-event' => sub {
             my ($w, $e) = @_;
-            handle_nchar_value ($w, $e, \$HPVALUE{$name}, $type[1]);
+            handle_nchar_value ($w, $e, \$$vhash{$name}, $type[1]);
         });
     set_widget_hint ($gentry, $$hash{$key}[DESC]);
     set_widget_sens ($gentry, $$hash{$key}[CMVER]);
@@ -496,7 +513,9 @@ sub new_color_button_for {
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     #
-    my $col = gdk_color_from_str ($HPVALUE{$name});
+    my $value = $HPVALUE{$name};
+    $value //= $$hash{$key}[CMDEF];
+    my $col = gdk_color_from_str ($value);
     my $hbox = Gtk2::HBox->new (FALSE, 5);
     my $glabel = Gtk2::Label->new ($label);
     my $button = Gtk2::ColorButton->new_with_color ($col);
@@ -534,7 +553,9 @@ sub new_selection_box_for {
             my ($w, $e) = @_;
             handle_selection_value ($w, $e, \$HPVALUE{$name});
         });
-    $combo->set_active ($HPVALUE{$name});
+    my $value = $HPVALUE{$name};
+    $value //= $$hash{$key}[CMDEF];
+    $combo->set_active ($value);
     set_widget_hint ($combo, $$hash{$key}[DESC]);
     set_widget_sens ($combo, $$hash{$key}[CMVER]);
     $glabel->set_sensitive ($combo->sensitive);
@@ -620,6 +641,15 @@ sub new_subpage_frame {
         '1',
         undef,
     ],
+    mp_rounds => [
+        'master_passphrase_pbkdf2_rounds',
+        $xl::s{l_oth_mp_rounds},
+        $xl::s{h_oth_mp_rounds},
+        'int,50000,1000000',
+        '3.13.2.110',
+        '50000',
+        undef,
+    ],
 );
 
 sub new_other_page() {
@@ -636,7 +666,11 @@ sub new_other_page() {
                new_subpage_frame (
                    new_vbox_pack (
                        new_check_button_for(\%pr::oth, 'use_netm')),
-                   $xl::s{netm_frame}, 'not-packed')
+                   $xl::s{netm_frame}, 'not-packed'),
+               new_subpage_frame (
+                   new_vbox_pack (
+                       new_text_box_for_int(\%pr::oth, 'mp_rounds')),
+                   $xl::s{mpass_frame}, 'not-packed')
            );
 }
 
@@ -664,7 +698,7 @@ sub new_other_page() {
         $xl::s{l_gui_dot_lines},
         $xl::s{h_gui_dot_lines},
         'bool',
-        '0.0.0',
+        '0.0.0,3.7.10.44',
         '0',
         undef,
     ],
@@ -785,6 +819,24 @@ sub new_other_page() {
         '0',
         undef,
     ],
+    type_any => [
+        'type_any_header',
+        $xl::s{l_gui_type_any},
+        $xl::s{h_gui_type_any},
+        'bool',
+        '3.12.0.44',
+        '0',
+        undef,
+    ],
+    next_del => [
+        'next_on_delete',
+        $xl::s{l_gui_next_del},
+        $xl::s{h_gui_next_del},
+        'bool',
+        '3.13.0.5',
+        '0',
+        undef,
+    ],
 );
 
 sub new_gui_page() {
@@ -793,7 +845,7 @@ sub new_gui_page() {
 
     my $cb_dot_lines = new_check_button_for (\%pr::gui, 'dot_lines');
     my $cb_toolbar_d = new_check_button_for (\%pr::gui, 'toolbar_d');
-    my $tb_zero_char = new_text_box_for_nchar (\%pr::gui, 'zero_char');
+    my $tb_zero_char = new_text_box_for_nchar (\%pr::gui, 'zero_char', \%HPVALUE);
 
     $gf->pack_start (new_subpage_frame (
                          new_vbox_pack (
@@ -807,6 +859,7 @@ sub new_gui_page() {
                              new_check_button_for (\%pr::gui, 'b_unread'),
                              new_check_button_for (\%pr::gui, 'swp_from'),
                              new_check_button_for (\%pr::gui, 'two_linev'),
+                             new_check_button_for (\%pr::gui, 'next_del'),
                              new_selection_box_for (\%pr::gui, 'from_show')),
                          $xl::s{mlist_frame}, 'not-packed'), 
                      FALSE, FALSE, FRAME_SPC);
@@ -819,7 +872,8 @@ sub new_gui_page() {
     $gf->pack_start (new_subpage_frame (
                          new_hbox_pack (
                              new_check_button_for (\%pr::gui, 'no_markup'),
-                             new_check_button_for (\%pr::gui, 'margin_co')),
+                             new_check_button_for (\%pr::gui, 'margin_co'),
+                             new_check_button_for (\%pr::gui, 'type_any')),
                          $xl::s{compo_frame}, 'not-packed'), 
                      FALSE, FALSE, FRAME_SPC);
     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
@@ -956,7 +1010,7 @@ sub new_gui_page() {
     addr_swc => [
         'address_search_wildcard',
         $xl::s{l_beh_addr_swc},
-        $xl::s{h_beh_addr_sw1},
+        $xl::s{h_beh_addr_swc},
         'bool',
         '3.9.3.18',
         '0',
@@ -965,7 +1019,7 @@ sub new_gui_page() {
     fold_swc => [
         'folder_search_wildcard',
         $xl::s{l_beh_fold_swc},
-        $xl::s{h_beh_fold_sw1},
+        $xl::s{h_beh_fold_swc},
         'bool',
         '3.9.3.18',
         '0',
@@ -1876,78 +1930,59 @@ sub print_help() {
     print $xl::s{about_title} . "\n";
     print $line;
     print _("Syntax:\n");
-    print _("    clawsker [options]\n");
+    print _("  clawsker [options]\n");
     print _("Options:\n");
-    print _("    --help                         Prints this help screen.\n");
-    print _("    --version                      Prints version infos.\n");
-    print _("    --verbose                      More messages on standard output.\n");
-    print _("    --alternate-config-dir <dir>   Uses <dir> as Claws Mail config dir.\n");
-    print _("    --clawsrc <file>               Uses <file> as full resource name.\n");
+    print _("  -h|--help                        Prints this help screen.\n");
+    print _("  -v|--version                     Prints version infos.\n");
+    print _("  -b|--verbose                     More messages on standard output.\n");
+    print _("  -a|--alternate-config-dir <dir>  Uses <dir> as Claws Mail config dir.\n");
+    print _("  -c|--clawsrc <file>              Uses <file> as full resource name.\n");
 }
 
-# handle errors which don't allow to run
-sub command_line_fatal {
-    my $reason = shift;
-    my $emsg = $xl::s{e_error} . $reason;
-    error_dialog ($emsg);
-    log_message ("$emsg", 'die');
-}
-
-# parse the command line
 sub parse_command_line {
+    my $cont = TRUE;
     $CLAWSV = get_claws_version ();
-    my $arg = 0;
-    while (defined($ARGV[$arg])) {
-        for ($ARGV[$arg]) {
-            /--help/ && do { 
-                print_help ();
-                return FALSE;
-            };
-            /--version/ && do { 
-                print_version ();
-                return FALSE;
-            };
-            /--verbose/ && do {
-                $VERBOSE = TRUE;
-                last;
-            };
-            /--use-claws-version/ && do {
-                ++$arg;
-                command_line_fatal ("required version")
-                    unless defined($ARGV[$arg]);
-                command_line_fatal ("required a dotted numeric value")
-                    unless ($ARGV[$arg] =~ /[\d\.]+/);
-                $CLAWSV = $ARGV[$arg];
-                last;
-            };
-            /--alternate-config-dir/ && do {
-                ++$arg;
-                command_line_fatal ($xl::s{e_requireddir})
-                    unless defined($ARGV[$arg]);
-                command_line_fatal ($xl::s{e_notadir})
-                    unless -d $ARGV[$arg];
-                $CONFIGDIR = $ARGV[$arg];
-                $CONFIGDIR .= "/" 
-                    unless ($CONFIGDIR =~ /.*\/$/);
-                $ALTCONFIGDIR = TRUE;
-                last;
-            };
-            /--clawsrc/ && do {
-                ++$arg;
-                command_line_fatal($xl::s{e_requiredfile})
-                    unless defined($ARGV[$arg]);
-                command_line_fatal($xl::s{e_notafile})
-                    unless -f $ARGV[$arg];
-                set_rc_filename ($ARGV[$arg]);
-                last;
-            };
-            /.*/ && command_line_fatal (
-                _("unknown option '{opt}'.\n", opt => $ARGV[$arg]));
+    eval {
+        GetOptions('h|help' => sub { print_help (); $cont = FALSE },
+            'v|version' => sub { print_version (); $cont = FALSE },
+            'b|verbose' => sub { $VERBOSE = TRUE },
+            'u|use-claws-version=s' => \&opt_use_claws_version,
+            'a|alternate-config-dir=s' => \&opt_alternate_config_dir,
+            'r|clawsrc=s' => \&opt_clawsrc)
+        or die _("try -h or --help for syntax.\n");
+    };
+    if ($@) {
+        my $msg = _("Error in options: {msg}\n", msg => $@);
+        if (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '') {
+            eval { Gtk2->init };
+            error_dialog ($msg) unless $@;
         }
-        ++$arg;
+        die $msg;
     }
-    # eveything continues...
-    return TRUE;
+    return $cont;
+}
+
+sub opt_use_claws_version {
+    my ($name, $value) = @_;
+    die _("Error: {opt} requires a dotted numeric value argument\n", opt => $name)
+        unless ($value =~ /^[\d\.]+$/);
+    $CLAWSV = $value;
+}
+
+sub opt_alternate_config_dir {
+    my ($name, $value) = @_;
+    die _("Error: '{dir}' is not a directory or does not exist\n", dir => $value)
+        unless -d $value;
+    $CONFIGDIR = $value;
+    $CONFIGDIR .= "/" unless ($CONFIGDIR =~ /.*\/$/);
+    $ALTCONFIGDIR = TRUE;
+}
+
+sub opt_clawsrc {
+    my ($name, $value) = @_;
+    die _("Error: '{value}' is not a file or does not exist\n", value => $value)
+        unless -f $value;
+    set_rc_filename ($value);
 }
 
 # update the hidden preferences status from loaded values
@@ -2038,7 +2073,7 @@ 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, see &lt;http://www.gnu.org/licenses/&gt;.";
-    my $year = "2007-2014";
+    my $year = "2007-2016";
     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
     my $url = "http://www.claws-mail.org/clawsker.php";
 
@@ -2079,9 +2114,24 @@ sub new_button_box {
     return $hbox;
 }
 
+sub get_app_icons {
+    my $dir = $DATADIR . '/icons/hicolor';
+    my @names = map {
+      join ('/', ($dir, , $_ . 'x' . $_, 'apps', $NAME . '.png'))
+    } (64, 128);
+    my @icons = ();
+    foreach (@names) {
+        my $icon = undef;
+        $icon = Gtk2::Gdk::Pixbuf->new_from_file($_) if (-f $_);
+        push @icons, $icon if ($icon);
+    }
+    return @icons;
+}
+
 # initialise
-$main_window = Gtk2::Window->new ('toplevel');
 exit unless parse_command_line ();
+Gtk2->init;
+$main_window = Gtk2::Window->new ('toplevel');
 exit unless load_preferences ();
 exit unless init_hidden_preferences ();
 # create main GUI
@@ -2092,6 +2142,7 @@ $box->pack_start (new_notebook (), FALSE, FALSE, 0);
 $box->pack_end (new_button_box ($main_window, $about), FALSE, FALSE, 0);
 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
 $main_window->set_title ($xl::s{win_title});
+$main_window->set_icon_list (get_app_icons ());
 $main_window->add ($box);
 $main_window->show_all;
 Gtk2->main;