source: perl/lib/Net/XMPP/Namespaces.pm @ 0337203

barnowl_perlaimdebianrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 0337203 was 0ff8d110, checked in by Alejandro R. Sedeño <asedeno@mit.edu>, 17 years ago
Adding XML::Stream, Net::XMPP, and Net::Jabber to perl/lib/
  • Property mode set to 100644
File size: 24.0 KB
Line 
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
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  <iq/> a generic wrapper, and as a way for adding data to any packet via
42  a child tag <x/>.  We will use <x/> to represent the packet, but in
43  reality it could be any child tag: <foo/>, <data/>, <error/>, etc.
44
45  The Info/Query <iq/> packet uses namespaces to determine the type of
46  information to access.  Usually there is a <query/> tag in the <iq/>
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    <iq type="get"><query xmlns="..."/></iq>
51
52      or
53
54    <iq type="get"><foo xmlns="..."/></iq>
55
56  After that Query stanza can be any number of other stanzas (<x/> 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 <item/> 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 seperate 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 <item/>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
158            values are:
159                       
160           array - The value to set and returned is an an array
161                   reference.  For example, <group/> in jabber:iq:roster.
162
163           child - This tells Stanza that it needs to look for the
164                   __netxmpp__ style namesapced children.  AddXXX() adds
165                   a new child, and GetXXX() will return a new Stanza
166                   object representing the packet.
167
168           flag - This is for child elements that are tags by themselves:
169                  <foo/>.  Since the presence of the tag is what is
170                  important, and there is no cdata to store, we just call
171                  it a flag.
172
173           jid - The value is a Jabber ID.  GetXXX() will return a
174                 Net::XMPP::JID object unless you pass it "jid", then it
175                 returns a string.
176
177           master - The GetXXX() and SetXXX() calls return and take a
178                    hash representing all of the GetXXX() and SetXXX()
179                    calls.  For example:
180
181                      SetTest(foo=>"bar",
182                              bar=>"baz");
183
184                    Translates into:
185
186                      SetFoo("bar");
187                      SetBar("baz");
188
189                    GetTest() would return a hash containing what the
190                    packet contains:
191
192                      { foo=>"bar",  bar=>"baz" }
193
194           raw - This will stick whatever raw XML you specify directly
195                 into the Stanza at the point where the path specifies.
196
197           scalar - This will set and get a scalar value.  This is the
198                    main workhorse as attributes and CDATA is represented
199                    by a scalar.  This is the default setting if you do
200                    not provide one.
201
202           special - The special type is unique in that instead of a
203                     string "special", you actually give it an array:
204
205                       [ "special" , <subtype> ]
206
207                     This allows Net::XMPP to be able to handle the
208                     SetXXXX() call in a special manner according to your
209                     choosing.  Right now this is mainly used by
210                     jabber:iq:time to automatically set the time info in
211                     the correct format, and jabber:iq:version to set the
212                     machine OS and add the Net::Jabber version to the
213                     return packet.  You will likely NOT need to use
214                     this, but I wanted to mention it.
215
216           timestamp - If you call SetXXX() but do not pass it anything,
217                       or pass it "", then Net::XMPP will place a
218                       timestamp in the xpath location.
219
220     path - This is the XPath path to where the bit data lives.  The
221            difference.  Now, this is not full XPath due to the nature
222            of how it gets used.  Instead of providing a rooted path
223            all the way to the top, it's a relative path ignoring what
224            the parent is.  For example, if the "tag" you specified was
225            "foo", and the path is "bar/text()", then the XPath will be
226            rooted in the XML of the <foo/> packet.  It will set and get
227            the CDATA from:
228
229               <foo><bar>xxxxx</bar></foo>
230
231            For a flag and a child type, just specify the child element.
232            Take a look at the code in this file for more help on what
233            this means.  Also, read up on XPath if you don't already know
234            what it is.
235
236     child - This is a hash reference that tells Net::XMPP how to handle
237             adding and getting child objects.  The keys for the hash are
238             as follows:
239
240             ns - the real or custom (__netxmpp__) namesapce to use for
241                  this child packet.
242
243             skip_xmlns => 1 - this tells Net::XMPP not to add an
244                               xmlns='' into the XML for the child
245                               object.
246
247             specify_name => 1 - allows you to call NewChild("ns","tag")
248                                 and specify the tag to use for the child
249                                 object.  This, IMHO, is BAD XML
250                                 practice.  You should always know what
251                                 the tag of the child is and use an
252                                 attribute or CDATA to change the type
253                                 of the stanza.  You do not want to use
254                                 this.
255
256             tag - If you use specify_name, then this is the default tag
257                   to use.  You do not want to use this.
258
259     calls - Array reference telling Net::XMPP what functions to create
260             for this name.  For most of the types above you will get
261             Get, Set, Defined, and Remove.  For child types you need to
262             decide how you API will look and specify them yourself:
263
264               ["Get","Defined"]
265               ["Add"]
266               ["Get","Add","Defined"]
267             
268            It all depends on how you want your API to look.
269     
270  Once more... The following:
271 
272    &add_ns(ns    => "mynamespace",
273            tag   => "mytag",
274            xpath => {
275                      JID       => { type=>'jid', path => '@jid' },
276                      Username  => { path => 'username/text()' },
277                      Test      => { type => 'master' }
278                     }
279           );
280
281  generates the following API calls:
282
283    GetJID()
284    SetJID()
285    DefinedJID()
286    RemoveJID()
287    GetUsername()
288    SetUsername()
289    DefinedUsername()
290    RemoveUsername()
291    GetTest()
292    SetTest()
293
294=head2 Wrap Up
295
296  Well.  I hope that I have not scared you off from writing a custom
297  namespace for you application and use Net::XMPP.  Look in the
298  Net::XMPP::Protocol manpage for an example on using the add_ns()
299  function to register your custom namespace so that Net::XMPP can
300  properly handle it.
301
302=head1 AUTHOR
303
304Ryan Eatmon
305
306=head1 COPYRIGHT
307
308This module is free software; you can redistribute it and/or modify
309it under the same terms as Perl itself.
310
311=cut
312
313use vars qw ( %NS %SKIPNS );
314
315$SKIPNS{'__netxmpp__'} = 1;
316
317#------------------------------------------------------------------------------
318# __netxmpp__:child:test
319#------------------------------------------------------------------------------
320{
321    &add_ns(ns    => "__netxmpptest__:child:test",
322            tag   => "test",
323            xpath => {
324                      Bar  => { path => 'bar/text()' },
325                      Foo  => { path => '@foo' },
326                      Test => { type => 'master' }
327                     }
328           );
329}
330
331#------------------------------------------------------------------------------
332# __netxmpp__:child:test:two
333#------------------------------------------------------------------------------
334{
335    &add_ns(ns    => "__netxmpptest__:child:test:two",
336            tag   => "test",
337            xpath => {
338                      Bob  => { path => 'owner/@bob' },
339                      Joe  => { path => 'joe/text()' },
340                      Test => { type => 'master' }
341                     }
342           );
343}
344
345#-----------------------------------------------------------------------------
346# urn:ietf:params:xml:ns:xmpp-bind
347#-----------------------------------------------------------------------------
348{
349    &add_ns(ns    => "urn:ietf:params:xml:ns:xmpp-bind",
350            tag   => "bind",
351            xpath => {
352                      JID      => { type => 'jid',
353                                    path => 'jid/text()',
354                                  },
355                      Resource => { path => 'resource/text()' },
356                      Bind     => { type => 'master' },
357                     },
358            docs  => {
359                      module => 'Net::XMPP',
360                     },
361           );
362}
363
364#-----------------------------------------------------------------------------
365# urn:ietf:params:xml:ns:xmpp-session
366#-----------------------------------------------------------------------------
367{
368    &add_ns(ns    => "urn:ietf:params:xml:ns:xmpp-session",
369            tag   => "session",
370            xpath => { Session => { type => 'master' } },
371            docs  => {
372                      module => 'Net::XMPP',
373                     },
374           );
375}
376
377#-----------------------------------------------------------------------------
378# jabber:iq:auth
379#-----------------------------------------------------------------------------
380{
381    &add_ns(ns    => "jabber:iq:auth",
382            tag   => "query",
383            xpath => {
384                      Digest   => { path => 'digest/text()' },
385                      Hash     => { path => 'hash/text()' },
386                      Password => { path => 'password/text()' },
387                      Resource => { path => 'resource/text()' },
388                      Sequence => { path => 'sequence/text()' },
389                      Token    => { path => 'token/text()' },
390                      Username => { path => 'username/text()' },
391                      Auth     => { type => 'master' },
392                     },
393            docs  => {
394                      module => 'Net::XMPP',
395                     },
396           );
397}
398
399#-----------------------------------------------------------------------------
400# jabber:iq:privacy
401#-----------------------------------------------------------------------------
402{
403    &add_ns(ns    => "jabber:iq:privacy",
404            tag   => "query",
405            xpath => {
406                      Active  => { path => 'active/@name' },
407                      Default => { path => 'default/@name' },
408                      List    => {
409                                  type  => 'child',
410                                  path  => 'list',
411                                  child => { ns => '__netxmpp__:iq:privacy:list', },
412                                  calls => [ 'Add' ],
413                                 },
414                      Lists   => {
415                                  type  => 'child',
416                                  path  => 'list',
417                                  child => { ns => '__netxmpp__:iq:privacy:list', },
418                                 },
419                      Privacy => { type => 'master' },
420                     },
421            docs  => {
422                      module => 'Net::XMPP',
423                     },
424           );
425}
426
427#-----------------------------------------------------------------------------
428# __netxmpp__:iq:privacy:list
429#-----------------------------------------------------------------------------
430{
431    &add_ns(ns    => '__netxmpp__:iq:privacy:list',
432            xpath => {
433                      Name  => { path => '@name' },
434                      Item  => {
435                                type  => 'child',
436                                path  => 'item',
437                                child => { ns => '__netxmpp__:iq:privacy:list:item', },
438                                calls => [ 'Add' ],
439                               },
440                      Items => {
441                                type  => 'child',
442                                path  => 'item',
443                                child => { ns => '__netxmpp__:iq:privacy:item', },
444                               },
445                      List  => { type => 'master' },
446                    },
447            docs  => {
448                      module => 'Net::XMPP',
449                      name   => 'jabber:iq:privacy - list objects',
450                     },
451           );
452}
453       
454#-----------------------------------------------------------------------------
455# __netxmpp__:iq:privacy:list:item
456#-----------------------------------------------------------------------------
457{
458    &add_ns(ns    => '__netxmpp__:iq:privacy:list:item',
459            xpath => {
460                      Action      => { path => '@action' },
461                      IQ          => {
462                                      type => 'flag',
463                                      path => 'iq',
464                                     },
465                      Message     => {
466                                      type => 'flag',
467                                      path => 'message',
468                                     },
469                      Order       => { path => '@order' },
470                      PresenceIn  => {
471                                      type => 'flag',
472                                      path => 'presence-in',
473                                     },
474                      PresenceOut => {
475                                      type => 'flag',
476                                      path => 'presence-out',
477                                     },
478                      Type        => { path => '@type' },
479                      Value       => { path => '@value' },
480                      Item        => { type => 'master' },
481                     },
482            docs  => {
483                      module => 'Net::XMPP',
484                      name   => 'jabber:iq:privacy - item objects',
485                     },
486           );
487}
488
489#-----------------------------------------------------------------------------
490# jabber:iq:register
491#-----------------------------------------------------------------------------
492{
493    &add_ns(ns    => "jabber:iq:register",
494            tag   => "query",
495            xpath => {
496                      Address      => { path => 'address/text()' },
497                      City         => { path => 'city/text()' },
498                      Date         => { path => 'date/text()' },
499                      Email        => { path => 'email/text()' },
500                      First        => { path => 'first/text()' },
501                      Instructions => { path => 'instructions/text()' },
502                      Key          => { path => 'key/text()' },
503                      Last         => { path => 'last/text()' },
504                      Misc         => { path => 'misc/text()' },
505                      Name         => { path => 'name/text()' },
506                      Nick         => { path => 'nick/text()' },
507                      Password     => { path => 'password/text()' },
508                      Phone        => { path => 'phone/text()' },
509                      Registered   => {
510                                       type => 'flag',
511                                       path => 'registered',
512                                      },
513                      Remove       => {
514                                       type => 'flag',
515                                       path => 'password/text()',
516                                      },
517                      State        => { path => 'state/text()' },
518                      Text         => { path => 'text/text()' },
519                      URL          => { path => 'url/text()' },
520                      Username     => { path => 'username/text()' },
521                      Zip          => { path => 'zip/text()' },
522                      Register     => { type => 'master' },
523                     },
524            docs  => {
525                      module => 'Net::XMPP',
526                     },
527           );
528}
529
530#-----------------------------------------------------------------------------
531# jabber:iq:roster
532#-----------------------------------------------------------------------------
533{
534    &add_ns(ns    => 'jabber:iq:roster',
535            tag   => "query",
536            xpath => {
537                      Item  => {
538                                type  => 'child',
539                                path  => 'item',
540                                child => { ns => '__netxmpp__:iq:roster:item', },
541                                calls => [ 'Add' ],
542                               },
543                      Items => {
544                                type  => 'child',
545                                path  => 'item',
546                                child => { ns => '__netxmpp__:iq:roster:item', },
547                                calls => [ 'Get' ],
548                               },
549                      Roster => { type => 'master' },
550                     },
551            docs  => {
552                      module => 'Net::XMPP',
553                     },
554           );
555}
556
557#-----------------------------------------------------------------------------
558# __netxmpp__:iq:roster:item
559#-----------------------------------------------------------------------------
560{
561    &add_ns(ns    => "__netxmpp__:iq:roster:item",
562            xpath => {
563                      Ask          => { path => '@ask' },
564                      Group        => {
565                                       type => 'array',
566                                       path => 'group/text()',
567                                      },
568                      JID          => {
569                                       type => 'jid',
570                                       path => '@jid',
571                                      },
572                      Name         => { path => '@name' },
573                      Subscription => { path => '@subscription' },
574                      Item         => { type => 'master' },
575                     },
576            docs  => {
577                      module => 'Net::XMPP',
578                      name   => 'jabber:iq:roster - item objects',
579                     },
580           );
581}
582
583
584
585sub add_ns
586{
587    my (%args) = @_;
588
589    # XXX error check...
590   
591    $NS{$args{ns}}->{tag} = $args{tag} if exists($args{tag});
592    $NS{$args{ns}}->{xpath} = $args{xpath};
593    if (exists($args{docs}))
594    {
595        $NS{$args{ns}}->{docs} = $args{docs};
596        $NS{$args{ns}}->{docs}->{name} = $args{ns}
597            unless exists($args{docs}->{name});
598    }
599}
600
601
6021;
Note: See TracBrowser for help on using the repository browser.