summaryrefslogtreecommitdiffstats
path: root/extensions/Bitly/lib/WebService.pm
blob: 4b44faa0e3593d0eab3bb3794cdc584a0044a47b (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
142
143
144
145
146
147
# 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;