line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Jabber::Connection; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# $Id: Connection.pm,v 1.7 2002/05/06 16:53:11 dj Exp $ |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Connection - Simple connectivity functions for Jabber |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# client connection: |
12
|
|
|
|
|
|
|
my $c = new Jabber::Connection( |
13
|
|
|
|
|
|
|
server => 'jabber.org', |
14
|
|
|
|
|
|
|
log => 1, |
15
|
|
|
|
|
|
|
); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# component connection: |
18
|
|
|
|
|
|
|
# my $c = new Jabber::Connection( |
19
|
|
|
|
|
|
|
# server => 'localhost:5700', |
20
|
|
|
|
|
|
|
# localname => 'comp.localhost', |
21
|
|
|
|
|
|
|
# ns => 'jabber:component:accept', |
22
|
|
|
|
|
|
|
# log => 1, |
23
|
|
|
|
|
|
|
# debug => 1, |
24
|
|
|
|
|
|
|
# ); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
die "oops: ".$c->lastError unless $c->connect(); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$c->register_beat(10, \&every_10_seconds); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$c->register_handler('presence',\&presence); |
31
|
|
|
|
|
|
|
$c->register_handler('iq',\&handle_iq_conference); |
32
|
|
|
|
|
|
|
$c->register_handler('iq',\&handle_iq_browse); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
$c->auth('qmacro','password','myresource'); # client auth |
35
|
|
|
|
|
|
|
# $c->auth('secret'); # component auth |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$c->send('<presence/>'); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
$c->start; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 DESCRIPTION |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
The Jabber::Connection package provides basic functions |
44
|
|
|
|
|
|
|
for connecting clients and components to a Jabber server. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
1
|
|
|
1
|
|
2266
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2605
|
|
49
|
1
|
|
|
1
|
|
1842
|
use XML::Parser; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
use IO::Socket::INET; |
51
|
|
|
|
|
|
|
use IO::Select; |
52
|
|
|
|
|
|
|
use Digest::SHA1 qw(sha1_hex); |
53
|
|
|
|
|
|
|
use Carp; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
use Jabber::NS qw(:all); |
56
|
|
|
|
|
|
|
use Jabber::NodeFactory; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
use constant BEAT => 5; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
use vars qw($VERSION); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
$VERSION = '0.04'; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my $id = 1; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 METHODS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=over 4 |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=item new() |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
The connection constructor. Returns a new Jabber::Connection object. |
73
|
|
|
|
|
|
|
The parameters are specified in a |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
param => value |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
list. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
For a basic client connection, you can specify the minimum |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
my $c = new Jabber::Connection(server => 'jabber.org'); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
If no port is specified, the default 5222 will be used. |
84
|
|
|
|
|
|
|
There are other parameters that can be passed: |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=over 4 |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item ns |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
the namespace that qualifies the connection stream. If |
91
|
|
|
|
|
|
|
left unspecified, this will default to 'jabber:client'. |
92
|
|
|
|
|
|
|
For a TCP socket-based component, specify |
93
|
|
|
|
|
|
|
'jabber:component:accept'. [ *** These are the only two |
94
|
|
|
|
|
|
|
stream namespaces supported now *** ] |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item localname |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
the name of the component in a component connection. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item ssl |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
whether the connection should use SSL |
103
|
|
|
|
|
|
|
[ *** not supported yet! *** ] |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=back |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
See the SYNOPSIS for examples of new(). |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=cut |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub new { |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my ($class, %args) = @_; |
114
|
|
|
|
|
|
|
my $self = {}; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# poss. keys in %args: server, namespace, localname, ssl (?) |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
croak "No host specified" unless $args{server}; |
119
|
|
|
|
|
|
|
($self->{host}, $self->{port}) = split(":", $args{server}); |
120
|
|
|
|
|
|
|
$self->{port} ||= 5222; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$self->{ns} = $args{ns} || NS_CLIENT; |
123
|
|
|
|
|
|
|
$self->{localname} = $args{localname}; |
124
|
|
|
|
|
|
|
$self->{debug} = $args{debug}; |
125
|
|
|
|
|
|
|
$self->{log} = $args{log}; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
$self->{parser} = new XML::Parser |
128
|
|
|
|
|
|
|
( |
129
|
|
|
|
|
|
|
Handlers => { |
130
|
|
|
|
|
|
|
Start => sub { $self->_startTag(@_) }, |
131
|
|
|
|
|
|
|
End => sub { $self->_endTag(@_) }, |
132
|
|
|
|
|
|
|
Char => sub { $self->_charData(@_) }, |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
)->parse_start(); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
$self->{nf} = Jabber::NodeFactory->new(); |
137
|
|
|
|
|
|
|
$self->{beatcount} = 0; |
138
|
|
|
|
|
|
|
$self->{connected} = 0; |
139
|
|
|
|
|
|
|
$self->{streamerror} = 0; |
140
|
|
|
|
|
|
|
return bless $self, $class; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item connect() |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Use this to establish the stream to the Jabber server. There |
148
|
|
|
|
|
|
|
are no parameters required. If a problem occurs, the function |
149
|
|
|
|
|
|
|
returns 0, and the error reason is available by calling |
150
|
|
|
|
|
|
|
C<lastError()>. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Example: |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
$c->connect(); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=cut |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub connect { |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
my $self = shift; |
161
|
|
|
|
|
|
|
$self->{socket} = new IO::Socket::INET |
162
|
|
|
|
|
|
|
( |
163
|
|
|
|
|
|
|
PeerAddr => $self->{host}, |
164
|
|
|
|
|
|
|
PeerPort => $self->{port}, |
165
|
|
|
|
|
|
|
Proto => 'tcp', |
166
|
|
|
|
|
|
|
); |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
unless ($self->{socket}) { |
169
|
|
|
|
|
|
|
$self->{errortext} = "Can't establish socket connection"; |
170
|
|
|
|
|
|
|
return 0; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
$self->{select} = new IO::Select($self->{socket}); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
$self->_write($self->_stream_header()); |
176
|
|
|
|
|
|
|
$self->_read(); |
177
|
|
|
|
|
|
|
if ($self->{streamerror}) { |
178
|
|
|
|
|
|
|
return 0; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
else { |
181
|
|
|
|
|
|
|
return $self->{connected} = 1; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item disconnect() |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Use this to terminate the stream and end the connection. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Example: |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
$c->disconnect(); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub disconnect { |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
my $self = shift; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# send unavailable presence? |
202
|
|
|
|
|
|
|
$self->_write("</stream:stream>"); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item process() |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Call this function to look for incoming fragments on the stream. |
210
|
|
|
|
|
|
|
You can specify an optional argument which is the number of seconds |
211
|
|
|
|
|
|
|
to wait while looking. If no argument is given, a value of 0 is |
212
|
|
|
|
|
|
|
assumed. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
An incoming fragment is parsed and assembled into a Node object which |
215
|
|
|
|
|
|
|
is dispatched to any handlers that have been registered for the Node |
216
|
|
|
|
|
|
|
object's tag name. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
Examples: |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
$c->process(); # look for any fragments but don't |
221
|
|
|
|
|
|
|
# wait around if there aren't any |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
$c->process(5); # wait for up to 5 seconds for fragments |
224
|
|
|
|
|
|
|
# to come in on the stream |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=cut |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub process { |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
my $self = shift; |
232
|
|
|
|
|
|
|
my $timeout = shift || 0; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
if ($self->{select}->can_read($timeout)) { |
235
|
|
|
|
|
|
|
return $self->_read(); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
return 1; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _getID { |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
$id++; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item auth() |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Perform authorization. This function takes either one or three |
253
|
|
|
|
|
|
|
arguments, depending on what type of connection has been made. |
254
|
|
|
|
|
|
|
If you have made a I<component> connection, the secret must be |
255
|
|
|
|
|
|
|
specified here as the single argument. If you have made a |
256
|
|
|
|
|
|
|
I<client> connection, the username, password and resource must |
257
|
|
|
|
|
|
|
be specified. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
Example: |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
$c->auth('secret'); # component auth |
262
|
|
|
|
|
|
|
$c->auth('user','password','resource'); # client auth |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
For a component authorization, the <handshake/> based process |
265
|
|
|
|
|
|
|
is used. For a client authorization, the JSM is queried for the |
266
|
|
|
|
|
|
|
supported authentication methods, and then one is picked, |
267
|
|
|
|
|
|
|
degrading gracefully through zero-k, digest and plaintext |
268
|
|
|
|
|
|
|
methods. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=cut |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub auth { |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
my $self = shift; |
275
|
|
|
|
|
|
|
my ($user, $pass, $resource, %args, $secret); |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
$self->_checkConnected; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
if ($self->{ns} eq NS_CLIENT) { |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
($user, $pass, $resource, %args) = @_; |
282
|
|
|
|
|
|
|
croak "Supply user/pass/resource" unless $user and $pass and $resource; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
my $auth_node = $self->{nf}->newNode('iq'); |
285
|
|
|
|
|
|
|
$auth_node->attr('type', IQ_GET); |
286
|
|
|
|
|
|
|
my $query = $auth_node->insertTag('query'); |
287
|
|
|
|
|
|
|
$query->attr('xmlns', NS_AUTH); |
288
|
|
|
|
|
|
|
$query->insertTag('username')->data($user); |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
my $get_result = $self->ask($auth_node); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Assume we can authenticate and prepare a set |
293
|
|
|
|
|
|
|
$auth_node->attr('type', IQ_SET); |
294
|
|
|
|
|
|
|
$auth_node->attr('id', $self->_getID()); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# Zero-k? |
297
|
|
|
|
|
|
|
if ($get_result->getTag('query')->getTag('token')) { |
298
|
|
|
|
|
|
|
$self->_debug("auth: zerok supported"); |
299
|
|
|
|
|
|
|
my $hash = sha1_hex($pass); |
300
|
|
|
|
|
|
|
my $seq = $get_result->getTag('query')->getTag('sequence')->data; |
301
|
|
|
|
|
|
|
$hash = sha1_hex($hash.$get_result->getTag('query')->getTag('token')->data); |
302
|
|
|
|
|
|
|
$hash = sha1_hex($hash) while $seq--; |
303
|
|
|
|
|
|
|
$query->insertTag('hash')->data($hash); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# digest? |
307
|
|
|
|
|
|
|
elsif ($get_result->getTag('query')->getTag('digest')) { |
308
|
|
|
|
|
|
|
$self->_debug("auth: digest supported"); |
309
|
|
|
|
|
|
|
$query->insertTag('digest')->data(sha1_hex($self->{streamid}.$pass)); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# plaintext? |
313
|
|
|
|
|
|
|
elsif ($get_result->getTag('query')->getTag('password')) { |
314
|
|
|
|
|
|
|
$self->_debug("auth: plaintext supported"); |
315
|
|
|
|
|
|
|
$query->insertTag('password')->data($pass); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
else { |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
croak "No authentication methods available"; |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# abort to do |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# Add resource (common to all auth methods) |
327
|
|
|
|
|
|
|
$query->insertTag('resource')->data($resource); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# Auth attempt |
330
|
|
|
|
|
|
|
my $set_result = $self->ask($auth_node); |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# XXX todo: perhaps return undef/0 instead of croaking? |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
unless ($set_result->attr('type') eq IQ_RESULT) { |
335
|
|
|
|
|
|
|
croak "auth failed"; } |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
elsif ($self->{ns} eq NS_ACCEPT) { |
340
|
|
|
|
|
|
|
($secret) = @_; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
my $handshake = $self->{nf}->newNode('handshake'); |
343
|
|
|
|
|
|
|
$handshake->data(sha1_hex($self->{streamid}.$secret)); |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
my $result = $self->ask($handshake); |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# XXX todo: will barf with stream:error if bad secret. |
348
|
|
|
|
|
|
|
# XXX need to catch. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub _stream_header { |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
my $self = shift; |
358
|
|
|
|
|
|
|
my $to = defined($self->{localname}) ? $self->{localname} : $self->{host}; |
359
|
|
|
|
|
|
|
my $hdr = qq[<?xml version='1.0'?><stream:stream xmlns='$self->{ns}' xmlns:stream='http://etherx.jabber.org/streams' to='$to']; |
360
|
|
|
|
|
|
|
# $hdr .= qq[ from='$self->{localname}'] if $self->{ns} eq NS_ACCEPT; |
361
|
|
|
|
|
|
|
$hdr .= qq[>]; |
362
|
|
|
|
|
|
|
return $hdr; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item send() |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Send data across the stream with this function. You can send either |
369
|
|
|
|
|
|
|
XML in string form, or send a Node object. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Examples: |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
$c->send('<presence/>'); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
my $msg = $nf->newNode('message')->insertTag('body')->data('hello'); |
376
|
|
|
|
|
|
|
$msg->attr('to','qmacro@jabber.org'); |
377
|
|
|
|
|
|
|
$c->send($msg); |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=cut |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub send { |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
my $self = shift; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
$self->_checkConnected; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
my $what = shift; |
388
|
|
|
|
|
|
|
if (ref($what) eq 'Jabber::NodeFactory::Node') { |
389
|
|
|
|
|
|
|
$what = $what->toStr(); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
$self->_write($what); |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub _write { |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
my $self = shift; |
399
|
|
|
|
|
|
|
my $data = shift; |
400
|
|
|
|
|
|
|
$self->_log("SEND: ".$data); |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
$self->{socket}->send($data); |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub _read { |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
my $self = shift; |
409
|
|
|
|
|
|
|
my $data; |
410
|
|
|
|
|
|
|
my $received; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
while (defined $self->{socket}->recv($data, 1024)) { # or POSIX::BUFSIZ? |
413
|
|
|
|
|
|
|
$received .= $data; |
414
|
|
|
|
|
|
|
last if length($data) != 1024; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
$self->_log("RECV: ".$received); |
417
|
|
|
|
|
|
|
$self->{parser}->parse_more($received); |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
return $received; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub _log { |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my $self = shift; |
426
|
|
|
|
|
|
|
my $string = shift; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
if ($self->{log}) { |
429
|
|
|
|
|
|
|
print STDERR $string, "\n"; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub _debug { |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
my $self = shift; |
439
|
|
|
|
|
|
|
my $string = shift; |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
if ($self->{debug}) { |
442
|
|
|
|
|
|
|
print STDERR $string, "\n"; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
sub _startTag { |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
my ($self, $expat, $tag, %attr) = @_; |
451
|
|
|
|
|
|
|
if ($tag eq "stream:stream") { |
452
|
|
|
|
|
|
|
$self->{confirmedhost} = $attr{from}; |
453
|
|
|
|
|
|
|
$self->{streamid} = $attr{id}; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
else { |
456
|
|
|
|
|
|
|
$self->{depth} += 1; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# Top level fragment |
459
|
|
|
|
|
|
|
if ($self->{depth} == 1) { |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# Check it's not an error |
462
|
|
|
|
|
|
|
if ($tag eq 'stream:error') { |
463
|
|
|
|
|
|
|
$self->{streamerror} = 1; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
# Not an error - create Node |
466
|
|
|
|
|
|
|
else { |
467
|
|
|
|
|
|
|
$self->_debug("startTag: creating new node for $tag"); |
468
|
|
|
|
|
|
|
$self->{node} = Jabber::NodeFactory::Node->new($tag); |
469
|
|
|
|
|
|
|
$self->{node}->attr($_, $attr{$_}) foreach keys %attr; |
470
|
|
|
|
|
|
|
$self->{currnode} = $self->{node}; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Some node within a fragment |
475
|
|
|
|
|
|
|
else { |
476
|
|
|
|
|
|
|
my $kid = $self->{currnode}->insertTag($tag); |
477
|
|
|
|
|
|
|
$kid->attr($_, $attr{$_}) foreach keys %attr; |
478
|
|
|
|
|
|
|
$self->{currnode} = $kid; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub _endTag { |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
my ($self, $expat, $tag) = @_; |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# Don't bother to do anything if there's an error |
488
|
|
|
|
|
|
|
return if $self->{streamerror}; |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
if ($self->{depth} == 1) { |
491
|
|
|
|
|
|
|
$self->_dispatch($self->{currnode}); |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
else { |
494
|
|
|
|
|
|
|
$self->{currnode} = $self->{currnode}->parent(); |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
$self->{depth} -= 1; |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub _charData { |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
my ($self, $expat, $data) = @_; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Die if we get an error mid-stream |
506
|
|
|
|
|
|
|
if ($self->{streamerror}) { |
507
|
|
|
|
|
|
|
$self->{errortext} = $data; |
508
|
|
|
|
|
|
|
croak "stream error: $data" if $self->{connected}; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# Otherwise append the data to the current node |
512
|
|
|
|
|
|
|
else { |
513
|
|
|
|
|
|
|
$self->{currnode}->data($self->{currnode}->data().$data); |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item lastError() |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
Returns the last error that occured. This will usually be the |
521
|
|
|
|
|
|
|
text from a stream error. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=cut |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub lastError { |
526
|
|
|
|
|
|
|
my $self = shift; |
527
|
|
|
|
|
|
|
$self->{errortext}; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub _dispatch { |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
my ($self, $node) = @_; |
534
|
|
|
|
|
|
|
$self->_debug("dispatching ".$node->name); |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# Expecting an answer? |
537
|
|
|
|
|
|
|
if ($self->{askID}) { |
538
|
|
|
|
|
|
|
$self->{askID} = undef; |
539
|
|
|
|
|
|
|
$self->{answer} = $node; |
540
|
|
|
|
|
|
|
return; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# Otherwise call the handlers |
544
|
|
|
|
|
|
|
my $parcel = undef; |
545
|
|
|
|
|
|
|
foreach my $handler (@{$self->{handlers}->{$node->name}}) { |
546
|
|
|
|
|
|
|
$parcel = $handler->($node, $parcel) || $parcel; |
547
|
|
|
|
|
|
|
last if defined $parcel and $parcel eq r_HANDLED; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item ask() |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Send something and wait for a response relating to what was sent. This |
556
|
|
|
|
|
|
|
relation is established using an id attribute in the top level tag of |
557
|
|
|
|
|
|
|
the node being sent. If there is no id attribute, one is inserted with |
558
|
|
|
|
|
|
|
a value automatically assigned. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=cut |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub ask { |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
my ($self, $node) = @_; |
565
|
|
|
|
|
|
|
$self->_debug("ask: ".$node->name); |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# Add id if needed and remember |
568
|
|
|
|
|
|
|
unless ($self->{askID} = $node->attr('id')) { |
569
|
|
|
|
|
|
|
$self->_debug("ask: no ID - getting one"); |
570
|
|
|
|
|
|
|
$self->{askID} = $node->attr('id',$self->_getID()); |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
$self->_debug("ask: id=".$self->{askID}); |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# Send |
575
|
|
|
|
|
|
|
$self->_write($node->toStr()); |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# Wait for response |
578
|
|
|
|
|
|
|
while (not defined $self->{answer}) { |
579
|
|
|
|
|
|
|
$self->_debug("ask: waiting on answer"); |
580
|
|
|
|
|
|
|
$self->process(1); |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
my $answer = $self->{answer}; |
584
|
|
|
|
|
|
|
$self->{answer} = undef; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
$self->_debug("ask: got answer: ".$answer->toStr()); |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
return $answer; |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item register_handler() |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
When a fragment is received and turned into a Node object, a dispatching |
595
|
|
|
|
|
|
|
process is started which will call handlers (callbacks) that you can set |
596
|
|
|
|
|
|
|
using this function. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
The function takes two arguments. The first is used to identify the node |
599
|
|
|
|
|
|
|
type (the element) - e.g. 'message', 'presence' or 'iq'. The second is |
600
|
|
|
|
|
|
|
a reference to a subroutine. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
You can register as many handlers as you wish. Each of the handlers |
603
|
|
|
|
|
|
|
registered for a specific node type will be called in turn (in the |
604
|
|
|
|
|
|
|
order that they were registered). Each of the handlers are passed two |
605
|
|
|
|
|
|
|
things - the node being dispatched, and a 'parcel' which can be used to |
606
|
|
|
|
|
|
|
share data between the handlers being called. The parcel value passed |
607
|
|
|
|
|
|
|
to the first handler in the call sequence is undef. Whatever value |
608
|
|
|
|
|
|
|
is returned by a particular handler is then passed onto the next |
609
|
|
|
|
|
|
|
handler. |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
If a handler returns nothing (e.g. by simply the C<return> statement), |
612
|
|
|
|
|
|
|
then the parcel data remains unaffected and is passed on intact to the |
613
|
|
|
|
|
|
|
next handler. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
(You don't have to do anything with the parcel; it's there just in |
616
|
|
|
|
|
|
|
case you want to pass something along the call sequence.) |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
If a handler returns the special value represented by the constant |
619
|
|
|
|
|
|
|
C<r_HANDLED>, the call sequence is ended - no more handlers in the |
620
|
|
|
|
|
|
|
list are called in the dispatch for that node. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
Examples: |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
$c->register_handler( |
625
|
|
|
|
|
|
|
message => sub { |
626
|
|
|
|
|
|
|
... |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
); |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
$c->register_handler('iq', \&handle_version); |
631
|
|
|
|
|
|
|
$c->register_handler('iq', \&handle_time); |
632
|
|
|
|
|
|
|
$c->register_handler('iq', \&handle_browse); |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=cut |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
sub register_handler { |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
my $self = shift; |
639
|
|
|
|
|
|
|
my ($tag, $handler) = @_; |
640
|
|
|
|
|
|
|
$self->_debug("registering handler $handler"); |
641
|
|
|
|
|
|
|
push @{$self->{handlers}->{$tag}}, $handler; |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=item register_beat() |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
You can register subroutines to be called on a regular basis using |
649
|
|
|
|
|
|
|
the C<heartbeat> feature. The first argument is the number of seconds |
650
|
|
|
|
|
|
|
('every N seconds'), the second is a subroutine reference. |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
Example: |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
$c->register_beat(1800, \&getRSS); |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
This example registers a subroutine getRSS() to be called every |
657
|
|
|
|
|
|
|
half an hour. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Note: the heart doesn't start beating until the start() function |
660
|
|
|
|
|
|
|
is called. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=cut |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
sub register_beat { |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
my $self = shift; |
667
|
|
|
|
|
|
|
my ($secs, $handler) = @_; |
668
|
|
|
|
|
|
|
$self->_debug("registering beat $handler"); |
669
|
|
|
|
|
|
|
push @{$self->{heartbeats}->{$secs}}, $handler; |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
=item start() |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
Start a process loop. This has a similar effect to something |
677
|
|
|
|
|
|
|
like |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
while (1) { $c->process(1) } |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
except that it also maintains a heartbeat (see |
682
|
|
|
|
|
|
|
register_beat()). |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=cut |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
sub start { |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
my $self = shift; |
689
|
|
|
|
|
|
|
$SIG{ALRM} = sub { $self->_heartbeat(); alarm BEAT; }; |
690
|
|
|
|
|
|
|
alarm BEAT ; |
691
|
|
|
|
|
|
|
1 while $self->process(1); |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub connected { |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
my $self = shift; |
698
|
|
|
|
|
|
|
$self->{connected}; |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
sub _checkConnected { |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
my $self = shift; |
706
|
|
|
|
|
|
|
croak "No connection/stream established!" unless $self->connected; |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
} |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
sub _heartbeat { |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
my $self = shift; |
714
|
|
|
|
|
|
|
$self->{beatcount} += BEAT; |
715
|
|
|
|
|
|
|
foreach my $beat (keys %{$self->{heartbeats}}) { |
716
|
|
|
|
|
|
|
if ($self->{beatcount} % $beat == 0) { |
717
|
|
|
|
|
|
|
$_->() foreach @{$self->{heartbeats}->{$beat}}; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=back |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=head1 SEE ALSO |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
Jabber::NodeFactory, Jabber::NS |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=head1 AUTHOR |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
DJ Adams |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=head1 VERSION |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
early |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head1 COPYRIGHT |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify |
739
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=cut |
742
|
|
|
|
|
|
|
1; |