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 | <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 | |
---|
304 | Ryan Eatmon |
---|
305 | |
---|
306 | =head1 COPYRIGHT |
---|
307 | |
---|
308 | This module is free software; you can redistribute it and/or modify |
---|
309 | it under the same terms as Perl itself. |
---|
310 | |
---|
311 | =cut |
---|
312 | |
---|
313 | use 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 | |
---|
585 | sub 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 | |
---|
602 | 1; |
---|