File Coverage

blib/lib/Net/XMPP/Namespaces.pm
Criterion Covered Total %
statement 15 15 100.0
branch 6 6 100.0
condition n/a
subroutine 4 4 100.0
pod 1 1 100.0
total 26 26 100.0


line stmt bran cond sub pod time code
1             ##############################################################################
2             #
3             # This library is free software; you can redistribute it and/or
4             # modify it under the terms of the GNU Library General Public
5             # License as published by the Free Software Foundation; either
6             # version 2 of the License, or (at your option) any later version.
7             #
8             # This library is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11             # Library General Public License for more details.
12             #
13             # You should have received a copy of the GNU Library General Public
14             # License along with this library; if not, write to the
15             # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16             # Boston, MA 02111-1307, USA.
17             #
18             # Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
19             #
20             ##############################################################################
21              
22             package Net::XMPP::Namespaces;
23              
24             =head1 NAME
25              
26             Net::XMPP::Namespaces - In depth discussion on how namespaces are handled
27              
28             =head1 SYNOPSIS
29              
30             Net::XMPP::Namespaces provides an depth look at how Net::XMPP handles
31             namespacs, and how to add your own custom ones. It also serves as the
32             storage bin for all of the Namespace information Net::XMPP requires.
33              
34             =head1 DESCRIPTION
35              
36             XMPP as a protocol is very well defined. There are three main top
37             level packets (message, iq, and presence). There is also a way to
38             extend the protocol in a very clear and strucutred way, via namespaces.
39              
40             Two major ways that namespaces are used in Jabber is for making the
41             a generic wrapper, and as a way for adding data to any packet via
42             a child tag . We will use to represent the packet, but in
43             reality it could be any child tag: , , , etc.
44              
45             The Info/Query packet uses namespaces to determine the type of
46             information to access. Usually there is a tag in the
47             that represents the namespace, but in fact it can be any tag. The
48             definition of the Query portion, is the first tag that has a namespace.
49              
50            
51              
52             or
53              
54            
55              
56             After that Query stanza can be any number of other stanzas ( tags)
57             you want to include. The Query packet is represented and available by
58             calling GetQuery() or GetChild(), and the other namespaces are
59             available by calling GetChild().
60              
61             The X tag is just a way to piggy back data on other packets. Like
62             embedding the timestamp for a message using jabber:x:delay, or signing
63             you presence for encryption using jabber:x:signed.
64              
65             To this end, Net::XMPP has sought to find a way to easily, and clearly
66             define the functions needed to access the XML for a namespace. We will
67             go over the full docs, and then show two examples of real namespaces so
68             that you can see what we are talking about.
69              
70             =head2 Overview
71              
72             To avoid a lot of nasty modules populating memory that are not used,
73             and to avoid having to change 15 modules when a minor change is
74             introduced, the Net::XMPP modules have taken AUTOLOADing to the
75             extreme. Namespaces.pm is nothing but a set of function calls that
76             generates a big hash of hashes. The hash is accessed by the Stanza.pm
77             AUTOLOAD function to do something. (This will make sense, I promise.)
78              
79             Before going on, I highly suggest you read a Perl book on AUTOLOAD and
80             how it works. From this point on I will assume that you understand it.
81              
82             When you create a Net::XMPP::IQ object and add a Query to it (NewChild)
83             several things are happening in the background. The argument to
84             NewChild is the namespace you want to add. (custom-namespace)
85              
86             Now that you have a Query object to work with you will call the GetXXX
87             functions, and SetXXX functions to set the data. There are no defined
88             GetXXX and SetXXXX functions. You cannot look in the Namespaces.pm
89             file and find them. Instead you will find something like this:
90              
91             &add_ns(ns => "mynamespace",
92             tag => "mytag",
93             xpath => {
94             JID => { type=>'jid', path => '@jid' },
95             Username => { path => 'username/text()' },
96             Test => { type => 'master' }
97             }
98             );
99              
100             When the GetUsername() function is called, the AUTOLOAD function looks
101             in the Namespaces.pm hash for a "Username" key. Based on the "type" of
102             the field (scalar being the default) it will use the "path" as an XPath
103             to retrieve the data and call the XPathGet() method in Stanza.pm.
104              
105             Confused yet?
106              
107             =head2 Net::XMPP private namespaces
108              
109             Now this is where this starts to get a little sticky. When you see a
110             namespace with __netxmpp__, or __netjabber__ from Net::Jabber, at the
111             beginning it is usually something custom to Net::XMPP and NOT part of
112             the actual XMPP protocol.
113              
114             There are some places where the structure of the XML allows for
115             multiple children with the same name. The main places you will see
116             this behavior is where you have multiple tags with the same name and
117             those have children under them (jabber:iq:roster).
118              
119             In jabber:iq:roster, the tag can be repeated multiple times,
120             and is sort of like a mini-namespace in itself. To that end, we treat
121             it like a separate namespace and defined a __netxmpp__:iq:roster:item
122             namespace to hold it. What happens is this, in my code I define that
123             the s tag is "item" and anything with that tag name is to create
124             a new Net::XMPP::Stanza object with the namespace
125             __netxmpp__:iq:roster:item which then becomes a child of the
126             jabber:iq:roster Stanza object. Also, when you want to add a new item
127             to a jabber:iq:roster project you call NewQuery with the private
128             namespace.
129              
130             I know this sounds complicated. And if after reading this entire
131             document it is still complicated, email me, ask questions, and I will
132             monitor it and adjust these docs to answer the questions that people
133             ask.
134              
135             =head2 add_ns()
136              
137             To repeat, here is an example call to add_ns():
138              
139             add_ns(ns => "mynamespace",
140             tag => "mytag",
141             xpath => {
142             JID => { type=>'jid', path => '@jid' },
143             Username => { path => 'username/text()' },
144             Test => { type => 'master' }
145             }
146             );
147              
148             ns - This is the new namespace that you are trying to add.
149              
150             tag - This is the root tag to use for objects based on this namespace.
151              
152             xpath - The hash reference passed in the add_ns call to each name of
153             entry tells Net::XMPP how to handle subsequent GetXXXX(), SetXXXX(),
154             DefinedXXXX(), RemoveXXXX(), AddXXXX() calls. The basic options you
155             can pass in are:
156              
157             type - This tells Stanza how to handle the call. The possible values are:
158              
159             array - The value to set and returned is an an array
160             reference. For example, in jabber:iq:roster.
161              
162             child - This tells Stanza that it needs to look for the
163             __netxmpp__ style namesapced children. AddXXX() adds
164             a new child, and GetXXX() will return a new Stanza
165             object representing the packet.
166              
167             flag - This is for child elements that are tags by themselves:
168             . Since the presence of the tag is what is
169             important, and there is no cdata to store, we just call
170             it a flag.
171              
172             jid - The value is a Jabber ID. GetXXX() will return a
173             Net::XMPP::JID object unless you pass it "jid", then it
174             returns a string.
175              
176             master - The GetXXX() and SetXXX() calls return and take a
177             hash representing all of the GetXXX() and SetXXX()
178             calls. For example:
179              
180             SetTest(foo=>"bar",
181             bar=>"baz");
182              
183             Translates into:
184              
185             SetFoo("bar");
186             SetBar("baz");
187              
188             GetTest() would return a hash containing what the
189             packet contains:
190              
191             { foo=>"bar", bar=>"baz" }
192              
193             raw - This will stick whatever raw XML you specify directly
194             into the Stanza at the point where the path specifies.
195              
196             scalar - This will set and get a scalar value. This is the
197             main workhorse as attributes and CDATA is represented
198             by a scalar. This is the default setting if you do
199             not provide one.
200              
201             special - The special type is unique in that instead of a
202             string "special", you actually give it an array:
203              
204             [ "special" , ]
205              
206             This allows Net::XMPP to be able to handle the
207             SetXXXX() call in a special manner according to your
208             choosing. Right now this is mainly used by
209             jabber:iq:time to automatically set the time info in
210             the correct format, and jabber:iq:version to set the
211             machine OS and add the Net::Jabber version to the
212             return packet. You will likely NOT need to use
213             this, but I wanted to mention it.
214              
215             timestamp - If you call SetXXX() but do not pass it anything,
216             or pass it "", then Net::XMPP will place a
217             timestamp in the xpath location.
218              
219             path - This is the XPath path to where the bit data lives. The
220             difference. Now, this is not full XPath due to the nature
221             of how it gets used. Instead of providing a rooted path
222             all the way to the top, it's a relative path ignoring what
223             the parent is. For example, if the "tag" you specified was
224             "foo", and the path is "bar/text()", then the XPath will be
225             rooted in the XML of the packet. It will set and get
226             the CDATA from:
227              
228             xxxxx
229              
230             For a flag and a child type, just specify the child element.
231             Take a look at the code in this file for more help on what
232             this means. Also, read up on XPath if you don't already know
233             what it is.
234              
235             child - This is a hash reference that tells Net::XMPP how to handle
236             adding and getting child objects. The keys for the hash are
237             as follows:
238              
239             ns - the real or custom (__netxmpp__) namesapce to use for
240             this child packet.
241              
242             skip_xmlns => 1 - this tells Net::XMPP not to add an
243             xmlns='' into the XML for the child
244             object.
245              
246             specify_name => 1 - allows you to call NewChild("ns","tag")
247             and specify the tag to use for the child
248             object. This, IMHO, is BAD XML
249             practice. You should always know what
250             the tag of the child is and use an
251             attribute or CDATA to change the type
252             of the stanza. You do not want to use
253             this.
254              
255             tag - If you use specify_name, then this is the default tag
256             to use. You do not want to use this.
257              
258             calls - Array reference telling Net::XMPP what functions to create
259             for this name. For most of the types above you will get
260             Get, Set, Defined, and Remove. For child types you need to
261             decide how you API will look and specify them yourself:
262              
263             ["Get","Defined"]
264             ["Add"]
265             ["Get","Add","Defined"]
266              
267             It all depends on how you want your API to look.
268              
269             Once more... The following:
270              
271             &add_ns(ns => "mynamespace",
272             tag => "mytag",
273             xpath => {
274             JID => { type=>'jid', path => '@jid' },
275             Username => { path => 'username/text()' },
276             Test => { type => 'master' }
277             }
278             );
279              
280             generates the following API calls:
281              
282             GetJID()
283             SetJID()
284             DefinedJID()
285             RemoveJID()
286             GetUsername()
287             SetUsername()
288             DefinedUsername()
289             RemoveUsername()
290             GetTest()
291             SetTest()
292              
293             =head2 Wrap Up
294              
295             Well. I hope that I have not scared you off from writing a custom
296             namespace for you application and use Net::XMPP. Look in the
297             Net::XMPP::Protocol manpage for an example on using the add_ns()
298             function to register your custom namespace so that Net::XMPP can
299             properly handle it.
300              
301             =head1 AUTHOR
302              
303             Originally authored by Ryan Eatmon.
304              
305             Previously maintained by Eric Hacker.
306              
307             Currently maintained by Darian Anthony Patrick.
308              
309             =head1 COPYRIGHT
310              
311             This module is free software, you can redistribute it and/or modify it
312             under the LGPL 2.1.
313              
314             =cut
315              
316             require 5.008;
317 15     15   63 use strict;
  15         21  
  15         485  
318 15     15   61 use warnings;
  15         31  
  15         378  
319              
320 15     15   57 use vars qw ( %NS %SKIPNS );
  15         20  
  15         8608  
321              
322             $SKIPNS{'__netxmpp__'} = 1;
323              
324             #------------------------------------------------------------------------------
325             # __netxmpp__:child:test
326             #------------------------------------------------------------------------------
327             {
328             &add_ns(ns => "__netxmpptest__:child:test",
329             tag => "test",
330             xpath => {
331             Bar => { path => 'bar/text()' },
332             Foo => { path => '@foo' },
333             Test => { type => 'master' }
334             }
335             );
336             }
337              
338             #------------------------------------------------------------------------------
339             # __netxmpp__:child:test:two
340             #------------------------------------------------------------------------------
341             {
342             &add_ns(ns => "__netxmpptest__:child:test:two",
343             tag => "test",
344             xpath => {
345             Bob => { path => 'owner/@bob' },
346             Joe => { path => 'joe/text()' },
347             Test => { type => 'master' }
348             }
349             );
350             }
351              
352             #-----------------------------------------------------------------------------
353             # urn:ietf:params:xml:ns:xmpp-bind
354             #-----------------------------------------------------------------------------
355             {
356             &add_ns(ns => "urn:ietf:params:xml:ns:xmpp-bind",
357             tag => "bind",
358             xpath => {
359             JID => { type => 'jid',
360             path => 'jid/text()',
361             },
362             Resource => { path => 'resource/text()' },
363             Bind => { type => 'master' },
364             },
365             docs => {
366             module => 'Net::XMPP',
367             },
368             );
369             }
370              
371             #-----------------------------------------------------------------------------
372             # urn:ietf:params:xml:ns:xmpp-session
373             #-----------------------------------------------------------------------------
374             {
375             &add_ns(ns => "urn:ietf:params:xml:ns:xmpp-session",
376             tag => "session",
377             xpath => { Session => { type => 'master' } },
378             docs => {
379             module => 'Net::XMPP',
380             },
381             );
382             }
383              
384             #-----------------------------------------------------------------------------
385             # jabber:iq:auth
386             #-----------------------------------------------------------------------------
387             {
388             &add_ns(ns => "jabber:iq:auth",
389             tag => "query",
390             xpath => {
391             Digest => { path => 'digest/text()' },
392             Hash => { path => 'hash/text()' },
393             Password => { path => 'password/text()' },
394             Resource => { path => 'resource/text()' },
395             Sequence => { path => 'sequence/text()' },
396             Token => { path => 'token/text()' },
397             Username => { path => 'username/text()' },
398             Auth => { type => 'master' },
399             },
400             docs => {
401             module => 'Net::XMPP',
402             },
403             );
404             }
405              
406             #-----------------------------------------------------------------------------
407             # jabber:iq:privacy
408             #-----------------------------------------------------------------------------
409             {
410             &add_ns(ns => "jabber:iq:privacy",
411             tag => "query",
412             xpath => {
413             Active => { path => 'active/@name' },
414             Default => { path => 'default/@name' },
415             List => {
416             type => 'child',
417             path => 'list',
418             child => { ns => '__netxmpp__:iq:privacy:list', },
419             calls => [ 'Add' ],
420             },
421             Lists => {
422             type => 'child',
423             path => 'list',
424             child => { ns => '__netxmpp__:iq:privacy:list', },
425             },
426             Privacy => { type => 'master' },
427             },
428             docs => {
429             module => 'Net::XMPP',
430             },
431             );
432             }
433              
434             #-----------------------------------------------------------------------------
435             # __netxmpp__:iq:privacy:list
436             #-----------------------------------------------------------------------------
437             {
438             &add_ns(ns => '__netxmpp__:iq:privacy:list',
439             xpath => {
440             Name => { path => '@name' },
441             Item => {
442             type => 'child',
443             path => 'item',
444             child => { ns => '__netxmpp__:iq:privacy:list:item', },
445             calls => [ 'Add' ],
446             },
447             Items => {
448             type => 'child',
449             path => 'item',
450             child => { ns => '__netxmpp__:iq:privacy:item', },
451             },
452             List => { type => 'master' },
453             },
454             docs => {
455             module => 'Net::XMPP',
456             name => 'jabber:iq:privacy - list objects',
457             },
458             );
459             }
460              
461             #-----------------------------------------------------------------------------
462             # __netxmpp__:iq:privacy:list:item
463             #-----------------------------------------------------------------------------
464             {
465             &add_ns(ns => '__netxmpp__:iq:privacy:list:item',
466             xpath => {
467             Action => { path => '@action' },
468             IQ => {
469             type => 'flag',
470             path => 'iq',
471             },
472             Message => {
473             type => 'flag',
474             path => 'message',
475             },
476             Order => { path => '@order' },
477             PresenceIn => {
478             type => 'flag',
479             path => 'presence-in',
480             },
481             PresenceOut => {
482             type => 'flag',
483             path => 'presence-out',
484             },
485             Type => { path => '@type' },
486             Value => { path => '@value' },
487             Item => { type => 'master' },
488             },
489             docs => {
490             module => 'Net::XMPP',
491             name => 'jabber:iq:privacy - item objects',
492             },
493             );
494             }
495              
496             #-----------------------------------------------------------------------------
497             # jabber:iq:register
498             #-----------------------------------------------------------------------------
499             {
500             &add_ns(ns => "jabber:iq:register",
501             tag => "query",
502             xpath => {
503             Address => { path => 'address/text()' },
504             City => { path => 'city/text()' },
505             Date => { path => 'date/text()' },
506             Email => { path => 'email/text()' },
507             First => { path => 'first/text()' },
508             Instructions => { path => 'instructions/text()' },
509             Key => { path => 'key/text()' },
510             Last => { path => 'last/text()' },
511             Misc => { path => 'misc/text()' },
512             Name => { path => 'name/text()' },
513             Nick => { path => 'nick/text()' },
514             Password => { path => 'password/text()' },
515             Phone => { path => 'phone/text()' },
516             Registered => {
517             type => 'flag',
518             path => 'registered',
519             },
520             Remove => {
521             type => 'flag',
522             path => 'remove',
523             },
524             State => { path => 'state/text()' },
525             Text => { path => 'text/text()' },
526             URL => { path => 'url/text()' },
527             Username => { path => 'username/text()' },
528             Zip => { path => 'zip/text()' },
529             Register => { type => 'master' },
530             },
531             docs => {
532             module => 'Net::XMPP',
533             },
534             );
535             }
536              
537             #-----------------------------------------------------------------------------
538             # jabber:iq:roster
539             #-----------------------------------------------------------------------------
540             {
541             &add_ns(ns => 'jabber:iq:roster',
542             tag => "query",
543             xpath => {
544             Item => {
545             type => 'child',
546             path => 'item',
547             child => { ns => '__netxmpp__:iq:roster:item', },
548             calls => [ 'Add' ],
549             },
550             Items => {
551             type => 'child',
552             path => 'item',
553             child => { ns => '__netxmpp__:iq:roster:item', },
554             calls => [ 'Get' ],
555             },
556             Roster => { type => 'master' },
557             },
558             docs => {
559             module => 'Net::XMPP',
560             },
561             );
562             }
563              
564             #-----------------------------------------------------------------------------
565             # __netxmpp__:iq:roster:item
566             #-----------------------------------------------------------------------------
567             {
568             &add_ns(ns => "__netxmpp__:iq:roster:item",
569             xpath => {
570             Ask => { path => '@ask' },
571             Group => {
572             type => 'array',
573             path => 'group/text()',
574             },
575             JID => {
576             type => 'jid',
577             path => '@jid',
578             },
579             Name => { path => '@name' },
580             Subscription => { path => '@subscription' },
581             Item => { type => 'master' },
582             },
583             docs => {
584             module => 'Net::XMPP',
585             name => 'jabber:iq:roster - item objects',
586             },
587             );
588             }
589              
590              
591              
592             sub add_ns
593             {
594 165     165 1 303 my (%args) = @_;
595              
596             # XXX error check...
597              
598 165 100       437 $NS{$args{ns}}->{tag} = $args{tag} if exists($args{tag});
599 165         250 $NS{$args{ns}}->{xpath} = $args{xpath};
600 165 100       292 if (exists($args{docs}))
601             {
602 135         172 $NS{$args{ns}}->{docs} = $args{docs};
603 135 100       377 $NS{$args{ns}}->{docs}->{name} = $args{ns}
604             unless exists($args{docs}->{name});
605             }
606             }
607              
608              
609             1;