summaryrefslogtreecommitdiffstats
path: root/extensions/Bitly/lib/WebService.pm
blob: b900e17eeb2c94f7eb31b154709e7e7d74a6b40c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 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::Bitly::WebService;

use 5.10.1;
use strict;
use warnings;

use base qw(Bugzilla::WebService);

use Bugzilla::CGI;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Search;
use Bugzilla::Search::Quicksearch;
use Bugzilla::Util ();
use Bugzilla::WebService::Util 'validate';
use JSON;
use LWP::UserAgent;
use URI;
use URI::Escape;
use URI::QueryParam;

use constant PUBLIC_METHODS => qw(
  list
  shorten
);

sub _validate_uri {
  my ($self, $params) = @_;

  # extract url from params
  if (!defined $params->{url}) {
    ThrowCodeError('param_required', {function => 'Bitly.shorten', param => 'url'});
  }
  my $url = ref($params->{url}) ? $params->{url}->[0] : $params->{url};

  # only allow buglist queries for this bugzilla install
  my $uri = URI->new($url);
  $uri->query(undef);
  $uri->fragment(undef);
  if ($uri->as_string ne Bugzilla->localconfig->{urlbase} . 'buglist.cgi') {
    ThrowUserError('bitly_unsupported');
  }

  return URI->new($url);
}

sub shorten {
  my ($self) = shift;
  my $uri = $self->_validate_uri(@_);

  # the list_id is user-specific, remove it
  $uri->query_param_delete('list_id');

  return $self->_bitly($uri);
}

sub list {
  my ($self) = shift;
  my $uri = $self->_validate_uri(@_);

  # map params to cgi vars, converting quicksearch if required
  my $params
    = $uri->query_param('quicksearch')
    ? Bugzilla::CGI->new(quicksearch($uri->query_param('quicksearch')))->Vars
    : Bugzilla::CGI->new($uri->query)->Vars;

  # execute the search
  my $search = Bugzilla::Search->new(
    params => $params,
    fields => ['bug_id'],
    limit  => Bugzilla->params->{max_search_results},
  );
  my $data = $search->data;

  # form a bug_id only url, sanity check the length
  $uri
    = URI->new(Bugzilla->localconfig->{urlbase}
      . 'buglist.cgi?bug_id='
      . join(',', map { $_->[0] } @$data));
  if (length($uri->as_string) > CGI_URI_LIMIT) {
    ThrowUserError('bitly_failure',
      {message => "Too many bugs returned by search"});
  }

  # shorten
  return $self->_bitly($uri);
}

sub _bitly {
  my ($self, $uri) = @_;

  # form request url
  # http://dev.bitly.com/links.html#v3_shorten
  my $bitly_url = sprintf(
    'https://api-ssl.bitly.com/v3/shorten?access_token=%s&longUrl=%s',
    Bugzilla->params->{bitly_token},
    uri_escape($uri->as_string)
  );

  # is Mozilla::CA isn't installed, skip certificate verification
  eval { require Mozilla::CA };
  $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = $@ ? 0 : 1;

  # request
  my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
  $ua->timeout(10);
  $ua->protocols_allowed(['http', 'https']);
  if (my $proxy_url = Bugzilla->params->{proxy_url}) {
    $ua->proxy(['http', 'https'], $proxy_url);
  }
  else {
    $ua->env_proxy();
  }
  my $response = $ua->get($bitly_url);
  if ($response->is_error) {
    ThrowUserError('bitly_failure', {message => $response->message});
  }
  my $result = decode_json($response->decoded_content);
  if ($result->{status_code} != 200) {
    ThrowUserError('bitly_failure', {message => $result->{status_txt}});
  }

  # return just the short url
  return {url => $result->{data}->{url}};
}

sub rest_resources {
  return [
    qr{^/bitly/shorten$}, {GET => {method => 'shorten',},},
    qr{^/bitly/list$},    {GET => {method => 'list',},},
  ];
}

1;