ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Parser.pm
Revision: 1.8
Committed: Sun Jul 8 08:37:21 2007 UTC (19 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.7: +10 -5 lines
Log Message:
fixed some bugs in the error handling

File Contents

# Content
1 package Net::XMPP2::Parser;
2 use warnings;
3 use strict;
4 # OMFG!!!111 THANK YOU FOR THIS MODULE TO HANDLE THE XMPP INSANITY:
5 use Net::XMPP2::Node;
6 use XML::Parser::Expat;
7
8 =head1 NAME
9
10 Net::XMPP2::Parser - Parser for XML streams (helper for Net::XMPP2)
11
12 =head1 SYNOPSIS
13
14 use Net::XMPP2::Parser;
15 ...
16
17 =head1 DESCRIPTION
18
19 This is a XMPP XML parser helper class, which helps me to cope with the XMPP XML.
20
21 See also L<Net::XMPP2::Writer> for a discussion of the issues with XML in XMPP.
22
23 =head1 METHODS
24
25 =over 4
26
27 =item B<new>
28
29 This creates a new Net::XMPP2::Parser and calls C<init>.
30
31 =cut
32
33 sub new {
34 my $this = shift;
35 my $class = ref($this) || $this;
36 my $self = {
37 stanza_cb => sub { die "No stanza callback provided!" },
38 error_cb => sub { warn "No error callback provided: $_[0]: $_[1]!" },
39 @_
40 };
41 bless $self, $class;
42 $self->init;
43 $self
44 }
45
46 =item B<set_stanza_cb ($cb)>
47
48 Sets the 'XML stanza' callback.
49
50 C<$cb> must be a code reference. The first argument to
51 the callback will be this Net::XMPP2::Parser instance and
52 the second will be the stanzas root Net::XMPP2::Node as first argument.
53
54 If the second argument is undefined the end of the stream has been found.
55
56 =cut
57
58 sub set_stanza_cb {
59 my ($self, $cb) = @_;
60 $self->{stanza_cb} = $cb;
61 }
62
63 =item B<set_error_cb ($cb)>
64
65 This sets the error callback that will be called when
66 the parser encounters an syntax error. The first argument
67 is the exception and the second is the data which caused the error.
68
69 =cut
70
71 sub set_error_cb {
72 my ($self, $cb) = @_;
73 $self->{error_cb} = $cb;
74 }
75
76 =item B<init>
77
78 This methods (re)initializes the parser.
79
80 =cut
81
82 sub init {
83 my ($self) = @_;
84 $self->{parser} = XML::Parser::ExpatNB->new (
85 Namespaces => 1,
86 ProtocolEncoding => 'UTF-8'
87 );
88 $self->{parser}->setHandlers (
89 Start => sub { $self->cb_start_tag (@_) },
90 End => sub { $self->cb_end_tag (@_) },
91 Char => sub { $self->cb_char_data (@_) },
92 );
93 $self->{nso} = {};
94 $self->{nodestack} = [];
95 }
96
97 =item B<nseq ($namespace, $tagname, $cmptag)>
98
99 This method checks whether the C<$cmptag> matches the C<$tagname>
100 in the C<$namespace>.
101
102 C<$cmptag> needs to come from the XML::Parser::Expat as it has
103 some magic attached that stores the namespace.
104
105 =cut
106
107 sub nseq {
108 my ($self, $ns, $name, $tag) = @_;
109
110 unless (exists $self->{nso}->{$ns}->{$name}) {
111 $self->{nso}->{$ns}->{$name} =
112 $self->{parser}->generate_ns_name ($name, $ns);
113 }
114
115 return $self->{parser}->eq_name ($self->{nso}->{$ns}->{$name}, $tag);
116 }
117
118 =item B<feed ($data)>
119
120 This method feeds a chunk of unparsed data to the parser.
121
122 =cut
123
124 sub feed {
125 my ($self, $data) = @_;
126 eval {
127 $self->{parser}->parse_more ($data);
128 };
129 if ($@) {
130 $self->{error_cb}->($@, $data, 'xml');
131 }
132 }
133
134
135 sub cb_start_tag {
136 my ($self, $p, $el, %attrs) = @_;
137 push @{$self->{nodestack}}, Net::XMPP2::Node->new ($p->namespace ($el), $el, \%attrs, $self);
138 }
139
140 sub cb_char_data {
141 my ($self, $p, $str) = @_;
142 unless (@{$self->{nodestack}}) {
143 warn "characters outside of tag: [$str]!\n";
144 return;
145 }
146 $self->{nodestack}->[-1]->add_text ($str);
147 }
148
149 sub cb_end_tag {
150 my ($self, $p, $el) = @_;
151
152 unless (@{$self->{nodestack}}) {
153 warn "end tag </$el> read without any starting tag!\n";
154 return;
155 }
156
157 if (!$p->eq_name ($self->{nodestack}->[-1]->name, $el)) {
158 warn "end tag </$el> doesn't match start tags ($self->{tags}->[-1]->[0])!\n";
159 return;
160 }
161
162 my $node = pop @{$self->{nodestack}};
163
164 # > 1 because we don't want the stream tag to save all our children...
165 if (@{$self->{nodestack}} > 1) {
166 $self->{nodestack}->[-1]->add_node ($node);
167 }
168
169 eval {
170 if (@{$self->{nodestack}} == 1) {
171 $self->{stanza_cb}->($self, $node);
172 } elsif (@{$self->{nodestack}} == 0) {
173 $self->{stanza_cb}->($self, undef);
174 }
175 };
176 if ($@) {
177 $self->{error_cb}->($@, undef, 'exception');
178 }
179 }
180
181 =back
182
183 =head1 AUTHOR
184
185 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
186
187 =head1 COPYRIGHT & LICENSE
188
189 Copyright 2007 Robin Redeker, all rights reserved.
190
191 This program is free software; you can redistribute it and/or modify it
192 under the same terms as Perl itself.
193
194 =cut
195
196 1; # End of Net::XMPP2