From 52fb6ab96259df5eb63ae4632317edcf5900f31c Mon Sep 17 00:00:00 2001 From: Miquel Lionel Date: Sun, 7 Nov 2021 21:26:49 +0100 Subject: GetFileTable function is actually used, and more - Make code a bit more readable by aligning some equal signs at variable declarations. - Account creation by invites now. Still experimental. add all file needed - Fix the bug that bring you to the admin panel when you try to delete a GPG form link. - Incorrect wording: "Notify by mail with a link to the invite" in the admin panel becomes "Notify by mail after successful account creation". - Add autofocus attribute to username input at front page. saves a click. We also add some sensible tabindex values for username, password and "Login" button. - create .gitignore --- .gitignore | 9 + Makefile | 9 +- gpigeon-template.cgi | 471 ++++++++++++++++++++++++++++++++------------------- 3 files changed, 316 insertions(+), 173 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a5c159 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.asc +config.mk +gpigeon.cgi +gpigeonctl +link-tmpl.cgi +invites-tmpl.cgi +invites.html +index.html +test/* diff --git a/Makefile b/Makefile index 92c561c..d74d9e5 100644 --- a/Makefile +++ b/Makefile @@ -60,8 +60,14 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi invites-tmpl-template.cgi printf "\n${RED}The path for the link template wasn't set in your config.mk. Fix that.${STOP}" ;\ exit 1;\ fi + @if test -n '$(INVITE_TEMPLATE_PATH)'; then \ + printf "Invite template is at ${BOLD}$(INVITE_TEMPLATE_PATH)${STOP}\n"; \ + else \ + printf "\n${RED}The path for the invite template wasn't set in your config.mk. Fix that.${STOP}" ;\ + exit 1;\ + fi @if test -n '$(UPLOAD_TMPDIR)'; then \ - printf "\nUploaded files will be temporary stored at ${BOLD}$(UPLOAD_TMPDIR)${STOP}"; \ + printf "Uploaded files will be temporary stored at ${BOLD}$(UPLOAD_TMPDIR)${STOP}\n"; \ else \ printf "\n${RED}The temporary directory for uploaded files wasn't set in your config.mk. Fix that.${STOP}" ;\ exit 1;\ @@ -104,6 +110,7 @@ gpigeon: gpigeon-template.cgi link-tmpl-template.cgi invites-tmpl-template.cgi @sed -e 's|db_path_goes_here|$(DB_PATH)|g' -i gpigeon.cgi; @sed -e 's|db_path_goes_here|$(DB_PATH)|g' invites-tmpl-template.cgi > invites-tmpl.cgi; @sed -e 's|link_template_path_goes_here|$(LINK_TEMPLATE_PATH)|g' -i gpigeon.cgi; + @sed -e 's|invite_template_goes_here|$(INVITE_TEMPLATE_PATH)|g' -i gpigeon.cgi; @sed -e 's|cookies_dir_goes_here|$(COOKIES_DIR)|g' -i gpigeon.cgi; @sed -e 's|bin_path_goes_here|$(BINPREFIX)|g' link-tmpl-template.cgi > link-tmpl.cgi; @sed -e 's|bin_path_goes_here|$(BINPREFIX)|g' -i invites-tmpl.cgi; diff --git a/gpigeon-template.cgi b/gpigeon-template.cgi index 69df82a..042b78f 100755 --- a/gpigeon-template.cgi +++ b/gpigeon-template.cgi @@ -29,48 +29,49 @@ use File::stat; use String::Random; delete @ENV{qw(IFS PATH CDPATH BASH_ENV)}; -$ENV{'PATH'} = q{bin_path_goes_here}; -my $rIP = $ENV{REMOTE_ADDR}; -my $uagent = $ENV{HTTP_USER_AGENT}; +$ENV{'PATH'} = q{bin_path_goes_here}; +my $rIP = $ENV{REMOTE_ADDR}; +my $uagent = $ENV{HTTP_USER_AGENT}; my %text_strings = ( - addr => 'Address', - addr_ok => 'is valid!', - addr_nok => 'is not valid !', - addr_unknown => 'Unknown', - create_link_btn => 'Generate link', - cookie_problems =>'You got a cookie problem.
Clean them and log in again', - delete_link_btn_text => 'Delete', - delete_links_btn_text => 'Delete all links', - disconnect_btn_text => 'Disconnect', - here => 'here', - landingpage_title => 'GPIGEON - Log in', - logout_btn_text => 'Logout', - loginbtn => 'Log in', + addr => 'Address', + addr_ok => 'is valid!', + addr_nok => 'is not valid !', + addr_unknown => 'Unknown', + create_link_btn => 'Create link', + create_invite_btn => 'Create invite', + cookie_problems =>'You got a cookie problem.
Clean them and log in again', + delete_link_btn_text => 'Delete', + delete_links_btn_text => 'Delete all links', + disconnect_btn_text => 'Disconnect', + here => 'here', + landingpage_title => 'GPIGEON - Log in', + logout_btn_text => 'Logout', + loginbtn => 'Log in', link_asker_field_label => "Asker's mail :", - link_del_ok => 'Successful removal !', - link_legend_textarea =>'Type your message below :', - link_ok_for => 'Generated a link for', - link_del_failed => 'Deletion failed and here is why : ', - link_generated_ok => "Here's the link", - mailto_body => 'Your link is ', - mailto_subject => 'Link to your one time GPG messaging form', - incorrect_ids => 'Username/password combination
is incorrect.
Try again.', - password_label => 'Password', - refresh_btn => 'Refresh', - theader_link => 'Link', - theader_for => 'For', - theader_deletion => 'Deletion', - theader_cdate => 'Created on', - username_label => 'Username', - web_title => 'GPIGEON.CGI - Main', - web_greet_msg => 'Hi and welcome. What will you do today ?', + link_del_ok => 'Successful removal !', + link_legend_textarea =>'Type your message below :', + link_ok_for => 'Generated a link for', + link_del_failed => 'Deletion failed and here is why : ', + link_generated_ok => "Here's the link", + mailto_body => 'Your link is ', + mailto_subject => 'Link to your one time GPG messaging form', + incorrect_ids => 'Username/password combination
is incorrect.
Try again.', + password_label => 'Password', + refresh_btn => 'Refresh', + theader_link => 'Link', + theader_for => 'For', + theader_deletion => 'Deletion', + theader_cdate => 'Created on', + username_label => 'Username', + web_title => 'GPIGEON.CGI - Main', + web_greet_msg => 'Hi and welcome. What will you do today ?', ); sub DbGetLine { my ($dbh, $query) = @_; - my $prep = $dbh->prepare( $query ); - my $exec = $prep->execute() or die $DBI::errstr; + my $prep = $dbh->prepare( $query ); + my $exec = $prep->execute() or die $DBI::errstr; if ($exec < 0){ print $DBI::errstr; @@ -84,14 +85,14 @@ sub DbGetLine { sub GetFileTable { my ($dir ,$hidden_loginfield) = @_; - my @table = (); + my @table = (); opendir my $dir_hnd, "$dir" or die "[GetFileTable function] Can't open $dir: $!"; while (readdir $dir_hnd) { if ($_ ne '.' and $_ ne '..'){ my $linkfile_fn = $_; - my $linkstats= stat("$dir/$linkfile_fn"); - my $mtime = scalar localtime $linkstats->mtime; - my $link_asker = undef; + my $linkstats = stat("$dir/$linkfile_fn"); + my $mtime = scalar localtime $linkstats->mtime; + my $link_asker = undef; if (open my $f_hnd , '<', "$dir/$linkfile_fn"){ for (1..2){ $link_asker = readline $f_hnd; @@ -131,7 +132,9 @@ sub GetFileTable { } sub LoginOk { - my ($dbh, $username, $pass, $userid, $magic_cookie, $uid_cookie, $cookiesdir) = @_; + my ($dbh, $username, $pass, $userid, + $magic_cookie, $uid_cookie, + $cookiesdir) = @_; my $loginsuccess = PasswdLogin($dbh, $username, $pass); if (not defined $loginsuccess){ $loginsuccess = CookieLogin($userid, $magic_cookie, $uid_cookie, $cookiesdir); @@ -162,8 +165,8 @@ sub CookieLogin { if (-e $login_cookiefile){ open my $in, '<', $login_cookiefile or die "[CookieLogin function] can't read file: $!"; $rip_line = readline $in; - $ua_line = readline $in; - $id_line = readline $in; + $ua_line = readline $in; + $id_line = readline $in; $uid_line = readline $in; close $in; chomp ($rip_line, $ua_line, $id_line); # chomp the \n @@ -172,15 +175,15 @@ sub CookieLogin { return; } - my %id_line_cookie = CGI::Cookie->parse($id_line); + my %id_line_cookie = CGI::Cookie->parse($id_line); my %uid_line_cookie = CGI::Cookie->parse($uid_line); - my $id_value = $id_line_cookie{'id'}->value; - my $uid_value = $uid_line_cookie{'uid'}->value; + my $id_value = $id_line_cookie{'id'}->value; + my $uid_value = $uid_line_cookie{'uid'}->value; - my $ip_match = $rip_line cmp $rIP; - my $ua_match = $ua_line cmp $uagent; - my $uid_match = $uid_cookie->value cmp $uid_value; - my $id_match = $magic_cookie->value cmp $id_value; + my $ip_match = $rip_line cmp $rIP; + my $ua_match = $ua_line cmp $uagent; + my $uid_match = $uid_cookie->value cmp $uid_value; + my $id_match = $magic_cookie->value cmp $id_value; if ($ip_match == 0 and $ua_match == 0 and $uid_match == 0 and $id_match == 0){ return $userid; @@ -215,6 +218,32 @@ sub PasswdLogin { return; } return $userid; # as an userid is always > 0, we can use it as return value + } else { + return; + } + } else { + $dbh->disconnect; + return; + } + $dbh->disconnect; + return; +} + +sub LoginCookieGen { + my ($userid, $magic_cookie, $cookiesdir) = @_; + if (not defined $magic_cookie){ + my $str_rand_obj = String::Random->new; + my $val = $str_rand_obj->randregex('\w{64}'); + if (not -d "$cookiesdir/$userid"){ + mkpath("$cookiesdir/$userid"); + } + my $cookiefile = "$cookiesdir/$userid/$val.txt"; + my $new_magic_cookie = CGI::Cookie->new( + -name => 'id', + -value => $val, + -expires => '+1y', + '-max-age' => '+1y', + -domain => ".$ENV{'SERVER_NAME'}", -path => '/', -secure => 1, -httponly => 1, @@ -253,35 +282,43 @@ sub UntaintCGIFilename { my $hostname = $ENV{'SERVER_NAME'}; -my $db_path = q{db_path_goes_here}; -my $cookiesdir = q{cookies_dir_goes_here}; -my $link_template_path = q{link_template_path_goes_here}; -my $invites_template_path = q{invite_template_goes_here}; +my $db_path = q{db_path_goes_here}; +my $cookiesdir = q{cookies_dir_goes_here}; +my $link_template_path = q{link_template_path_goes_here}; +my $invites_template_path = q{invite_template_goes_here}; -my $cgi_query_get = CGI->new; -my $username = $cgi_query_get->param('username'); -my $pass = $cgi_query_get->param('password'); -my $disconnect = $cgi_query_get->param('disconnect'); +my $cgi_query_get = CGI->new; +my $username = $cgi_query_get->param('username'); +my $pass = $cgi_query_get->param('password'); +my $disconnect = $cgi_query_get->param('disconnect'); my ( $checkedornot, $hidden_loginfield, $magic_cookie, - $uid_cookie, $idval, $refresh_form, $userid) = undef; -my $linkgen_notif = my $mailisok_notif = my $deletion_notif = my $login_notif = ''; -my @created_links = (); -my %cur_cookies = CGI::Cookie->fetch; -$uid_cookie = $cur_cookies{'uid'}; -$magic_cookie = $cur_cookies{'id'}; -my $dbh = DBI->connect("DBI:SQLite:dbname=$db_path", undef, undef, { RaiseError => 1}) - or die $DBI::errstr; + $uid_cookie, $idval, $refresh_form, + $userid) = undef; +my $linkgen_notif = my $mailisok_notif = my $deletion_notif = my $login_notif = ''; +my @created_links = (); +my %cur_cookies = CGI::Cookie->fetch; +$uid_cookie = $cur_cookies{'uid'}; +$magic_cookie = $cur_cookies{'id'}; +my $dbh = DBI->connect("DBI:SQLite:dbname=$db_path", undef, undef, { RaiseError => 1}) + or die $DBI::errstr; + +if ($adminpanselect){ + $adminpan_field = q{}; +} + if (not defined $magic_cookie){ # cookie is not set $hidden_loginfield = qq{}; $refresh_form = qq{
$hidden_loginfield + $adminpan_field
}; }else{ $hidden_loginfield = qq{}; $refresh_form = qq{
+ $adminpan_field
}; $idval = $magic_cookie->value; @@ -321,7 +358,8 @@ if ($disconnect and defined $magic_cookie){ # if we disconnect and cookie is act ); my $f = "$cookiesdir/$userid/$idval.txt"; if (-e "$f"){ - unlink "$f" or die "cant delete cookie at $f :$!\n"; + unlink "$f" or die "cant delete cookie at $f :$!\n"; # delet it + } print "Set-Cookie: $delete_uid_cookie\n"; print "Set-Cookie: $delete_id_cookie\n"; @@ -333,104 +371,191 @@ my $loginok = LoginOk($dbh, $username, $pass, $userid, $magic_cookie, $uid_cooki print "Cache-Control: no-store, must-revalidate\n"; if($loginok){ - $userid = $loginok; - LoginCookieGen($userid, $magic_cookie, $cookiesdir); + $userid = $loginok; my $user_mailaddr = DbGetLine($dbh, qq{SELECT mail from pigeons where userid='$userid';}); - my $nick = DbGetLine($dbh, qq{SELECT name from pigeons where userid='$userid';}); - my $gpgid = DbGetLine($dbh, qq{SELECT gpgfp from pigeons where userid='$userid';}); + my $nick = DbGetLine($dbh, qq{SELECT name from pigeons where userid='$userid';}); + my $isadmin = DbGetLine($dbh, qq{SELECT isadmin from pigeons where userid='$userid';}); + LoginCookieGen($userid, $magic_cookie, $cookiesdir); + + if ($isadmin){ + $adminbtn = qq{
+ $hidden_loginfield + + +
}; + if (not -d "i/$userid"){ + mkpath("./i/$userid"); + } + } + if (not -d "./l/$userid"){ mkpath("./l/$userid"); } if (defined $cgi_query_get->param('supprlien')){ my $pending_deletion = $cgi_query_get->param('supprlien'); - my $linkfile_fn = "./l/$userid/$pending_deletion"; - if (unlink UntaintCGIFilename($linkfile_fn)){ - $deletion_notif=qq{$text_strings{link_del_ok}}; - } - else { - $deletion_notif=qq{$text_strings{link_del_failed} $linkfile_fn : $!}; + + #make sure a form file deletion POST request don't go deleting other things + if ($pending_deletion =~ /^l\/$userid\/([\w]+)\.cgi$/ or $pending_deletion =~ /^i\/$userid\/([\w]+)\.cgi$/) { + if (unlink UntaintCGIFilename($pending_deletion)){ + $deletion_notif = qq{$text_strings{link_del_ok}}; + } + else { + $deletion_notif = qq{$text_strings{link_del_failed} $pending_deletion: $!}; + } } } if (defined $cgi_query_get->param('supprtout')){ rmtree("./l/$userid", {keep_root=>1, safe=>1}); - $deletion_notif=qq{$text_strings{link_del_ok}}; + $deletion_notif = qq{$text_strings{link_del_ok}}; + } + + if (defined $cgi_query_get->param('delallinvites')){ + rmtree("./i/$userid", {keep_root=>1, safe=>1}); + $deletion_notif = qq{$text_strings{link_del_ok}}; } + if (defined $cgi_query_get->param('geninv')){ + my $invite_asker = scalar $cgi_query_get->param('opt-mail'); + $mailisok_notif = qq{$text_strings{addr} $invite_asker $text_strings{addr_nok}}; + my $str_rand_obj = String::Random->new; + my $random_fn = $str_rand_obj->randregex('\w{64}'); + my $NEW_FORM_FILENAME = "$random_fn.cgi"; + my $HREF_LINK = "https://$hostname/cgi-bin/i/$userid/$NEW_FORM_FILENAME"; + my $INVITES_PATH = "./i/$userid/$NEW_FORM_FILENAME"; + + open my $in, '<', $invites_template_path or die "Can't read link template file: $!"; + open my $out, '>', $INVITES_PATH or die "Can't write to link file: $!"; + while( <$in> ) { + if ( Email::Valid->address($invite_asker) ){ + $mailisok_notif = qq{$text_strings{addr} $invite_asker $text_strings{addr_ok}}; + s/mail = undef;/mail = q{$invite_asker};/g; + s/{mailfield_goes_here}/{}/g; + } + s/{mailfield_goes_here}/{}/g; + + if (defined $cgi_query_get->param('mailnotif') ){ + s/EMAIL_NOTIF = q{0}/EMAIL_NOTIF = q{1}/g + } + + if (defined $cgi_query_get->param('adminprom') ){ + s/is_admin_goes_here/1/g + } + else{ + s/is_admin_goes_here/0/g + } + + s/{user_mailaddr_goes_here}/{$user_mailaddr}/g; + print $out $_; + } + + close $in or die; + chmod(0755,$INVITES_PATH) or die; + close $out or die; + + $linkgen_notif = qq{$text_strings{link_generated_ok}:
$HREF_LINK
}; + } + if (defined $cgi_query_get->param('mail')){ my $link_asker = scalar $cgi_query_get->param('mail'); if ( Email::Valid->address($link_asker) ){ - $mailisok_notif = qq{$text_strings{addr} $link_asker $text_strings{addr_ok}}; - my $str_rand_obj = String::Random->new; - my $random_fn = $str_rand_obj->randregex('\w{64}'); - my $GENERATED_FORM_FILENAME = "$random_fn.cgi"; - my $HREF_LINK = "https://$hostname/cgi-bin/l/$userid/$GENERATED_FORM_FILENAME"; - my $LINK_PATH = "./l/$userid/$GENERATED_FORM_FILENAME"; + $mailisok_notif = qq{$text_strings{addr} $link_asker $text_strings{addr_ok}}; + my $str_rand_obj = String::Random->new; + my $random_fn = $str_rand_obj->randregex('\w{64}'); + my $NEW_FORM_FILENAME = "$random_fn.cgi"; + my $HREF_LINK = "https://$hostname/cgi-bin/l/$userid/$NEW_FORM_FILENAME"; + my $LINK_PATH = "./l/$userid/$NEW_FORM_FILENAME"; open my $in, '<', $link_template_path or die "Can't read link template file: $!"; open my $out, '>', $LINK_PATH or die "Can't write to link file: $!"; while( <$in> ) { s/{link_user}/{$link_asker}/g; s/{user_mailaddr_goes_here}/{$user_mailaddr}/g; - s/{gpgid_goes_here}/{$gpgid}/g; print $out $_; } close $in or die; chmod(0755,$LINK_PATH) or die; close $out or die; - $linkgen_notif = qq{$text_strings{link_generated_ok}:
$HREF_LINK
}; + $linkgen_notif = qq{$text_strings{link_generated_ok}:
$HREF_LINK
}; } else{ $mailisok_notif = qq{$text_strings{addr} $link_asker $text_strings{addr_nok}}; } } - - - opendir my $dir_hnd, "./l/$userid" or die "Can't open ./l: $!"; - while (readdir $dir_hnd) { - if ($_ ne '.' and $_ ne '..'){ - my $linkfile_fn = $_; - my $linkstats = stat("./l/$userid/$linkfile_fn"); - my $linkcdate = scalar localtime $linkstats->mtime; - my $link_asker = undef; - if (open my $linkfile_handle , '<', "./l/$userid/$linkfile_fn"){ - for (1..2){ - $link_asker = readline $linkfile_handle; - $link_asker =~ s/q\{(.*?)\}//i; - $link_asker = $1; - } - close $linkfile_handle; - - if (not defined $link_asker){ - $link_asker = $text_strings{unknown}; - } - #create links table html - push @created_links, - qq{ - ici - $link_asker - $linkcdate - -
- $hidden_loginfield - - -
- - }; - } - else { - close $linkfile_handle; - die 'Content-type: text/plain', "\n\n", "Error: Can't open $linkfile_fn: $!"; - } - } + + my @links_table = GetFileTable("l/$userid", $hidden_loginfield, $adminpan_field); + + print 'Content-type: text/html',"\n\n"; + if ($adminpanselect and $isadmin){ + my @invites_table = GetFileTable("i/$userid", $hidden_loginfield, $adminpan_field); + + + print qq{ + + + + + + + $text_strings{web_title} + + +

GPIGEON - Admin panel

+

Welcome to the admin panel. Here, you can view and generate account invites and also search and delete users.

+
+ $hidden_loginfield + +
+
+ + +
+ $refresh_form +
+
+ $hidden_loginfield + $adminpan_field + + + + +
+
+
+ +
+ $hidden_loginfield + $adminpan_field + +
+ $deletion_notif + + + + + + + + @invites_table + +
🔗 $text_strings{theader_link}📧 $text_strings{theader_for} ❌ $text_strings{theader_deletion}
+ + }; } - closedir $dir_hnd; - print 'Content-type: text/html',"\n\n", - qq{ + else { + print qq{ @@ -440,8 +565,9 @@ if($loginok){ $text_strings{web_title} -

$text_strings{web_title}

+

$text_strings{web_title}

$text_strings{web_greet_msg}

+ $adminbtn
@@ -452,7 +578,7 @@ if($loginok){ $hidden_loginfield Mail de la personne:
- +
$mailisok_notif @@ -461,21 +587,22 @@ if($loginok){
$hidden_loginfield - - +
$deletion_notif - - @created_links + + @links_table +
🔗 $text_strings{theader_link} 📧 $text_strings{theader_for} 📅 $text_strings{theader_creationdate} ❌ $text_strings{theader_deletion}
}; + } } else{ $dbh->disconnect; @@ -487,42 +614,42 @@ else{ } print "Content-type: text/html\n\n", - qq{ - - - - - - $text_strings{landingpage_title} - - -

$text_strings{landingpage_title}

-
- - - - - - - - - - - - - - - - - - - -
$text_strings{username_label} :
$text_strings{password_label} :
$login_notif
-
- -

Source code here. It is similar to hawkpost.co. - - - }; +qq{ + + + + + +$text_strings{landingpage_title} + + +

$text_strings{landingpage_title}

+
+ + + + + + + + + + + + + + + + + + + +
$text_strings{username_label}
$text_strings{password_label}
$login_notif
+
+ +

Source code here. It is similar to hawkpost.co. + + +}; } -- cgit v1.2.3-70-g09d2