1#
2# BZ::Client.pm - Web services client for the Bugzilla server
3#
4
5package BZ::Client;
6
7use BZ::Client::XMLRPC();
8use HTTP::Cookies();
9
10our $VERSION = '1.04';
11
12
13sub new($%) {
14    my $class = shift;
15    my $self = { @_ };
16    bless($self, ref($class) || $class);
17    return $self;
18}
19
20sub url($;$) {
21    my $self = shift;
22    if (@_) {
23        $self->{'url'} = shift;
24    } else {
25        return $self->{'url'};
26    }
27}
28
29sub user($;$) {
30    my $self = shift;
31    if (@_) {
32        $self->{'user'} = shift;
33    } else {
34        return $self->{'user'};
35    }
36}
37
38sub password($;$) {
39    my $self = shift;
40    if (@_) {
41        $self->{'password'} = shift;
42    } else {
43        return $self->{'password'};
44    }
45}
46
47sub error($$;$$) {
48    my($self, $message, $http_code, $xmlrpc_code) = @_;
49    require BZ::Client::Exception;
50    BZ::Client::Exception->throw(message => $message,
51                                 http_code => $http_code,
52                                 xmlrpc_code => $xmlrpc_code);
53}
54
55sub log($$$) {
56    my($self, $level, $msg) = @_;
57    my $logger = $self->logger();
58    if ($logger) {
59        &$logger($level, $msg);
60    }
61}
62
63sub logger($;$) {
64    my($self) = shift;
65    if (@_) {
66        $self->{'logger'} = shift;
67    } else {
68        return $self->{'logger'};
69    }
70}
71
72sub logDirectory($;$) {
73    my($self) = shift;
74    if (@_) {
75        $self->{'logDirectory'} = shift;
76    } else {
77        return $self->{'logDirectory'};
78    }
79}
80
81sub xmlrpc($;$) {
82    my $self = shift;
83    if (@_) {
84        $self->{'xmlrpc'} = shift;
85    } else {
86        my $xmlrpc = $self->{'xmlrpc'};
87        if (!$xmlrpc) {
88            my $url = $self->url() || $self->error("The Bugzilla servers URL is not set.");
89            $xmlrpc = BZ::Client::XMLRPC->new("url" => $url);
90            $xmlrpc->logDirectory($self->logDirectory());
91            $xmlrpc->logger($self->logger());
92            $self->xmlrpc($xmlrpc);
93        }
94        return $xmlrpc;
95    }
96}
97
98sub login($) {
99    my $self = shift;
100    my $user = $self->user() || $self->error("The Bugzilla servers user name is not set.");
101    my $password = $self->password() || $self->error("The Bugzilla servers password is not set.");
102
103    my $params = { "login" => $user,
104                   "password" => $password,
105                   "remember" => BZ::Client::XMLRPC::boolean->new(0) };
106    my $cookies = HTTP::Cookies->new();
107    my $response = $self->_api_call("User.login", $params, $cookies);
108    if (!defined($response->{'id'})  ||  $response->{'id'} !~ /^\d+$/s) {
109        $self->error("Server did not return a valid user ID.");
110    }
111    $self->{"cookies"} = $cookies;
112    return;
113}
114
115sub logout($) {
116    my $self = shift;
117    my $cookies = $self->{"cookies"};
118    if ($cookies) {
119        $self->{"cookies"} = undef;
120        my $xmlrpc = $self->xmlrpc();
121        $xmlrpc->request("methodName" => "User.logout", params => [] );
122    }
123}
124
125sub is_logged_in($) {
126    my $self = shift;
127    return $self->{"cookies"} ? 1 : 0;
128}
129
130sub api_call($$$) {
131    my($self, $methodName, $params) = @_;
132    if (!$self->is_logged_in()) {
133        $self->login();
134    }
135    return $self->_api_call($methodName, $params);
136}
137
138sub _api_call($$$;$) {
139    my($self, $methodName, $params, $cookies) = @_;
140    $self->log("debug", "BZ::Client::_api_call, sending request for method $methodName to " . $self->url());
141    my $xmlrpc = $self->xmlrpc();
142    if ($cookies) {
143        $xmlrpc->user_agent()->cookie_jar($cookies);
144    }
145    my $response = $xmlrpc->request("methodName" => $methodName, params => [ $params ] );
146    if (!$response) {
147        $self->error("Empty response from server.");
148    }
149    if (ref($response) ne "HASH") {
150        $self->error("Invalid response from server: $response");
151    }
152    $self->log("debug", "BZ::Client::_api_call, got response for method $methodName");
153    return $response;
154}
155
1561;
157
158=pod
159
160=head1 NAME
161
162  BZ::Client - A client for the Bugzilla web services API.
163
164=head1 SYNOPSIS
165
166  my $client = BZ::Client->new("url" => $url,
167                               "user" => $user,
168                               "password" => $password);
169  $client->login();
170
171=head1 CLASS METHODS
172
173This section lists the class methods of BZ::Client.
174
175=head1 new
176
177  my $client = BZ::Client->new("url" => $url,
178                               "user" => $user,
179                               "password" => $password);
180
181The new method constructs a new instance of BZ::Client. Whenever you
182want to connect to the Bugzilla server, you must first create a
183Bugzilla client. The methods input is a hash of parameters.
184
185=over
186
187=item url
188
189The Bugzilla servers URL, for example C<https://bugzilla.mozilla.org/>.
190
191=item user
192
193The user name to use when logging in to the Bugzilla server. Typically,
194this will be your email address.
195
196=item password
197
198The password to use when logging in to the Bugzilla server.
199
200=back
201
202=head1 INSTANCE METHODS
203
204This section lists the methods, which an instance of BZ::Client can
205perform.
206
207=head2 url
208
209  my $url = $client->url();
210  $client->url($url);
211
212Returns or sets the Bugzilla servers URL.
213
214=head2 user
215
216  my $user = $client->user();
217  $client->user($user);
218
219Returns or sets the user name to use when logging in to the Bugzilla
220server. Typically, this will be your email address.
221
222=head2 password
223
224  my $password = $client->password();
225  $client->password($password);
226
227Returns or sets the password to use when logging in to the Bugzilla server.
228
229=head2 login
230
231Used to login to the Bugzilla server. There is no need to call this method
232explicitly: It is done automatically, whenever required.
233
234=head2 api_call
235
236  my $response = $client->api_call($methodName, $params);
237
238Used by subclasses of L<BZ::Client::API> to invoke methods of the Bugzilla
239API. Takes a method name and a hash ref of parameters as input. Returns a
240hash ref of named result objects.
241
242=head1 SEE ALSO
243
244  L<BZ::Client::Exception>
245
246