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