diff --git a/mozilla/webtools/PLIF/PLIF/DataSource/User.pm b/mozilla/webtools/PLIF/PLIF/DataSource/User.pm
index 568823d8dd0..034ef39d3cd 100644
--- a/mozilla/webtools/PLIF/PLIF/DataSource/User.pm
+++ b/mozilla/webtools/PLIF/PLIF/DataSource/User.pm
@@ -49,6 +49,7 @@ sub getUserByUsername {
# decent databases can do this in one go. Those that can't can do
# it in a generic two-step process:
return $self->getUserByID($app, $self->getUserIDByUsername($app, $username));
+ # XXX no error checking! if getUserID... return undef, return ()!
# return the same as getUserByID()
}
@@ -56,7 +57,24 @@ sub getUserIDByUsername {
my $self = shift;
my($app, $username) = @_;
$self->notImplemented();
- # return userID
+ # return userID or undef
+}
+
+sub getUserByContactDetails {
+ my $self = shift;
+ my($app, $contactName, $address) = @_;
+ # decent databases can do this in one go. Those that can't can do
+ # it in a generic two-step process:
+ return $self->getUserByID($app, $self->getUserIDByContactDetails($app, $username));
+ # XXX no error checking! if getUserID... return undef, return ()!
+ # return the same as getUserByID()
+}
+
+sub getUserIDByContactDetails {
+ my $self = shift;
+ my($app, $contactName, $address) = @_;
+ $self->notImplemented();
+ # return userID or undef
}
sub getUserByID {
diff --git a/mozilla/webtools/PLIF/PLIF/DataSource/User/MySQL.pm b/mozilla/webtools/PLIF/PLIF/DataSource/User/MySQL.pm
index 8a6bf01350f..37a773c7e5e 100644
--- a/mozilla/webtools/PLIF/PLIF/DataSource/User/MySQL.pm
+++ b/mozilla/webtools/PLIF/PLIF/DataSource/User/MySQL.pm
@@ -48,8 +48,23 @@ sub getUserIDByUsername {
return $self->database($app)->execute('SELECT userData.userID
FROM userData, userDataTypes
WHERE userData.fieldID = userDataTypes.fieldID
+ AND userDataTypes.category = 'contact'
AND CONCAT(userDataTypes.data, userData.data) = ?', $username)->row->[0];
- # return userID
+ # XXX no error checking!
+ # return userID or undef
+}
+
+sub getUserIDByContactDetails {
+ my $self = shift;
+ my($app, $contactName, $address) = @_;
+ return $self->database($app)->execute('SELECT userData.userID
+ FROM userData, userDataTypes
+ WHERE userData.fieldID = userDataTypes.fieldID
+ AND userDataTypes.category = 'contact'
+ AND userDataTypes.name = ?
+ AND userData.data = ?', $contactName, $address)->row->[0];
+ # XXX no error checking!
+ # return userID or undef
}
sub getUserByID {
diff --git a/mozilla/webtools/PLIF/PLIF/Input.pm b/mozilla/webtools/PLIF/PLIF/Input.pm
index cfc5f363790..76138b14fd0 100644
--- a/mozilla/webtools/PLIF/PLIF/Input.pm
+++ b/mozilla/webtools/PLIF/PLIF/Input.pm
@@ -74,6 +74,22 @@ sub peekArgument {
return undef;
}
+# returns all the arguments in a form of a hash:
+# key => value
+sub getArguments {
+ my $self = shift;
+ $self->notImplemented();
+}
+
+sub hash {
+ my $self = shift;
+ return {
+ 'arguments' => $self->getArguments(),
+ 'protocol' => $self->defaultOutputProtocol(),
+ # XXX also UA, etc...
+ };
+}
+
# 'username' and 'password' are two out of band arguments that may be
# provided as well, they are accessed as properties of the input
# object (e.g., |if (defined($input->username)) {...}|). Input
@@ -82,6 +98,12 @@ sub peekArgument {
# username would be "AIM: HixieDaPixie". Other services, which get the
# username from the user (e.g. HTTP), should pass the username
# directly. See the user service for more details.
+#
+# 'username' should only be provided if the user attempted to log in.
+#
+# 'address' is an out of band argument that should only be provided
+# for input devices that know the address of the user (and can thus
+# construct the username).
# XXX I don't like having these here:
diff --git a/mozilla/webtools/PLIF/PLIF/Input/Arguments.pm b/mozilla/webtools/PLIF/PLIF/Input/Arguments.pm
index 54a1e98f2b7..08feef35ef0 100644
--- a/mozilla/webtools/PLIF/PLIF/Input/Arguments.pm
+++ b/mozilla/webtools/PLIF/PLIF/Input/Arguments.pm
@@ -59,6 +59,18 @@ sub getArgument {
}
}
+# Returns all the arguments present.
+sub getArguments {
+ my $self = shift;
+ my $result = {};
+ foreach my $argument (keys %$self) {
+ if ($argument =~ /^argument (.*)$/o) {
+ $result->{$1} = \@{$self->{$argument}};
+ }
+ }
+ return $result;
+}
+
# Returns the values given for that argument if it already exists,
# otherwise undef. In a scalar context, returns the first value (or
# undef if the argument was never given). In an array context, returns
diff --git a/mozilla/webtools/PLIF/PLIF/Input/CommandLine.pm b/mozilla/webtools/PLIF/PLIF/Input/CommandLine.pm
index c5df12ca36d..6bc192ddfa6 100644
--- a/mozilla/webtools/PLIF/PLIF/Input/CommandLine.pm
+++ b/mozilla/webtools/PLIF/PLIF/Input/CommandLine.pm
@@ -90,7 +90,7 @@ sub createArgument {
$self->app->output->request($argument);
# get input from user :-)
my $term = Term::ReadLine->new($self->app->name);
- my $value = $term->readline(''); # argument is prompt
+ my $value = $term->readline(''); # (the parameter passed is the prompt, if any)
# if we cached the input device:
# $term->addhistory($value);
$self->setArgument($argument, $value);
diff --git a/mozilla/webtools/PLIF/PLIF/Output/Generic.pm b/mozilla/webtools/PLIF/PLIF/Output/Generic.pm
index 2c7d9862158..d3772c05f64 100644
--- a/mozilla/webtools/PLIF/PLIF/Output/Generic.pm
+++ b/mozilla/webtools/PLIF/PLIF/Output/Generic.pm
@@ -113,6 +113,7 @@ sub output {
$expander = $self->app->getService('string.expander');
$self->assert($expander, 1, 'Could not find a string expander.');
}
+ $self->fillData($data);
$self->outputter->output($self->app, $session, $expander->expand($self->app, $session, $self->actualProtocol, $string, $data));
}
@@ -126,6 +127,14 @@ sub methodMissing {
my $self = shift;
my($method, @arguments) = @_;
if (not $self->app->dispatchMethod('dispatcher.output.'.$self->actualProtocol, 'output', $method, $self, @arguments)) {
- $self->SUPER::methodMissing(@_); # this does the same, but for 'dispatcher.output.generic' handlers
+ $self->SUPER::methodMissing(@_); # this does the same, but for 'dispatcher.output.generic' handlers, since that is our $self->protocol
}
}
+
+sub fillData {
+ my $self = shift;
+ my($data) = @_;
+ $data->{'app'} = $app->hash;
+ $data->{'session'} = $self->session->hash;
+ $data->{'input'} = $app->input->hash;
+}
diff --git a/mozilla/webtools/PLIF/PLIF/Program.pm b/mozilla/webtools/PLIF/PLIF/Program.pm
index 7f4ac63a94c..84610242bb7 100644
--- a/mozilla/webtools/PLIF/PLIF/Program.pm
+++ b/mozilla/webtools/PLIF/PLIF/Program.pm
@@ -55,8 +55,10 @@ sub run {
$self->objects([]);
if ($self->verifyInput()) {
if ($self->input->command) {
+ $self->command($command);
$self->dispatch($self->input->command);
} else {
+ $self->command('');
$self->noCommand();
}
} # verifyInput should deal with the errors
@@ -65,6 +67,7 @@ sub run {
$self->dump(3, "previous command didn't go over well: $@");
$self->output->reportFatalError($@);
}
+ $self->command(undef);
# In case we used a progressive output device, let it shut
# down. It's important to do this, because it holds a
# reference to us and we wouldn't want a memory leak...
@@ -135,7 +138,7 @@ sub output {
sub verifyInput {
my $self = shift;
# we invoke all the input verifiers until one fails
- my($result) = $self->getSelectingServiceList('input.verify')->verifyInput($self);
+ my $result = $self->getSelectingServiceList('input.verify')->verifyInput($self);
if (defined($result)) {
# if one failed, then the result will be the object that should report the error
$result->reportInputVerificationError($self);
@@ -150,6 +153,11 @@ sub selectOutputProtocol {
return $self->input->defaultOutputProtocol;
}
+sub hash {
+ my $self = shift;
+ return { 'name' => $self->name };
+}
+
# Implementation Specific Methods
# At least some of these should be overriden by real applications
@@ -184,3 +192,8 @@ sub noCommand {
my $self = shift;
$self->unknownCommand(@_);
}
+
+sub name {
+ my $self = shift;
+ $self->notImplemented();
+}
diff --git a/mozilla/webtools/PLIF/PLIF/Service/Coses.pm b/mozilla/webtools/PLIF/PLIF/Service/Coses.pm
index ef3c969f66e..73d363bfdf4 100644
--- a/mozilla/webtools/PLIF/PLIF/Service/Coses.pm
+++ b/mozilla/webtools/PLIF/PLIF/Service/Coses.pm
@@ -130,6 +130,9 @@ sub expand {
} elsif ($node eq 'text') {
if ($attributes->{'value'}) {
$result .= $self->evaluateExpression($attributes->{'value'}, $scope);
+ # XXX we need to also support:
+ # insert text escaped (as HTML, XML, URI, etc)
+ # insert a hash as a particular data structure (CGI arguments, an XML fragment, etc)
next node; # skip contents if attribute 'value' is present
}
} elsif ($node eq 'br') {
diff --git a/mozilla/webtools/PLIF/PLIF/Service/GenericOutputs.pm b/mozilla/webtools/PLIF/PLIF/Service/GenericOutputs.pm
new file mode 100644
index 00000000000..0797d3a2c94
--- /dev/null
+++ b/mozilla/webtools/PLIF/PLIF/Service/GenericOutputs.pm
@@ -0,0 +1,80 @@
+# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
+#
+# This file is MPL/GPL dual-licensed under the following terms:
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.1 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and
+# limitations under the License.
+#
+# The Original Code is PLIF 1.0.
+# The Initial Developer of the Original Code is Ian Hickson.
+#
+# Alternatively, the contents of this file may be used under the terms
+# of the GNU General Public License Version 2 or later (the "GPL"), in
+# which case the provisions of the GPL are applicable instead of those
+# above. If you wish to allow use of your version of this file only
+# under the terms of the GPL and not to allow others to use your
+# version of this file under the MPL, indicate your decision by
+# deleting the provisions above and replace them with the notice and
+# other provisions required by the GPL. If you do not delete the
+# provisions above, a recipient may use your version of this file
+# under either the MPL or the GPL.
+
+package PLIF::Service::GenericOutputs;
+use strict;
+use vars qw(@ISA);
+use PLIF::Service;
+@ISA = qw(PLIF::Service);
+1;
+
+sub provides {
+ my $class = shift;
+ my($service) = @_;
+ return ($service eq 'dispatcher.output.generic' or
+ $service eq 'dataSource.strings.default' or
+ $class->SUPER::provides($service));
+}
+
+# dispatcher.output.generic
+# this is typically used by input devices
+sub outputRequest {
+ my $self = shift;
+ my($app, $argument) = @_;
+ $output->output(undef, 'request', {
+ 'command' => $app->command,
+ 'argument' => $argument,
+ });
+}
+
+# dispatcher.output.generic
+sub outputReportFatalError {
+ my $self = shift;
+ my($error) = @_;
+ $self->output(undef, 'error', {
+ 'error' => $error,
+ });
+}
+
+# dataSource.strings.default
+sub getDefaultString {
+ my $self = shift;
+ my($app, $protocol, $string) = @_;
+ if ($protocol eq 'stdout') {
+ if ($string eq 'request') {
+ return '''? ';
+ } elsif ($string eq 'error') {
+ return 'Error:
';
+ }
+ } elsif ($protocol eq 'http') {
+ if ($string eq 'error') {
+ return 'HTTP/1.1 500 Internal Error
Content-Type: text/plain
Error:
';
+ }
+ }
+ return; # nope, sorry
+}
diff --git a/mozilla/webtools/PLIF/PLIF/Service/Login.pm b/mozilla/webtools/PLIF/PLIF/Service/Login.pm
new file mode 100644
index 00000000000..b6b0d30888f
--- /dev/null
+++ b/mozilla/webtools/PLIF/PLIF/Service/Login.pm
@@ -0,0 +1,229 @@
+# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
+#
+# This file is MPL/GPL dual-licensed under the following terms:
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.1 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and
+# limitations under the License.
+#
+# The Original Code is PLIF 1.0.
+# The Initial Developer of the Original Code is Ian Hickson.
+#
+# Alternatively, the contents of this file may be used under the terms
+# of the GNU General Public License Version 2 or later (the "GPL"), in
+# which case the provisions of the GPL are applicable instead of those
+# above. If you wish to allow use of your version of this file only
+# under the terms of the GPL and not to allow others to use your
+# version of this file under the MPL, indicate your decision by
+# deleting the provisions above and replace them with the notice and
+# other provisions required by the GPL. If you do not delete the
+# provisions above, a recipient may use your version of this file
+# under either the MPL or the GPL.
+
+package PLIF::Service::Login;
+use strict;
+use vars qw(@ISA);
+use PLIF::Service;
+@ISA = qw(PLIF::Service);
+1;
+
+sub provides {
+ my $class = shift;
+ my($service) = @_;
+ return ($service eq 'input.verify' or
+ $service eq 'input.verify.user.generic' or
+ $service eq 'user.login' or
+ $service eq 'dispatcher.commands' or
+ $service eq 'dispatcher.output.generic' or
+ $service eq 'dataSource.strings.default' or
+ $class->SUPER::provides($service));
+}
+
+# input.verify
+sub verifyInput {
+ my $self = shift;
+ my($app) = @_;
+ # let's see if there are any protocol-specific user authenticators
+ my @result = $self->getSelectingServiceList('input.verify.user.'.$app->input->protocol)->authenticateUser($app);
+ if (not @result) {
+ # ok, let's try the generic authenticators...
+ @result = $self->getSelectingServiceList('input.verify.user.generic')->authenticateUser($app);
+ }
+ # now let's see what that gave us
+ if (@result) {
+ # horrah, somebody knew what to do!
+ if (defined($result[0])) {
+ $app->addObject($result[0]); # they will have returned a user object
+ } else {
+ # hmm, so apparently user is not authentic
+ return $self; # supports user.login (reportInputVerificationError)
+ }
+ }
+ return; # nope, nothing to see here... (no error, anyway)
+}
+
+# input.verify.user.generic
+sub authenticateUser {
+ my $self = shift;
+ my($app) = @_;
+ if (defined($app->input->username)) {
+ return $app->getService('user.factory')->getUserByCredentials($app->input->username, $app->input->password);
+ } else {
+ return; # return nothing (not undef)
+ }
+}
+
+# input.verify
+sub reportInputVerificationError {
+ my $self = shift;
+ my($app) = @_;
+ $app->output->loginFailed(1); # 1 means 'invalid username/password'
+}
+
+# dispatcher.commands
+sub cmdSendPassword {
+ my $self = shift;
+ my($app) = @_;
+ my $protocol = $app->input->getAttribute('protocol');
+ my $address = $app->input->getAttribute('address');
+ if (defined($method) and defined($address)) {
+ my $user = $app->getService('user.factory')->getUserByContactDetails($app, $protocol, $address);
+ my $password;
+ if (defined($user)) {
+ $password = $self->changePassword($app, $user);
+ } else {
+ ($user, $password) = $self->createUser($app, $protocol, $address);
+ }
+ $self->sendPassword($app, $user, $protocol, $password);
+ } else {
+ $app->output->loginFailed(0); # 0 means 'no username/password'
+ }
+}
+
+# user.login
+sub hasRight {
+ my $self = shift;
+ my($app, $right) = @_;
+ my $user = $self->getObject('user');
+ if (defined($user)) {
+ if ($user->hasRight($right)) {
+ return $user;
+ } else {
+ $app->output->loginInsufficient($right);
+ }
+ } else {
+ $self->requireLogin($app);
+ }
+ return undef;
+}
+
+# user.login
+# this assumes user is not logged in
+sub requireLogin {
+ my $self = shift;
+ my($app) = @_;
+ my $address = $app->input->address;
+ if (defined($address) and not defined($app->getService('user.factory')->getUserByContactDetails($app, $app->input->protocol, $address))) {
+ my($user, $password) = $self->createUser($app, $app->input->protocol, $address);
+ $self->sendPassword($app, $user, $app->input->protocol, $password);
+ } else {
+ $app->output->loginFailed(0);
+ }
+}
+
+# dispatcher.output.generic
+sub outputLoginInsufficient {
+ my $self = shift;
+ my($app, $output, $right) = @_;
+ $output->output(undef, 'loginAccessDenied', {
+ 'right' => $right,
+ });
+}
+
+# dispatcher.output.generic
+sub outputLoginFailed {
+ my $self = shift;
+ my($app, $output, $tried) = @_;
+ $output->output(undef, 'loginFailed', {
+ 'tried' => $tried,
+ });
+}
+
+# dispatcher.output.generic
+sub outputLoginDetailsSent {
+ my $self = shift;
+ my($app, $output, $address, $protocol) = @_;
+ $output->output(undef, 'loginDetailsSent', {
+ 'address' => $address,
+ 'protocol' => $protocol,
+ });
+}
+
+# dispatcher.output.generic
+sub outputLoginDetails {
+ my $self = shift;
+ my($app, $output, $username, $password) = @_;
+ $output->output(undef, 'loginDetails', {
+ 'username' => $username,
+ 'password' => $password,
+ });
+}
+
+# dataSource.strings.default
+sub getDefaultString {
+ my $self = shift;
+ my($app, $protocol, $string) = @_;
+ if ($protocol eq 'stdout') {
+ if ($string eq 'loginAccessDenied') {
+ return 'Access Denied
';
+ } elsif ($string eq 'loginFailed') {
+ return 'Wrong username or password.You must give your username or password.
';
+ } elsif ($string eq 'loginDetailsSent') {
+ return 'Login details were sent. (Protocol: ; Address: )';
+ }
+ } elsif ($protocol eq 'http') {
+ if ($string eq 'loginAccessDenied') {
+ return 'HTTP/1.1 401 Access Denied
Content-Type: text/plain
Access Denied';
+ } elsif ($string eq 'loginFailed') {
+ return 'HTTP/1.1 401 Login Required
WWW-Authenticate: Basic realm=""
Content-Type: text/plain
Wrong username or password.You must give your username or password.';
+ } elsif ($string eq 'loginDetailsSent') {
+ return 'HTTP/1.1 200 OK
Content-Type: text/plain
Login details were sent.
Protocol:
Address: )';
+ }
+ }
+ return; # nope, sorry
+}
+
+
+# internal routines
+
+sub changePassword {
+ my $self = shift;
+ my($app, $user) = @_;
+ my($password, $crypt) = $app->getService('service.passwords')->newPassword();
+ $user->password($crypt);
+ return $password;
+}
+
+sub createUser {
+ my $self = shift;
+ my($app, $protocol, $address) = @_;
+ my($password, $crypt) = $app->getService('service.passwords')->newPassword();
+ my $user = $app->getService('user.factory')->getNewUser($app, $crypt);
+ $user->getField('contact', $protocol)->data = $address;
+ return ($user, $password);
+}
+
+sub sendPassword {
+ my $self = shift;
+ my($app, $user, $protocol, $password) = @_;
+ my $field = $user->hasField('contact', $protocol);
+ $self->assert(defined($field), 1, 'Tried to send a password using a protocol that the user has no mention of!'); # XXX grammar... :-)
+ $app->output($protocol, $user)->loginDetails($app, $field->username, $password);
+ $app->output->loginDetailsSent($app, $field->address, $protocol);
+}
diff --git a/mozilla/webtools/PLIF/PLIF/Service/Session.pm b/mozilla/webtools/PLIF/PLIF/Service/Session.pm
index 8ae731a1770..63938d165fa 100644
--- a/mozilla/webtools/PLIF/PLIF/Service/Session.pm
+++ b/mozilla/webtools/PLIF/PLIF/Service/Session.pm
@@ -59,3 +59,7 @@ sub getAddress {
my($protocol) = @_;
return undef; # 'not known over this protocol'
}
+
+sub hash {
+ return {};
+}
diff --git a/mozilla/webtools/PLIF/PLIF/Service/User.pm b/mozilla/webtools/PLIF/PLIF/Service/User.pm
index 5721b767032..d3a9003049d 100644
--- a/mozilla/webtools/PLIF/PLIF/Service/User.pm
+++ b/mozilla/webtools/PLIF/PLIF/Service/User.pm
@@ -71,6 +71,17 @@ sub getUserByUsername {
}
}
+sub getUserByContactDetails {
+ my $self = shift;
+ my($app, $contactName, $address) = @_;
+ my(@data) = $app->getService('dataSource.user')->getUserByContactDetails($app, $contactName, $address);
+ if (@data) {
+ return $self->objectCreate($app, @data);
+ } else {
+ return undef;
+ }
+}
+
sub getUserByID {
my $self = shift;
my($app, $id) = @_;
@@ -211,14 +222,13 @@ sub resetAddressChange {
sub hash {
my $self = shift;
- my $result = {
- 'userID' => $self->userID,
- 'mode' => $self->mode,
- 'adminMessage' => $self->adminMessage,
- 'fields' => {},
- 'groups' => $self->groups,
- 'rights' => keys(%{$self->rights});
- };
+ my $result = $self->SUPER::hash();
+ $result->{'userID'} = $self->userID,
+ $result->{'mode'} = $self->mode,
+ $result->{'adminMessage'} = $self->adminMessage,
+ $result->{'groups'} = $self->groups,
+ $result->{'rights'} = keys(%{$self->rights});
+ $result->{'fields'} = {},
foreach my $field (values(%{$self->fieldsByID})) {
# XXX should we also pass the field metadata on? (e.g. typeData)
$result->{'fields'}->{$field->fieldID} = $field->data;