ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Writer.pm
Revision: 1.22
Committed: Tue Jul 24 12:31:15 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.21: +18 -4 lines
Log Message:
documentation update

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     $self->{writer} = XML::Writer->new (OUTPUT => \$self->{write_buf}, NAMESPACES => 1);
134     }
135    
136 elmex 1.16 =item B<flush ()>
137 elmex 1.1
138     This method flushes the internal write buffer and will invoke the C<write_cb>
139     callback. (see also C<new ()> above)
140    
141     =cut
142    
143     sub flush {
144     my ($self) = @_;
145     $self->{write_cb}->(substr $self->{write_buf}, 0, (length $self->{write_buf}), '');
146     }
147    
148 elmex 1.21 =item B<send_init_stream ($language, $domain, $namespace)>
149 elmex 1.1
150     This method will generate a XMPP stream header. C<$domain> has to be the
151     domain of the server (or endpoint) we want to connect to.
152    
153 elmex 1.21 C<$namespace> is the namespace uri or the tag (from L<Net::XMPP2::Namespaces>)
154     for the stream namespace. (This is used by L<Net::XMPP2::Component> to connect
155     as component to a server). C<$namespace> can also be undefined, in this case
156     the C<client> namespace will be used.
157    
158 elmex 1.1 =cut
159    
160     sub send_init_stream {
161 elmex 1.21 my ($self, $language, $domain, $ns) = @_;
162    
163     $ns ||= 'client';
164 elmex 1.1
165     my $w = $self->{writer};
166     $w->xmlDecl ('UTF-8');
167     $w->addPrefix (xmpp_ns ('stream'), 'stream');
168 elmex 1.21 $w->addPrefix (xmpp_ns ($ns), '');
169     $w->forceNSDecl (xmpp_ns ($ns));
170 elmex 1.1 $w->startTag (
171     [xmpp_ns ('stream'), 'stream'],
172     to => $domain,
173     version => '1.0',
174     [xmpp_ns ('xml'), 'lang'] => $language
175     );
176     $self->flush;
177     }
178    
179 elmex 1.21 =item B<send_handshake ($streamid, $secret)>
180    
181     This method sends a component handshake. Please note that C<$secret>
182     must be XML escaped!
183    
184     =cut
185    
186     sub send_handshake {
187     my ($self, $id, $secret) = @_;
188     my $out_secret = encode ("UTF-8", $secret);
189     my $out = lc sha1_hex ($id . $out_secret);
190     simxml ($self->{writer}, defns => 'component', node => {
191     ns => 'component', name => 'handshake', childs => [ $out ]
192     });
193     $self->flush;
194     }
195    
196 elmex 1.16 =item B<send_end_of_stream>
197 elmex 1.1
198     Sends end of the stream.
199    
200     =cut
201    
202     sub send_end_of_stream {
203     my ($self) = @_;
204     my $w = $self->{writer};
205     $w->endTag ([xmpp_ns ('stream'), 'stream']);
206     $self->flush;
207     }
208    
209 elmex 1.16 =item B<send_sasl_auth ($mechanisms)>
210 elmex 1.1
211     This methods sends the start of a SASL authentication. C<$mechanisms> is
212     a string with space seperated mechanisms that are supported by the other
213     end.
214    
215     =cut
216    
217     sub send_sasl_auth {
218     my ($self, $mechanisms, $user, $domain, $pass) = @_;
219    
220     my $sasl = Authen::SASL->new (
221     mechanism => $mechanisms,
222     callback => {
223 elmex 1.15 # XXX: removed authname, because it ensures maximum connectivitiy
224     # along multiple server implementations - XMPP is such a crap
225     # authname => $user . '@' . $domain,
226 elmex 1.1 user => $user,
227     pass => $pass,
228     }
229     );
230    
231     $self->{sasl} = $sasl->client_new ('xmpp', $domain);
232    
233     my $w = $self->{writer};
234     $w->addPrefix (xmpp_ns ('sasl'), '');
235     $w->startTag ([xmpp_ns ('sasl'), 'auth'], mechanism => $self->{sasl}->mechanism);
236     $w->characters (MIME::Base64::encode_base64 ($self->{sasl}->client_start, ''));
237     $w->endTag;
238     $self->flush;
239     }
240    
241 elmex 1.16 =item B<send_sasl_response ($challenge)>
242 elmex 1.1
243     This method generated the SASL authentication response to a C<$challenge>.
244     You must not call this method without calling C<send_sasl_auth ()> before.
245    
246     =cut
247    
248     sub send_sasl_response {
249     my ($self, $challenge) = @_;
250     $challenge = MIME::Base64::decode_base64 ($challenge);
251     my $ret = '';
252     unless ($challenge =~ /rspauth=/) { # rspauth basically means: we are done
253     $ret = $self->{sasl}->client_step ($challenge);
254     unless ($ret) {
255     die "Error in SASL authentication in client step with challenge: '$challenge'\n";
256     }
257     }
258     my $w = $self->{writer};
259     $w->addPrefix (xmpp_ns ('sasl'), '');
260     $w->startTag ([xmpp_ns ('sasl'), 'response']);
261     $w->characters (MIME::Base64::encode_base64 ($ret, ''));
262     $w->endTag;
263     $self->flush;
264     }
265    
266 elmex 1.16 =item B<send_starttls>
267 elmex 1.2
268     Sends the starttls command to the server.
269    
270     =cut
271    
272     sub send_starttls {
273     my ($self) = @_;
274     my $w = $self->{writer};
275     $w->addPrefix (xmpp_ns ('tls'), '');
276     $w->emptyTag ([xmpp_ns ('tls'), 'starttls']);
277     $self->flush;
278     }
279    
280 elmex 1.16 =item B<send_iq ($id, $type, $create_cb, %attrs)>
281 elmex 1.1
282 elmex 1.3 This method sends an IQ stanza of type C<$type> (to be compliant
283 elmex 1.22 only use: 'get', 'set', 'result' and 'error').
284    
285     If C<$create_cb> is a code reference it will be called with an XML::Writer
286     instance as first argument, which must be used to fill the IQ stanza. If
287    
288     C<$create_cb> is a hash reference the hash will be used as key=>value arguments
289     for the C<simxml> function defined in L<Net::XMPP2::Util>. C<simxml> will then
290     be used to generate the contents of the IQ stanza. (This is very convenient
291     when you want to write the contents of stanzas in the code and don't want to
292     build a DOM tree yourself...).
293    
294 elmex 1.3 If C<$create_cb> is undefined an empty tag will be generated.
295 elmex 1.1
296 elmex 1.22 Example:
297    
298     $writer->send_iq ('newid', 'get', {
299     defns => 'version',
300     node => { name => 'query', ns => 'version' }
301     }, to => 'jabber.org')
302    
303 elmex 1.1 C<%attrs> should have further attributes for the IQ stanza tag.
304     For example 'to' or 'from'. If the C<%attrs> contain a 'lang' attribute
305     it will be put into the 'xml' namespace.
306    
307 elmex 1.3 C<$id> is the id to give this IQ stanza and is mandatory in this API.
308 elmex 1.1
309     =cut
310    
311     sub send_iq {
312     my ($self, $id, $type, $create_cb, %attrs) = @_;
313 elmex 1.18
314     $create_cb = _trans_create_cb ($create_cb);
315 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_iq_cb => $create_cb, $id, $type, \%attrs);
316 elmex 1.18
317 elmex 1.1 my $w = $self->{writer};
318     $w->addPrefix (xmpp_ns ('bind'), '');
319     my (@from) = ($self->{jid} ? (from => $self->{jid}) : ());
320     if ($attrs{lang}) {
321     push @from, ([ xmpp_ns ('xml'), 'lang' ] => delete $attrs{leng})
322     }
323 elmex 1.6 push @from, (id => $id) if defined $id;
324 elmex 1.3 if (defined $create_cb) {
325 elmex 1.6 $w->startTag ('iq', type => $type, @from, %attrs);
326 elmex 1.3 $create_cb->($w);
327     $w->endTag;
328     } else {
329 elmex 1.6 $w->emptyTag ('iq', type => $type, @from, %attrs);
330 elmex 1.3 }
331     $self->flush;
332     }
333    
334 elmex 1.16 =item B<send_presence ($id, $type, $create_cb, %attrs)>
335 elmex 1.3
336     Sends a presence stanza.
337    
338     C<$create_cb> has the same meaning as for C<send_iq>.
339     C<%attrs> will let you pass further optional arguments like 'to'.
340    
341     C<$type> is the type of the presence, which may be one of:
342    
343     unavailable, subscribe, subscribed, unsubscribe, unsubscribed, probe, error
344    
345 elmex 1.18 Or undef, in case you want to send a 'normal' presence.
346 elmex 1.3 Or something completly different if you don't like the RFC 3921 :-)
347    
348     C<%attrs> contains further attributes for the presence tag or may contain one of the
349     following exceptional keys:
350    
351     If C<%attrs> contains a 'show' key: a child xml tag with that name will be geenerated
352     with the value as the content, which should be one of 'away', 'chat', 'dnd' and 'xa'.
353    
354     If C<%attrs> contains a 'status' key: a child xml tag with that name will be generated
355     with the value as content. If the value of the 'status' key is an hash reference
356     the keys will be interpreted as language identifiers for the xml:lang attribute
357     of each status element. If one of these keys is the empty string '' no xml:lang attribute
358     will be generated for it. The values will be the character content of the status tags.
359    
360     If C<%attrs> contains a 'priority' key: a child xml tag with that name will be generated
361     with the value as content, which must be a number between -128 and +127.
362    
363     Note: If C<$create_cb> is undefined and one of the above attributes (show,
364     status or priority) were given, the generates presence tag won't be empty.
365    
366     =cut
367    
368     sub _generate_key_xml {
369     my ($w, $key, $value) = @_;
370     $w->startTag ($key);
371     $w->characters ($value);
372 elmex 1.1 $w->endTag;
373     }
374    
375 elmex 1.3 sub _generate_key_xmls {
376     my ($w, $key, $value) = @_;
377     if (ref ($value) eq 'HASH') {
378     for (keys %$value) {
379 elmex 1.11 $w->startTag ($key, ($_ ne '' ? ([xmpp_ns ('xml'), 'lang'] => $_) : ()));
380 elmex 1.3 $w->characters ($value->{$_});
381     $w->endTag;
382     }
383     } else {
384     $w->startTag ($key);
385     $w->characters ($value);
386     $w->endTag;
387     }
388 elmex 1.1 }
389    
390 elmex 1.18 sub _trans_create_cb {
391     my ($cb) = @_;
392     return unless defined $cb;
393     if (ref ($cb) eq 'HASH') {
394     my $args = $cb;
395     $cb = sub {
396     my ($w) = @_;
397     simxml ($w, %$args);
398     }
399     }
400     $cb
401     }
402    
403 elmex 1.19 sub _fetch_cb_additions {
404     my ($self, $key, $create_cb, @args) = @_;
405     my @add_cbs;
406     $self->{$key}->(@args, \@add_cbs);
407     @add_cbs = map { _trans_create_cb ($_) } @add_cbs;
408    
409     if (@add_cbs) {
410     my $crcb = $create_cb;
411     $create_cb = sub {
412     my (@args) = @_;
413     $crcb->(@args) if $crcb;
414     for (@add_cbs) { $_->(@args) }
415     }
416     }
417    
418     $create_cb
419     }
420    
421 elmex 1.1 sub send_presence {
422 elmex 1.4 my ($self, $id, $type, $create_cb, %attrs) = @_;
423 elmex 1.3
424 elmex 1.18 $create_cb = _trans_create_cb ($create_cb);
425 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_pres_cb => $create_cb, $id, $type, \%attrs);
426 elmex 1.18
427 elmex 1.1 my $w = $self->{writer};
428     $w->addPrefix (xmpp_ns ('client'), '');
429 elmex 1.3
430     my @add;
431 elmex 1.4 push @add, (type => $type) if defined $type;
432     push @add, (id => $id) if defined $id;
433 elmex 1.3
434 elmex 1.7 my %fattrs =
435     map { $_ => $attrs{$_} }
436 elmex 1.12 grep { my $k = $_; not grep { $k eq $_ } qw/show priority status/ }
437 elmex 1.7 keys %attrs;
438    
439 elmex 1.3 if (defined $create_cb) {
440 elmex 1.7 $w->startTag ('presence', @add, %fattrs);
441     _generate_key_xml ($w, show => $attrs{show}) if defined $attrs{show};
442     _generate_key_xml ($w, priority => $attrs{priority}) if defined $attrs{priority};
443     _generate_key_xmls ($w, status => $attrs{status}) if defined $attrs{status};
444 elmex 1.3 $create_cb->($w);
445     $w->endTag;
446     } else {
447     if (exists $attrs{show} or $attrs{priority} or $attrs{status}) {
448 elmex 1.7 $w->startTag ('presence', @add, %fattrs);
449     _generate_key_xml ($w, show => $attrs{show}) if defined $attrs{show};
450     _generate_key_xml ($w, priority => $attrs{priority}) if defined $attrs{priority};
451     _generate_key_xmls ($w, status => $attrs{status}) if defined $attrs{status};
452 elmex 1.3 $w->endTag;
453     } else {
454 elmex 1.7 $w->emptyTag ('presence', @add, %fattrs);
455 elmex 1.3 }
456     }
457    
458 elmex 1.1 $self->flush;
459     }
460    
461 elmex 1.16 =item B<send_message ($id, $to, $type, $create_cb, %attrs)>
462 elmex 1.3
463     Sends a message stanza.
464    
465     C<$to> is the destination JID of the message. C<$type> is
466 elmex 1.6 the type of the message, and if C<$type> is undefined it will default to 'chat'.
467 elmex 1.3 C<$type> must be one of the following: 'chat', 'error', 'groupchat', 'headline'
468     or 'normal'.
469    
470     C<$create_cb> has the same meaning as in C<send_iq>.
471    
472     C<%attrs> contains further attributes for the message tag or may contain one of the
473     following exceptional keys:
474    
475     If C<%attrs> contains a 'body' key: a child xml tag with that name will be generated
476     with the value as content. If the value of the 'body' key is an hash reference
477     the keys will be interpreted as language identifiers for the xml:lang attribute
478     of each body element. If one of these keys is the empty string '' no xml:lang attribute
479     will be generated for it. The values will be the character content of the body tags.
480    
481     If C<%attrs> contains a 'subject' key: a child xml tag with that name will be generated
482     with the value as content. If the value of the 'subject' key is an hash reference
483     the keys will be interpreted as language identifiers for the xml:lang attribute
484     of each subject element. If one of these keys is the empty string '' no xml:lang attribute
485     will be generated for it. The values will be the character content of the subject tags.
486    
487     If C<%attrs> contains a 'thread' key: a child xml tag with that name will be generated
488     and the value will be the character content.
489    
490     =cut
491    
492 elmex 1.1 sub send_message {
493 elmex 1.4 my ($self, $id, $to, $type, $create_cb, %attrs) = @_;
494 elmex 1.3
495 elmex 1.18 $create_cb = _trans_create_cb ($create_cb);
496 elmex 1.19 $create_cb = $self->_fetch_cb_additions (send_msg_cb => $create_cb, $id, $to, $type, \%attrs);
497 elmex 1.18
498 elmex 1.1 my $w = $self->{writer};
499     $w->addPrefix (xmpp_ns ('client'), '');
500 elmex 1.3
501 elmex 1.4 my @add;
502     push @add, (id => $id) if defined $id;
503    
504 elmex 1.3 $type ||= 'chat';
505    
506 elmex 1.7 my %fattrs =
507     map { $_ => $attrs{$_} }
508 elmex 1.12 grep { my $k = $_; not grep { $k eq $_ } qw/subject body thread/ }
509 elmex 1.7 keys %attrs;
510    
511 elmex 1.3 if (defined $create_cb) {
512 elmex 1.7 $w->startTag ('message', @add, to => $to, type => $type, %fattrs);
513     _generate_key_xmls ($w, subject => $attrs{subject}) if defined $attrs{subject};
514     _generate_key_xmls ($w, body => $attrs{body}) if defined $attrs{body};
515     _generate_key_xml ($w, thread => $attrs{thread}) if defined $attrs{thread};
516 elmex 1.3 $create_cb->($w);
517     $w->endTag;
518     } else {
519     if (exists $attrs{subject} or $attrs{body} or $attrs{thread}) {
520 elmex 1.7 $w->startTag ('message', @add, to => $to, type => $type, %fattrs);
521     _generate_key_xmls ($w, subject => $attrs{subject}) if defined $attrs{subject};
522     _generate_key_xmls ($w, body => $attrs{body}) if defined $attrs{body};
523     _generate_key_xml ($w, thread => $attrs{thread}) if defined $attrs{thread};
524 elmex 1.3 $w->endTag;
525     } else {
526 elmex 1.7 $w->emptyTag ('message', @add, to => $to, type => $type, %fattrs);
527 elmex 1.3 }
528     }
529    
530 elmex 1.1 $self->flush;
531     }
532    
533 elmex 1.4
534 elmex 1.16 =item B<write_error_tag ($error_stanza_node, $error_type, $error)>
535 elmex 1.4
536     C<$error_type> is one of 'cancel', 'continue', 'modify', 'auth' and 'wait'.
537     C<$error> is the name of the error tag child element. If C<$error> is one of
538     the following:
539    
540     'bad-request', 'conflict', 'feature-not-implemented', 'forbidden', 'gone',
541     'internal-server-error', 'item-not-found', 'jid-malformed', 'not-acceptable',
542     'not-allowed', 'not-authorized', 'payment-required', 'recipient-unavailable',
543     'redirect', 'registration-required', 'remote-server-not-found',
544     'remote-server-timeout', 'resource-constraint', 'service-unavailable',
545     'subscription-required', 'undefined-condition', 'unexpected-request'
546    
547     then a default can be select for C<$error_type>, and the argument can be undefined.
548    
549     Note: This method is currently a bit limited in the generation of the xml
550     for the errors, if you need more please contact me.
551    
552     =cut
553    
554     our %STANZA_ERRORS = (
555     'bad-request' => ['modify', 400],
556     'conflict' => ['cancel', 409],
557     'feature-not-implemented' => ['cancel', 501],
558     'forbidden' => ['auth', 403],
559     'gone' => ['modify', 302],
560     'internal-server-error' => ['wait', 500],
561     'item-not-found' => ['cancel', 404],
562     'jid-malformed' => ['modify', 400],
563     'not-acceptable' => ['modify', 406],
564     'not-allowed' => ['cancel', 405],
565     'not-authorized' => ['auth', 401],
566     'payment-required' => ['auth', 402],
567     'recipient-unavailable' => ['wait', 404],
568     'redirect' => ['modify', 302],
569     'registration-required' => ['auth', 407],
570     'remote-server-not-found' => ['cancel', 404],
571     'remote-server-timeout' => ['wait', 504],
572     'resource-constraint' => ['wait', 500],
573     'service-unavailable' => ['cancel', 503],
574     'subscription-required' => ['auth', 407],
575     'undefined-condition' => ['cancel', 500],
576     'unexpected-request' => ['wait', 400],
577     );
578    
579     sub write_error_tag {
580     my ($self, $errstanza, $type, $error) = @_;
581    
582     my $w = $self->{writer};
583    
584     $_->write_on ($w) for $errstanza->nodes;
585    
586     my @add;
587    
588     unless (defined $type and defined $STANZA_ERRORS{$error}) {
589     $type = $STANZA_ERRORS{$error}->[0];
590     }
591    
592 elmex 1.13 push @add, (code => $STANZA_ERRORS{$error}->[1]);
593 elmex 1.4
594     $w->addPrefix (xmpp_ns ('client'), '');
595     $w->startTag ([xmpp_ns ('client') => 'error'], type => $type, @add);
596     $w->addPrefix (xmpp_ns ('stanzas'), '');
597     $w->emptyTag ([xmpp_ns ('stanzas') => $error]);
598     $w->endTag;
599     }
600    
601 elmex 1.16 =back
602    
603 elmex 1.1 =head1 AUTHOR
604    
605 elmex 1.16 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
606 elmex 1.1
607     =head1 COPYRIGHT & LICENSE
608    
609     Copyright 2007 Robin Redeker, all rights reserved.
610    
611     This program is free software; you can redistribute it and/or modify it
612     under the same terms as Perl itself.
613    
614     =cut
615    
616     1; # End of Net::XMPP2