The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Net::Jabber::Namespaces - In depth discussion on how namespaces are handled

SYNOPSIS

  Net::Jabber::Namespaces is a pure documentation
  module.  It provides no code for execution, just
  documentation on how the Net::Jabber modules handle
  namespaces.

DESCRIPTION

  Jabber as a protocol is very well defined.  There are
  three main top level packets (message, iq, and presence).
  There is also a way to extend the protocol in a very
  clear and strucutred way, via namespaces.

  Two major ways that namespaces are used in Jabber is
  for making the <iq/> a generic wrapper, and as a way
  for adding data to any packet via <x/>.

  The Info/Query <iq/> packet uses namespaces to determine
  the type of information to access.  Usually there is
  a <query/> tag in the <iq/> that represents the
  namespace, but in fact it can be any tag.  The
  definition of the Query portion, is the first
  tag that has a namespace.

    <iq type="get"><query xmlns="..."/></iq>

      or

    <iq type="get"><foo xmlns="..."/></iq>

  After that Query cna be any number of <x/> tags you want
  to include.

  The X tag is just a way to piggy back data on other
  packets.  Like embedding the timestamp for a message
  using jabber:x:delay, or signing you presence for
  encryption using jabber:x:signed.

  To this end, Net::Jabber has sought to find a way to
  easily, and clearly define the functions needed to
  access the XML for a namespace.  We will go over the
  full docs, and then show two examples of real namespaces
  so that you can see what we are talking about.

Overview

  To avoid a lot of nasty modules populating memory that
  are not used, and to avoid having to change 15 modules
  when a minor change is introduced, the Net::Jabber
  modules have taken AUTOLOADing to the extreme.
  Query.pm, X.pm and Data.pm are nothing but a hash of
  hashes data structure that is accessed by the Jabber.pm
  AutoLoad function to do something.  (This will make
  sense.)

  Before going on, I highly suggest you read a Perl book
  on AUTOLOAD and how it works.  From this point on I
  will assume that you understand it.

  When you create a Net::Jabber::IQ object and add a
  Query to it (NewQuery) somethings are happening in
  the background.  The argument to NewQuery is the
  namespace you want to add. (jabber:iq:register)

  Now that you have a Query object to work with you
  will call the GetXXX functions, and SetXXX functions
  to set the data.  There are no defined GetXXX and
  SetXXXX functions.  You cannot look in the Query.pm
  file and find them.  Instead you will find this:

$NAMESPACES{"jabber:iq:register"}->{Username}->{Get} = "username"; $NAMESPACES{"jabber:iq:register"}->{Username}->{Set} = ["scalar","username"]; $NAMESPACES{"jabber:iq:register"}->{Username}->{Defined} = "username"; $NAMESPACES{"jabber:iq:register"}->{Username}->{Hash} = "child-data";

  When the GetUsername() function is called, the AUTOLOAD
  function looks in the Query.pm %NAMESPACES hash for
  a "Username" key, and then for a "Get" key under that.  It
  will then use that value was the argument to a Get()
  function defined in Jabber.pm.

Net::Jabber private namespaces

   Now this is where this starts to get a little sticky.
  When you see a namespace with __netjabber__ at
  the beginning it is usually something custom to
  Net::Jabber and NOT part of the actual Jabber protocol.

  There are some places where the structure of the XML
  is a little more loose and not strongly structured.
  While it is still strongly defined, it is not 100%
  clear on how exactly to handle the tags.  The main
  places you will see this behavior is where you have
  multiple tags with the same name and those have
  children under them (jabber:iq:roster), or where the
  tags are not defined but are to be treated as children
  just because they are there (jabber:iq:browse).

  In jabber:iq:roster, the <item/> tag can be repeated
  multiple times, and is sort of like a mini-namespace
  in itself.  To that end, I treat it like a seperate
  namespace and defined a __netjabber__:iq:roster:item
  namespace to hold it.  What happens is this, in my code
  I define that the <item/>s tag is "item" and anything
  with that tag name is to create a new Net::Jabber::Query
  object with the namespace __netjabber__:iq:roster:item
  which then becomes a {query} child of the jabber:iq:roster
  Query object.  Also, when you want to add a new item
  to a jabber:iq:roster project you call NewQuery with
  the private namespace.

  In jabber:iq:browse, the children are not defined.
  Any child node you see under that <iq/> tag defines
  what the child type is.  So I again created a private
  namespace, and add every child, except <ns/>, as a
  new Queyr object.

  I know this sounds complicated.  And if after reading
  this entire document it is still complicated, email me,
  ask questions, and I will monitor it and adjust these
  docs to answer the questions that people ask.

Get() function

  The values of the argument come in two forms, either a string
  or an array.  For the most part, you will only use a string.
  The string is the key into the hash in the Query object.
  So above code would look into the Query hash for the
  "username" key and return the value from there.  The string
  also represents the value of the tag or attribute in the XML.

    "username" -> hash{username} -> <username/>

  One of the functions introduced early on in the Net::Jabber
  lifecycle was one that you could call and set all of the
  values of an object.  SetIQ() in the IQ.pm module for
  example.  Moving into this new AUTOLOAD format I added
  a GetIQ() function that would return a hash with the values
  populated as if you had called SetIQ().  This is done when
  you set the string to "__netjabber__:master".  So for the
  jabber:iq:register example above:

$NAMESPACES{"jabber:iq:register"}->{Register}->{Get} = "__netjabber__:master"; $NAMESPACES{"jabber:iq:register"}->{Register}->{Set} = ["master"];

  GetReigster() would return a hash with the data in the
  Query.

  The second form is an array:
    [ key, namespace ]

  like GetItem under the jabber:iq:search namespace:
    [ "query", "__netjabber__:iq:search" ]

  This is used only when the GetXXXXX function is supposed
  to return another object.  For example, under
  jabber:iq:search, the agent can return multiple <item/>
  tags.  These are turned in to Query objects as well and
  stored in the {DATA}->{query} hash array.  The array value
  tells the Get() function where to look "query", and which
  Query objects to return, anything with the namespace
  "__netjabber__:iq:search".

Set() function

  This arguement value is always an array.  Though the
  array can take a few forms depending on what you are
  trying to set.

  The first is the simplest, and the one you will more
  than likely only use:

    [ type, key ]

  In our jabber:iq:register example above {Username}->{Set}
  is [ "scalar", "username" ].  The type is scalar, and
  the key is username (again the key is the hash entry, and
  the XML Tag or attribute).  The valid types are:

    scalar  - store the value as a string or number
    array   - store the value as an array (like in
              jabber:iq:roster with <group/>)
    flag    - sets the value to "" just to show that the
              XML should appear as a flag.  <tag/>
    jid     - Stores the value as a Net::Jabber::JID
              object
    master  - run through all of the SetXXXX functions
              and set the value based on the hash passed
              in as an argument.  (More on this below...)
    special - there are several cases where we want to
              default the value if there is none there.
              mainly used in the jabber:iq:time and
              jabber:iq:version functions, these will
              default the value to something that makes
              sense.  You will probably never use this.

  As I mentioned, one of the functions introduced early on
  in the Net::Jabber lifecycle was one that you could call
  and set all of the values of an object.  SetIQ() in the
  IQ.pm module for example.  This is where the "master"
  type comes in.  When you declare that something is the
  "master" it means that it can set all of the things in
  the object via the SetXXXXXX functions based on the hash
  that is passed to it.  So for jabber:iq:register:

$NAMESPACES{"jabber:iq:register"}->{Register}->{Set} = ["master"];

  SetRegister(%hash) is considered to be the "master"
  function for that namespace.

  The second is more complicated and involves adding a new
  XML child into the current tag.  This is only done
  inside of the ParseTree function when taking XML and
  building the Net::Jabber::xxxxxx objects that represent
  the tree.  It takes the form:

    [ "add", object type, namespace ]
    [ "add", object type, namespace, tags to ignore ]

  Looking at the jabber:iq:roster namespace:

$NAMESPACES{"jabber:iq:roster"}->{Item}->{Set} = ["add","Query","__netjabber__:iq:roster:item"];

  A call to SetItem will add a new Net::Jabber::Query,
  with the namespace __netjabber__:iq:roster:item, and
  then populate the new Query object with the data you
  passed to the SetItem function.  (This will be covered
  more under the ParseTree function described at the end.)

  Looking at the __netjabber__:iq:browse:item namespace:

$NAMESPACES{"__netjabber__:iq:browse"}->{Item}->{Set} = ["add","Query","__netjabber__:iq:browse","ns"];

  Everything looks the same except for the extra "ns"
  at the end.  This tells Net::Jabber that you can create
  a new object from this XML data *UNLESS* the tag name
  is <ns/>.  If you look at the jabber:iq:browse namespace
  in the JPG, you will see that the children can have
  any name, except for <ns/> which is used to define which
  namespaces the jid provides.  (This will be covered
  more under the ParseTree function described at the end.)

Defined() function

  This function always takes a string as the argument.
  The string is the key value for the data hash, and tells
  Net::Jabber where to go look to see if the value is
  defined.  This one should not need any more explantion
  other than that.  All it does is check for exists() on
  the data hash for this value.

Hash() function

  This function takes only a string as its argument.
  The value of the string determines how this bit
  of data is handled when Net::Jabber attempts to read
  or set the XML.  The valid values are:

    att             - in the parent XML this bit of
                      data is an attribute on the root
                      tag in the XML you are currently
                      processing.
    data            - in the parent XML this bit of
                      data is the CDATA of the root
                      tag in the XML you are currently
                      processing.
    child-data      - in the parent XML this bit of
                      data is the CDATA of a child tag
                      (defined by the NAMESPACES hash)
                      of the root tag in the XML you
                      are currently processing.
    child-flag      - in the parent XML this bit of
                      data says to include an empty child
                      tag (defined by the NAMESPACES hash)
                      of the root tag in the XML you
                      are currently processing.
    child-add       - in the parent XML this bit of
                      data says to create a new XML
                      child and populate it by recursing
                      and looking at the Hash() values
                      for its data.
    att-<tag>-<att> - in the parent XML this bit of
                      data says to set the <att>
                      attribute on the child with tag
                      <tag> in the root tag you are
                      currently processing.

  I know this sounds really complicated, but its not.
  Just look over the %NAMESPACES structures and think
  about how the XML looks for the namespace you are
  looking at.

Add() function

  This function takes an array as its argument.  The array
  has two very similer forms:

    [ object type, namespace, Set* function to call ]
    [ object type, namespace, Set* function to call, tag value ]

  When the Add function gets called it will create a
  Net::Jabber::"object type" object with the namespace
  specified, and then call the SetXXXXX function based
  on the third value.  If a tag value is specified in
  the array, then that becomes the root tag for that
  object, otherwise the first argument to the AddXXXXX
  function defines the root tag.

  Looking at the jabber:iq:roster namespace:

$NAMESPACES{"jabber:iq:roster"}->{Item}->{Add} = ["Query","__netjabber__:iq:roster:item","Item","item"];

  A call to AddItem() will create a Net::Jabber::Query
  object with __netjabber__:iq:roster:item as the namespace.
  It will call SetItem on this new object to handle the
  data passed to AddItem(), *AND* it will set the root
  tag to "item" so that an <item/> tag is created.

  Looking at the jabber:iq:browse namespace:

$NAMESPACES{"jabber:iq:browse"}->{Item}->{Add} = ["Query","__netjabber__:iq:browse","Browse"];

  A call to AddItem() will create a Net::Jabber::Query
  object with __netjabber__:iq:browse:item as the namespace.
  It will call SetBrowse on this new object to handle the
  data passed to AddItem().  The root tag of new object
  is not defined though.  Instead, since the
  jabber:iq:browse namespaces does not define the
  children tags, and uses the value of the tag to
  set what the browse item means, the call to AddItem()
  must include the root tag as the first argument:

    my $browseItem =
      $browseQuery->
        AddItem("conference",
                type=>"private",
                jid=>"conference.jabber.org",
                name=>"Private Chatrooms");

ParseTree() function

  This function you will never call, but it will
  be calling the functions that you define in your
  call to DefineNamespace.  We will quickly run through
  how it works so you can get a better understanding
  of how the all of the above fits together.

  ParseTree() takes an XML::Stream::Hash tree and
  runs through every function that hash a {Set} entry
  in the definition Hash (see above).  For each
  function it calls Hash() on that function name
  to get the method of XML access for that function.
  Based on that value, it reaches into the
  XML::Stream::Hash and pulls the needed data out and
  calls the appropritate SetXXXXXXX() function to store
  that data.  If the Hash type is "child-add", then
  it calls Add() and passes it the XML::Stream::Hash
  tree with the {root} tag altered and ultimatly
  ParseTree is called on that new tree and starts again.

  After the defined {Set} functions have been looked at,
  the function looks for any child tags that have xmlns
  as an attribute.  If the current object is an <iq/>
  then it treats the first tag with an xmlns as the
  <query/> and creates that.  Otherwise, if the current
  object is a Query object, then this must be a private
  namespace for Net::Jabber, so create a Query object.
  Finally, if that fails, and this is not the first
  child with xmlns, or this is not a Query object then
  create an X object.

  Scrub.

  Rinse.

  Repeat.

Wrap Up

  Well.  I hope that I have not scared you off from
  writing a custom namespace for you application and
  use Net::Jabber.  Look in the Net::Jabber::Protocol
  manpage for an example on using the DefineNamespace
  function to register your custom namespace so that
  Net::Jabber can properly handle it.

AUTHOR

By Ryan Eatmon in May 2001 for http://jabber.org/

COPYRIGHT

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.