123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266 |
- # Joiner - a user registration module for Oddmuse
- #
- # Copyright (C) 2014 Aki Goto <tyatsumi@gmail.com>
- #
- # Based on Login Module for Oddmuse (login.pl)
- # Copyright (C) 2004 Fletcher T. Penney <fletcher@freeshell.org>
- #
- # Codes included from questionasker.pl for Oddmuse
- # Copyright (C) 2004 Brock Wilcox <awwaiid@thelackthereof.org>
- # Copyright (C) 2006, 2007 Alex Schroeder <alex@gnu.org>
- #
- # Codes included from ReCaptcha Extension for Oddmuse
- # Copyleft 2008 by B.w.Curry <http://www.raiazome.com>.
- # Copyright 2004, 2008 by Brock Wilcox <awwaiid@thelackthereof.org>.
- # Copyright 2006, 2007, 2008 by Alex Schroeder <alex@gnu.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.
- #
- # 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, see <http://www.gnu.org/licenses/>.
- use strict;
- use v5.10;
- AddModuleDescription('joiner.pl', 'Joiner Extension');
- our ($q, $Now, %Action, @MyAdminCode, @MyInitVariables, $UserGotoBar, $DataDir, $FullUrl, $SiteName, %CookieParameters, @QuestionaskerQuestions, $QuestionaskerRememberAnswer, $QuestionaskerSecretKey, $ReCaptchaSecretKey, $ReCaptchaRememberAnswer);
- =head1 DESCRIPTION
- This is a user registration module for Oddmuse based on Fletcher's login.pl.
- File locking and some functions are improved.
- =head1 MENUS
- When not logged in, 'Login' and 'Register' menus are shown on UserGotoBar.
- When logged in, 'Logout' and 'Account Settings' menus are shown on UserGotoBar.
- In 'Account Settings', you can change password and email address.
- 'Forgot Password?' menu is in 'Login' menu.
- In Administration menu, 'Account Management' menu is shown.
- In this menu, you can ban accounts.
- =head1 REGISTRATION
- To register account, use 'Register' menu.
- You have to confirm the email address entered by visiting the link
- on the confirmation email sent to the address.
- =head1 CONFIGURATION
- You can set configuration variables below.
- $JoinerSalt:
- To increase security for storing passwords, specify arbitrary string.
- Default = ''.
- $JoinerGeneratorSalt:
- To increase security for auto generated passwords and ticket keys,
- specify arbitrary string.
- Default = ''.
- $JoinerEmailSenderAddress
- The sender address of the emails sent by this module.
- Default = 'www-data@example.net'.
- $JoinerCommentAllowed
- If 0, you must loggin to write comments.
- Default = 1.
- $JoinerMinimumPasswordLength
- Default = 6.
- $JoinerWait
- Retrying the email-sending commands is restricted to certain frequency.
- Specify the waiting duration for retry in seconds.
- Default = 60 * 10.
- $JoinerQuestionModule
- Specify cooperative anti-spam extension.
- Supported values are 'Questionasker', 'ReCaptcha' and 'GdSecurityImage'.
- Corresponding anti-spam extension need to be installed separately.
- Default = (auto detect).
- $JoinerDataDir
- When using with Namespaces Extension, specify original root data directory
- to concentrate Joiner data files in it.
- Default = $DataDir.
- $JoinerEmailCommand
- The command used to send email.
- Default = '/usr/sbin/sendmail -oi -t'.
- $JoinerEmailRegExp
- Email address format.
- Default = '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$'.
- =head1 DATA STRUCTURE
- Account data is stored in $DataDir/joiner directory.
- Registration pending email data is stored in $DataDir/joiner_email directory.
- Their data format is same as wiki page's.
- =cut
- our ($JoinerSalt, $JoinerGeneratorSalt, $JoinerEmailSenderAddress,
- $JoinerCommentAllowed, $JoinerMinimumPasswordLength, $JoinerWait,
- $JoinerQuestionModule, $JoinerDataDir, $JoinerEmailCommand, $JoinerEmailRegExp);
- our ($JoinerDir, $JoinerEmailDir, $JoinerMessage, $JoinerLoggedIn);
- use Digest::MD5;
- $Action{joiner_register} = \&JoinerDoRegister;
- $Action{joiner_process_registration} = \&JoinerDoProcessRegistration;
- $Action{joiner_confirm_registration} = \&JoinerDoConfirmRegistration;
- $Action{joiner_login} = \&JoinerDoLogin;
- $Action{joiner_process_login} = \&JoinerDoProcessLogin;
- $Action{joiner_ticket} = \&JoinerDoProcessLogin;
- $Action{joiner_logout} = \&JoinerDoLogout;
- $Action{joiner_account_settings} = \&JoinerDoAccountSettings;
- $Action{joiner_change_password} = \&JoinerDoChangePassword;
- $Action{joiner_process_change_password} = \&JoinerDoProcessChangePassword;
- $Action{joiner_forgot_password} = \&JoinerDoForgotPassword;
- $Action{joiner_process_forgot_password} = \&JoinerDoProcessForgotPassword;
- $Action{joiner_recover} = \&JoinerDoProcessLogin;
- $Action{joiner_change_email} = \&JoinerDoChangeEmail;
- $Action{joiner_process_change_email} = \&JoinerDoProcessChangeEmail;
- $Action{joiner_confirm_email} = \&JoinerDoConfirmEmail;
- $Action{joiner_manage} = \&JoinerDoManage;
- $Action{joiner_ban} = \&JoinerDoBan;
- $Action{joiner_process_ban} = \&JoinerDoProcessBan;
- push(@MyAdminCode, \&JoinerAdminCode);
- push(@MyInitVariables, \&JoinerInitVariables);
- sub JoinerGetPasswordHash {
- my ($raw_password) = @_;
- return Digest::MD5::md5_hex($JoinerSalt . $raw_password);
- }
- sub JoinerRequestLockOrError {
- my ($name) = @_;
- # 10 tries, 3 second wait, die on error
- return RequestLockDir($name, 10, 3, 1);
- }
- sub JoinerGetEmailFile {
- my ($email) = @_;
- return "$JoinerEmailDir/$email.email";
- }
- sub JoinerGetAccountFile {
- my ($username) = @_;
- return "$JoinerDir/$username.account";
- }
- # Always call JoinerCreateAccount within a lock.
- sub JoinerCreateAccount {
- my ($username, $password, $email, $key) = @_;
- my ($account_status, $account_data)
- = ReadFile(JoinerGetAccountFile($username));
- if ($account_status) {
- return T('Username:') . ' ' .
- Ts('The username %s already exists.', $username);
- }
- my ($email_status, $email_data) = ReadFile(JoinerGetEmailFile($email));
- if ($email_status) {
- my $email_page = ParseData($email_data);
- if ($email_page->{confirmed}) {
- return Ts('The email address %s has already been used.', $email);
- }
- if ($email_page->{registration_time} + $JoinerWait > $Now) {
- my $min = 1 + int(($email_page->{registration_time} + $JoinerWait - $Now) / 60);
- return Ts('Wait %s minutes before try again.', $min);
- }
- }
- my %email_page = ();
- $email_page{username} = $username;
- $email_page{email} = $email;
- $email_page{confirmed} = 0;
- $email_page{registration_time} = $Now;
- CreateDir($JoinerEmailDir);
- WriteStringToFile(JoinerGetEmailFile($email), EncodePage(%email_page));
- my %page;
- $page{username} = $username;
- $page{password} = $password;
- $page{email} = $email;
- $page{key} = $key;
- $page{confirmed} = 0;
- $page{registration_time} = $Now;
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%page));
- return '';
- }
- sub JoinerSendRegistrationConfirmationEmail {
- my ($email, $username, $key) = @_;
- my $link = "$FullUrl?action=joiner_confirm_registration&joiner_username=" . UrlEncode($username) . "&joiner_key=$key";
- open (my $EMAIL, '|', $JoinerEmailCommand);
- print $EMAIL "To: $email\n";
- print $EMAIL "From: $JoinerEmailSenderAddress\n";
- print $EMAIL "Subject: $SiteName " . T('Registration Confirmation') . "\n";
- print $EMAIL "\n";
- print $EMAIL T('Visit the link below to confirm registration.') . "\n";
- print $EMAIL "\n";
- print $EMAIL "$link\n";
- print $EMAIL "\n";
- close $EMAIL;
- }
- sub JoinerSendRecoverAccountEmail {
- my ($email, $username, $key) = @_;
- my $link = "$FullUrl?action=joiner_recover&joiner_username=" . UrlEncode($username) . "&joiner_key=$key";
- open (my $EMAIL, '|', $JoinerEmailCommand);
- print $EMAIL "To: $email\n";
- print $EMAIL "From: $JoinerEmailSenderAddress\n";
- print $EMAIL "Subject: " . T('Recover Account') . " - $SiteName\n";
- print $EMAIL "\n";
- print $EMAIL T('You can login by following the link below. Then set new password.') . "\n";
- print $EMAIL "\n";
- print $EMAIL "$link\n";
- print $EMAIL "\n";
- close $EMAIL;
- }
- sub JoinerSendChangeEmailEmail {
- my ($email, $username, $key) = @_;
- my $link = "$FullUrl?action=joiner_confirm_email&joiner_username=" . UrlEncode($username) . "&joiner_key=$key";
- open (my $EMAIL, '|', $JoinerEmailCommand);
- print $EMAIL "To: $email\n";
- print $EMAIL "From: $JoinerEmailSenderAddress\n";
- print $EMAIL "Subject: " . T('Change Email Address') . " - $SiteName\n";
- print $EMAIL "\n";
- print $EMAIL T('To confirm changing email address, follow the link below.') . "\n";
- print $EMAIL "\n";
- print $EMAIL "$link\n";
- print $EMAIL "\n";
- close $EMAIL;
- }
- sub JoinerQuestionaskerGetQuestion {
- my $need_button = shift;
- my $button = $need_button ? $q->submit(-value=>T('Go!')) : '';
- my $question_number = int(rand(scalar(@QuestionaskerQuestions)));
- return $q->div({-class=>'question'},
- $q->p(T('To submit this form you must answer this question:')),
- $q->blockquote($q->p($QuestionaskerQuestions[$question_number][0]),
- $q->p($q->input({-type=>'text', -name=>'answer'}),
- $q->input({-type=>'hidden', -name=>'question_num',
- -value=>$question_number}),
- $button)));
- }
- sub JoinerQuestionaskerCheck {
- my $question_num = GetParam('question_num', undef);
- my $answer = GetParam('answer', undef);
- unless (UserIsEditor()
- or $QuestionaskerRememberAnswer && GetParam($QuestionaskerSecretKey, 0)
- or $QuestionaskerQuestions[$question_num][1]($answer)) {
- # logging to the error log file of the server
- # warn "Q: '$QuestionaskerQuestions[$question_num][0]', A: '$answer'\n";
- return 0;
- }
- # Set the secret key only if a question has in fact been answered
- if (not GetParam($QuestionaskerSecretKey, 0)
- and $QuestionaskerQuestions[$question_num][1]($answer)) {
- SetParam($QuestionaskerSecretKey, 1)
- }
- return 1;
- }
- sub JoinerReCaptchaCheck {
- my $correct = 0;
- unless (UserIsEditor() or UserIsAdmin()
- or $ReCaptchaRememberAnswer && GetParam($ReCaptchaSecretKey, 0)
- or $correct = ReCaptchaCheckAnswer() # remember this!
- ) {
- # logging to the error log file of the server
- # warn "Q: '$ReCaptchaQuestions[$question_num][0]', A: '$answer'\n";
- return 0;
- }
- if (not GetParam($ReCaptchaSecretKey, 0) and $correct) {
- SetParam($ReCaptchaSecretKey, 1);
- }
- return 1;
- }
- sub JoinerGetQuestion {
- if ($JoinerQuestionModule eq 'Questionasker') {
- if (not $QuestionaskerRememberAnswer && GetParam($QuestionaskerSecretKey, 0)
- and not UserIsEditor()) {
- return JoinerQuestionaskerGetQuestion();
- }
- } elsif ($JoinerQuestionModule eq 'ReCaptcha') {
- if (not $ReCaptchaRememberAnswer && GetParam($ReCaptchaSecretKey, 0)
- and not UserIsEditor()) {
- return ReCaptchaGetQuestion();
- }
- } elsif ($JoinerQuestionModule eq 'GdSecurityImage') {
- return GdSecurityImageGetHtml();
- }
- return '';
- }
- sub JoinerCheckQuestion {
- if ($JoinerQuestionModule eq 'Questionasker') {
- if (!JoinerQuestionaskerCheck()) {
- $JoinerMessage = T('Question:') . ' ' . T('You did not answer correctly.');
- return 0;
- }
- } elsif ($JoinerQuestionModule eq 'ReCaptcha') {
- if (!JoinerReCaptchaCheck()) {
- $JoinerMessage = T('CAPTCHA:') . ' ' . T('You did not answer correctly.');
- return 0;
- }
- } elsif ($JoinerQuestionModule eq 'GdSecurityImage') {
- if (!GdSecurityImageCheck()) {
- $JoinerMessage = T('CAPTCHA:') . ' ' . T('Please type the six characters from the anti-spam image');
- return 0;
- }
- }
- return 1;
- }
- sub JoinerDoRegister {
- print GetHeader('', T('Registration'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print $q->start_p();
- print T('The username must be valid page name.');
- print $q->end_p();
- print $q->start_p();
- print T('Confirmation email will be sent to the email address.');
- print $q->end_p();
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_registration'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_password'}, T('Password:'))),
- $q->td($q->password_field(-name=>'joiner_password', -id=>'joiner_password')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_repeated_password'}, T('Repeat Password:'))),
- $q->td($q->password_field(-name=>'joiner_repeated_password', -id=>'joiner_repeated_password')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_email'}, T('Email:'))),
- $q->td($q->textfield(-name=>'joiner_email', -id=>'joiner_email')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
- print $q->table($table);
- print JoinerGetQuestion();
- print $q->end_form;
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessRegistration {
- my $username = GetParam('joiner_username', '');
- my $password = GetParam('joiner_password', '');
- my $repeated_password = GetParam('joiner_repeated_password', '');
- my $email = GetParam('joiner_email', '');
- my $message;
- $message = ValidId($username);
- if ($message ne '') {
- $JoinerMessage = T('Username:') . ' ' . $message;
- JoinerDoRegister();
- return;
- }
- if (!($email =~ /$JoinerEmailRegExp/)) {
- $JoinerMessage = T('Email:') . ' ' . T('Bad email address format.');
- JoinerDoRegister();
- return;
- }
- if (length($password) < $JoinerMinimumPasswordLength) {
- $JoinerMessage = T('Password:') . ' ' . Ts('Password needs to have at least %s characters.', $JoinerMinimumPasswordLength);
- JoinerDoRegister();
- return;
- }
- if ($repeated_password ne $password) {
- $JoinerMessage = T('Password:') . ' ' . T('Repeat Password:') . ' ' . T('Passwords differ.');
- JoinerDoRegister();
- return;
- }
- if (!JoinerCheckQuestion()) {
- JoinerDoRegister();
- return;
- }
- my $hash = JoinerGetPasswordHash($password);
- my $key = Digest::MD5::md5_hex($JoinerGeneratorSalt . rand());
- JoinerRequestLockOrError('joiner');
- $message = JoinerCreateAccount($username, $hash, $email, $key);
- ReleaseLockDir('joiner');
- if ($message ne '') {
- $JoinerMessage = $message;
- JoinerDoRegister();
- return;
- }
- JoinerSendRegistrationConfirmationEmail($email, $username, $key);
- print GetHeader('', T('Email Sent'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Ts('Confirmation email has been sent to %s. Visit the link on the mail to confirm registration.', $email);
- print $q->end_p();
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerShowRegistrationConfirmationFailed {
- print GetHeader('', T('Failed to Confirm Registration'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoConfirmRegistration {
- my $username = GetParam('joiner_username', '');
- my $key = GetParam('joiner_key', '');
- my $message = ValidId($username);
- if ($message ne '') {
- $JoinerMessage = T('Username:') . ' ' . $message;
- JoinerShowRegistrationConfirmationFailed();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Invalid key.');
- JoinerShowRegistrationConfirmationFailed();
- return;
- }
- my $page = ParseData($data);
- if ($key ne $page->{key}) {
- $JoinerMessage = T('Invalid key.');
- JoinerShowRegistrationConfirmationFailed();
- return;
- }
- if ($page->{registration_time} + $JoinerWait < $Now) {
- $JoinerMessage = T('The key expired.');
- JoinerShowRegistrationConfirmationFailed();
- return;
- }
- $page->{key} = '';
- $page->{confirmed} = 1;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- my $email = $page->{email};
- JoinerRequestLockOrError('joiner');
- my ($email_status, $email_data) = ReadFile(JoinerGetEmailFile($email));
- ReleaseLockDir('joiner');
- if ($email_status) {
- my $email_page = ParseData($email_data);
- $email_page->{confirmed} = 1;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerEmailDir);
- WriteStringToFile(JoinerGetEmailFile($email), EncodePage(%$email_page));
- ReleaseLockDir('joiner');
- }
- print GetHeader('', T('Registration Confirmed'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print T('Now, you can login by using username and password.');
- print $q->end_p();
- print $q->start_p();
- print ScriptLink('action=joiner_login', T('Login'));
- print $q->end_p();
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoLogin {
- print GetHeader('', T('Login'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_login'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_password'}, T('Password:'))),
- $q->td($q->password_field(-name=>'joiner_password', -id=>'joiner_password')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
- print $q->table($table);
- print JoinerGetQuestion();
- print $q->end_form;
- print $q->start_p();
- print ScriptLink('action=joiner_forgot_password', T('Forgot your password?'));
- print $q->end_p();
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessLogin {
- my $username = GetParam('joiner_username', '');
- my $password = GetParam('joiner_password', '');
- my $key = GetParam('joiner_key', '');
- my $message = ValidId($username);
- if ($message ne '') {
- $JoinerMessage = T('Username:') . ' ' . $message;
- JoinerDoLogin();
- return;
- }
- if (!($key ne '' && $password eq '') && !JoinerCheckQuestion()) {
- JoinerDoLogin();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Login failed.');
- JoinerDoLogin();
- return;
- }
- my $page = ParseData($data);
- my $hash = JoinerGetPasswordHash($password);
- if ($hash eq $page->{password}) {
- $page->{recover} = 0;
- SetParam('joiner_recover', 0);
- } elsif ($key ne '' && $key eq $page->{recover_key}) {
- if ($page->{recover_time} + $JoinerWait < $Now) {
- $JoinerMessage = T('The key expired.');
- JoinerDoLogin();
- return;
- }
- $page->{recover} = 1;
- SetParam('joiner_recover', 1);
- } else {
- $JoinerMessage = T('Login failed.');
- JoinerDoLogin();
- return;
- }
- if ($page->{banned}) {
- $JoinerMessage = T('You are banned.');
- JoinerDoLogin();
- return;
- }
- if (!$page->{confirmed}) {
- $JoinerMessage = T('You must confirm email address.');
- JoinerDoLogin();
- return;
- }
- my $session = Digest::MD5::md5_hex(rand());
- $page->{session} = $session;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- SetParam('username', $username);
- SetParam('joiner_session', $session);
- print GetHeader('', T('Logged in'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Ts('%s has logged in.', $username);
- print $q->end_p();
- if ($page->{recover}) {
- print $q->start_p();
- print T('You should set new password immediately.');
- print $q->end_p();
- print $q->start_p();
- print ScriptLink('action=joiner_change_password', T('Change Password'));
- print $q->end_p();
- }
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoLogout {
- my $username = GetParam('username', '');
- SetParam('username', '');
- SetParam('joiner_session', '');
- print GetHeader('', T('Logged out'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Ts('%s has logged out.', $username);
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoAccountSettings {
- if (!JoinerIsLoggedIn()) {
- JoinerDoLogin();
- return;
- }
- my $username = GetParam('username', '');
- print GetHeader('', T('Account Settings'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print T('Username:') . ' ' . $username;
- print $q->end_p();
- print $q->start_p();
- print ScriptLink('action=joiner_logout', T('Logout'));
- print $q->end_p();
- print $q->start_p();
- print ScriptLink('action=joiner_change_password', T('Change Password'));
- print $q->end_p();
- print $q->start_p();
- print ScriptLink('action=joiner_change_email', T('Change Email Address'));
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoChangePassword {
- if (!JoinerIsLoggedIn()) {
- JoinerDoLogin();
- return;
- }
- my $username = GetParam('username', '');
- my $recover = GetParam('joiner_recover', '');
- print GetHeader('', T('Change Password'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_change_password'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($username));
- if (!$recover) {
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_current_password'}, T('Current Password:'))),
- $q->td($q->password_field(-name=>'joiner_current_password', -id=>'joiner_current_password')));
- }
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_new_password'}, T('New Password:'))),
- $q->td($q->password_field(-name=>'joiner_new_password', -id=>'joiner_new_password')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_repeat_new_password'}, T('Repeat New Password:'))),
- $q->td($q->password_field(-name=>'joiner_repeat_new_password', -id=>'joiner_repeat_new_password')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
- print $q->table($table);
- print $q->end_form;
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessChangePassword {
- if (!JoinerIsLoggedIn()) {
- JoinerDoLogin();
- return;
- }
- my $username = GetParam('username', '');
- my $current_password = GetParam('joiner_current_password', '');
- my $new_password = GetParam('joiner_new_password', '');
- my $repeat_new_password = GetParam('joiner_repeat_new_password', '');
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Login failed.');
- JoinerDoChangePassword();
- return;
- }
- my $page = ParseData($data);
- my $hash = JoinerGetPasswordHash($current_password);
- if (!$page->{recover} && $hash ne $page->{password}) {
- $JoinerMessage = T('Current Password:') . ' ' . T('Password is wrong.');
- JoinerDoChangePassword();
- return;
- }
- if (length($new_password) < $JoinerMinimumPasswordLength) {
- $JoinerMessage = T('New Password:') . ' ' . Ts('Password needs to have at least %s characters.', $JoinerMinimumPasswordLength);
- JoinerDoChangePassword();
- return;
- }
- if ($repeat_new_password ne $new_password) {
- $JoinerMessage = T('New Password:') . ' ' . T('Repeat New Password:') . ' ' . T('Passwords differ.');
- JoinerDoChangePassword();
- return;
- }
- $page->{password} = JoinerGetPasswordHash($new_password);
- $page->{key} = '';
- $page->{recover} = '';
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- SetParam('joiner_recover', 0);
- print GetHeader('', T('Password Changed'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print T('Your password has been changed.');
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoForgotPassword {
- print GetHeader('', T('Forgot Password'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print $q->start_p();
- print T('Enter email address, and recovery login ticket will be sent.');
- print $q->end_p();
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_forgot_password'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_email'}, T('Email:'))),
- $q->td($q->textfield(-name=>'joiner_email', -id=>'joiner_email')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
- print $q->table($table);
- print JoinerGetQuestion();
- print $q->end_form;
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessForgotPassword {
- my $email = GetParam('joiner_email', '');
- if (!($email =~ /$JoinerEmailRegExp/)) {
- $JoinerMessage = T('Email:') . ' ' . T('Bad email address format.');
- JoinerDoForgotPassword();
- return;
- }
- if (!JoinerCheckQuestion()) {
- JoinerDoForgotPassword();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($email_status, $email_data) = ReadFile(JoinerGetEmailFile($email));
- ReleaseLockDir('joiner');
- if (!$email_status) {
- $JoinerMessage = T('Email:') . ' ' . T('Not found.');
- JoinerDoForgotPassword();
- return;
- }
- my $email_page = ParseData($email_data);
- my $username = $email_page->{username};
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Username:') . ' ' . T('Not found.');
- JoinerDoForgotPassword();
- return;
- }
- my $page = ParseData($data);
- if ($email ne $page->{email}) {
- $JoinerMessage = T('The mail address is not valid anymore.');
- JoinerDoForgotPassword();
- return;
- }
- if ($page->{recover_time} + $JoinerWait > $Now) {
- my $min = 1 + int(($page->{recover_time} + $JoinerWait - $Now) / 60);
- $JoinerMessage = Ts('Wait %s minutes before try again.', $min);
- JoinerDoForgotPassword();
- return;
- }
- my $key = Digest::MD5::md5_hex($JoinerGeneratorSalt . rand());
- $page->{recover_time} = $Now;
- $page->{recover_key} = $key;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- JoinerSendRecoverAccountEmail($email, $username, $key);
- print GetHeader('', T('Email Sent'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Ts('An email has been sent to %s with further instructions.', $email);
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoChangeEmail {
- if (!JoinerIsLoggedIn()) {
- JoinerDoLogin();
- return;
- }
- my $username = GetParam('username', '');
- print GetHeader('', T('Change Email Address'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_change_email'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($username));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_email'}, T('New Email Address:'))),
- $q->td($q->textfield(-name=>'joiner_email', -id=>'joiner_email')));
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_password'}, T('Password:'))),
- $q->td($q->password_field(-name=>'joiner_password', -id=>'joiner_password')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
- print $q->table($table);
- print $q->end_form;
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessChangeEmail {
- if (!JoinerIsLoggedIn()) {
- JoinerDoLogin();
- return;
- }
- my $username = GetParam('username', '');
- my $email = GetParam('joiner_email', '');
- my $password = GetParam('joiner_password', '');
- if (!($email =~ /$JoinerEmailRegExp/)) {
- $JoinerMessage = T('Email:') . ' ' . T('Bad email address format.');
- JoinerDoChangeEmail();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($email_status, $email_data) = ReadFile(JoinerGetEmailFile($email));
- ReleaseLockDir('joiner');
- if ($email_status) {
- my $email_page = ParseData($email_data);
- if ($email_page->{confirmed} && $email_page->{username} ne $username) {
- $JoinerMessage = T('Email:') . ' ' .
- Ts('The email address %s has already been used.', $email);
- JoinerDoChangeEmail();
- return;
- }
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Failed to load account.');
- JoinerDoChangeEmail();
- return;
- }
- my $page = ParseData($data);
- if ($page->{change_email_time} + $JoinerWait > $Now) {
- my $min = 1 + int(($page->{change_email_time} + $JoinerWait - $Now) / 60);
- $JoinerMessage = Ts('Wait %s minutes before try again.', $min);
- JoinerDoChangeEmail();
- return;
- }
- my $hash = JoinerGetPasswordHash($password);
- if ($hash ne $page->{password}) {
- $JoinerMessage = T('Password:') . ' ' . T('Password is wrong.');
- JoinerDoChangeEmail();
- return;
- }
- my $key = Digest::MD5::md5_hex(rand());
- $page->{change_email} = $email;
- $page->{change_email_key} = $key;
- $page->{change_email_time} = $Now;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- JoinerSendChangeEmailEmail($email, $username, $key);
- print GetHeader('', T('Email Sent'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Ts('An email has been sent to %s with a login ticket.', $email);
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerShowConfirmEmailFailed {
- print GetHeader('', T('Confirmation Failed'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print $q->start_p();
- print T('Failed to confirm.');
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoConfirmEmail {
- my $username = GetParam('joiner_username', '');
- my $key = GetParam('joiner_key', '');
- my $message = ValidId($username);
- if ($message ne '') {
- $JoinerMessage = $message;
- JoinerShowConfirmEmailFailed();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Failed to load account.');
- JoinerShowConfirmEmailFailed();
- return;
- }
- my $page = ParseData($data);
- if ($key ne $page->{change_email_key}) {
- $JoinerMessage = T('Invalid key.');
- JoinerShowConfirmEmailFailed();
- return;
- }
- my $new_email = $page->{change_email};
- $page->{email} = $new_email;
- $page->{change_email} = '';
- $page->{change_email_key} = '';
- $page->{change_email_time} = '';
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- my %email_page = ();
- $email_page{username} = $username;
- $email_page{email} = $new_email;
- $email_page{confirmed} = 1;
- $email_page{registration_time} = $Now;
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerEmailDir);
- WriteStringToFile(JoinerGetEmailFile($new_email), EncodePage(%email_page));
- ReleaseLockDir('joiner');
- print GetHeader('', T('Email Address Changed'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print Tss('Email address for %1 has been changed to %2.', $username, $new_email);
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoManage {
- UserIsAdminOrError();
- print GetHeader('', T('Account Management'), '');
- print $q->start_div({-class=>'joiner'});
- print $q->start_p();
- print ScriptLink('action=joiner_ban', T('Ban Account'));
- print $q->end_p();
- print $q->end_div();
- print PrintFooter();
- }
- sub JoinerDoBan {
- UserIsAdminOrError();
- print GetHeader('', T('Ban Account'), '');
- print $q->start_div({-class=>'joiner'});
- if ($JoinerMessage) {
- print $q->start_p() . $q->b($JoinerMessage) . $q->end_p();
- }
- print $q->start_p();
- print T('Enter username of the account to ban:');
- print $q->end_p();
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_ban'});
- print $q->input({-type=>'hidden', -name=>'joiner_ban', -value=>'1'});
- my $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Ban', -value=>T('Ban'))));
- print $q->table($table);
- print $q->end_form;
- print $q->start_p();
- print T('Enter username of the account to unban:');
- print $q->end_p();
- print GetFormStart(undef, undef, undef);
- print $q->input({-type=>'hidden', -name=>'action', -value=>'joiner_process_ban'});
- print $q->input({-type=>'hidden', -name=>'joiner_ban', -value=>'0'});
- $table = '';
- $table .= $q->Tr($q->td($q->label({-for=>'joiner_username'}, T('Username:'))),
- $q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
- $table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Unban', -value=>T('Unban'))));
- print $q->table($table);
- print $q->end_form;
- print $q->end_div();
- PrintFooter();
- }
- sub JoinerDoProcessBan {
- UserIsAdminOrError();
- my $username = GetParam('joiner_username', '');
- my $ban = GetParam('joiner_ban', '');
- my $message = ValidId($username);
- if ($message ne '') {
- $JoinerMessage = $message;
- JoinerDoBan();
- return;
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerMessage = T('Failed to load account.');
- JoinerDoBan();
- return;
- }
- my $page = ParseData($data);
- if ($ban) {
- if ($page->{banned}) {
- $JoinerMessage = Ts('%s is already banned.', $username);
- JoinerDoBan();
- return;
- }
- $page->{banned} = 1;
- $page->{session} = '';
- $JoinerMessage = Ts('%s has been banned.', $username);
- } else {
- if (!$page->{banned}) {
- $JoinerMessage = Ts('%s is not banned.', $username);
- JoinerDoBan();
- return;
- }
- $page->{banned} = 0;
- $JoinerMessage = Ts('%s has been unbanned.', $username);
- }
- JoinerRequestLockOrError('joiner');
- CreateDir($JoinerDir);
- WriteStringToFile(JoinerGetAccountFile($username), EncodePage(%$page));
- ReleaseLockDir('joiner');
- JoinerDoBan();
- }
- sub JoinerIsLoggedIn {
- if ($JoinerLoggedIn ne '') {
- return $JoinerLoggedIn;
- }
- my $username = GetParam('username', '');
- my $session = GetParam('joiner_session', '');
- my $message = ValidId($username);
- if ($message ne '') {
- $JoinerLoggedIn = 0;
- return $JoinerLoggedIn;
- }
- JoinerRequestLockOrError('joiner');
- my ($status, $data) = ReadFile(JoinerGetAccountFile($username));
- ReleaseLockDir('joiner');
- if (!$status) {
- $JoinerLoggedIn = 0;
- return $JoinerLoggedIn;
- }
- my $page = ParseData($data);
- if (!$page->{confirmed}) {
- $JoinerLoggedIn = 0;
- return $JoinerLoggedIn;
- }
- if ($session ne $page->{session}) {
- $JoinerLoggedIn = 0;
- return $JoinerLoggedIn;
- }
- if ($page->{banned}) {
- $JoinerLoggedIn = 0;
- return $JoinerLoggedIn;
- }
- $JoinerLoggedIn = 1;
- return $JoinerLoggedIn;
- }
- *OldJoinerUserCanEdit = \&UserCanEdit;
- *UserCanEdit = \&NewJoinerUserCanEdit;
- sub NewJoinerUserCanEdit {
- my ($id, $editing, $comment) = @_;
- if (!OldJoinerUserCanEdit($id, $editing, $comment)) {
- return 0;
- }
- return 1 if UserIsAdmin();
- return 1 if UserIsEditor();
- return 1 if $JoinerCommentAllowed and ($comment or (GetParam('aftertext', '') and not GetParam('text', '')));
- return JoinerIsLoggedIn();
- }
- *OldJoinerGetHeader = \&GetHeader;
- *GetHeader = \&NewJoinerGetHeader;
- sub NewJoinerGetHeader {
- if (JoinerIsLoggedIn()) {
- $UserGotoBar = ScriptLink('action=joiner_logout', T('Logout')) . ' ' .
- ScriptLink('action=joiner_account_settings', T('Account Settings')) . ' ' .
- $UserGotoBar;
- } else {
- $UserGotoBar = ScriptLink('action=joiner_login', T('Login')) . ' ' .
- ScriptLink('action=joiner_register', T('Register')) . ' ' .
- $UserGotoBar;
- }
- my ($id, $title, $oldId, $nocache, $status) = @_;
- return OldJoinerGetHeader($id, $title, $oldId, $nocache, $status);
- }
- sub JoinerAdminCode {
- my ($id, $menuref, $restref) = @_;
- push(@$menuref, ScriptLink('action=joiner_manage', T('Account Management')));
- }
- sub JoinerInitVariables {
- $JoinerSalt = '' unless defined $JoinerSalt;
- $JoinerGeneratorSalt = '' unless defined $JoinerGeneratorSalt;
- $JoinerEmailSenderAddress = 'www-data@example.net' unless defined $JoinerEmailSenderAddress;
- $JoinerCommentAllowed = 1 unless defined $JoinerCommentAllowed;
- $JoinerMinimumPasswordLength = 6 unless defined $JoinerMinimumPasswordLength;
- $JoinerWait = 60 * 10 unless defined $JoinerWait;
- if (!defined($JoinerQuestionModule)) {
- if (defined &QuestionaskerInit) {
- $JoinerQuestionModule = 'Questionasker';
- } elsif (defined &ReCaptchaInit) {
- $JoinerQuestionModule = 'ReCaptcha';
- } elsif (defined &GdSecurityImageInitVariables) {
- $JoinerQuestionModule = 'GdSecurityImage';
- } else {
- $JoinerQuestionModule = '';
- }
- }
- $JoinerDataDir = $DataDir unless defined $JoinerDataDir;
- $JoinerEmailCommand = '/usr/sbin/sendmail -oi -t' unless defined $JoinerEmailCommand;
- $JoinerEmailRegExp = '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$' unless defined $JoinerEmailRegExp;
- $JoinerDir = "$JoinerDataDir/joiner";
- $JoinerEmailDir = "$JoinerDataDir/joiner_email";
- $JoinerLoggedIn = '';
- $CookieParameters{'joiner_session'} = '';
- $CookieParameters{'joiner_recover'} = '';
- }
|