summaryrefslogtreecommitdiffstats
path: root/extensions/TellUsMore/lib/WebService.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/TellUsMore/lib/WebService.pm')
-rw-r--r--extensions/TellUsMore/lib/WebService.pm259
1 files changed, 259 insertions, 0 deletions
diff --git a/extensions/TellUsMore/lib/WebService.pm b/extensions/TellUsMore/lib/WebService.pm
new file mode 100644
index 000000000..3ace06ef3
--- /dev/null
+++ b/extensions/TellUsMore/lib/WebService.pm
@@ -0,0 +1,259 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::TellUsMore::WebService;
+
+use strict;
+use warnings;
+
+use base qw(Bugzilla::WebService Bugzilla::Extension);
+
+use Bugzilla::Component;
+use Bugzilla::Constants;
+use Bugzilla::Error;
+use Bugzilla::Mailer;
+use Bugzilla::Product;
+use Bugzilla::User;
+use Bugzilla::UserAgent;
+use Bugzilla::Util;
+use Bugzilla::Version;
+
+use Bugzilla::Extension::TellUsMore::Constants;
+
+use Data::Dumper;
+use Email::MIME;
+use MIME::Base64;
+
+sub submit {
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # validation
+
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ if ($user->email ne TELL_US_MORE_LOGIN) {
+ ThrowUserError('tum_auth_failure');
+ }
+
+ if (Bugzilla->params->{disable_bug_updates}) {
+ ThrowUserError('tum_updates_disabled');
+ }
+
+ $self->_validate_params($params);
+ $self->_set_missing_params($params);
+
+ my $creator = $self->_get_user($params->{creator});
+ if ($creator && $creator->disabledtext ne '') {
+ ThrowUserError('tum_account_disabled', { user => $creator });
+ }
+
+ $self->_validate_rate($params);
+
+ # create transient entry and email
+
+ $dbh->bz_start_transaction();
+ my $token = Bugzilla::Token::GenerateUniqueToken('tell_us_more', 'token');
+ my $id = $self->_insert($params, $token);
+ my $email = $self->_generate_email($params, $token, $creator);
+ $dbh->bz_commit_transaction();
+
+ # send email
+
+ MessageToMTA($email);
+
+ # done, return the id from the tell_us_more table
+
+ return $id;
+}
+
+sub _validate_params {
+ my ($self, $params) = @_;
+
+ $self->_validate_mandatory($params, 'Submission', MANDATORY_BUG_FIELDS);
+ $self->_remove_invalid_fields($params, MANDATORY_BUG_FIELDS, OPTIONAL_BUG_FIELDS);
+
+ if (!validate_email_syntax($params->{creator})) {
+ ThrowUserError('illegal_email_address', { addr => $params->{creator} });
+ }
+
+ if ($params->{attachments}) {
+ if (scalar @{$params->{attachments}} > MAX_ATTACHMENT_COUNT) {
+ ThrowUserError('tum_too_many_attachments', { max => MAX_ATTACHMENT_COUNT });
+ }
+ my $i = 0;
+ foreach my $attachment (@{$params->{attachments}}) {
+ $i++;
+ $self->_validate_mandatory($attachment, "Attachment $i", MANDATORY_ATTACH_FIELDS);
+ $self->_remove_invalid_fields($attachment, MANDATORY_ATTACH_FIELDS, OPTIONAL_ATTACH_FIELDS);
+ if (length(decode_base64($attachment->{content})) > MAX_ATTACHMENT_SIZE * 1024) {
+ ThrowUserError('tum_attachment_too_large', { filename => $attachment->{filename}, max => MAX_ATTACHMENT_SIZE });
+ }
+ }
+ }
+
+ # products are mapped to components of the target-product
+
+ Bugzilla::Component->new({ name => $params->{product}, product => $self->_target_product })
+ || ThrowUserError('invalid_product_name', { product => $params->{product} });
+}
+
+sub _set_missing_params {
+ my ($self, $params) = @_;
+
+ # set the product and component correctly
+
+ $params->{component} = $params->{product};
+ $params->{product} = TARGET_PRODUCT;
+
+ # priority, bug_severity
+
+ $params->{priority} = Bugzilla->params->{defaultpriority};
+ $params->{bug_severity} = Bugzilla->params->{defaultseverity};
+
+ # map invalid versions to 'unspecified'
+
+ if (!$params->{version}) {
+ $params->{version} = DEFAULT_VERSION;
+ } else {
+ Bugzilla::Version->new({ product => $self->_target_product, name => $params->{version} })
+ || ($params->{version} = DEFAULT_VERSION);
+ }
+
+ # set url
+
+ $params->{bug_file_loc} = $params->{url};
+
+ # detect the opsys and platform from user_agent
+
+ $ENV{HTTP_USER_AGENT} = $params->{user_agent};
+ $params->{rep_platform} = detect_platform();
+ $params->{op_sys} = detect_op_sys();
+
+ # set group based on restricted
+
+ $params->{group} = $params->{restricted} ? SECURITY_GROUP : '';
+ delete $params->{restricted};
+}
+
+sub _get_user {
+ my ($self, $email) = @_;
+
+ return Bugzilla::User->new({ name => $email });
+}
+
+sub _insert {
+ my ($self, $params, $token) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ local $Data::Dumper::Purity = 1;
+ local $Data::Dumper::Sortkeys = 1;
+ my $content = Dumper($params);
+ trick_taint($content);
+
+ my $sth = $dbh->prepare('
+ INSERT INTO tell_us_more(token, mail, creation_ts, content)
+ VALUES(?, ?, ?, ?)
+ ');
+ $sth->bind_param(1, $token);
+ $sth->bind_param(2, $params->{creator});
+ $sth->bind_param(3, $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'));
+ $sth->bind_param(4, $content, $dbh->BLOB_TYPE);
+ $sth->execute();
+
+ return $dbh->bz_last_key('tell_us_more', 'id');
+}
+
+sub _generate_email {
+ my ($self, $params, $token, $user) = @_;
+
+ # create email parts
+
+ my $template = Bugzilla->template_inner;
+ my ($message_header, $message_text, $message_html);
+ my $vars = {
+ token_url => correct_urlbase() . 'page.cgi?id=tellusmore.html&token=' . url_quote($token),
+ recipient_email => $params->{creator},
+ recipient_name => ($user ? $user->name : $params->{creator_name}),
+ };
+
+ my $prefix = $user ? 'existing' : 'new';
+ $template->process("email/$prefix-account.header.tmpl", $vars, \$message_header)
+ || ThrowCodeError('template_error', { template_error_msg => $template->error() });
+ $template->process("email/$prefix-account.txt.tmpl", $vars, \$message_text)
+ || ThrowCodeError('template_error', { template_error_msg => $template->error() });
+ $template->process("email/$prefix-account.html.tmpl", $vars, \$message_html)
+ || ThrowCodeError('template_error', { template_error_msg => $template->error() });
+
+ # create email object
+
+ my @parts = (
+ Email::MIME->create(
+ attributes => { content_type => "text/plain" },
+ body => $message_text,
+ ),
+ Email::MIME->create(
+ attributes => { content_type => "text/html" },
+ body => $message_html,
+ ),
+ );
+ my $email = new Email::MIME("$message_header\n");
+ $email->content_type_set('multipart/alternative');
+ $email->parts_set(\@parts);
+
+ return $email;
+}
+
+sub _validate_mandatory {
+ my ($self, $params, $name, @fields) = @_;
+
+ my @missing_fields;
+ foreach my $field (@fields) {
+ if (!exists $params->{$field} || $params->{$field} eq '') {
+ push @missing_fields, $field;
+ }
+ }
+
+ if (scalar @missing_fields) {
+ ThrowUserError('tum_missing_fields', { name => $name, missing => \@missing_fields });
+ }
+}
+
+sub _remove_invalid_fields {
+ my ($self, $params, @valid_fields) = @_;
+
+ foreach my $field (keys %$params) {
+ if (!grep { $_ eq $field } @valid_fields) {
+ delete $params->{$field};
+ }
+ }
+}
+
+sub _validate_rate {
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my ($report_count) = $dbh->selectrow_array('
+ SELECT COUNT(*)
+ FROM tell_us_more
+ WHERE mail = ?
+ AND creation_ts >= NOW() - ' . $dbh->sql_interval(1, 'MINUTE')
+ , undef, $params->{creator}
+ );
+ if ($report_count + 1 > MAX_REPORTS_PER_MINUTE) {
+ ThrowUserError('tum_rate_exceeded', { max => MAX_REPORTS_PER_MINUTE });
+ }
+}
+
+sub _target_product {
+ my ($self) = @_;
+
+ my $product = Bugzilla::Product->new({ name => TARGET_PRODUCT })
+ || ThrowUserError('invalid_product_name', { product => TARGET_PRODUCT });
+ return $product;
+}
+
+1;