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
22package Net::XMPP::Namespaces;
23
24=head1 NAME
25
26Net::XMPP::Namespaces - In depth discussion on how namespaces are handled
27
28=head1 SYNOPSIS
29
30Net::XMPP::Namespaces provides an depth look at how Net::XMPP handles
31namespacs, and how to add your own custom ones.  It also serves as the
32storage bin for all of the Namespace information Net::XMPP requires.
33
34=head1 DESCRIPTION
35
36XMPP as a protocol is very well defined.  There are three main top
37level packets (message, iq, and presence).  There is also a way to
38extend the protocol in a very clear and strucutred way, via namespaces.
39
40Two major ways that namespaces are used in Jabber is for making the
41<iq/> a generic wrapper, and as a way for adding data to any packet via
42a child tag <x/>.  We will use <x/> to represent the packet, but in
43reality it could be any child tag: <foo/>, <data/>, <error/>, etc.
44
45The Info/Query <iq/> packet uses namespaces to determine the type of
46information to access.  Usually there is a <query/> tag in the <iq/>
47that represents the namespace, but in fact it can be any tag.  The
48definition of the Query portion, is the first tag that has a namespace.
49
50    <iq type="get"><query xmlns="..."/></iq>
51
52or
53
54    <iq type="get"><foo xmlns="..."/></iq>
55
56After that Query stanza can be any number of other stanzas (<x/> tags)
57you want to include.  The Query packet is represented and available by
58calling GetQuery() or GetChild(), and the other namespaces are
59available by calling GetChild().
60
61The X tag is just a way to piggy back data on other packets.  Like
62embedding the timestamp for a message using jabber:x:delay, or signing
63you presence for encryption using jabber:x:signed.
64
65To this end, Net::XMPP has sought to find a way to easily, and clearly
66define the functions needed to access the XML for a namespace.  We will
67go over the full docs, and then show two examples of real namespaces so
68that you can see what we are talking about.
69
70=head2 Overview
71
72To avoid a lot of nasty modules populating memory that are not used,
73and to avoid having to change 15 modules when a minor change is
74introduced, the Net::XMPP modules have taken AUTOLOADing to the
75extreme.  Namespaces.pm is nothing but a set of function calls that
76generates a big hash of hashes.  The hash is accessed by the Stanza.pm
77AUTOLOAD function to do something.  (This will make sense, I promise.)
78
79Before going on, I highly suggest you read a Perl book on AUTOLOAD and
80how it works.  From this point on I will assume that you understand it.
81
82When you create a Net::XMPP::IQ object and add a Query to it (NewChild)
83several things are happening in the background.  The argument to
84NewChild is the namespace you want to add. (custom-namespace)
85
86Now that you have a Query object to work with you will call the GetXXX
87functions, and SetXXX functions to set the data.  There are no defined
88GetXXX and SetXXXX functions.  You cannot look in the Namespaces.pm
89file 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
100When the GetUsername() function is called, the AUTOLOAD function looks
101in the Namespaces.pm hash for a "Username" key.  Based on the "type" of
102the field (scalar being the default) it will use the "path" as an XPath
103to retrieve the data and call the XPathGet() method in Stanza.pm.
104
105Confused yet?
106
107=head2 Net::XMPP private namespaces
108
109Now this is where this starts to get a little sticky.  When you see a
110namespace with __netxmpp__, or __netjabber__ from Net::Jabber, at the
111beginning it is usually something custom to Net::XMPP and NOT part of
112the actual XMPP protocol.
113
114There are some places where the structure of the XML allows for
115multiple children with the same name.  The main places you will see
116this behavior is where you have multiple tags with the same name and
117those have children under them (jabber:iq:roster).
118
119In jabber:iq:roster, the <item/> tag can be repeated multiple times,
120and is sort of like a mini-namespace in itself.  To that end, we treat
121it like a separate namespace and defined a __netxmpp__:iq:roster:item
122namespace to hold it.  What happens is this, in my code I define that
123the <item/>s tag is "item" and anything with that tag name is to create
124a new Net::XMPP::Stanza object with the namespace
125__netxmpp__:iq:roster:item which then becomes a child of the
126jabber:iq:roster Stanza object.  Also, when you want to add a new item
127to a jabber:iq:roster project you call NewQuery with the private
128namespace.
129
130I know this sounds complicated.  And if after reading this entire
131document it is still complicated, email me, ask questions, and I will
132monitor it and adjust these docs to answer the questions that people
133ask.
134
135=head2 add_ns()
136
137To 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
148ns - This is the new namespace that you are trying to add.
149
150tag - This is the root tag to use for objects based on this namespace.
151
152xpath - The hash reference passed in the add_ns call to each name of
153entry tells Net::XMPP how to handle subsequent GetXXXX(), SetXXXX(),
154DefinedXXXX(), RemoveXXXX(), AddXXXX() calls.  The basic options you
155can pass in are:
156
157type - 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, <group/> 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                  <foo/>.  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" , <subtype> ]
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 <foo/> packet.  It will set and get
226            the CDATA from:
227
228               <foo><bar>xxxxx</bar></foo>
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
295Well.  I hope that I have not scared you off from writing a custom
296namespace for you application and use Net::XMPP.  Look in the
297Net::XMPP::Protocol manpage for an example on using the add_ns()
298function to register your custom namespace so that Net::XMPP can
299properly handle it.
300
301=head1 AUTHOR
302
303Originally authored by Ryan Eatmon.
304
305Previously maintained by Eric Hacker.
306
307Currently maintained by Darian Anthony Patrick.
308
309=head1 COPYRIGHT
310
311This module is free software, you can redistribute it and/or modify it
312under the LGPL 2.1.
313
314=cut
315
316require 5.008;
317use strict;
318use warnings;
319
320use vars qw ( %NS %SKIPNS );
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
592sub add_ns
593{
594    my (%args) = @_;
595
596    # XXX error check...
597
598    $NS{$args{ns}}->{tag} = $args{tag} if exists($args{tag});
599    $NS{$args{ns}}->{xpath} = $args{xpath};
600    if (exists($args{docs}))
601    {
602        $NS{$args{ns}}->{docs} = $args{docs};
603        $NS{$args{ns}}->{docs}->{name} = $args{ns}
604            unless exists($args{docs}->{name});
605    }
606}
607
608
6091;
610