File Coverage

blib/lib/Net/SloppyXMPP.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Net::SloppyXMPP;
2              
3 1     1   24381 use strict;
  1         2  
  1         39  
4 1     1   6 use warnings;
  1         2  
  1         31  
5 1     1   1097 use Encode;
  1         13718  
  1         114  
6 1     1   968 use IO::Socket::INET;
  1         36255  
  1         10  
7 1     1   1406 use XML::Simple;
  0            
  0            
8             use Data::Dumper;
9              
10             our $VERSION = '0.06';
11              
12             =head1 NAME
13              
14             Net::SloppyXMPP - A rather sloppy XMPP client implementation
15              
16             =head1 DESCRIPTION
17              
18             In an attempt to drastically reduce external dependencies, this module doesn't use a lot of them.
19             Therefore, it doesn't do a whole lot via proper standards.
20              
21             The XML parser is a combination of a mess of regex hacks and some processing through XML::Simple.
22              
23             XML namespaces aren't really used properly.
24              
25             There's no guarantee that this will work for anything.
26              
27             Reinventing the wheel? You betcha. Unfortunately, neither L nor L would
28             work in the fashion I needed. It doesn't help that L is unmaintained (or so it seems)
29             these days. L requires LibIDN, which has been too big of an issue to deal with
30             where I'm needing to implement an XMPP client.
31              
32             SASL and TLS are both available, but not required. Just disable one or both of them if you don't
33             want or can't use them. SASL features are provided via L and are only used if
34             C is true (it's true unless you specifically set it to false). TLS features are provided
35             via L and are only used if C is true (it's true unless you specifically set
36             it to false).
37              
38             One of the goals of this implementation is to ensure that it will work on as many platforms as possible,
39             especially those that can't use a few of the dependencies of the other XMPP modules available for Perl.
40              
41             =head1 WHO SHOULD USE THIS?
42              
43             Probably no one. It's sloppy. It's untested. It's incomplete. But if the description above didn't
44             scare you away, you might be a good candidate. You'll probably need to track down some bugs in it
45             before you can really use it. If you're using Openfire 3.6.2 as an XMPP server, you might have good
46             luck in using it straight away. If you're using Google's XMPP service, you won't have any luck (yet).
47              
48             If you really want to use this module, but it doesn't work for you, please post your troubles on the
49             CPAN bug tracker. If you need support for additional XMPP servers, I'd love to add such support.
50             To do that, I might need access to the XMPP server with a test username/password. I'd really rather not
51             setup loads of XMPP servers for testing purposes. Providing me with a test account will help the process
52             of adding additional XMPP servers.
53              
54             But like I said, maybe no one should be using this module. Other seemingly good XMPP modules are
55             available on CPAN. Some examples: L and L.
56              
57             =head1 EXAMPLE
58              
59             use Net::SloppyXMPP;
60              
61             my $xmpp = Net::SloppyXMPP->new(
62             debug => 1,
63             tickdelay => 1,
64             #usetls => 0, # set this if you don't want TLS
65             #usesasl => 0, # set this if you don't want SASL
66             domain => 'yourdomain.xyz',
67             username => 'yourusername',
68             password => 'yourpassword',
69             resource => 'yourresourcename', # or don't set and a default will be supplied
70             initialpresence => 'available', # available, busy, dnd, defaults to available
71             initialstatus => 'I am alive!', # defaults to ''
72             message_callback => \&messageCallback,
73             );
74             die qq(XMPP didn't create.\n) unless $xmpp;
75              
76             sub messageCallback
77             {
78             my $xmpp = shift;
79             my $data = shift;
80             print Dumper($data);
81             }
82              
83             my $xmppConnect = $xmpp->connect;
84             die qq(XMPP didn't connect.\n) unless $xmppConnect;
85              
86             # if you want SloppyXMPP to control your main loop
87             $xmpp->run(\&tick);
88             sub tick
89             {
90             # do stuff in here that needs to happen each loop (use as a main loop)
91             my $xmpp = shift; # if you need it, same object as the $xmpp you already used
92             print "This runs every $xmpp->{tickdelay} seconds.\n";
93             }
94              
95             # or if you want to run your own loop, do this:
96             sub loop
97             {
98             print "Doing something useful here...\n";
99              
100             # ... more useful code ...
101              
102             $xmpp->tick; # runs the SloppyXMPP loop once
103              
104             # ... and more useful code ...
105             }
106             loop();
107              
108             =head1 ABSTRACT
109              
110             Not complete, just like the module itself. Feel free to read the source code to figure out how to
111             use it. A bit of help is sprinkled about the page below.
112              
113             B Most of these functions are internal functions not to be used outside of the module.
114             If you use them yourself, I don't want to get bug reports about it. If it just says
115             "C" but doesn't say you can't use it, you're probably okay to use it. If it says
116             something like "C", don't use it. You're likely to upset the delicate
117             balance of nature and might cause mass casualties, famine, hurricanes, tornadoes, floods, or drought.
118             You've been warned.
119              
120             If you've avoided my warning above and are using a function that you really have no business using,
121             let me know (see my contact info at the end of this doc) so I can create a more proper interface
122             into whatever it is that you're doing improperly.
123              
124             =head2 new
125              
126             my $xmpp = Net::SloppyXMPP->new(
127             someoption => "somevalue", # see below
128             anotheroption => "anothervalue", # for the options
129             );
130              
131             =over
132              
133             =item usetls
134              
135             Specify the use of TLS.
136             TLS requires L, but it'll only be loaded if this is true.
137             Your XMPP server must support TLS.
138             Default true if not set.
139              
140             =item usesasl
141              
142             Specify the use of SASL for authentication.
143             SASL requires L and L, but they'll only be loaded if this is true.
144             Your XMPP server must support SASL.
145             Default true if not set.
146              
147             =item usesrv
148              
149             Specify the use of SRV records to determine XMPP host/port based on domain.
150             This requires L, but it'll only be loaded if this is true.
151             If your domain doesn't use C<_xmpp-client._tcp.yourdomain.com> SRV records, this will fail.
152             Default true if not set.
153              
154             =item domain
155              
156             The domain.
157             If your XMPP user is C, the domain is C.
158             I.
159              
160             =item host
161              
162             The IP/domain of the XMPP server to connect to.
163             You can use either C<"yourdomain.xyz"> or C<"yourdomain.xyz:5222"> formats.
164             If you're using SRV records (see C above), don't set this.
165             I, but only if C is false.
166              
167             =item port
168              
169             The port of the XMPP server to connect to.
170             If you've set the port number along with the host (see C above), don't set this.
171             If you're using SRV records (see C above), don't set this.
172             I, but only if C is false.
173              
174             =item username
175              
176             The username.
177             If your XMPP user is C, the username is C.
178             I.
179              
180             =item password
181              
182             The password.
183             This probably doesn't need introduction.
184             I.
185              
186             =item resource
187              
188             The resource.
189             If you don't know what this is, you probably don't need to set it.
190             In the JID C, the resource is C.
191             A default is provided if you don't set it.
192              
193             =item message_callback
194              
195             The function or code that you want to run on each incoming message.
196             Must be a coderef.
197             A default (NOOP with complaint) provided if you don't set it.
198              
199             =item debug
200              
201             The debug level.
202             The higher the number, the more debug messages you'll get.
203             If you don't want to get I messages, set it to -1.
204             Default is C<0>.
205              
206             =item tickdelay
207              
208             The delay in the C loop, in floating-point seconds.
209             If you don't use C (see below), you won't need this.
210             Default is C<0.5> seconds.
211              
212             =item initialpresence
213              
214             Your initial presence on the XMPP server upon connection.
215             Set it to any valid presence value (such as C, C, C).
216             Can be changed at any time while connected via the C function (see below).
217             Default is C.
218              
219             =item initialstatus
220              
221             Your initial status message on the XMPP server upon connection.
222             Set it to some string.
223             Can be changed at any time while connected via the C function (see below).
224             Default is empty string.
225              
226             =item socket_write_len
227              
228             If you don't know what this is for, don't mess with it.
229             Sets the amount to write to the socket at one time.
230             Default is C<4096>.
231              
232             =item socket_read_len
233              
234             If you don't know what this is for, don't mess with it.
235             Sets the amount to read from the socket at one time.
236             Default is C<4096>.
237              
238             =item pingfreq
239              
240             If you don't know what this is for, don't mess with it.
241             Sets the number of seconds between automatic pings.
242             Set it to C<0> if you wish to disable it.
243             Default is C<300> seconds (5 minutes).
244              
245             =back
246              
247             =cut
248              
249             sub new
250             {
251             my $class = shift;
252             my %args = @_;
253             my $self = bless({}, $class);
254              
255             $self->{debug} = $args{debug} || 0;
256              
257             $self->{tickdelay} = int((defined($args{tickdelay}) ? $args{tickdelay} : 0.5) * 100) / 100 || 0.5;
258             $self->{tick_callback} = sub { $self->debug(0, __PACKAGE__." has no tick callback."); };
259              
260             $self->{pingtimer} = time();
261             $self->{pingfreq} = (defined($args{pingfreq}) ? abs($args{pingfreq}) : 300);
262              
263             $self->{message_callback} = sub { $self->debug(0, __PACKAGE__." has no message callback."); };
264             if (defined($args{message_callback}))
265             {
266             if (ref($args{message_callback}) eq 'CODE')
267             {
268             $self->{message_callback} = $args{message_callback};
269             }
270             else
271             {
272             $self->debug(0, __PACKAGE__."->new message_callback must be coderef.");
273             return 0;
274             }
275             }
276              
277             @{$self->{write_queue}} = ();
278             @{$self->{read_queue}} = ();
279             $self->{read_buffer} = '';
280              
281             $self->{usetls} = (defined($args{usetls}) ? $args{usetls} : 1);
282             $self->{usesrv} = (defined($args{usesrv}) ? $args{usesrv} : 1);
283             $self->{usesasl} = (defined($args{usesasl}) ? $args{usesasl} : 1);
284              
285             if ($self->{usesrv})
286             {
287             require Megagram::ResolveSRV;
288             import Megagram::ResolveSRV;
289             $self->{rsrv} = Megagram::ResolveSRV->new;
290             }
291              
292             if ($self->{usetls})
293             {
294             require Net::SSLeay;
295             import Net::SSLeay qw(die_if_ssl_error);
296              
297             #### NET::SSLeay is not really thread-safe it seems... fixable? FIXME
298              
299             Net::SSLeay::load_error_strings();
300             Net::SSLeay::SSLeay_add_ssl_algorithms();
301             Net::SSLeay::randomize();
302             $Net::SSLeay::ssl_version = 10; # Insist on TLSv1
303             }
304              
305             if ($self->{usesasl})
306             {
307             $self->debug(2, "We'll be using SASL for Authentication.");
308             eval
309             {
310             require Authen::SASL;
311             import Authen::SASL;
312             require MIME::Base64;
313             import MIME::Base64;
314             };
315             }
316              
317             $self->{domain} = $args{domain} or do
318             {
319             $self->debug(0, __PACKAGE__."->new requires domain.");
320             return 0;
321             };
322              
323             $self->{host} = $args{host};
324             $self->{port} = $args{port};
325              
326             if (defined($self->{host}) && $self->{host} =~ s/:(\d+)//)
327             {
328             my $port = $1;
329             if ($self->{port})
330             {
331             $self->debug(0, __PACKAGE__."->new uses either domain=host:port or domain=host, port=port, not both.");
332             return 0;
333             }
334             $self->{port} = $port;
335             }
336              
337             if ($self->{usesrv} && ($self->{host} || $self->{port}))
338             {
339             $self->debug(0, __PACKAGE__."->new does not accept host or port if usesrv is true.");
340             return 0;
341             }
342              
343             if (!$self->{usesrv} && (!$self->{host} || !$self->{port}))
344             {
345             $self->debug(0, __PACKAGE__."->new requires host and port (host=host:port or host=host, port=port) when usesrv is false.");
346             return 0;
347             }
348              
349             $self->{username} = $args{username} or do
350             {
351             $self->debug(0, __PACKAGE__."->new requires username.");
352             return 0;
353             };
354             $self->{password} = $args{password};
355             $self->{resource} = $args{resource} || "Perl-Net-SimpleXMPP-$VERSION-".int(rand() * 100000);
356             $self->{initialpresence} = $args{initialpresence} || 'available';
357             $self->{initialstatus} = $args{initialstatus} || '';
358              
359             $self->{socket_write_len} = $args{socket_write_len} || 4096;
360             $self->{socket_read_len} = $args{socket_read_len} || 4096;
361              
362             $self->{xmpp_features} = {};
363             $self->{authenticated} = 0;
364             $self->{resourcebound} = 0;
365             $self->{sessionstarted} = 0;
366              
367             @{$self->{roster}} = ();
368              
369             return $self;
370             }
371              
372             =head2 debug
373              
374             Used internally.
375             B
376             Debug messages are written to this function.
377             Debug messages only appear (via STDERR) when C<< ($debugvalue <= $xmpp-{debug}) >>.
378              
379             =cut
380              
381             sub debug
382             {
383             my $self = shift;
384             my $level = shift || 1;
385             return 0 unless ($level <= $self->{debug});
386             my $text = shift;
387             warn $text."\n";
388             }
389              
390             =head2 connect
391              
392             Initiates the XMPP connection.
393              
394             =cut
395              
396             sub connect
397             {
398             my $self = shift;
399              
400             my @hosts;
401              
402             if ($self->{usesrv})
403             {
404             @hosts = $self->{rsrv}->resolve('_xmpp-client._tcp.'.$self->{domain});
405             return -1 unless ($hosts[0]); # no XMPP service listed
406             }
407             else
408             {
409             @hosts = ({target => $self->{host}, port => $self->{port}});
410             }
411              
412             foreach my $host (@hosts)
413             {
414             my $target = $host->{target};
415             my $port = $host->{port};
416             $self->debug(3, "Connecting to $target:$port");
417             $self->{socket} = IO::Socket::INET->new(
418             PeerAddr => $target,
419             PeerPort => $port,
420             Proto => 'tcp',
421             Blocking => 1,
422             );
423             last if (defined($self->{socket}) && $self->{socket}->connected);
424             }
425              
426             unless (defined($self->{socket}) && $self->{socket}->connected)
427             {
428             $self->debug(1, "SOCKETERROR: $!");
429             return 0;
430             }
431              
432             $self->{socket}->blocking(0); # set to non-blocking
433             binmode($self->{socket}, ':raw');
434              
435             $self->sendhandshake;
436              
437             return 1;
438             }
439              
440             =head2 sendhandshake
441              
442             Used internally.
443             B
444             Sends the XMPP handshake.
445              
446             =cut
447              
448             sub sendhandshake
449             {
450             my $self = shift;
451              
452             # start the handshake
453             $self->write(qq());
454             }
455              
456             =head2 check_socket_connected
457              
458             Used internally.
459             Checks to see if the socket is currently connected.
460             Doesn't test to see if the socket is TLS or not.
461              
462             =cut
463              
464             sub check_socket_connected
465             {
466             my $self = shift;
467              
468             unless (defined($self->{socket}) && $self->{socket}->connected)
469             {
470             $self->debug(1, "SOCKET NOT CONNECTED.");
471             return 0;
472             }
473              
474             return 1;
475             }
476              
477             =head2 disconnect
478              
479             Disconnects the socket.
480             Also shuts down the TLS connection cleanly.
481              
482             =cut
483              
484             sub disconnect
485             {
486             my $self = shift;
487              
488             $self->debug(1, "DISCONNECT.");
489              
490             $self->{tick_running} = 0;
491              
492             if ($self->{usetls})
493             {
494             Net::SSLeay::free($self->{ssl});
495             delete($self->{ssl});
496             Net::SSLeay::free($self->{ctx});
497             delete($self->{ctx});
498             }
499              
500             close($self->{socket});
501             delete($self->{socket});
502              
503             return 1;
504             }
505              
506             =head2 ready
507              
508             Used internally.
509             Determines if the XMPP socket is ready to be used.
510             It's ready after authentication was successful, the resource is bound, and the session has started.
511              
512             =cut
513              
514             sub ready
515             {
516             my $self = shift;
517             return $self->{authenticated} && $self->{resourcebound} && $self->{sessionstarted};
518             }
519              
520             =head2 use_tls
521              
522             Used internally.
523             Determines whether the socket is TLS'ified or not.
524              
525             =cut
526              
527             sub use_tls
528             {
529             my $self = shift;
530             return (($self->{usetls} && defined($self->{ssl})) ? 1 : 0);
531             }
532              
533             =head2 setup_tls
534              
535             Used internally.
536             B
537             Sets up the TLS connection over the socket.
538              
539             =cut
540              
541             sub setup_tls
542             {
543             my $self = shift;
544             return 0 unless $self->{usetls};
545             return 0 unless $self->check_socket_connected;
546              
547             $self->debug(2, "Using TLS, setting up.");
548              
549             $self->{ctx} = Net::SSLeay::CTX_new() or die "SSL ERROR: CTX_new\n";
550              
551             Net::SSLeay::CTX_set_mode($self->{ctx}, 1 | 2);
552             die_if_ssl_error("SSL ERROR: CTX_set_mode");
553              
554             Net::SSLeay::CTX_set_options($self->{ctx}, &Net::SSLeay::OP_ALL);
555             die_if_ssl_error("SSL ERROR: CTX_set_options");
556              
557             $self->{ssl} = Net::SSLeay::new($self->{ctx}) or die "SSL ERROR: new\n";
558              
559             Net::SSLeay::set_fd($self->{ssl}, fileno($self->{socket}));
560             die_if_ssl_error("SSL ERROR: set_fd");
561              
562             Net::SSLeay::connect($self->{ssl});
563             die_if_ssl_error("SSL ERROR: connect");
564              
565             return 1;
566             }
567              
568             =head2 run
569              
570             $xmpp->run(\&mycallbackfunction);
571             # .. or ..
572             $xmpp->run(sub {
573             my $xmpp = shift;
574             print "This is my callback function!\n";
575             });
576              
577             Starts the SloppyXMPP-controlled main loop.
578             If you don't want SloppyXMPP to control your loop, use C instead.
579             Runs C once, runs your callback function, and then sleeps for C<< $xmpp->{tickdelay} >> seconds.
580              
581             =cut
582              
583             sub run
584             {
585             my $self = shift;
586             my $callback = shift;
587              
588             unless (ref($callback) eq 'CODE')
589             {
590             $self->debug(0, __PACKAGE__."->run requires callback and it must be a valid sub or a reference to one.");
591             return 0;
592             }
593              
594             $self->{tick_callback} = $callback;
595             $self->{tick_running} = 1;
596              
597             $self->debug(3, "BEGIN RUN LOOP");
598              
599             while ($self->{tick_running})
600             {
601             $self->debug(6, __PACKAGE__."->run TICK!");
602             $self->tick;
603             &{$self->{tick_callback}}($self);
604             select(undef, undef, undef, ($self->ready ? $self->{tickdelay} : 0.01));
605             }
606             }
607              
608             =head2 tick
609              
610             Runs the SloppyXMPP loop once.
611             Don't use this if you're using C.
612              
613             =cut
614              
615             sub tick
616             {
617             my $self = shift;
618              
619             $self->ping if (($self->{pingfreq} > 0) && ($self->ready) && (time() - $self->{pingtimer} > $self->{pingfreq}));
620             $self->socket_read;
621             $self->process_read_buffer if length($self->{read_buffer});
622             $self->process_read_queue if $self->readable;
623             $self->socket_write;
624             }
625              
626             =head2 message
627              
628             $xmpp->message({
629             to => 'fred@fakedomain.xyz',
630             message => 'This is a message.',
631             });
632              
633             $xmpp->message({
634             to => [
635             'fred@fakedomain.xyz',
636             'jane@fakedomain.xyz',
637             ],
638             message => 'This is a message.',
639             });
640              
641             Sends a message to an XMPP user.
642             If C is an arrayref, it will send to multiple parties.
643              
644             =cut
645              
646             sub message
647             {
648             my $self = shift;
649             my $data = shift;
650              
651             my @to = ((ref($data->{to}) eq 'ARRAY') ? @{$data->{to}} : ($data->{to}));
652              
653             foreach my $to (@to)
654             {
655             $to =~ s/[<>"']//g;
656              
657             my $message = XMLout({
658             to => $to,
659             type => 'normal',
660             body => ["$data->{message}"],
661             }, RootName => 'message');
662              
663             $self->debug(5, qq(Message send to [$to] message [$message]));
664             $self->write($message);
665             }
666             }
667              
668             =head2 write
669              
670             Used internally.
671             B
672             Writes raw data to the socket write queue.
673              
674             =cut
675              
676             sub write
677             {
678             my $self = shift;
679             push(@{$self->{write_queue}}, shift);
680             $self->socket_write; # force a write, if it can write.
681             }
682              
683             =head2 read
684              
685             Used internally.
686             B
687             Reads data from the read queue.
688             Used by the event manager.
689              
690             =cut
691              
692             sub read
693             {
694             my $self = shift;
695             shift(@{$self->{read_queue}});
696             }
697              
698             =head2 unread
699              
700             Used internally.
701             B
702             If C was used, but the data can't be used, put it back in the queue.
703              
704             =cut
705              
706             sub unread
707             {
708             my $self = shift;
709             unshift(@{$self->{read_queue}}, @_);
710             }
711              
712             =head2 readable
713              
714             Used internally.
715             B
716             Determines if there is any data to be read in the read queue.
717              
718             =cut
719              
720             sub readable
721             {
722             my $self = shift;
723             return (scalar(@{$self->{read_queue}}) ? 1 : 0);
724             }
725              
726             =head2 socket_write
727              
728             Used internally.
729             B
730             Writes data from the socket write queue to the socket.
731              
732             =cut
733              
734             sub socket_write
735             {
736             my $self = shift;
737             return 0 unless $self->check_socket_connected;
738              
739             $self->debug(6, "SOCKET_WRITE");
740              
741             my $total_written_len = 0;
742             my $start_pos = 0;
743              
744             while (my $data = shift(@{$self->{write_queue}}))
745             {
746             $data = encode_utf8($data);
747              
748             $self->debug(4, "SOCKET_WRITE-1: [$data]");
749              
750             while ($data)
751             {
752             my $data_to_write = (($start_pos <= length($data)) ? substr($data, $start_pos, $self->{socket_write_len}) : '');
753             my $data_to_write_len = length($data_to_write);
754             last unless $data_to_write_len;
755              
756             $self->debug(3, "SOCKET_WRITE-CHUNK: [$data_to_write_len] [$data_to_write]");
757              
758             my $data_written_len = 0;
759              
760             if ($self->use_tls)
761             {
762             $self->debug(3, "SOCKET_WRITE with TLS");
763             $data_written_len = Net::SSLeay::write($self->{ssl}, $data_to_write);
764             }
765             else
766             {
767             $self->debug(3, "SOCKET_WRITE no TLS");
768             $data_written_len = syswrite($self->{socket}, $data_to_write);
769             }
770              
771             if ($data_written_len > 0)
772             {
773             $start_pos += $data_written_len;
774             $total_written_len += $data_written_len;
775             }
776             else
777             {
778             unshift(@{$self->{write_queue}}, $data);
779             $self->debug(2, "SOCKET_WRITE ERROR: Didn't write. [$data_to_write_len] [$data_written_len] [$!]");
780             return $total_written_len;
781             }
782             }
783             }
784              
785             return $total_written_len;
786             }
787              
788             =head2 socket_read
789              
790             Used internally.
791             B
792             Reads data from the socket and pushes it into the socket read buffer to be processed by C.
793              
794             =cut
795              
796             sub socket_read
797             {
798             my $self = shift;
799             return 0 unless $self->check_socket_connected;
800              
801             $self->debug(6, "SOCKET_READ");
802              
803             my $total_read_len = 0;
804              
805             while (1)
806             {
807             my $data;
808             my $data_read_len = 0;
809              
810             if ($self->use_tls)
811             {
812             $data = Net::SSLeay::read($self->{ssl}) || '';
813             $data_read_len = length($data) if defined($data);
814             }
815             else
816             {
817             $data_read_len = sysread($self->{socket}, $data, $self->{socket_read_len});
818             }
819              
820             last unless $data_read_len;
821              
822             $data = decode_utf8($data);
823             $self->debug(3, "SOCKET_READ: [$data]");
824             $self->{read_buffer} .= $data;
825             $total_read_len += $data_read_len;
826             }
827              
828             return $total_read_len;
829             }
830              
831             =head2 process_read_buffer
832              
833             Used internally.
834             B
835             Processes data in the socket read buffer and pushes it into the read queue to be processed by C.
836              
837             =cut
838              
839             sub process_read_buffer
840             {
841             my $self = shift;
842              
843             return 0 unless $self->{read_buffer};
844              
845             $self->debug(4, "PROCESS_READ_BUFFER");
846              
847             $self->debug(3, "PROCESS_READ_BUFFER-1: [$self->{read_buffer}]");
848             $self->{read_buffer} =~ s#(<\?.*?\?>)##;
849             $self->{read_buffer} =~ s#(
850             $self->{read_buffer} =~ s#xmlns:stream=#xmlns-stream=#g;
851             $self->{read_buffer} =~ s#:stream=#=#g;
852             $self->{read_buffer} =~ s#]*?>##g;
853             $self->debug(3, "PROCESS_READ_BUFFER-2: [$self->{read_buffer}]");
854              
855             $self->{read_buffer} =~ s#^\s*(<\s*([a-z0-9\-_]+)\s*\b[^>]*?\s*)/>#$1>#i; # convert self-closing first tag to empty non-self-closing tag, to prep for next regex
856              
857             while ($self->{read_buffer} =~ s#^\s*(<\s*([a-z0-9\-_]+)\s*\b.*?)##is)
858             {
859             my $section = $1;
860             my $opentag = $2;
861             $self->debug(5, "PROCESS_READ_BUFFER-OPENTAG: $opentag");
862             $self->debug(5, "PROCESS_READ_BUFFER-SECTION: $section");
863             push(@{$self->{read_queue}}, XMLin($section, KeepRoot => 1));
864             }
865             }
866              
867             =head2 process_read_queue
868              
869             Used internally.
870             B
871             Handles events, errors, etc.
872              
873             =cut
874              
875             sub process_read_queue
876             {
877             my $self = shift;
878              
879             $self->debug(4, "PROCESS_READ_QUEUE");
880              
881             while (my $data = $self->read)
882             {
883             $self->debug(4, Dumper($data));
884              
885             if (defined($data->{features}))
886             {
887             $self->debug(3, "PRQ: FEATURES DEFINED");
888             $self->{xmpp_features} = $data->{features};
889             $self->debug(4, Dumper($self));
890             }
891              
892             if (defined($data->{features}) && defined($data->{features}->{starttls}) && $self->{usetls})
893             {
894             $self->debug(3, "PRQ: STARTTLS");
895             delete($data->{features}->{starttls}->{required});
896             $self->write(XMLout($data->{features}->{starttls}, RootName => 'starttls'));
897             }
898             elsif (defined($data->{features}) && defined($data->{features}->{starttls}) && defined($data->{features}->{starttls}->{required}) && !$self->{usetls})
899             {
900             $self->debug(3, "PRQ: STARTTLS IS REQUIRED");
901             $self->disconnect;
902             }
903             elsif (defined($data->{proceed}) && defined($data->{proceed}->{xmlns}) && $data->{proceed}->{xmlns} =~ m/xmpp-tls$/i)
904             {
905             $self->debug(3, "PRQ: PROCEED WITH TLS");
906             if ($self->setup_tls)
907             {
908             $self->debug(3, "PRQ: SETUPTLS SUCCESS");
909             $self->sendhandshake;
910             }
911             }
912             elsif (defined($data->{features}) && defined($data->{features}->{auth}))
913             {
914             $self->debug(3, "PRQ: AUTHENTICATION WILL COMMENCE");
915             $self->authenticate;
916             }
917             elsif (defined($data->{features}) && defined($data->{features}->{bind}))
918             {
919             $self->debug(3, "PRQ: RESOURCE BIND WILL COMMENCE");
920             $self->bindresource;
921             }
922             elsif (defined($data->{iq}))
923             {
924             $self->debug(3, "PRQ: IQ RECEIVED");
925             if (defined($data->{iq}->{bind}))
926             {
927             $self->debug(3, "PRQ: BIND IQ RECEIVED");
928             if ($data->{iq}->{type} eq 'error')
929             {
930             $self->debug(3, "PRQ: BIND IQ ERROR");
931             $self->debug(3, Dumper($data));
932             }
933             else
934             {
935             $self->{resourcebound} = 1;
936             $self->{jid} = $data->{iq}->{bind}->{jid};
937             $self->debug(3, Dumper($self->{xmpp_features}));
938             if (defined($self->{xmpp_features}->{session}))
939             {
940             $self->startsession;
941             }
942             }
943             }
944             elsif (defined($data->{iq}->{session}))
945             {
946             $self->debug(3, "PRQ: SESSION IQ RECEIVED");
947             if ($data->{iq}->{type} eq 'error')
948             {
949             $self->debug(3, "PRQ: SESSION IQ ERROR");
950             $self->debug(3, Dumper($data));
951             }
952             else
953             {
954             $self->{sessionstarted} = 1;
955             $self->rosterfetch;
956             }
957             }
958             elsif (defined($data->{iq}->{query}->{xmlns}) && $data->{iq}->{query}->{xmlns} eq 'jabber:iq:roster')
959             {
960             $self->debug(3, "PRQ: ROSTER RECEIVED");
961             $self->presence($self->{initialpresence}, $self->{initialstatus});
962             $self->rosterreceived($data->{iq}->{query});
963             }
964             elsif (defined($data->{iq}->{ping}))
965             {
966             $self->debug(3, "PRQ: PING IQ RECEIVED");
967             $self->pong($data->{iq});
968             }
969             }
970             elsif (defined($data->{message}))
971             {
972             $self->debug(3, "PRQ: MESSAGE RECEIVED");
973             if (defined($data->{message}->{composing}))
974             {
975             $self->debug(3, "PRQ: MESSAGE IS BEING COMPOSED");
976             $self->messagecomposingstarted($data->{message});
977             }
978             elsif (defined($data->{message}->{body}))
979             {
980             my $body = $data->{message}->{body};
981             $self->debug(3, "PRQ: MESSAGE BODY RECEIVED");
982             $self->debug(3, "MESSAGE: $body");
983             $self->messagereceived($data->{message});
984             }
985             elsif (defined($data->{message}->{paused}))
986             {
987             $self->debug(3, "PRQ: MESSAGE IS BEING COMPOSED HAS PAUSED");
988             $self->messagecomposingpaused($data->{message});
989             }
990             elsif (defined($data->{message}->{active}))
991             {
992             $self->debug(3, "PRQ: MESSAGE IS BEING COMPOSED HAS ENDED");
993             $self->messagecomposingended($data->{message});
994             }
995             }
996             elsif (defined($data->{challenge}))
997             {
998             $self->debug(3, "PRQ: SASL CHALLENGE RECEIVED");
999             $self->saslchallenge($data);
1000             }
1001             elsif (defined($data->{failure}))
1002             {
1003             $self->debug(3, "PRQ: FAILURE DETECTED");
1004             if (defined($data->{failure}->{'not-authorized'}))
1005             {
1006             $self->debug(3, "PRQ: AUTHENTICATION FAILURE");
1007             }
1008             }
1009             elsif (defined($data->{success}))
1010             {
1011             $self->debug(3, "PRQ: SUCCESS DETECTED");
1012             $self->saslsuccess($data);
1013             }
1014             }
1015             }
1016              
1017             =head2 authenticated
1018              
1019             Used internally.
1020             Returns true if this connection has been authenticated successfully.
1021              
1022             =cut
1023              
1024             sub authenticated
1025             {
1026             my $self = shift;
1027             return $self->{authenticated};
1028             }
1029              
1030             =head2 authenticate
1031              
1032             Used internally.
1033             B
1034             Begins the authentication process.
1035              
1036             =cut
1037              
1038             sub authenticate
1039             {
1040             my $self = shift;
1041             return 0 unless $self->check_socket_connected;
1042             return 0 if ($self->{usetls} && !$self->use_tls); # want TLS, but not ready
1043             return 0 unless (defined($self->{xmpp_features}) && defined($self->{xmpp_features}->{auth}));
1044              
1045             $self->debug(2, "Authenticating with XMPP");
1046              
1047             if ($self->{usesasl})
1048             {
1049             my $xmlns = $self->{xmpp_features}->{mechanisms}->{xmlns};
1050             my $mechanisms = $self->{xmpp_features}->{mechanisms}->{mechanism};
1051             my @mechanisms = (ref($mechanisms) eq 'ARRAY' ? @{$mechanisms} : ($mechanisms));
1052             my $sasl = Authen::SASL->new(
1053             mechanism => join(' ', @mechanisms),
1054             callback => {
1055             user => $self->{username},
1056             pass => $self->{password},
1057             },
1058             );
1059             $self->debug(5, Dumper($sasl));
1060             $self->{sasl} = $sasl->client_new('xmpp', $self->{domain});
1061             my $mechanism = $self->{sasl}->mechanism;
1062             my $response = MIME::Base64::encode_base64($self->{sasl}->client_start, '');
1063             $self->write(qq());
1064             }
1065             }
1066              
1067             =head2 saslchallenge
1068              
1069             Used internally.
1070             B
1071             Handles the SASL challenge.
1072              
1073             =cut
1074              
1075             sub saslchallenge
1076             {
1077             my $self = shift;
1078             my $data = shift;
1079             my $challenge = MIME::Base64::decode_base64($data->{challenge}->{content});
1080              
1081             $self->debug(2, "SASLCHALLENGE!");
1082             $self->debug(3, Dumper($challenge));
1083              
1084             $self->debug(1, "SENDING RESPONSE TO SASLCHALLENGE");
1085             my $xmlns = $data->{challenge}->{xmlns};
1086             my $response = $self->{sasl}->client_step($challenge);
1087             unless ($response)
1088             {
1089             $self->debug(1, "SASL ERROR for $challenge");
1090             return 0;
1091             }
1092             $response = MIME::Base64::encode_base64($response);
1093             $self->write(qq($response));
1094             }
1095              
1096             =head2 saslsuccess
1097              
1098             Used internally.
1099             B
1100             Handles SASL challenge success.
1101              
1102             =cut
1103              
1104             sub saslsuccess
1105             {
1106             my $self = shift;
1107             my $data = shift;
1108             my $success = MIME::Base64::decode_base64($data->{success}->{content});
1109              
1110             $self->debug(1, "SUCCESSFUL SASLCHALLENGE");
1111             $self->debug(3, Dumper($success));
1112              
1113             $self->{authenticated} = 1;
1114              
1115             $self->sendhandshake;
1116             }
1117              
1118             =head2 bindresource
1119              
1120             Used internally.
1121             B
1122             Binds this connection to a specific resource.
1123              
1124             =cut
1125              
1126             sub bindresource
1127             {
1128             my $self = shift;
1129              
1130             $self->debug(1, "RESOURCE BIND");
1131              
1132             my $xmlns = $self->{xmpp_features}->{bind}->{xmlns};
1133             $self->write(qq($self->{resource}));
1134             }
1135              
1136             =head2 startsession
1137              
1138             Used internally.
1139             B
1140             Starts the XMPP session.
1141              
1142             =cut
1143              
1144             sub startsession
1145             {
1146             my $self = shift;
1147              
1148             $self->debug(1, "SESSION START");
1149              
1150             my $xmlns = $self->{xmpp_features}->{session}->{xmlns};
1151             $self->write(qq());
1152             }
1153              
1154             =head2 presence
1155              
1156             $xmpp->presence('available', 'Playing music and eating chips.');
1157              
1158             Sets your presence and status.
1159              
1160             =cut
1161              
1162             sub presence
1163             {
1164             my $self = shift;
1165             my $presence = shift || 'available';
1166             my $status = shift || '';
1167             return 0 unless $self->ready;
1168              
1169             $self->debug(1, "SET PRESENCE -> $presence -> $status");
1170             $self->write(qq($presence$status));
1171             return 1;
1172             }
1173              
1174             =head2 messagecomposingstarted
1175              
1176             Used internally.
1177             B
1178             Event handler uses this function to handle the C event.
1179             This happens when some user starts typing a message to you.
1180             Not all XMPP clients send this notification.
1181              
1182             =cut
1183              
1184             sub messagecomposingstarted
1185             {
1186             my $self = shift;
1187             my $data = shift;
1188              
1189             $self->debug(1, "A MESSAGE IS BEING COMPOSED TO YOU FROM [$data->{from}]");
1190             }
1191              
1192             =head2 messagecomposingpaused
1193              
1194             Used internally.
1195             B
1196             Event handler uses this function to handle the C event.
1197             This happens when the person typing the message stopped typing (but didn't erase
1198             their message, send the message, or close the message window).
1199              
1200             =cut
1201              
1202             sub messagecomposingpaused
1203             {
1204             my $self = shift;
1205             my $data = shift;
1206              
1207             $self->debug(1, "A MESSAGE BEING COMPOSED TO YOU FROM [$data->{from}] HAS PAUSED.");
1208             }
1209              
1210             =head2 messagecomposingended
1211              
1212             Used internally.
1213             B
1214             Event handler uses this function to handle the C event.
1215             This happens when the person typing the message quit their message (erased
1216             their message, sent the message, or closed the message window).
1217              
1218             =cut
1219              
1220             sub messagecomposingended
1221             {
1222             my $self = shift;
1223             my $data = shift;
1224              
1225             $self->debug(1, "A MESSAGE BEING COMPOSED TO YOU FROM [$data->{from}] HAS ENDED.");
1226             }
1227              
1228             =head2 messagereceived
1229              
1230             Used internally.
1231             B
1232             Event handler uses this function to handle the C event.
1233             This happens when a message is received from another XMPP user.
1234              
1235             =cut
1236              
1237             sub messagereceived
1238             {
1239             my $self = shift;
1240             my $data = shift;
1241              
1242             $self->debug(1, "MESSAGE RECEIVED FROM [$data->{from}]");
1243              
1244             $self->debug(2, <
1245             ---------------
1246             To: $data->{to}
1247             From: $data->{from}
1248             Body: $data->{body}
1249             ---------------
1250             XYZ
1251              
1252             &{$self->{message_callback}}($self, $data);
1253             }
1254              
1255             =head2 roster
1256              
1257             my $roster = $xmpp->roster;
1258              
1259             Returns an arrayref that contains the roster.
1260              
1261             =cut
1262              
1263             sub roster
1264             {
1265             my $self = shift;
1266             return $self->{roster};
1267             }
1268              
1269             =head2 rosterfetch
1270              
1271             Used internally.
1272             B
1273             Requests the roster from the XMPP server.
1274             Only has to happen once at connection time.
1275              
1276             =cut
1277              
1278             sub rosterfetch
1279             {
1280             my $self = shift;
1281              
1282             $self->debug(1, "GETTING ROSTER");
1283              
1284             $self->write(qq());
1285             }
1286              
1287             =head2 rosterreceived
1288              
1289             Used internally.
1290             B
1291             The roster arrived from the XMPP server.
1292             This populates the proper variable that contains the roster arrayref.
1293             Access this data via C (see above).
1294              
1295             =cut
1296              
1297             sub rosterreceived
1298             {
1299             my $self = shift;
1300             my $roster = shift;
1301              
1302             $self->debug(1, "RECEIVED ROSTER");
1303              
1304             my @contacts = (ref($roster->{item}) eq 'ARRAY' ? @{$roster->{item}} : ($roster->{item}));
1305              
1306             $self->{roster} = \@contacts;
1307              
1308             $self->debug(3, Dumper($self->{roster}));
1309             }
1310              
1311             =head2 ping
1312              
1313             Used internally.
1314             B
1315             Sends a ping to the server.
1316              
1317             =cut
1318              
1319             sub ping
1320             {
1321             my $self = shift;
1322             my $id = 'ping'.int(rand() * 100000);
1323             $self->write(qq());
1324             $self->{pingtimer} = time();
1325             }
1326              
1327             =head2 pong
1328              
1329             Used internally.
1330             B
1331             Sends a pong (ping response) to the server.
1332              
1333             =cut
1334              
1335             sub pong
1336             {
1337             my $self = shift;
1338             my $data = shift;
1339             my $from = $data->{from};
1340             my $id = $data->{id};
1341             $self->write(qq());
1342             $self->{pingtimer} = time();
1343             }
1344              
1345             =head1 TODO
1346              
1347             =over
1348              
1349             =item *
1350              
1351             Event callbacks. There aren't any. They are planned and should be reasonably easy to setup.
1352             This module isn't all that useful without them.
1353              
1354             =item *
1355              
1356             Test on more XMPP servers. This has only been tested on the Openfire XMPP Server, version 3.6.2.
1357              
1358             =item *
1359              
1360             Make sure it works on Google's XMPP servers. Right now, it doesn't.
1361              
1362             =back
1363              
1364             =head1 BUGS
1365              
1366             Find bugs? Of course you will. Report them on the CPAN bug tracker. Don't email me directly about bugs.
1367             If it works for you, I'd love to hear about it. Find my email address in my CPAN profile (C).
1368             Make sure to put "C" in the subject line or I might ignore it completely.
1369             Please don't send HTML email if at all possible. I greatly prefer plaintext email.
1370              
1371             If you have a patch for this module, post it on the CPAN bug tracker. If it fits the goal of this module,
1372             I'll be very happy to merge it in. If it doesn't fit the goal, I won't, even if you think it makes sense.
1373              
1374             =over
1375              
1376             =item *
1377              
1378             This is version 0.04 of a module called SloppyXMPP. If you don't hit any bugs, you might want to try
1379             your luck at the lottery today.
1380              
1381             =item *
1382              
1383             Doesn't work with Google's XMPP server right now. I plan to make it work.
1384              
1385             =back
1386              
1387             =head1 COPYRIGHT/LICENSE
1388              
1389             Copyright 2009 Megagram. You can use any one of these licenses: Perl Artistic, GPL (version >= 2), BSD.
1390              
1391             =head2 Perl Artistic License
1392              
1393             Read it at L.
1394             This is the license we prefer.
1395              
1396             =head2 GNU General Public License (GPL) Version 2
1397              
1398             This program is free software; you can redistribute it and/or
1399             modify it under the terms of the GNU General Public License
1400             as published by the Free Software Foundation; either version 2
1401             of the License, or (at your option) any later version.
1402              
1403             This program is distributed in the hope that it will be useful,
1404             but WITHOUT ANY WARRANTY; without even the implied warranty of
1405             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1406             GNU General Public License for more details.
1407              
1408             You should have received a copy of the GNU General Public License
1409             along with this program. If not, see http://www.gnu.org/licenses/
1410              
1411             See the full license at L.
1412              
1413             =head2 GNU General Public License (GPL) Version 3
1414              
1415             This program is free software: you can redistribute it and/or modify
1416             it under the terms of the GNU General Public License as published by
1417             the Free Software Foundation, either version 3 of the License, or
1418             (at your option) any later version.
1419              
1420             This program is distributed in the hope that it will be useful,
1421             but WITHOUT ANY WARRANTY; without even the implied warranty of
1422             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1423             GNU General Public License for more details.
1424              
1425             You should have received a copy of the GNU General Public License
1426             along with this program. If not, see http://www.gnu.org/licenses/
1427              
1428             See the full license at L.
1429              
1430             =head2 BSD License
1431              
1432             Copyright (c) 2009 Megagram.
1433             All rights reserved.
1434              
1435             Redistribution and use in source and binary forms, with or without modification, are permitted
1436             provided that the following conditions are met:
1437              
1438             * Redistributions of source code must retain the above copyright notice, this list of conditions
1439             and the following disclaimer.
1440             * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
1441             and the following disclaimer in the documentation and/or other materials provided with the
1442             distribution.
1443             * Neither the name of Megagram nor the names of its contributors may be used to endorse
1444             or promote products derived from this software without specific prior written permission.
1445              
1446             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
1447             WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
1448             PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
1449             ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1450             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1451             INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1452             OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
1453             IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1454              
1455             =cut