ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Writer.pm
Revision: 1.24
Committed: Wed Jul 25 13:53:33 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.23: +4 -2 lines
Log Message:
implemented OOB and fixed bugs in Disco and further developed in band registration.

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::Writer;
2     use strict;
3     use XML::Writer;
4 elmex 1.20 use Authen::SASL qw/Perl/;
5 elmex 1.1 use MIME::Base64;
6     use Net::XMPP2::Namespaces qw/xmpp_ns/;
7 elmex 1.18 use Net::XMPP2::Util qw/simxml/;
8 elmex 1.21 use Digest::SHA1 qw/sha1_hex/;
9     use Encode;
10 elmex 1.1
11     =head1 NAME
12    
13 elmex 1.17 Net::XMPP2::Writer - "XML" writer for XMPP
14 elmex 1.1
15     =head1 SYNOPSIS
16    
17     use Net::XMPP2::Writer;
18     ...
19    
20 elmex 1.3 =head1 DESCRIPTION
21    
22 elmex 1.5 This module contains some helper functions for writing XMPP "XML",
23 elmex 1.3 which is not real XML at all ;-( I use L<XML::Writer> and tune it
24 elmex 1.5 until it creates "XML" that is accepted by most servers propably
25 elmex 1.16 (all of the XMPP servers I tested should work (jabberd14, jabberd2,
26     ejabberd, googletalk).
27 elmex 1.3
28     I hope the semantics of L<XML::Writer> don't change much over the future,
29     but if they do and you run into problems, please report them!
30    
31 elmex 1.5 The whole "XML" concept of XMPP is fundamentally broken anyway. It's supposed
32 elmex 1.3 to be an subset of XML. But a subset of XML productions is not XML. Strictly
33 elmex 1.5 speaking you need a special XMPP "XML" parser and writer to be 100% conformant.
34 elmex 1.3
35 elmex 1.14 On top of that XMPP B<requires> you to parse these partial "XML" documents.
36 elmex 1.10 But a partial XML document is not well-formed, heck, it's not even a XML document!.
37     And a parser should bail out with an error. But XMPP doesn't care, it just relies on
38     implementation dependend behaviour of chunked parsing modes for SAX parsing.
39     This functionality isn't even specified by the XML recommendation in any way.
40     The recommendation even says that it's undefined what happens if you process
41     not-well-formed XML documents.
42    
43 elmex 1.9 But I try to be as "XML" XMPP conformant as possible (it should be around 99-100%).
44 elmex 1.5 But it's hard to say what XML is conformant, as the specifications of XMPP "XML" and XML
45 elmex 1.3 are contradicting. For example XMPP also says you only have to generated and accept
46     utf-8 encodings of XML, but the XML recommendation says that each parser has
47 elmex 1.14 to accept utf-8 B<and> utf-16. So, what do you do? Do you use a XML conformant parser
48 elmex 1.3 or do you write your own?
49    
50 elmex 1.5 I'm using XML::Parser::Expat because expat knows how to parse broken (aka 'partial')
51     "XML" documents, as XMPP requires. Another argument is that if you capture a XMPP
52 elmex 1.3 conversation to the end, and even if a '</stream:stream>' tag was captured, you
53     wont have a valid XML document. The problem is that you have to resent a <stream> tag
54 elmex 1.10 after TLS and SASL authentication each! Awww... I'm repeating myself.
55 elmex 1.3
56     But well... Net::XMPP2 does it's best with expat to cope with the fundamental brokeness
57 elmex 1.5 of "XML" in XMPP.
58 elmex 1.3
59 elmex 1.5 Back to the issue with "XML" generation: I've discoverd that many XMPP servers (eg.
60 elmex 1.3 jabberd14 and ejabberd) have problems with XML namespaces. Thats the reason why
61 elmex 1.9 I'm assigning the namespace prefixes manually: The servers just don't accept validly
62 elmex 1.3 namespaced XML. The draft 3921bis does even state that a client SHOULD generate a 'stream'
63     prefix for the <stream> tag.
64    
65 elmex 1.5 I advice you to explictly set the namespaces too if you generate "XML" for XMPP yourself,
66 elmex 1.3 at least until all or most of the XMPP servers have been fixed. Which might take some
67     years :-) And maybe will happen never.
68    
69 elmex 1.5 And another note: As XMPP requires all predefined entity characters to be escaped
70     in character data you need a "XML" writer that will escape everything:
71    
72     RFC 3920 - 11.1. Restrictions:
73    
74     character data or attribute values containing unescaped characters
75     that map to the predefined entities (Section 4.6 therein);
76     such characters MUST be escaped
77    
78     This means:
79     You have to escape '>' in the character data. I don't know whether XML::Writer
80 elmex 1.9 does that. And I honestly don't care much about this. XMPP is broken by design and
81     I have barely time to writer my own XML parsers and writers to suit their sick taste
82     of "XML". (Do I repeat myself?)
83 elmex 1.5
84 elmex 1.10 I would be happy if they finally say (in RFC3920): "XMPP is NOT XML. It's just
85     XML-like, and some XML utilities allow you to process this kind of XML.".
86    
87 elmex 1.1 =head1 METHODS
88    
89 elmex 1.16 =over 4
90    
91     =item B<new (%args)>
92 elmex 1.1
93     This methods takes following arguments:
94    
95     =over 4
96    
97     =item write_cb
98    
99     The callback that is called when a XML stanza was completly written
100     and is ready for transfer. The first argument of the callback
101     will be the character data to send to the socket.
102    
103 elmex 1.9 =back
104    
105 elmex 1.1 And calls C<init>.
106    
107     =cut
108    
109     sub new {
110     my $this = shift;
111     my $class = ref($this) || $this;
112 elmex 1.19 my $self = {
113     write_cb => sub {},
114     send_iq_cb => sub {},
115     send_msg_cb => sub {},
116     send_pres_cb => sub {},
117     @_
118     };
119 elmex 1.1 bless $self, $class;
120     $self->init;
121     return $self;
122     }
123    
124 elmex 1.16 =item B<init>
125 elmex 1.1
126     (Re)initializes the writer.
127    
128     =cut
129    
130     sub init {
131     my ($self) = @_;
132     $self->{write_buf} = "";
133 elmex 1.24 $self->{writer} =
134     XML::Writer->new (OUTPUT => \$self->{write_buf}, NAMESPACES => 1, UNSAFE => 1);
135 elmex 1.1 }
136    
137 elmex 1.16 =item B<flush ()>
138 elmex 1.1
139     This method flushes the internal write buffer and will invoke the C<write_cb>
140     callback. (see also C<new ()> above)
141    
142     =cut
143    
144     sub flush {
145     my ($self) = @_;
146     $self->{write_cb}->(substr $self->{write_buf}, 0, (length $self->{write_buf}), '');
147     }
148    
149 elmex 1.21 =item B<send_init_stream ($language, $domain, $namespace)>
150 elmex 1.1
151     This method will generate a XMPP stream header. C<$domain> has to be the
152     domain of the server (or endpoint) we want to connect to.
153    
154 elmex 1.21 C<$namespace> is the namespace uri or the tag (from L<Net::XMPP2::Namespaces>)
155     for the stream namespace. (This is used by L<Net::XMPP2::Component> to connect
156     as component to a server). C<$namespace> can also be undefined, in this case
157     the C<client> namespace will be used.
158    
159 elmex 1.1 =cut
160    
161     sub send_init_stream {
162 elmex 1.21 my ($self, $language, $domain, $ns) = @_;
163    
164     $ns ||= 'client';
165 elmex 1.1
166     my $w = $self->{writer};
167     $w->xmlDecl ('UTF-8');
168     $w->addPrefix (xmpp_ns ('stream'), 'stream');
169 elmex 1.21 $w->addPrefix (xmpp_ns ($ns), '');
170     $w->forceNSDecl (xmpp_ns ($ns));
171 elmex 1.1 $w->startTag (
172     [xmpp_ns ('stream'), 'stream'],
173     to => $domain,
174     version => '1.0',
175     [xmpp_ns ('xml'), 'lang'] => $language
176     );
177     $self->flush;
178     }
179    
180 elmex 1.21 =item B<send_handshake ($streamid, $secret)>
181    
182     This method sends a component handshake. Please note that C<$secret>
183     must be XML escaped!
184    
185     =cut
186    
187     sub send_handshake {
188     my ($self, $id, $secret) = @_;
189     my $out_secret = encode ("UTF-8", $secret);
190     my $out = lc sha1_hex ($id . $out_secret);
191     simxml ($self->{writer}, defns => 'component', node => {
192     ns => 'component', name => 'handshake', childs => [ $out ]
193     });
194     $self->flush;
195     }
196    
197 elmex 1.16 =item B<send_end_of_stream>
198 elmex 1.1
199     Sends end of the stream.
200    
201     =cut
202    
203     sub send_end_of_stream {
204     my ($self) = @_;
205     my $w = $self->{writer};
206     $w->endTag ([xmpp_ns ('stream'), 'stream']);
207     $self->flush;
208     }
209    
210 elmex 1.16 =item B<send_sasl_auth ($mechanisms)>
211 elmex 1.1
212     This methods sends the start of a SASL authentication. C<$mechanisms> is
213     a string with space seperated mechanisms that are supported by the other
214     end.
215    
216     =cut
217    
218     sub send_sasl_auth {
219     my ($self, $mechanisms, $user, $domain, $pass) = @_;
220    
221     my $sasl = Authen::SASL->new (
222     mechanism => $mechanisms,
223     callback => {
224 elmex 1.15 # XXX: removed authname, because it ensures maximum connectivitiy
225     # along multiple server implementations - XMPP is such a crap
226     # authname => $user . '@' . $domain,
227 elmex 1.1 user => $user,
228     pass => $pass,
229     }
230     );
231    
232     $self->{sasl} = $sasl->client_new ('xmpp', $domain);
233    
234     my $w = $self->{writer};
235     $w->addPrefix (xmpp_ns ('sasl'), '');
236     $w->startTag ([xmpp_ns ('sasl'), 'auth'], mechanism => $self->{sasl}->mechanism);
237     $w->characters (MIME::Base64::encode_base64 ($self->{sasl}->client_start, ''));
238     $w->endTag;
239     $self->flush;
240     }
241    
242 elmex 1.16 =item B<send_sasl_response ($challenge)>
243 elmex 1.1
244     This method generated the SASL authentication response to a C<$challenge>.
245     You must not call this method without calling C<send_sasl_auth ()> before.
246    
247     =cut
248    
249     sub send_sasl_response {
250     my ($self, $challenge) = @_;
251     $challenge = MIME::Base64::decode_base64 ($challenge);
252     my $ret = '';
253     unless ($challenge =~ /rspauth=/) { # rspauth basically means: we are done
254     $ret = $self->{sasl}->client_step ($challenge);
255     unless ($ret) {
256     die "Error in SASL authentication in client step with challenge: '$challenge'\n";
257     }
258     }
259     my $w = $self->{writer};
260     $w->addPrefix (xmpp_ns ('sasl'), '');
261     $w->startTag ([xmpp_ns ('sasl'), 'response']);
262     $w->characters (MIME::Base64::encode_base64 ($ret, ''));
263     $w->endTag;
264     $self->flush;
265     }
266    
267 elmex 1.16 =item B<send_starttls>
268 elmex 1.2
269     Sends the starttls command to the server.
270    
271     =cut
272    
273     sub send_starttls {
274     my ($self) = @_;
275     my $w = $self->{writer};
276     $w->addPrefix (xmpp_ns ('tls'), '');
277     $w->emptyTag ([xmpp_ns ('tls'), 'starttls']);
278     $self->flush;
279     }
280    
281 elmex 1.16 =item B<send_iq ($id, $type, $create_cb, %attrs)>
282 elmex 1.1
283 elmex 1.3 This method sends an IQ stanza of type C<$type> (to be compliant
284 elmex 1.22 only use: 'get', 'set', 'result' and 'error').
285    
286     If C<$create_cb> is a code reference it will be called with an XML::Writer
287 elmex 1.24 instance as first argument, which must be used to fill the IQ stanza. The
288     XML::Writer is in UNSAFE mode, so you can safely use C<raw()> to write out XML.
289 elmex 1.22
290     C<$create_cb> is a hash reference the hash will be used as key=>value arguments
291     for the C<simxml> function defined in L<Net::XMPP2::Util>. C<simxml> will then
292     be used to generate the contents of the IQ stanza. (This is very convenient
293     when you want to write the contents of stanzas in the code and don't want to
294     build a DOM tree yourself...).
295    
296 elmex 1.3 If C<$create_cb> is undefined an empty tag will be generated.
297 elmex 1.1
298 elmex 1.22 Example:
299    
300     $writer->send_iq ('newid', 'get', {
301     defns => 'version',
302     node => { name => 'query', ns => 'version' }
303     }, to => 'jabber.org')
304    
305 elmex 1.1 C<%attrs> should have further attributes for the IQ stanza tag.
306     For example 'to' or 'from'. If the C<%attrs> contain a 'lang' attribute
307     it will be put into the 'xml' namespace.
308    
309 elmex 1.3 C<$id> is the id to give this IQ stanza and is mandatory in this API.
310 elmex 1.1
311     =cut
312    
313     sub send_iq {
314     my ($self, $id, $type, $create_cb, %attrs) = @_;
315 elmex 1.18
316     $create_cb = _trans_create_cb ($create_cb);
317 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_iq_cb => $create_cb, $id, $type, \%attrs);
318 elmex 1.18
319 elmex 1.1 my $w = $self->{writer};
320     $w->addPrefix (xmpp_ns ('bind'), '');
321     my (@from) = ($self->{jid} ? (from => $self->{jid}) : ());
322     if ($attrs{lang}) {
323     push @from, ([ xmpp_ns ('xml'), 'lang' ] => delete $attrs{leng})
324     }
325 elmex 1.6 push @from, (id => $id) if defined $id;
326 elmex 1.3 if (defined $create_cb) {
327 elmex 1.6 $w->startTag ('iq', type => $type, @from, %attrs);
328 elmex 1.3 $create_cb->($w);
329     $w->endTag;
330     } else {
331 elmex 1.6 $w->emptyTag ('iq', type => $type, @from, %attrs);
332 elmex 1.3 }
333     $self->flush;
334     }
335    
336 elmex 1.16 =item B<send_presence ($id, $type, $create_cb, %attrs)>
337 elmex 1.3
338     Sends a presence stanza.
339    
340     C<$create_cb> has the same meaning as for C<send_iq>.
341     C<%attrs> will let you pass further optional arguments like 'to'.
342    
343     C<$type> is the type of the presence, which may be one of:
344    
345     unavailable, subscribe, subscribed, unsubscribe, unsubscribed, probe, error
346    
347 elmex 1.18 Or undef, in case you want to send a 'normal' presence.
348 elmex 1.3 Or something completly different if you don't like the RFC 3921 :-)
349    
350     C<%attrs> contains further attributes for the presence tag or may contain one of the
351     following exceptional keys:
352    
353     If C<%attrs> contains a 'show' key: a child xml tag with that name will be geenerated
354     with the value as the content, which should be one of 'away', 'chat', 'dnd' and 'xa'.
355    
356     If C<%attrs> contains a 'status' key: a child xml tag with that name will be generated
357     with the value as content. If the value of the 'status' key is an hash reference
358     the keys will be interpreted as language identifiers for the xml:lang attribute
359     of each status element. If one of these keys is the empty string '' no xml:lang attribute
360     will be generated for it. The values will be the character content of the status tags.
361    
362     If C<%attrs> contains a 'priority' key: a child xml tag with that name will be generated
363     with the value as content, which must be a number between -128 and +127.
364    
365     Note: If C<$create_cb> is undefined and one of the above attributes (show,
366     status or priority) were given, the generates presence tag won't be empty.
367    
368     =cut
369    
370     sub _generate_key_xml {
371     my ($w, $key, $value) = @_;
372     $w->startTag ($key);
373     $w->characters ($value);
374 elmex 1.1 $w->endTag;
375     }
376    
377 elmex 1.3 sub _generate_key_xmls {
378     my ($w, $key, $value) = @_;
379     if (ref ($value) eq 'HASH') {
380     for (keys %$value) {
381 elmex 1.11 $w->startTag ($key, ($_ ne '' ? ([xmpp_ns ('xml'), 'lang'] => $_) : ()));
382 elmex 1.3 $w->characters ($value->{$_});
383     $w->endTag;
384     }
385     } else {
386     $w->startTag ($key);
387     $w->characters ($value);
388     $w->endTag;
389     }
390 elmex 1.1 }
391    
392 elmex 1.18 sub _trans_create_cb {
393     my ($cb) = @_;
394     return unless defined $cb;
395     if (ref ($cb) eq 'HASH') {
396     my $args = $cb;
397     $cb = sub {
398     my ($w) = @_;
399     simxml ($w, %$args);
400     }
401     }
402     $cb
403     }
404    
405 elmex 1.19 sub _fetch_cb_additions {
406     my ($self, $key, $create_cb, @args) = @_;
407     my @add_cbs;
408 elmex 1.23 my (@add_cbs) = $self->{$key}->(@args);
409 elmex 1.19 @add_cbs = map { _trans_create_cb ($_) } @add_cbs;
410    
411     if (@add_cbs) {
412     my $crcb = $create_cb;
413     $create_cb = sub {
414     my (@args) = @_;
415     $crcb->(@args) if $crcb;
416     for (@add_cbs) { $_->(@args) }
417     }
418     }
419    
420     $create_cb
421     }
422    
423 elmex 1.1 sub send_presence {
424 elmex 1.4 my ($self, $id, $type, $create_cb, %attrs) = @_;
425 elmex 1.3
426 elmex 1.18 $create_cb = _trans_create_cb ($create_cb);
427 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_pres_cb => $create_cb, $id, $type, \%attrs);
428 elmex 1.18
429 elmex 1.1 my $w = $self->{writer};
430     $w->addPrefix (xmpp_ns ('client'), '');
431 elmex 1.3
432     my @add;
433 elmex 1.4 push @add, (type => $type) if defined $type;
434     push @add, (id => $id) if defined $id;
435 elmex 1.3
436 elmex 1.7 my %fattrs =
437     map { $_ => $attrs{$_} }
438 elmex 1.12 grep { my $k = $_; not grep { $k eq $_ } qw/show priority status/ }
439 elmex 1.7 keys %attrs;
440    
441 elmex 1.3 if (defined $create_cb) {
442 elmex 1.7 $w->startTag ('presence', @add, %fattrs);
443     _generate_key_xml ($w, show => $attrs{show}) if defined $attrs{show};
444     _generate_key_xml ($w, priority => $attrs{priority}) if defined $attrs{priority};
445     _generate_key_xmls ($w, status => $attrs{status}) if defined $attrs{status};
446 elmex 1.3 $create_cb->($w);
447     $w->endTag;
448     } else {
449     if (exists $attrs{show} or $attrs{priority} or $attrs{status}) {
450 elmex 1.7 $w->startTag ('presence', @add, %fattrs);
451     _generate_key_xml ($w, show => $attrs{show}) if defined $attrs{show};
452     _generate_key_xml ($w, priority => $attrs{priority}) if defined $attrs{priority};
453     _generate_key_xmls ($w, status => $attrs{status}) if defined $attrs{status};
454 elmex 1.3 $w->endTag;
455     } else {
456 elmex 1.7 $w->emptyTag ('presence', @add, %fattrs);
457 elmex 1.3 }
458     }
459    
460 elmex 1.1 $self->flush;
461     }
462    
463 elmex 1.16 =item B<send_message ($id, $to, $type, $create_cb, %attrs)>
464 elmex 1.3
465     Sends a message stanza.
466    
467     C<$to> is the destination JID of the message. C<$type> is
468 elmex 1.6 the type of the message, and if C<$type> is undefined it will default to 'chat'.
469 elmex 1.3 C<$type> must be one of the following: 'chat', 'error', 'groupchat', 'headline'
470     or 'normal'.
471    
472     C<$create_cb> has the same meaning as in C<send_iq>.
473    
474     C<%attrs> contains further attributes for the message tag or may contain one of the
475     following exceptional keys:
476    
477     If C<%attrs> contains a 'body' key: a child xml tag with that name will be generated
478     with the value as content. If the value of the 'body' key is an hash reference
479     the keys will be interpreted as language identifiers for the xml:lang attribute
480     of each body element. If one of these keys is the empty string '' no xml:lang attribute
481     will be generated for it. The values will be the character content of the body tags.
482    
483     If C<%attrs> contains a 'subject' key: a child xml tag with that name will be generated
484     with the value as content. If the value of the 'subject' key is an hash reference
485     the keys will be interpreted as language identifiers for the xml:lang attribute
486     of each subject element. If one of these keys is the empty string '' no xml:lang attribute
487     will be generated for it. The values will be the character content of the subject tags.
488    
489     If C<%attrs> contains a 'thread' key: a child xml tag with that name will be generated
490     and the value will be the character content.
491    
492     =cut
493    
494 elmex 1.1 sub send_message {
495 elmex 1.4 my ($self, $id, $to, $type, $create_cb, %attrs) = @_;
496 elmex 1.3
497 elmex 1.18 $create_cb = _trans_create_cb ($create_cb);
498 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_msg_cb => $create_cb, $id, $to, $type, \%attrs);
499 elmex 1.18
500 elmex 1.1 my $w = $self->{writer};
501     $w->addPrefix (xmpp_ns ('client'), '');
502 elmex 1.3
503 elmex 1.4 my @add;
504     push @add, (id => $id) if defined $id;
505    
506 elmex 1.3 $type ||= 'chat';
507    
508 elmex 1.7 my %fattrs =
509     map { $_ => $attrs{$_} }
510 elmex 1.12 grep { my $k = $_; not grep { $k eq $_ } qw/subject body thread/ }
511 elmex 1.7 keys %attrs;
512    
513 elmex 1.3 if (defined $create_cb) {
514 elmex 1.7 $w->startTag ('message', @add, to => $to, type => $type, %fattrs);
515     _generate_key_xmls ($w, subject => $attrs{subject}) if defined $attrs{subject};
516     _generate_key_xmls ($w, body => $attrs{body}) if defined $attrs{body};
517     _generate_key_xml ($w, thread => $attrs{thread}) if defined $attrs{thread};
518 elmex 1.3 $create_cb->($w);
519     $w->endTag;
520     } else {
521     if (exists $attrs{subject} or $attrs{body} or $attrs{thread}) {
522 elmex 1.7 $w->startTag ('message', @add, to => $to, type => $type, %fattrs);
523     _generate_key_xmls ($w, subject => $attrs{subject}) if defined $attrs{subject};
524     _generate_key_xmls ($w, body => $attrs{body}) if defined $attrs{body};
525     _generate_key_xml ($w, thread => $attrs{thread}) if defined $attrs{thread};
526 elmex 1.3 $w->endTag;
527     } else {
528 elmex 1.7 $w->emptyTag ('message', @add, to => $to, type => $type, %fattrs);
529 elmex 1.3 }
530     }
531    
532 elmex 1.1 $self->flush;
533     }
534    
535 elmex 1.4
536 elmex 1.16 =item B<write_error_tag ($error_stanza_node, $error_type, $error)>
537 elmex 1.4
538     C<$error_type> is one of 'cancel', 'continue', 'modify', 'auth' and 'wait'.
539     C<$error> is the name of the error tag child element. If C<$error> is one of
540     the following:
541    
542     'bad-request', 'conflict', 'feature-not-implemented', 'forbidden', 'gone',
543     'internal-server-error', 'item-not-found', 'jid-malformed', 'not-acceptable',
544     'not-allowed', 'not-authorized', 'payment-required', 'recipient-unavailable',
545     'redirect', 'registration-required', 'remote-server-not-found',
546     'remote-server-timeout', 'resource-constraint', 'service-unavailable',
547     'subscription-required', 'undefined-condition', 'unexpected-request'
548    
549     then a default can be select for C<$error_type>, and the argument can be undefined.
550    
551     Note: This method is currently a bit limited in the generation of the xml
552     for the errors, if you need more please contact me.
553    
554     =cut
555    
556     our %STANZA_ERRORS = (
557     'bad-request' => ['modify', 400],
558     'conflict' => ['cancel', 409],
559     'feature-not-implemented' => ['cancel', 501],
560     'forbidden' => ['auth', 403],
561     'gone' => ['modify', 302],
562     'internal-server-error' => ['wait', 500],
563     'item-not-found' => ['cancel', 404],
564     'jid-malformed' => ['modify', 400],
565     'not-acceptable' => ['modify', 406],
566     'not-allowed' => ['cancel', 405],
567     'not-authorized' => ['auth', 401],
568     'payment-required' => ['auth', 402],
569     'recipient-unavailable' => ['wait', 404],
570     'redirect' => ['modify', 302],
571     'registration-required' => ['auth', 407],
572     'remote-server-not-found' => ['cancel', 404],
573     'remote-server-timeout' => ['wait', 504],
574     'resource-constraint' => ['wait', 500],
575     'service-unavailable' => ['cancel', 503],
576     'subscription-required' => ['auth', 407],
577     'undefined-condition' => ['cancel', 500],
578     'unexpected-request' => ['wait', 400],
579     );
580    
581     sub write_error_tag {
582     my ($self, $errstanza, $type, $error) = @_;
583    
584     my $w = $self->{writer};
585    
586     $_->write_on ($w) for $errstanza->nodes;
587    
588     my @add;
589    
590     unless (defined $type and defined $STANZA_ERRORS{$error}) {
591     $type = $STANZA_ERRORS{$error}->[0];
592     }
593    
594 elmex 1.13 push @add, (code => $STANZA_ERRORS{$error}->[1]);
595 elmex 1.4
596     $w->addPrefix (xmpp_ns ('client'), '');
597     $w->startTag ([xmpp_ns ('client') => 'error'], type => $type, @add);
598     $w->addPrefix (xmpp_ns ('stanzas'), '');
599     $w->emptyTag ([xmpp_ns ('stanzas') => $error]);
600     $w->endTag;
601     }
602    
603 elmex 1.16 =back
604    
605 elmex 1.1 =head1 AUTHOR
606    
607 elmex 1.16 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
608 elmex 1.1
609     =head1 COPYRIGHT & LICENSE
610    
611     Copyright 2007 Robin Redeker, all rights reserved.
612    
613     This program is free software; you can redistribute it and/or modify it
614     under the same terms as Perl itself.
615    
616     =cut
617    
618     1; # End of Net::XMPP2