PageRenderTime 297ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 1ms

/linkedfs/usr/lib/perl5/vendor_perl/5.8.4/Net/Jabber/Protocol.pm

https://bitbucket.org/harakiri/trk
Perl | 5203 lines | 3848 code | 616 blank | 739 comment | 210 complexity | d025d29b6a5082a5a1478879b6db137d MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  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. # Jabber
  19. # Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
  20. #
  21. ##############################################################################
  22. package Net::Jabber::Protocol;
  23. =head1 NAME
  24. Net::Jabber::Protocol - Jabber Protocol Library
  25. =head1 SYNOPSIS
  26. Net::Jabber::Protocol is a module that provides a developer easy
  27. access to the Jabber Instant Messaging protocol. It provides high
  28. level functions to the Net::Jabber Client, Component, and Server
  29. objects. These functions are automatically indluded in those modules
  30. through AUTOLOAD and delegates.
  31. =head1 DESCRIPTION
  32. Protocol.pm seeks to provide enough high level APIs and automation of
  33. the low level APIs that writing a Jabber Client/Transport in Perl is
  34. trivial. For those that wish to work with the low level you can do
  35. that too, but those functions are covered in the documentation for
  36. each module.
  37. Net::Jabber::Protocol provides functions to login, send and receive
  38. messages, set personal information, create a new user account, manage
  39. the roster, and disconnect. You can use all or none of the functions,
  40. there is no requirement.
  41. For more information on how the details for how Net::Jabber is written
  42. please see the help for Net::Jabber itself.
  43. For more information on writing a Client see Net::Jabber::Client.
  44. For more information on writing a Transport see Net::Jabber::Transport.
  45. =head2 Modes
  46. Several of the functions take a mode argument that let you specify how
  47. the function should behave:
  48. block - send the packet with an ID, and then block until an answer
  49. comes back. You can optionally specify a timeout so that
  50. you do not block forever.
  51. nonblock - send the packet with an ID, but then return that id and
  52. control to the master program. Net::Jabber is still
  53. tracking this packet, so you must use the CheckID function
  54. to tell when it comes in. (This might not be very
  55. useful...)
  56. passthru - send the packet with an ID, but do NOT register it with
  57. Net::Jabber, then return the ID. This is useful when
  58. combined with the XPath function because you can register
  59. a one shot function tied to the id you get back.
  60. =head2 Basic Functions
  61. use Net::Jabber qw( Client );
  62. $Con = new Net::Jabber::Client(); # From
  63. $status = $Con->Connect(hostname=>"jabber.org"); # Net::Jabber::Client
  64. or
  65. use Net::Jabber qw( Component );
  66. $Con = new Net::Jabber::Component(); #
  67. $status = $Con->Connect(hostname=>"jabber.org", # From
  68. secret=>"bob"); # Net::Jabber::Component
  69. $Con->SetCallBacks(send=>\&sendCallBack,
  70. receive=>\&receiveCallBack,
  71. message=>\&messageCallBack,
  72. iq=>\&handleTheIQTag);
  73. $Con->SetMessageCallBacks(normal=>\&messageNormalCB,
  74. chat=>\&messageChatCB);
  75. $Con->SetPresenceCallBacks(available=>\&presenceAvailableCB,
  76. unavailable=>\&presenceUnavailableCB);
  77. $Con->SetIQCallBacks("jabber:iq:roster"=>
  78. {
  79. get=>\&iqRosterGetCB,
  80. set=>\&iqRosterSetCB,
  81. result=>\&iqRosterResultCB,
  82. },
  83. etc...
  84. );
  85. $Con->SetXPathCallBacks("/message[@type='chat']"=>&messageChatCB,
  86. "/message[@type='chat']"=>&otherMessageChatCB,
  87. ...
  88. );
  89. $Con->RemovePathCallBacks("/message[@type='chat']"=>&otherMessageChatCB);
  90. $Con->Info(name=>"Jarl",
  91. version=>"v0.6000");
  92. $error = $Con->GetErrorCode();
  93. $Con->SetErrorCode("Timeout limit reached");
  94. $status = $Con->Process();
  95. $status = $Con->Process(5);
  96. $Con->Send($object);
  97. $Con->Send("<tag>XML</tag>");
  98. $Con->Send($object,1);
  99. $Con->Send("<tag>XML</tag>",1);
  100. $Con->Disconnect();
  101. =head2 ID Functions
  102. $id = $Con->SendWithID($sendObj);
  103. $id = $Con->SendWithID("<tag>XML</tag>");
  104. $receiveObj = $Con->SendAndReceiveWithID($sendObj);
  105. $receiveObj = $Con->SendAndReceiveWithID($sendObj,
  106. 10);
  107. $receiveObj = $Con->SendAndReceiveWithID("<tag>XML</tag>");
  108. $receiveObj = $Con->SendAndReceiveWithID("<tag>XML</tag>",
  109. 5);
  110. $yesno = $Con->ReceivedID($id);
  111. $receiveObj = $Con->GetID($id);
  112. $receiveObj = $Con->WaitForID($id);
  113. $receiveObj = $Con->WaitForID($id,
  114. 20);
  115. =head2 Namespace Functions
  116. $Con->DefineNamespace(xmlns=>"foo:bar",
  117. type=>"Query",
  118. functions=>[{name=>"Foo",
  119. get=>"foo",
  120. set=>["scalar","foo"],
  121. defined=>"foo",
  122. hash=>"child-data"},
  123. {name=>"Bar",
  124. get=>"bar",
  125. set=>["scalar","bar"],
  126. defined=>"bar",
  127. hash=>"child-data"},
  128. {name=>"FooBar",
  129. get=>"__netjabber__:master",
  130. set=>["master"]}]);
  131. =head2 Message Functions
  132. $Con->MessageSend(to=>"bob@jabber.org",
  133. subject=>"Lunch",
  134. body=>"Let's go grab some...\n",
  135. thread=>"ABC123",
  136. priority=>10);
  137. =head2 Presence Functions
  138. $Con->PresenceSend();
  139. $Con->PresenceSend(type=>"unavailable");
  140. $Con->PresenceSend(show=>"away");
  141. $Con->PresenceSend(signature=>...signature...);
  142. =head2 Subscription Functions
  143. $Con->Subscription(type=>"subscribe",
  144. to=>"bob@jabber.org");
  145. $Con->Subscription(type=>"unsubscribe",
  146. to=>"bob@jabber.org");
  147. $Con->Subscription(type=>"subscribed",
  148. to=>"bob@jabber.org");
  149. $Con->Subscription(type=>"unsubscribed",
  150. to=>"bob@jabber.org");
  151. =head2 Presence DB Functions
  152. $Con->PresenceDBParse(Net::Jabber::Presence);
  153. $Con->PresenceDBDelete("bob\@jabber.org");
  154. $Con->PresenceDBDelete(Net::Jabber::JID);
  155. $Con->PresenceDBClear();
  156. $presence = $Con->PresenceDBQuery("bob\@jabber.org");
  157. $presence = $Con->PresenceDBQuery(Net::Jabber::JID);
  158. @resources = $Con->PresenceDBResources("bob\@jabber.org");
  159. @resources = $Con->PresenceDBResources(Net::Jabber::JID);
  160. =head2 IQ Functions
  161. =head2 Agents Functions
  162. %agents = $Con->AgentsGet();
  163. %agents = $Con->AgentsGet(to=>"transport.jabber.org");
  164. =head2 Auth Functions
  165. @result = $Con->AuthSend();
  166. @result = $Con->AuthSend(username=>"bob",
  167. password=>"bobrulez",
  168. resource=>"Bob");
  169. =head2 Browse Functions
  170. %hash = $Con->BrowseRequest(jid=>"jabber.org");
  171. %hash = $Con->BrowseRequest(jid=>"jabber.org",
  172. timeout=>10);
  173. $id = $Con->BrowseRequest(jid=>"jabber.org",
  174. mode=>"nonblock");
  175. $id = $Con->BrowseRequest(jid=>"jabber.org",
  176. mode=>"passthru");
  177. =head2 Browse DB Functions
  178. $Con->BrowseDBDelete("jabber.org");
  179. $Con->BrowseDBDelete(Net::Jabber::JID);
  180. $presence = $Con->BrowseDBQuery(jid=>"bob\@jabber.org");
  181. $presence = $Con->BrowseDBQuery(jid=>Net::Jabber::JID);
  182. $presence = $Con->BrowseDBQuery(jid=>"users.jabber.org",
  183. timeout=>10);
  184. $presence = $Con->BrowseDBQuery(jid=>"conference.jabber.org",
  185. refresh=>1);
  186. =head2 Bystreams Functions
  187. %hash = $Con->ByteStreamsProxyRequest(jid=>"proxy.server");
  188. %hash = $Con->ByteStreamsProxyRequest(jid=>"proxy.server",
  189. timeout=>10);
  190. $id = $Con->ByteStreamsProxyRequest(jid=>"proxy.server",
  191. mode=>"nonblock");
  192. $id = $Con->ByteStreamsProxyRequest(jid=>"proxy.server",
  193. mode=>"passthru");
  194. %hash = $Con->ByteStreamsProxyParse($query);
  195. $status = $Con->ByteStreamsProxyActivate(sid=>"stream_id",
  196. jid=>"proxy.server");
  197. $status = $Con->ByteStreamsProxyActivate(sid=>"stream_id",
  198. jid=>"proxy.server",
  199. timeout=>10);
  200. $id = $Con->ByteStreamsProxyActivate(sid=>"stream_id",
  201. jid=>"proxy.server",
  202. mode=>"nonblock");
  203. $id = $Con->ByteStreamsProxyActivate(sid=>"stream_id",
  204. jid=>"proxy.server",
  205. mode=>"passthru");
  206. $jid = $Con->ByteStreamsOffer(sid=>"stream_id",
  207. streamhosts=>[{jid=>"jid",
  208. host=>"host",
  209. port=>"port",
  210. zeroconf=>"zero",
  211. },
  212. ...
  213. ],
  214. jid=>"bob\@jabber.org");
  215. $jid = $Con->ByteStreamsOffer(sid=>"stream_id",
  216. streamhosts=>[{},{},...],
  217. jid=>"bob\@jabber.org",
  218. timeout=>10);
  219. $id = $Con->ByteStreamsOffer(sid=>"stream_id",
  220. streamhosts=>[{},{},...],
  221. jid=>"bob\@jabber.org",
  222. mode=>"nonblock");
  223. $id = $Con->ByteStreamsOffer(sid=>"stream_id",
  224. streamhosts=>[{},{},...],
  225. jid=>"bob\@jabber.org",
  226. mode=>"passthru");
  227. =head2 Disco Functions
  228. %hash = $Con->DiscoInfoRequest(jid=>"jabber.org");
  229. %hash = $Con->DiscoInfoRequest(jid=>"jabber.org",
  230. node=>"node...");
  231. %hash = $Con->DiscoInfoRequest(jid=>"jabber.org",
  232. node=>"node...",
  233. timeout=>10);
  234. $id = $Con->DiscoInfoRequest(jid=>"jabber.org",
  235. mode=>"nonblock");
  236. $id = $Con->DiscoInfoRequest(jid=>"jabber.org",
  237. node=>"node...",
  238. mode=>"nonblock");
  239. $id = $Con->DiscoInfoRequest(jid=>"jabber.org",
  240. mode=>"passthru");
  241. $id = $Con->DiscoInfoRequest(jid=>"jabber.org",
  242. node=>"node...",
  243. mode=>"passthru");
  244. %hash = $Con->DiscoInfoParse($query);
  245. %hash = $Con->DiscoItemsRequest(jid=>"jabber.org");
  246. %hash = $Con->DiscoItemsRequest(jid=>"jabber.org",
  247. timeout=>10);
  248. $id = $Con->DiscoItemsRequest(jid=>"jabber.org",
  249. mode=>"nonblock");
  250. $id = $Con->DiscoItemsRequest(jid=>"jabber.org",
  251. mode=>"passthru");
  252. %hash = $Con->DiscoItemsParse($query);
  253. =head2 Feature Negotiation Functions
  254. %hash = $Con->FeatureNegRequest(jid=>"jabber.org",
  255. features=>{ feat1=>["opt1","opt2",...],
  256. feat2=>["optA","optB",...]
  257. }
  258. );
  259. %hash = $Con->FeatureNegRequest(jid=>"jabber.org",
  260. features=>{ ... },
  261. timeout=>10);
  262. $id = $Con->FeatureNegRequest(jid=>"jabber.org",
  263. features=>{ ... },
  264. mode=>"nonblock");
  265. $id = $Con->FeatureNegRequest(jid=>"jabber.org",
  266. features=>{ ... },
  267. mode=>"passthru");
  268. my $query = $self->FeatureNegQuery(\{ ... });
  269. $iq->AddQuery($query);
  270. %hash = $Con->FeatureNegParse($query);
  271. =head2 File Transfer Functions
  272. $method = $Con->FileTransferOffer(jid=>"bob\@jabber.org",
  273. sid=>"stream_id",
  274. filename=>"/path/to/file",
  275. methods=>["http://jabber.org/protocol/si/profile/bytestreams",
  276. "jabber:iq:oob",
  277. ...
  278. ]
  279. );
  280. $method = $Con->FileTransferOffer(jid=>"bob\@jabber.org",
  281. sid=>"stream_id",
  282. filename=>"/path/to/file",
  283. methods=>\@methods,
  284. timeout=>"10");
  285. $id = $Con->FileTransferOffer(jid=>"bob\@jabber.org",
  286. sid=>"stream_id",
  287. filename=>"/path/to/file",
  288. methods=>\@methods,
  289. mode=>"nonblock");
  290. $id = $Con->FileTransferOffer(jid=>"bob\@jabber.org",
  291. sid=>"stream_id",
  292. filename=>"/path/to/file",
  293. methods=>\@methods,
  294. mode=>"passthru");
  295. =head2 Last Functions
  296. $Con->LastQuery();
  297. $Con->LastQuery(to=>"bob@jabber.org");
  298. %result = $Con->LastQuery(mode=>"block");
  299. %result = $Con->LastQuery(to=>"bob@jabber.org",
  300. mode=>"block");
  301. %result = $Con->LastQuery(to=>"bob@jabber.org",
  302. mode=>"block",
  303. timeout=>10);
  304. %result = $Con->LastQuery(mode=>"block",
  305. timeout=>10);
  306. $Con->LastSend(to=>"bob@jabber.org");
  307. $seconds = $Con->LastActivity();
  308. =head2 Register Functions
  309. %hash = $Con->RegisterRequest();
  310. %hash = $Con->RegisterRequest(to=>"transport.jabber.org");
  311. %hash = $Con->RegisterRequest(to=>"transport.jabber.org",
  312. timeout=>10);
  313. @result = $Con->RegisterSend(to=>"somewhere",
  314. username=>"newuser",
  315. resource=>"New User",
  316. password=>"imanewbie",
  317. email=>"newguy@new.com",
  318. key=>"some key");
  319. @result = $Con->RegisterSendData("users.jabber.org",
  320. first=>"Bob",
  321. last=>"Smith",
  322. nick=>"bob",
  323. email=>"foo@bar.net");
  324. =head2 Roster Functions
  325. %roster = $Con->RosterParse($iq);
  326. %roster = $Con->RosterGet();
  327. $Con->RosterRequest();
  328. $Con->RosterAdd(jid=>"bob\@jabber.org",
  329. name=>"Bob");
  330. $Con->RosterRemove(jid=>"bob@jabber.org");
  331. =head2 RPC Functions
  332. $query = $Con->RPCEncode(type=>"methodCall",
  333. methodName=>"methodName",
  334. params=>[param,param,...]);
  335. $query = $Con->RPCEncode(type=>"methodResponse",
  336. params=>[param,param,...]);
  337. $query = $Con->RPCEncode(type=>"methodResponse",
  338. faultCode=>4,
  339. faultString=>"Too many params");
  340. @response = $Con->RPCParse($iq);
  341. @response = $Con->RPCCall(to=>"dataHouse.jabber.org",
  342. methodname=>"numUsers",
  343. params=>[ param,param,... ]
  344. );
  345. $Con->RPCResponse(to=>"you\@jabber.org",
  346. params=>[ param,param,... ]);
  347. $Con->RPCResponse(to=>"you\@jabber.org",
  348. faultCode=>"4",
  349. faultString=>"Too many parameters"
  350. );
  351. $Con->RPCSetCallBacks(myMethodA=>\&methoda,
  352. myMethodB=>\&do_somthing,
  353. etc...
  354. );
  355. =head2 Search Functions
  356. %fields = $Con->SearchRequest();
  357. %fields = $Con->SearchRequest(to=>"users.jabber.org");
  358. %fields = $Con->SearchRequest(to=>"users.jabber.org",
  359. timeout=>10);
  360. $Con->SearchSend(to=>"somewhere",
  361. name=>"",
  362. first=>"Bob",
  363. last=>"",
  364. nick=>"bob",
  365. email=>"",
  366. key=>"some key");
  367. $Con->SearchSendData("users.jabber.org",
  368. first=>"Bob",
  369. last=>"",
  370. nick=>"bob",
  371. email=>"");
  372. =head2 Time Functions
  373. $Con->TimeQuery();
  374. $Con->TimeQuery(to=>"bob@jabber.org");
  375. %result = $Con->TimeQuery(mode=>"block");
  376. %result = $Con->TimeQuery(to=>"bob@jabber.org",
  377. mode=>"block");
  378. $Con->TimeSend(to=>"bob@jabber.org");
  379. =head2 Version Functions
  380. $Con->VersionQuery();
  381. $Con->VersionQuery(to=>"bob@jabber.org");
  382. %result = $Con->VersionQuery(mode=>"block");
  383. %result = $Con->VersionQuery(to=>"bob@jabber.org",
  384. mode=>"block");
  385. $Con->VersionSend(to=>"bob@jabber.org",
  386. name=>"Net::Jabber",
  387. ver=>"1.0a",
  388. os=>"Perl");
  389. =head2 Multi-User Chat Functions
  390. $Con->MUCJoin(room=>"jabber",
  391. server=>"conference.jabber.org",
  392. nick=>"nick");
  393. $Con->MUCJoin(room=>"jabber",
  394. server=>"conference.jabber.org",
  395. nick=>"nick",
  396. password=>"secret");
  397. =head1 METHODS
  398. =head2 Basic Functions
  399. GetErrorCode() - returns a string that will hopefully contain some
  400. useful information about why a function returned
  401. an undef to you.
  402. SetErrorCode(string) - set a useful error message before you return
  403. an undef to the caller.
  404. SetCallBacks(message=>function, - sets the callback functions for
  405. presence=>function, the top level tags listed. The
  406. iq=>function) available tags to look for are
  407. SetCallBacks(xdb=>function, <message/>, <presence/>, and
  408. db:verify=>function, <iq/>. If a packet is received
  409. db:result=>function) with an ID which is found in the
  410. SetCallBacks(send=>function, registerd ID list (see RegisterID
  411. receive=>function, below) then it is not sent to
  412. update=>function) these functions, instead it
  413. is inserted into a LIST and can
  414. be retrieved by some functions
  415. we will mention later.
  416. send and receive are used to
  417. log what XML is sent and received.
  418. update is used as way to update
  419. your program while waiting for
  420. a packet with an ID to be
  421. returned (useful for GUI apps).
  422. A major change that came with
  423. the last release is that the
  424. session id is passed to the
  425. callback as the first argument.
  426. This was done to facilitate
  427. the Server module.
  428. The next argument depends on
  429. which callback you are talking
  430. about. message, presence, iq,
  431. xdb, db:verify, and db:result
  432. all get passed in Net::Jabber
  433. objects that match those types.
  434. send and receive get passed in
  435. strings. update gets passed
  436. nothing, not even the session id.
  437. If you set the function to undef,
  438. then the callback is removed from
  439. the list.
  440. SetPresenceCallBacks(type=>function - sets the callback functions for
  441. etc...) the specified presence type. The
  442. function takes types as the main
  443. key, and lets you specify a
  444. function for each type of packet
  445. you can get.
  446. "available"
  447. "unavailable"
  448. "subscribe"
  449. "unsubscribe"
  450. "subscribed"
  451. "unsubscribed"
  452. "probe"
  453. "error"
  454. When it gets a <presence/> packet
  455. it checks the type='' for a defined
  456. callback. If there is one then it
  457. calls the function with two
  458. arguments:
  459. the session ID, and the
  460. Net::Jabber::Presence object.
  461. If you set the function to undef,
  462. then the callback is removed from
  463. the list.
  464. NOTE: If you use this, which is a cleaner method,
  465. then you must *NOT* specify a callback for
  466. presence in the SetCallBacks function.
  467. Net::Jabber defines a few default
  468. callbacks for various types:
  469. "subscribe" -
  470. replies with subscribed
  471. "unsubscribe" -
  472. replies with unsubscribed
  473. "subscribed" -
  474. replies with subscribed
  475. "unsubscribed" -
  476. replies with unsubscribed
  477. SetMessageCallBacks(type=>function, - sets the callback functions for
  478. etc...) the specified message type. The
  479. function takes types as the main
  480. key, and lets you specify a
  481. function for each type of packet
  482. you can get.
  483. "normal"
  484. "chat"
  485. "groupchat"
  486. "headline"
  487. "error"
  488. When it gets a <message/> packet
  489. it checks the type='' for a
  490. defined callback. If there is one
  491. then it calls the function with
  492. two arguments:
  493. the session ID, and the
  494. Net::Jabber::Message object.
  495. If you set the function to undef,
  496. then the callback is removed from
  497. the list.
  498. NOTE: If you use this, which is a cleaner method,
  499. then you must *NOT* specify a callback for
  500. message in the SetCallBacks function.
  501. SetIQCallBacks(namespace=>{ - sets the callback functions for
  502. get=>function, the specified namespace. The
  503. set=>function, function takes namespaces as the
  504. result=>function main key, and lets you specify a
  505. }, function for each type of packet
  506. etc...) you can get.
  507. "get"
  508. "set"
  509. "result"
  510. When it gets an <iq/> packet it
  511. checks the type='' and the
  512. xmlns='' for a defined callback.
  513. If there is one then it calls
  514. the function with two arguments:
  515. the session ID, and the
  516. Net::Jabber::xxxx object.
  517. If you set the function to undef,
  518. then the callback is removed from
  519. the list.
  520. NOTE: If you use this, which is a cleaner method,
  521. then you must *NOT* specify a callback for
  522. iq in the SetCallBacks function.
  523. Net::Jabber defines a few default
  524. callbacks for various types and
  525. namespaces:
  526. jabber:iq:last(get) -
  527. replies with the current last
  528. activity
  529. jabber:iq:last(result) -
  530. reformats the <iq/> into a
  531. <message/> and submits it as
  532. if the packet was received.
  533. jabber:iq:rpc(set) -
  534. calls the rpc method and
  535. returns the response
  536. jabber:iq:time(get) -
  537. replys with the current time
  538. jabber:iq:time(result) -
  539. reformats the <iq/> into a
  540. <message/> and submits it as
  541. if the packet was received.
  542. jabber:iq:version(get) -
  543. replys with the info for the
  544. Client/Component (as defined
  545. in the Info function)
  546. jabber:iq:version(result) -
  547. reformats the <iq/> into a
  548. <message/> and submits it as
  549. if the packet was received.
  550. SetXPathCallBacks(xpath=>function, - registers a callback function for
  551. etc...) each xpath specified. If
  552. Net::Jabber matches the xpath,
  553. then it calls the function with
  554. two arguments:
  555. the session ID, and the
  556. Net::Jabber::Message object.
  557. Xpaths are rooted at each packet:
  558. /message[@type="chat"]
  559. /iq/*[xmlns="jabber:iq:roster"][1]
  560. ...
  561. RemoveXPathCallBacks(xpath=>function, - unregisters a callback function
  562. etc...) for each xpath specified.
  563. Info(name=>string, - Set some information so that Net::Jabber
  564. version=>string) can auto-reply to some packets for you to
  565. reduce the work you have to do.
  566. NOTE: This requires that you use the
  567. SetIQCallBacks methodology and not the
  568. SetCallBacks for <iq/> packets.
  569. Process(integer) - takes the timeout period as an argument. If no
  570. timeout is listed then the function blocks until
  571. a packet is received. Otherwise it waits that
  572. number of seconds and then exits so your program
  573. can continue doing useful things. NOTE: This is
  574. important for GUIs. You need to leave time to
  575. process GUI commands even if you are waiting for
  576. packets. The following are the possible return
  577. values, and what they mean:
  578. 1 - Status ok, data received.
  579. 0 - Status ok, no data received.
  580. undef - Status not ok, stop processing.
  581. IMPORTANT: You need to check the output of every
  582. Process. If you get an undef then the connection
  583. died and you should behave accordingly.
  584. Send(object, - takes either a Net::Jabber::xxxxx object or
  585. ignoreActivity) an XML string as an argument and sends it to
  586. Send(string, the server. If you set ignoreActivty to 1,
  587. ignoreActivity) then the XML::Stream module will not record
  588. this packet as couting towards user activity.
  589. =head2 ID Functions
  590. SendWithID(object) - takes either a Net::Jabber::xxxxx object or an
  591. SendWithID(string) XML string as an argument, adds the next
  592. available ID number and sends that packet to
  593. the server. Returns the ID number assigned.
  594. SendAndReceiveWithID(object, - uses SendWithID and WaitForID to
  595. timeout) provide a complete way to send and
  596. SendAndReceiveWithID(string, receive packets with IDs. Can take
  597. timeout) either a Net::Jabber::xxxxx object
  598. or an XML string. Returns the
  599. proper Net::Jabber::xxxxx object
  600. based on the type of packet
  601. received. The timeout is passed
  602. on to WaitForID, see that function
  603. for how the timeout works.
  604. ReceivedID(integer) - returns 1 if a packet has been received with
  605. specified ID, 0 otherwise.
  606. GetID(integer) - returns the proper Net::Jabber::xxxxx object based
  607. on the type of packet received with the specified
  608. ID. If the ID has been received the GetID returns
  609. 0.
  610. WaitForID(integer, - blocks until a packet with the ID is received.
  611. timeout) Returns the proper Net::Jabber::xxxxx object
  612. based on the type of packet received. If the
  613. timeout limit is reached then if the packet
  614. does come in, it will be discarded.
  615. NOTE: Only <iq/> officially support ids, so sending a <message/>, or
  616. <presence/> with an id is a risk. The server will ignore the
  617. id tag and pass it through, so both clients must support the
  618. id tag for these functions to be useful.
  619. =head2 Namespace Functions
  620. DefineNamespace(xmlns=>string, - This function is very complex.
  621. type=>string, It is a little too complex to
  622. functions=>array) discuss within the confines of
  623. this small paragraph. Please
  624. refer to the man page for
  625. Net::Jabber::Namespaces for the
  626. full documentation on this
  627. subject.
  628. =head2 Message Functions
  629. MessageSend(hash) - takes the hash and passes it to SetMessage in
  630. Net::Jabber::Message (refer there for valid
  631. settings). Then it sends the message to the
  632. server.
  633. =head2 Presence Functions
  634. PresenceSend() - no arguments will send an empty
  635. PresenceSend(hash, Presence to the server to tell it
  636. signature=>string) that you are available. If you
  637. provide a hash, then it will pass
  638. that hash to the SetPresence()
  639. function as defined in the
  640. Net::Jabber::Presence module.
  641. Optionally, you can specify a
  642. signature and a jabber:x:signed
  643. will be placed in the <presence/>.
  644. =head2 Subscription Functions
  645. Subscription(hash) - taks the hash and passes it to SetPresence in
  646. Net::Jabber::Presence (refer there for valid
  647. settings). Then it sends the subscription to
  648. server.
  649. The valid types of subscription are:
  650. subscribe - subscribe to JID's presence
  651. unsubscribe - unsubscribe from JID's presence
  652. subscribed - response to a subscribe
  653. unsubscribed - response to an unsubscribe
  654. =head2 Presence DB Functions
  655. PresenceDBParse(Net::Jabber::Presence) - for every presence that you
  656. receive pass the Presence
  657. object to the DB so that
  658. it can track the resources
  659. and priorities for you.
  660. Returns either the presence
  661. passed in, if it not able
  662. to parsed for the DB, or the
  663. current presence as found by
  664. the PresenceDBQuery
  665. function.
  666. PresenceDBDelete(string|Net::Jabber::JID) - delete thes JID entry
  667. from the DB.
  668. PresenceDBClear() - delete all entries in the database.
  669. PresenceDBQuery(string|Net::Jabber::JID) - returns the NJ::Presence
  670. that was last received for
  671. the highest priority of
  672. this JID. You can pass
  673. it a string or a NJ::JID
  674. object.
  675. PresenceDBResources(string|Net::Jabber::JID) - returns an array of
  676. resources in order
  677. from highest priority
  678. to lowest.
  679. =head2 IQ Functions
  680. =head2 Agents Functions
  681. AgentsGet(to=>string, - takes all of the information and
  682. AgentsGet() builds a Net::Jabber::IQ::Agents packet.
  683. It then sends that packet either to the
  684. server, or to the specified transport,
  685. with an ID and waits for that ID to return.
  686. Then it looks in the resulting packet and
  687. builds a hash that contains the values
  688. of the agent list. The hash is layed out
  689. like this: (NOTE: the jid is the key to
  690. distinguish the various agents)
  691. $hash{<JID>}->{order} = 4
  692. ->{name} = "ICQ Transport"
  693. ->{transport} = "ICQ #"
  694. ->{description} = "ICQ..blah.."
  695. ->{service} = "icq"
  696. ->{register} = 1
  697. ->{search} = 1
  698. etc...
  699. The order field determines the order that
  700. it came from the server in... in case you
  701. care. For more info on the valid fields
  702. see the Net::Jabber::Query jabber:iq:agent
  703. namespace.
  704. =head2 Auth Functions
  705. AuthSend(username=>string, - takes all of the information and
  706. password=>string, builds a Net::Jabber::IQ::Auth packet.
  707. resource=>string) It then sends that packet to the
  708. server with an ID and waits for that
  709. ID to return. Then it looks in
  710. resulting packet and determines if
  711. authentication was successful for not.
  712. The array returned from AuthSend looks
  713. like this:
  714. [ type , message ]
  715. If type is "ok" then authentication
  716. was successful, otherwise message
  717. contains a little more detail about the
  718. error.
  719. =head2 Browse Functions
  720. BrowseRequest(jid=>string, - sends a jabber:iq:browse request to
  721. mode=>string, the jid passed as an argument.
  722. timeout=>int) Returns a hash with the resulting
  723. tree if mode is set to "block":
  724. $browse{'category'} = "conference"
  725. $browse{'children'}->[0]
  726. $browse{'children'}->[1]
  727. $browse{'children'}->[11]
  728. $browse{'jid'} = "conference.jabber.org"
  729. $browse{'name'} = "Jabber.org Conferencing Center"
  730. $browse{'ns'}->[0]
  731. $browse{'ns'}->[1]
  732. $browse{'type'} = "public"
  733. The ns array is an array of the
  734. namespaces that this jid supports.
  735. The children array points to hashs
  736. of this form, and represent the fact
  737. that they can be browsed to.
  738. See MODES above for using the mode
  739. and timeout.
  740. =head2 Browse DB Functions
  741. BrowseDBDelete(string|Net::Jabber::JID) - delete thes JID browse
  742. data from the DB.
  743. BrowseDBQuery(jid=>string | NJ::JID, - returns the browse data
  744. timeout=>integer, for the requested JID. If
  745. refresh=>0|1) the DB does not contain
  746. the data for the JID, then
  747. it attempts to fetch the
  748. data via BrowseRequest().
  749. The timeout is passed to
  750. the BrowseRequest() call,
  751. and refresh tells the DB
  752. to request the data, even
  753. if it already has some.
  754. =head2 Bytestreams Functions
  755. ByteStreamsProxyRequest(jid=>string, - sends a bytestreams request
  756. mode=>string, to the jid passed as an
  757. timeout=>int) argument. Returns an array
  758. ref with the resulting tree
  759. if mode is set to "block".
  760. See ByteStreamsProxyParse
  761. for the format of the
  762. resulting tree.
  763. See MODES above for using
  764. the mode and timeout.
  765. ByteStreamsProxyParse(Net::Jabber::Query) - parses the query and
  766. returns an array ref
  767. to the resulting tree:
  768. $host[0]->{jid} = "bytestreams1.proxy.server";
  769. $host[0]->{host} = "proxy1.server";
  770. $host[0]->{port} = "5006";
  771. $host[1]->{jid} = "bytestreams2.proxy.server";
  772. $host[1]->{host} = "proxy2.server";
  773. $host[1]->{port} = "5007";
  774. ...
  775. ByteStreamsProxyActivate(jid=>string, - sends a bytestreams activate
  776. sid=>string, to the jid passed as an
  777. mode=>string, argument. Returns 1 if the
  778. timeout=>int) proxy activated (undef if
  779. it did not) if mode is set
  780. to "block".
  781. sid is the stream id that
  782. is being used to talk about
  783. this stream.
  784. See MODES above for using
  785. the mode and timeout.
  786. ByteStreamsOffer(jid=>string, - sends a bytestreams offer
  787. sid=>string, to the jid passed as an
  788. streamhosts=>arrayref argument. Returns the jid
  789. mode=>string, of the streamhost that the
  790. timeout=>int) user selected if mode is set
  791. to "block".
  792. streamhosts is the same
  793. format as the array ref
  794. returned from
  795. ByteStreamsProxyParse.
  796. See MODES above for using
  797. the mode and timeout.
  798. =head2 Disco Functions
  799. DiscoInfoRequest(jid=>string, - sends a disco#info request to
  800. node=>string, the jid passed as an argument,
  801. mode=>string, and the node if specified.
  802. timeout=>int) Returns a hash with the resulting
  803. tree if mode is set to "block".
  804. See DiscoInfoParse for the format
  805. of the resulting tree.
  806. See MODES above for using the mode
  807. and timeout.
  808. DiscoInfoParse(Net::Jabber::Query) - parses the query and
  809. returns a hash ref
  810. to the resulting tree:
  811. $info{identity}->[0]->{category} = "groupchat";
  812. $info{identity}->[0]->{name} = "Public Chatrooms";
  813. $info{identity}->[0]->{type} = "public";
  814. $info{identity}->[1]->{category} = "groupchat";
  815. $info{identity}->[1]->{name} = "Private Chatrooms";
  816. $info{identity}->[1]->{type} = "private";
  817. $info{feature}->{http://jabber.org/protocol/disco#info} = 1;
  818. $info{feature}->{http://jabber.org/protocol/muc#admin} = 1;
  819. DiscoItemsRequest(jid=>string, - sends a disco#items request to
  820. mode=>string, the jid passed as an argument.
  821. timeout=>int) Returns a hash with the resulting
  822. tree if mode is set to "block".
  823. See DiscoItemsParse for the format
  824. of the resulting tree.
  825. See MODES above for using the mode
  826. and timeout.
  827. DiscoItemsParse(Net::Jabber::Query) - parses the query and
  828. returns a hash ref
  829. to the resulting tree:
  830. $items{jid}->{node} = name;
  831. $items{"proxy.server"}->{""} = "Bytestream Proxy Server";
  832. $items{"conf.server"}->{"public"} = "Public Chatrooms";
  833. $items{"conf.server"}->{"private"} = "Private Chatrooms";
  834. =head2 Feature Negotiation Functions
  835. FeatureNegRequest(jid=>string, - sends a feature negotiation to
  836. features=>hash ref, the jid passed as an argument,
  837. mode=>string, using the features specified.
  838. timeout=>int) Returns a hash with the resulting
  839. tree if mode is set to "block".
  840. See DiscoInfoQuery for the format
  841. of the features hash ref.
  842. See DiscoInfoParse for the format
  843. of the resulting tree.
  844. See MODES above for using the mode
  845. and timeout.
  846. FeatureNegParse(Net::Jabber::Query) - parses the query and
  847. returns a hash ref
  848. to the resulting tree:
  849. $features->{feat1} = ["opt1","opt2",...];
  850. $features->{feat2} = ["optA","optB",...];
  851. ....
  852. If this is a result:
  853. $features->{feat1} = "opt2";
  854. $features->{feat2} = "optA";
  855. ....
  856. FeatureNeqQuery(hash ref) - takes a hash ref and turns it into a
  857. feature negotiation query that you can
  858. AddQuery into your packaet. The format
  859. of the hash ref is as follows:
  860. $features->{feat1} = ["opt1","opt2",...];
  861. $features->{feat2} = ["optA","optB",...];
  862. ....
  863. =head2 File Transfer Functions
  864. FileTransferOffer(jid=>string, - sends a file transfer stream
  865. sid=>string, initiation to the jid passed
  866. filename=>string, as an argument. Returns the
  867. mode=>string, method (if the users accepts),
  868. timeout=>int) undef (if the user declines),
  869. if the mode is set to "block".
  870. See MODES above for using
  871. the mode and timeout.
  872. =head2 Last Functions
  873. LastQuery(to=>string, - asks the jid specified for its last
  874. mode=>string, activity. If the to is blank, then it
  875. timeout=>int) queries the server. Returns a hash with
  876. LastQuery() the various items set if mode is set to
  877. "block":
  878. $last{seconds} - Seconds since activity
  879. $last{message} - Message for activity
  880. See MODES above for using the mode
  881. and timeout.
  882. LastSend(to=>string, - sends the specified last to the specified jid.
  883. hash) the hash is the seconds and message as shown
  884. in the Net::Jabber::Query man page.
  885. LastActivity() - returns the number of seconds since the last activity
  886. by the user.
  887. =head2 IQ::Register Functions
  888. RegisterRequest(to=>string, - send an <iq/> request to the specified
  889. timeout=>int) server/transport, if not specified it
  890. RegisterRequest() sends to the current active server.
  891. The function returns a hash that
  892. contains the required fields. Here
  893. is an example of the hash:
  894. $hash{fields} - The raw fields from
  895. the iq:register.
  896. To be used if there
  897. is no x:data in the
  898. packet.
  899. $hash{instructions} - How to fill out
  900. the form.
  901. $hash{form} - The new dynamic forms.
  902. In $hash{form}, the fields that are
  903. present are the required fields the
  904. server needs.
  905. RegisterSend(hash) - takes the contents of the hash and passes it
  906. to the SetRegister function in the module
  907. Net::Jabber::Query jabber:iq:register namespace.
  908. This function returns an array that looks like
  909. this:
  910. [ type , message ]
  911. If type is "ok" then registration was
  912. successful, otherwise message contains a
  913. little more detail about the error.
  914. RegisterSendData(string|JID, - takes the contents of the hash and
  915. hash) builds a jabebr:x:data return packet
  916. which it sends in a Net::Jabber::Query
  917. jabber:iq:register namespace packet.
  918. The first argument is the JID to send
  919. the packet to. This function returns
  920. an array that looks like this:
  921. [ type , message ]
  922. If type is "ok" then registration was
  923. successful, otherwise message contains
  924. a little more detail about the error.
  925. =head2 IQ::Roster Functions
  926. RosterParse(IQ object) - returns a hash that contains the roster
  927. parsed into the following data structure:
  928. $roster{'bob@jabber.org'}->{name}
  929. - Name you stored in the roster
  930. $roster{'bob@jabber.org'}->{subscription}
  931. - Subscription status
  932. (to, from, both, none)
  933. $roster{'bob@jabber.org'}->{ask}
  934. - The ask status from this user
  935. (subscribe, unsubscribe)
  936. $roster{'bob@jabber.org'}->{groups}
  937. - Array of groups that
  938. bob@jabber.org is in
  939. RosterGet() - sends an empty Net::Jabber::IQ::Roster tag to the
  940. server so the server will send the Roster to the
  941. client. Returns the above hash from RosterParse.
  942. RosterRequest() - sends an empty Net::Jabber::IQ::Roster tag to the
  943. server so the server will send the Roster to the
  944. client.
  945. RosterAdd(hash) - sends a packet asking that the jid be
  946. added to the roster. The hash format
  947. is defined in the SetItem function
  948. in the Net::Jabber::Query jabber:iq:roster
  949. namespace.
  950. RosterRemove(hash) - sends a packet asking that the jid be
  951. removed from the roster. The hash
  952. format is defined in the SetItem function
  953. in the Net::Jabber::Query jabber:iq:roster
  954. namespace.
  955. =head2 IQ::RPC Functions
  956. RPCParse(IQ object) - returns an array. The first argument tells
  957. the status "ok" or "fault". The second
  958. argument is an array if "ok", or a hash if
  959. "fault".
  960. RPCCall(to=>jid|string, - takes the methodName and params,
  961. methodName=>string, builds the RPC calls and sends it
  962. params=>array, to the specified address. Returns
  963. mode=>string, the above data from RPCParse.
  964. timeout=>int)
  965. See MODES above for using the mode
  966. and timeout.
  967. RPCResponse(to=>jid|string, - generates a response back to
  968. params=>array, the caller. If any part of
  969. faultCode=>int, fault is specified, then it
  970. faultString=>string) wins.
  971. Note: To ensure that you get the correct type for a param sent
  972. back, you can specify the type by prepending the type to
  973. the value:
  974. "i4:5" or "int:5"
  975. "boolean:0"
  976. "string:56"
  977. "double:5.0"
  978. "datetime:20020415T11:11:11"
  979. "base64:...."
  980. RPCSetCallBacks(method=>function, - sets the callback functions
  981. method=>function, for the specified methods.
  982. etc...) The method comes from the
  983. <methodName/> and is case
  984. sensitive. The single
  985. arguemnt is a ref to an
  986. array that contains the
  987. <params/>. The function you
  988. write should return one of two
  989. things:
  990. ["ok", [...] ]
  991. The [...] is a list of the
  992. <params/> you want to return.
  993. ["fault", {faultCode=>1,
  994. faultString=>...} ]
  995. If you set the function to undef,
  996. then the method is removed from
  997. the list.
  998. =head2 IQ::Search Functions
  999. SearchRequest(to=>string, - send an <iq/> request to the specified
  1000. mode=>string, server/transport, if not specified it
  1001. timeout=>int) sends to the current active server.
  1002. SearchRequest() The function returns a hash that
  1003. contains the required fields. Here
  1004. is an example of the hash:
  1005. $hash{fields} - The raw fields from
  1006. the iq:register. To
  1007. be used if there is
  1008. no x:data in the
  1009. packet.
  1010. $hash{instructions} - How to fill out
  1011. the form.
  1012. $hash{form} - The new dynamic forms.
  1013. In $hash{form}, the fields that are
  1014. present are the required fields the
  1015. server needs.
  1016. See MODES above for using the mode
  1017. and timeout.
  1018. SearchSend(to=>string|JID, - takes the contents of the hash and
  1019. hash) passes it to the SetSearch function
  1020. in the Net::Jabber::Query
  1021. jabber:iq:search namespace. And then
  1022. sends the packet.
  1023. SearchSendData(string|JID, - takes the contents of the hash and
  1024. hash) builds a jabebr:x:data return packet
  1025. which it sends in a Net::Jabber::Query
  1026. jabber:iq:search namespace packet.
  1027. The first argument is the JID to send
  1028. the packet to.
  1029. =head2 IQ::Time Functions
  1030. TimeQuery(to=>string, - asks the jid specified for its localtime.
  1031. mode=>string, If the to is blank, then it queries the
  1032. timeout=>int) server. Returns a hash with the various
  1033. TimeQuery() items set if mode is set to "block":
  1034. $time{utc} - Time in UTC
  1035. $time{tz} - Timezone
  1036. $time{display} - Display string
  1037. See MODES above for using the mode
  1038. and timeout.
  1039. TimeSend(to=>string) - sends the current UTC time to the specified
  1040. jid.
  1041. =head2 IQ::Version Functions
  1042. VersionQuery(to=>string, - asks the jid specified for its
  1043. mode=>string, client version information. If the
  1044. timeout=>int) to is blank, then it queries the
  1045. VersionQuery() server. Returns ahash with the
  1046. various items set if mode is set to
  1047. "block":
  1048. $version{name} - Name
  1049. $version{ver} - Version
  1050. $version{os} - Operating System/
  1051. Platform
  1052. See MODES above for using the mode
  1053. and timeout.
  1054. VersionSend(to=>string, - sends the specified version information
  1055. name=>string, to the jid specified in the to.
  1056. ver=>string,
  1057. os=>string)
  1058. =head2 Multi-User Chat Functions
  1059. MUCJoin(room=>string, - Sends the appropriate MUC protocol to join
  1060. server=>string, the specified room with the specified nick.
  1061. nick=>string,
  1062. password=>string)
  1063. =head1 AUTHOR
  1064. By Ryan Eatmon in February of 2002 for http://jabber.org
  1065. =head1 COPYRIGHT
  1066. This module is free software; you can redistribute it and/or modify
  1067. it under the same terms as Perl itself.
  1068. =cut
  1069. use strict;
  1070. use Carp;
  1071. use vars qw($VERSION $SOCKS);
  1072. $VERSION = "1.30";
  1073. sub new
  1074. {
  1075. my $proto = shift;
  1076. my $self = { };
  1077. $self->{VERSION} = $VERSION;
  1078. bless($self, $proto);
  1079. return $self;
  1080. }
  1081. ###############################################################################
  1082. #+-----------------------------------------------------------------------------
  1083. #|
  1084. #| Base API
  1085. #|
  1086. #+-----------------------------------------------------------------------------
  1087. ###############################################################################
  1088. ###############################################################################
  1089. #
  1090. # GetErrorCode - if you are returned an undef, you can call this function
  1091. # and hopefully learn more information about the problem.
  1092. #
  1093. ###############################################################################
  1094. sub GetErrorCode
  1095. {
  1096. my $self = shift;
  1097. return ((exists($self->{ERRORCODE}) && ($self->{ERRORCODE} ne "")) ?
  1098. $self->{ERRORCODE} :
  1099. $!
  1100. );
  1101. }
  1102. ###############################################################################
  1103. #
  1104. # SetErrorCode - sets the error code so that the caller can find out more
  1105. # information about the problem
  1106. #
  1107. ###############################################################################
  1108. sub SetErrorCode
  1109. {
  1110. my $self = shift;
  1111. my ($errorcode) = @_;
  1112. $self->{ERRORCODE} = $errorcode;
  1113. }
  1114. ###############################################################################
  1115. #
  1116. # CallBack - Central callback function. If a packet comes back with an ID
  1117. # and the tag and ID have been registered then the packet is not
  1118. # returned as normal, instead it is inserted in the LIST and
  1119. # stored until the user wants to fetch it. If the tag and ID
  1120. # are not registered the function checks if a callback exists
  1121. # for this tag, if it does then that callback is called,
  1122. # otherwise the function drops the packet since it does not know
  1123. # how to handle it.
  1124. #
  1125. ###############################################################################
  1126. sub CallBack
  1127. {
  1128. my $self = shift;
  1129. my $sid = shift;
  1130. my ($object) = @_;
  1131. my $tag;
  1132. my $id;
  1133. my $tree;
  1134. if (ref($object) !~ /^Net::Jabber/)
  1135. {
  1136. if ($self->{DEBUG}->GetLevel() >= 1 || exists($self->{CB}->{receive}))
  1137. {
  1138. my $xml = $object->GetXML();
  1139. $self->{DEBUG}->Log1("CallBack: sid($sid) received($xml)");
  1140. &{$self->{CB}->{receive}}($sid,$xml) if exists($self->{CB}->{receive});
  1141. }
  1142. $tag = $object->get_tag();
  1143. $id = "";
  1144. $id = $object->get_attrib("id")
  1145. if defined($object->get_attrib("id"));
  1146. $tree = $object;
  1147. }
  1148. else
  1149. {
  1150. $tag = $object->GetTag();
  1151. $id = $object->GetID();
  1152. $tree = $object->GetTree();
  1153. }
  1154. $self->{DEBUG}->Log1("CallBack: tag($tag)");
  1155. $self->{DEBUG}->Log1("CallBack: id($id)") if ($id ne "");
  1156. my $pass = 1;
  1157. $pass = 0
  1158. if (!exists($self->{CB}->{$tag}) &&
  1159. !exists($self->{CB}->{XPath}) &&
  1160. !$self->CheckID($tag,$id)
  1161. );
  1162. if ($pass)
  1163. {
  1164. $self->{DEBUG}->Log1("CallBack: we either want it or were waiting for it.");
  1165. my $NJObject;
  1166. if (ref($object) !~ /^Net::Jabber/)
  1167. {
  1168. $NJObject = $self->BuildObject($tag,$object);
  1169. }
  1170. else
  1171. {
  1172. $NJObject = $object;
  1173. }
  1174. if ($NJObject == -1)
  1175. {
  1176. $self->{DEBUG}->Log1("CallBack: DANGER!! DANGER!! We didn't build a packet! We're all gonna die!!");
  1177. }
  1178. else
  1179. {
  1180. if ($self->CheckID($tag,$id))
  1181. {
  1182. $self->{DEBUG}->Log1("CallBack: found registry entry: tag($tag) id($id)");
  1183. $self->DeregisterID($tag,$id);
  1184. if ($self->TimedOutID($id))
  1185. {
  1186. $self->{DEBUG}->Log1("CallBack: dropping packet due to timeout");
  1187. $self->CleanID($id);
  1188. }
  1189. else
  1190. {
  1191. $self->{DEBUG}->Log1("CallBack: they still want it... we still got it...");
  1192. $self->GotID($id,$NJObject);
  1193. }
  1194. }
  1195. else
  1196. {
  1197. $self->{DEBUG}->Log1("CallBack: no registry entry");
  1198. foreach my $xpath (keys(%{$self->{CB}->{XPath}}))
  1199. {
  1200. if ($NJObject->GetTree()->XPathCheck($xpath))
  1201. {
  1202. foreach my $func (keys(%{$self->{CB}->{XPath}->{$xpath}}))
  1203. {
  1204. $self->{DEBUG}->Log1("CallBack: goto xpath($xpath) function($func)");
  1205. &{$self->{CB}->{XPath}->{$xpath}->{$func}}($sid,$NJObject);
  1206. }
  1207. }
  1208. }
  1209. if (exists($self->{CB}->{$tag}))
  1210. {
  1211. $self->{DEBUG}->Log1("CallBack: goto user function($self->{CB}->{$tag})");
  1212. &{$self->{CB}->{$tag}}($sid,$NJObject);
  1213. }
  1214. else
  1215. {
  1216. $self->{DEBUG}->Log1("CallBack: no defined function. Dropping packet.");
  1217. }
  1218. }
  1219. }
  1220. }
  1221. else
  1222. {
  1223. $self->{DEBUG}->Log1("CallBack: a packet that no one wants... how sad. =(");
  1224. }
  1225. }
  1226. ###############################################################################
  1227. #
  1228. # BuildObject - turn the packet into an object.
  1229. #
  1230. ###############################################################################
  1231. sub BuildObject
  1232. {
  1233. my $self = shift;
  1234. my ($tag,$object) = @_;
  1235. my $NJObject = -1;
  1236. if ($tag eq "iq")
  1237. {
  1238. $NJObject = new Net::Jabber::IQ($object);
  1239. }
  1240. elsif ($tag eq "presence")
  1241. {
  1242. $NJObject = new Net::Jabber::Presence($object);
  1243. }
  1244. elsif ($tag eq "message")
  1245. {
  1246. $NJObject = new Net::Jabber::Message($object);
  1247. }
  1248. elsif ($tag eq "xdb")
  1249. {
  1250. $NJObject = new Net::Jabber::XDB($object);
  1251. }
  1252. elsif ($tag eq "db:result")
  1253. {
  1254. $NJObject = new Net::Jabber::Dialback::Result($object);
  1255. }
  1256. elsif ($tag eq "db:verify")
  1257. {
  1258. $NJObject = new Net::Jabber::Dialback::Verify($object);
  1259. }
  1260. return $NJObject;
  1261. }
  1262. ###############################################################################
  1263. #
  1264. # SetCallBacks - Takes a hash with top level tags to look for as the keys
  1265. # and pointers to functions as the values. The functions
  1266. # are called and passed the XML::Parser::Tree objects
  1267. # generated by XML::Stream.
  1268. #
  1269. ###############################################################################
  1270. sub SetCallBacks
  1271. {
  1272. my $self = shift;
  1273. while($#_ >= 0)
  1274. {
  1275. my $func = pop(@_);
  1276. my $tag = pop(@_);
  1277. $self->{DEBUG}->Log1("SetCallBacks: tag($tag) func($func)");
  1278. if (defined($func))
  1279. {
  1280. $self->{CB}->{$tag} = $func;
  1281. }
  1282. else
  1283. {
  1284. delete($self->{CB}->{$tag});
  1285. }
  1286. $self->{STREAM}->SetCallBacks(update=>$func) if ($tag eq "update");
  1287. }
  1288. }
  1289. ###############################################################################
  1290. #
  1291. # SetIQCallBacks - define callbacks for the namespaces inside an iq.
  1292. #
  1293. ###############################################################################
  1294. sub SetIQCallBacks
  1295. {
  1296. my $self = shift;
  1297. while($#_ >= 0)
  1298. {
  1299. my $hash = pop(@_);
  1300. my $namespace = pop(@_);
  1301. foreach my $type (keys(%{$hash}))
  1302. {
  1303. if (defined($hash->{$type}))
  1304. {
  1305. $self->{CB}->{IQns}->{$namespace}->{$type} = $hash->{$type};
  1306. }
  1307. else
  1308. {
  1309. delete($self->{CB}->{IQns}->{$namespace}->{$type});
  1310. }
  1311. }
  1312. }
  1313. }
  1314. ###############################################################################
  1315. #
  1316. # SetPresenceCallBacks - define callbacks for the different presence packets.
  1317. #
  1318. ###############################################################################
  1319. sub SetPresenceCallBacks
  1320. {
  1321. my $self = shift;
  1322. my (%types) = @_;
  1323. foreach my $type (keys(%types))
  1324. {
  1325. if (defined($types{$type}))
  1326. {
  1327. $self->{CB}->{Pres}->{$type} = $types{$type};
  1328. }
  1329. else
  1330. {
  1331. delete($self->{CB}->{Pres}->{$type});
  1332. }
  1333. }
  1334. }
  1335. ###############################################################################
  1336. #
  1337. # SetMessageCallBacks - define callbacks for the different message packets.
  1338. #
  1339. ###############################################################################
  1340. sub SetMessageCallBacks
  1341. {
  1342. my $self = shift;
  1343. my (%types) = @_;
  1344. foreach my $type (keys(%types))
  1345. {
  1346. if (defined($types{$type}))
  1347. {
  1348. $self->{CB}->{Mess}->{$type} = $types{$type};
  1349. }
  1350. else
  1351. {
  1352. delete($self->{CB}->{Mess}->{$type});
  1353. }
  1354. }
  1355. }
  1356. ###############################################################################
  1357. #
  1358. # SetXPathCallBacks - define callbacks for packets based on XPath.
  1359. #
  1360. ###############################################################################
  1361. sub SetXPathCallBacks
  1362. {
  1363. my $self = shift;
  1364. my (%xpaths) = @_;
  1365. foreach my $xpath (keys(%xpaths))
  1366. {
  1367. $self->{DEBUG}->Log1("SetXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
  1368. $self->{CB}->{XPath}->{$xpath}->{$xpaths{$xpath}} = $xpaths{$xpath};
  1369. }
  1370. }
  1371. ###############################################################################
  1372. #
  1373. # RemoveXPathCallBacks - remove callbacks for packets based on XPath.
  1374. #
  1375. ###############################################################################
  1376. sub RemoveXPathCallBacks
  1377. {
  1378. my $self = shift;
  1379. my (%xpaths) = @_;
  1380. foreach my $xpath (keys(%xpaths))
  1381. {
  1382. $self->{DEBUG}->Log1("RemoveXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
  1383. delete($self->{CB}->{XPath}->{$xpath}->{$xpaths{$xpath}});
  1384. }
  1385. }
  1386. ###############################################################################
  1387. #
  1388. # Info - set the base information about this Jabber Client/Component for
  1389. # use in a default response.
  1390. #
  1391. ###############################################################################
  1392. sub Info
  1393. {
  1394. my $self = shift;
  1395. my %args;
  1396. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  1397. foreach my $arg (keys(%args))
  1398. {
  1399. $self->{INFO}->{$arg} = $args{$arg};
  1400. }
  1401. }
  1402. ###############################################################################
  1403. #
  1404. # Send - Takes either XML or a Net::Jabber::xxxx object and sends that
  1405. # packet to the server.
  1406. #
  1407. ###############################################################################
  1408. sub Send
  1409. {
  1410. my $self = shift;
  1411. my $object = shift;
  1412. my $ignoreActivity = shift;
  1413. $ignoreActivity = 0 unless defined($ignoreActivity);
  1414. if (ref($object) eq "")
  1415. {
  1416. $self->SendXML($object,$ignoreActivity);
  1417. }
  1418. else
  1419. {
  1420. $self->SendXML($object->GetXML(),$ignoreActivity);
  1421. }
  1422. }
  1423. ###############################################################################
  1424. #
  1425. # SendXML - Sends the XML packet to the server
  1426. #
  1427. ###############################################################################
  1428. sub SendXML
  1429. {
  1430. my $self = shift;
  1431. my $xml = shift;
  1432. my $ignoreActivity = shift;
  1433. $ignoreActivity = 0 unless defined($ignoreActivity);
  1434. $self->{DEBUG}->Log1("SendXML: sent($xml)");
  1435. &{$self->{CB}->{send}}($self->{SESSION}->{id},$xml) if exists($self->{CB}->{send});
  1436. $self->{STREAM}->IgnoreActivity($self->{SESSION}->{id},$ignoreActivity);
  1437. $self->{STREAM}->Send($self->{SESSION}->{id},$xml);
  1438. $self->{STREAM}->IgnoreActivity($self->{SESSION}->{id},0);
  1439. }
  1440. ###############################################################################
  1441. #
  1442. # SendWithID - Take either XML or a Net::Jabber::xxxx object and send it
  1443. # with the next available ID number. Then return that ID so
  1444. # the client can track it.
  1445. #
  1446. ###############################################################################
  1447. sub SendWithID
  1448. {
  1449. my $self = shift;
  1450. my ($object) = @_;
  1451. #--------------------------------------------------------------------------
  1452. # Take the current XML stream and insert an id attrib at the top level.
  1453. #--------------------------------------------------------------------------
  1454. my $id = $self->UniqueID();
  1455. $self->{DEBUG}->Log1("SendWithID: id($id)");
  1456. my $xml;
  1457. if (ref($object) eq "")
  1458. {
  1459. $self->{DEBUG}->Log1("SendWithID: in($object)");
  1460. $xml = $object;
  1461. $xml =~ s/^(\<[^\>]+)(\>)/$1 id\=\'$id\'$2/;
  1462. my ($tag) = ($xml =~ /^\<(\S+)\s/);
  1463. $self->RegisterID($tag,$id);
  1464. }
  1465. else
  1466. {
  1467. $self->{DEBUG}->Log1("SendWithID: in(",$object->GetXML(),")");
  1468. $object->SetID($id);
  1469. $xml = $object->GetXML();
  1470. $self->RegisterID($object->GetTag(),$id);
  1471. }
  1472. $self->{DEBUG}->Log1("SendWithID: out($xml)");
  1473. #--------------------------------------------------------------------------
  1474. # Send the new XML string.
  1475. #--------------------------------------------------------------------------
  1476. $self->SendXML($xml);
  1477. #--------------------------------------------------------------------------
  1478. # Return the ID number we just assigned.
  1479. #--------------------------------------------------------------------------
  1480. return $id;
  1481. }
  1482. ###############################################################################
  1483. #
  1484. # UniqueID - Increment and return a new unique ID.
  1485. #
  1486. ###############################################################################
  1487. sub UniqueID
  1488. {
  1489. my $self = shift;
  1490. my $id_num = $self->{LIST}->{currentID};
  1491. $self->{LIST}->{currentID}++;
  1492. return "netjabber-$id_num";
  1493. }
  1494. ###############################################################################
  1495. #
  1496. # SendAndReceiveWithID - Take either XML or a Net::Jabber::xxxxx object and
  1497. # send it with the next ID. Then wait for that ID
  1498. # to come back and return the response in a
  1499. # Net::Jabber::xxxx object.
  1500. #
  1501. ###############################################################################
  1502. sub SendAndReceiveWithID
  1503. {
  1504. my $self = shift;
  1505. my ($object,$timeout) = @_;
  1506. &{$self->{CB}->{startwait}}() if exists($self->{CB}->{startwait});
  1507. $self->{DEBUG}->Log1("SendAndReceiveWithID: object($object)");
  1508. my $id = $self->SendWithID($object);
  1509. $self->{DEBUG}->Log1("SendAndReceiveWithID: sent with id($id)");
  1510. my $packet = $self->WaitForID($id,$timeout);
  1511. &{$self->{CB}->{endwait}}() if exists($self->{CB}->{endwait});
  1512. return $packet;
  1513. }
  1514. ###############################################################################
  1515. #
  1516. # ReceivedID - returns 1 if a packet with the ID has been received, or 0
  1517. # if it has not.
  1518. #
  1519. ###############################################################################
  1520. sub ReceivedID
  1521. {
  1522. my $self = shift;
  1523. my ($id) = @_;
  1524. $self->{DEBUG}->Log1("ReceivedID: id($id)");
  1525. return 1 if exists($self->{LIST}->{$id});
  1526. $self->{DEBUG}->Log1("ReceivedID: nope...");
  1527. return 0;
  1528. }
  1529. ###############################################################################
  1530. #
  1531. # GetID - Return the Net::Jabber::xxxxx object that is stored in the LIST
  1532. # that matches the ID if that ID exists. Otherwise return 0.
  1533. #
  1534. ###############################################################################
  1535. sub GetID
  1536. {
  1537. my $self = shift;
  1538. my ($id) = @_;
  1539. $self->{DEBUG}->Log1("GetID: id($id)");
  1540. return $self->{LIST}->{$id} if $self->ReceivedID($id);
  1541. $self->{DEBUG}->Log1("GetID: haven't gotten that id yet...");
  1542. return 0;
  1543. }
  1544. ###############################################################################
  1545. #
  1546. # CleanID - Delete the list entry for this id since we don't want a leak.
  1547. #
  1548. ###############################################################################
  1549. sub CleanID
  1550. {
  1551. my $self = shift;
  1552. my ($id) = @_;
  1553. $self->{DEBUG}->Log1("CleanID: id($id)");
  1554. delete($self->{LIST}->{$id});
  1555. }
  1556. ###############################################################################
  1557. #
  1558. # WaitForID - Keep looping and calling Process(1) to poll every second
  1559. # until the response from the server occurs.
  1560. #
  1561. ###############################################################################
  1562. sub WaitForID
  1563. {
  1564. my $self = shift;
  1565. my ($id,$timeout) = @_;
  1566. $timeout = "300" unless defined($timeout);
  1567. $self->{DEBUG}->Log1("WaitForID: id($id)");
  1568. my $endTime = time + $timeout;
  1569. while(!$self->ReceivedID($id) && ($endTime >= time))
  1570. {
  1571. $self->{DEBUG}->Log1("WaitForID: haven't gotten it yet... let's wait for more packets");
  1572. return unless (defined($self->Process(1)));
  1573. &{$self->{CB}->{update}}() if exists($self->{CB}->{update});
  1574. }
  1575. if (!$self->ReceivedID($id))
  1576. {
  1577. $self->TimeoutID($id);
  1578. $self->{DEBUG}->Log1("WaitForID: timed out...");
  1579. return;
  1580. }
  1581. else
  1582. {
  1583. $self->{DEBUG}->Log1("WaitForID: we got it!");
  1584. my $packet = $self->GetID($id);
  1585. $self->CleanID($id);
  1586. return $packet;
  1587. }
  1588. }
  1589. ###############################################################################
  1590. #
  1591. # GotID - Callback to store the Net::Jabber::xxxxx object in the LIST at
  1592. # the ID index. This is a private helper function.
  1593. #
  1594. ###############################################################################
  1595. sub GotID
  1596. {
  1597. my $self = shift;
  1598. my ($id,$object) = @_;
  1599. $self->{DEBUG}->Log1("GotID: id($id) xml(",$object->GetXML(),")");
  1600. $self->{LIST}->{$id} = $object;
  1601. }
  1602. ###############################################################################
  1603. #
  1604. # CheckID - Checks the ID registry if this tag and ID have been registered.
  1605. # 0 = no, 1 = yes
  1606. #
  1607. ###############################################################################
  1608. sub CheckID
  1609. {
  1610. my $self = shift;
  1611. my ($tag,$id) = @_;
  1612. $id = "" unless defined($id);
  1613. $self->{DEBUG}->Log1("CheckID: tag($tag) id($id)");
  1614. return 0 if ($id eq "");
  1615. $self->{DEBUG}->Log1("CheckID: we have that here somewhere...");
  1616. return exists($self->{IDRegistry}->{$tag}->{$id});
  1617. }
  1618. ###############################################################################
  1619. #
  1620. # TimeoutID - Timeout the tag and ID in the registry so that the CallBack
  1621. # can know what to put in the ID list and what to pass on.
  1622. #
  1623. ###############################################################################
  1624. sub TimeoutID
  1625. {
  1626. my $self = shift;
  1627. my ($id) = @_;
  1628. $self->{DEBUG}->Log1("TimeoutID: id($id)");
  1629. $self->{LIST}->{$id} = 0;
  1630. }
  1631. ###############################################################################
  1632. #
  1633. # TimedOutID - Timeout the tag and ID in the registry so that the CallBack
  1634. # can know what to put in the ID list and what to pass on.
  1635. #
  1636. ###############################################################################
  1637. sub TimedOutID
  1638. {
  1639. my $self = shift;
  1640. my ($id) = @_;
  1641. return (exists($self->{LIST}->{$id}) && ($self->{LIST}->{$id} == 0));
  1642. }
  1643. ###############################################################################
  1644. #
  1645. # RegisterID - Register the tag and ID in the registry so that the CallBack
  1646. # can know what to put in the ID list and what to pass on.
  1647. #
  1648. ###############################################################################
  1649. sub RegisterID
  1650. {
  1651. my $self = shift;
  1652. my ($tag,$id) = @_;
  1653. $self->{DEBUG}->Log1("RegisterID: tag($tag) id($id)");
  1654. $self->{IDRegistry}->{$tag}->{$id} = 1;
  1655. }
  1656. ###############################################################################
  1657. #
  1658. # DeregisterID - Delete the tag and ID in the registry so that the CallBack
  1659. # can knows that it has been received.
  1660. #
  1661. ###############################################################################
  1662. sub DeregisterID
  1663. {
  1664. my $self = shift;
  1665. my ($tag,$id) = @_;
  1666. $self->{DEBUG}->Log1("DeregisterID: tag($tag) id($id)");
  1667. delete($self->{IDRegistry}->{$tag}->{$id});
  1668. }
  1669. ###############################################################################
  1670. #
  1671. # DefineNamespace - adds the namespace and corresponding functions onto the
  1672. # of available functions based on namespace.
  1673. #
  1674. ###############################################################################
  1675. sub DefineNamespace
  1676. {
  1677. my $self = shift;
  1678. my %args;
  1679. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  1680. croak("You must specify xmlns=>'' for the function call to DefineNamespace")
  1681. if !exists($args{xmlns});
  1682. croak("You must specify type=>'' for the function call to DefineNamespace")
  1683. if !exists($args{type});
  1684. croak("You must specify functions=>'' for the function call to DefineNamespace")
  1685. if !exists($args{functions});
  1686. eval("delete(\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}}) if exists(\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}})");
  1687. foreach my $function (@{$args{functions}})
  1688. {
  1689. my %tempHash = %{$function};
  1690. my %funcHash;
  1691. foreach my $func (keys(%tempHash))
  1692. {
  1693. $funcHash{ucfirst(lc($func))} = $tempHash{$func};
  1694. }
  1695. croak("You must specify name=>'' for each function in call to DefineNamespace")
  1696. if !exists($funcHash{Name});
  1697. my $name = delete($funcHash{Name});
  1698. if (!exists($funcHash{Set}) && exists($funcHash{Get}))
  1699. {
  1700. croak("The DefineNamespace arugments have changed, and I cannot determine the\nnew values automatically for name($name). Please read the man page\nfor Net::Jabber::Namespaces. I apologize for this incompatability.\n");
  1701. }
  1702. if (exists($funcHash{Type}) || exists($funcHash{Path}) ||
  1703. exists($funcHash{Child}) || exists($funcHash{Calls}))
  1704. {
  1705. foreach my $type (keys(%funcHash))
  1706. {
  1707. eval("\$Net::Jabber::$args{type}::NAMESPACES{'$args{xmlns}'}->{'$name'}->{XPath}->{'$type'} = \$funcHash{'$type'};");
  1708. }
  1709. next;
  1710. }
  1711. my $type = $funcHash{Set}->[0];
  1712. my $xpath = $funcHash{Set}->[1];
  1713. if (exists($funcHash{Hash}))
  1714. {
  1715. $xpath = "text()" if ($funcHash{Hash} eq "data");
  1716. $xpath .= "/text()" if ($funcHash{Hash} eq "child-data");
  1717. $xpath = "\@$xpath" if ($funcHash{Hash} eq "att");
  1718. $xpath = "$1/\@$2" if ($funcHash{Hash} =~ /^att-(\S+)-(.+)$/);
  1719. }
  1720. if ($type eq "master")
  1721. {
  1722. eval("\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}}->{\$name}->{XPath}->{Type} = 'master';");
  1723. next;
  1724. }
  1725. if ($type eq "scalar")
  1726. {
  1727. eval("\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}}->{\$name}->{XPath}->{Path} = '$xpath';");
  1728. next;
  1729. }
  1730. if ($type eq "flag")
  1731. {
  1732. eval("\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}}->{\$name}->{XPath}->{Type} = 'flag';");
  1733. eval("\$Net::Jabber::$args{type}::NAMESPACES{\$args{xmlns}}->{\$name}->{XPath}->{Path} = '$xpath';");
  1734. next;
  1735. }
  1736. if (($funcHash{Hash} eq "child-add") && exists($funcHash{Add}))
  1737. {
  1738. eval("\$Net::Jabber::$args{type}::NAMESPACES{'$args{xmlns}'}->{'$name'}->{XPath}->{Type} = 'node';");
  1739. eval("\$Net::Jabber::$args{type}::NAMESPACES{'$args{xmlns}'}->{'$name'}->{XPath}->{Path} = \$funcHash{Add}->[3];");
  1740. eval("\$Net::Jabber::$args{type}::NAMESPACES{'$args{xmlns}'}->{'$name'}->{XPath}->{Child} = [\$funcHash{Add}->[0],\$funcHash{Add}->[1]];");
  1741. eval("\$Net::Jabber::$args{type}::NAMESPACES{'$args{xmlns}'}->{'$name'}->{XPath}->{Calls} = ['Add'];");
  1742. next;
  1743. }
  1744. }
  1745. }
  1746. ###############################################################################
  1747. #
  1748. # MessageSend - Takes the same hash that Net::Jabber::Message->SetMessage
  1749. # takes and sends the message to the server.
  1750. #
  1751. ###############################################################################
  1752. sub MessageSend
  1753. {
  1754. my $self = shift;
  1755. my $mess = new Net::Jabber::Message();
  1756. $mess->SetMessage(@_);
  1757. $self->Send($mess);
  1758. }
  1759. ###############################################################################
  1760. #
  1761. # PresenceDBParse - adds the presence information to the Presence DB so
  1762. # you can keep track of the current state of the JID and
  1763. # all of it's resources.
  1764. #
  1765. ###############################################################################
  1766. sub PresenceDBParse
  1767. {
  1768. my $self = shift;
  1769. my ($presence) = @_;
  1770. my $type = $presence->GetType();
  1771. $type = "" unless defined($type);
  1772. return $presence unless (($type eq "") ||
  1773. ($type eq "available") ||
  1774. ($type eq "unavailable"));
  1775. my $fromJID = $presence->GetFrom("jid");
  1776. my $fromID = $fromJID->GetJID();
  1777. $fromID = "" unless defined($fromID);
  1778. my $resource = $fromJID->GetResource();
  1779. $resource = " " unless ($resource ne "");
  1780. my $priority = $presence->GetPriority();
  1781. $priority = 0 unless defined($priority);
  1782. $self->{DEBUG}->Log1("PresenceDBParse: fromJID(",$fromJID->GetJID("full"),") resource($resource) priority($priority) type($type)");
  1783. $self->{DEBUG}->Log2("PresenceDBParse: xml(",$presence->GetXML(),")");
  1784. if (exists($self->{PRESENCEDB}->{$fromID}))
  1785. {
  1786. my $oldPriority = $self->{PRESENCEDB}->{$fromID}->{resources}->{$resource};
  1787. $oldPriority = "" unless defined($oldPriority);
  1788. my $loc = 0;
  1789. foreach my $index (0..$#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}})
  1790. {
  1791. $loc = $index
  1792. if ($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}->[$index]->{resource} eq $resource);
  1793. }
  1794. splice(@{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}},$loc,1);
  1795. delete($self->{PRESENCEDB}->{$fromID}->{resources}->{$resource});
  1796. delete($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority})
  1797. if (exists($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}) &&
  1798. ($#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}} == -1));
  1799. delete($self->{PRESENCEDB}->{$fromID})
  1800. if (scalar(keys(%{$self->{PRESENCEDB}->{$fromID}})) == 0);
  1801. $self->{DEBUG}->Log1("PresenceDBParse: remove ",$fromJID->GetJID("full")," from the DB");
  1802. }
  1803. if (($type eq "") || ($type eq "available"))
  1804. {
  1805. my $loc = -1;
  1806. foreach my $index (0..$#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}}) {
  1807. $loc = $index
  1808. if ($self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$index]->{resource} eq $resource);
  1809. }
  1810. $loc = $#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}}+1
  1811. if ($loc == -1);
  1812. $self->{PRESENCEDB}->{$fromID}->{resources}->{$resource} = $priority;
  1813. $self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$loc]->{presence} =
  1814. $presence;
  1815. $self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$loc]->{resource} =
  1816. $resource;
  1817. $self->{DEBUG}->Log1("PresenceDBParse: add ",$fromJID->GetJID("full")," to the DB");
  1818. }
  1819. my $currentPresence = $self->PresenceDBQuery($fromJID);
  1820. return (defined($currentPresence) ? $currentPresence : $presence);
  1821. }
  1822. ###############################################################################
  1823. #
  1824. # PresenceDBDelete - delete the JID from the DB completely.
  1825. #
  1826. ###############################################################################
  1827. sub PresenceDBDelete
  1828. {
  1829. my $self = shift;
  1830. my ($jid) = @_;
  1831. my $indexJID = $jid;
  1832. $indexJID = $jid->GetJID() if (ref($jid) eq "Net::Jabber::JID");
  1833. return if !exists($self->{PRESENCEDB}->{$indexJID});
  1834. delete($self->{PRESENCEDB}->{$indexJID});
  1835. $self->{DEBUG}->Log1("PresenceDBDelete: delete ",$indexJID," from the DB");
  1836. }
  1837. ###############################################################################
  1838. #
  1839. # PresenceDBClear - delete all of the JIDs from the DB completely.
  1840. #
  1841. ###############################################################################
  1842. sub PresenceDBClear
  1843. {
  1844. my $self = shift;
  1845. $self->{DEBUG}->Log1("PresenceDBClear: clearing the database");
  1846. foreach my $indexJID (keys(%{$self->{PRESENCEDB}}))
  1847. {
  1848. delete($self->{PRESENCEDB}->{$indexJID});
  1849. $self->{DEBUG}->Log3("PresenceDBClear: deleting ",$indexJID," from the DB");
  1850. }
  1851. $self->{DEBUG}->Log3("PresenceDBClear: database is empty");
  1852. }
  1853. ###############################################################################
  1854. #
  1855. # PresenceDBQuery - retrieve the last Net::Jabber::Presence received with
  1856. # the highest priority.
  1857. #
  1858. ###############################################################################
  1859. sub PresenceDBQuery
  1860. {
  1861. my $self = shift;
  1862. my ($jid) = @_;
  1863. my $indexJID = $jid;
  1864. $indexJID = $jid->GetJID() if (ref($jid) eq "Net::Jabber::JID");
  1865. return if !exists($self->{PRESENCEDB}->{$indexJID});
  1866. return if (scalar(keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}})) == 0);
  1867. my $highPriority =
  1868. (sort {$b cmp $a} keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}}))[0];
  1869. return $self->{PRESENCEDB}->{$indexJID}->{priorities}->{$highPriority}->[0]->{presence};
  1870. }
  1871. ###############################################################################
  1872. #
  1873. # PresenceDBResources - returns a list of the resources from highest
  1874. # priority to lowest.
  1875. #
  1876. ###############################################################################
  1877. sub PresenceDBResources
  1878. {
  1879. my $self = shift;
  1880. my ($jid) = @_;
  1881. my $indexJID = $jid;
  1882. $indexJID = $jid->GetJID() if (ref($jid) eq "Net::Jabber::JID");
  1883. my @resources;
  1884. return if !exists($self->{PRESENCEDB}->{$indexJID});
  1885. foreach my $priority (sort {$b cmp $a} keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}}))
  1886. {
  1887. foreach my $index (0..$#{$self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}})
  1888. {
  1889. next if ($self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}->[$index]->{resource} eq " ");
  1890. push(@resources,$self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}->[$index]->{resource});
  1891. }
  1892. }
  1893. return @resources;
  1894. }
  1895. ###############################################################################
  1896. #
  1897. # PresenceSend - Sends a presence tag to announce your availability
  1898. #
  1899. ###############################################################################
  1900. sub PresenceSend
  1901. {
  1902. my $self = shift;
  1903. my %args;
  1904. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  1905. $args{ignoreactivity} = 0 unless exists($args{ignoreactivity});
  1906. my $ignoreActivity = delete($args{ignoreactivity});
  1907. my $presence = new Net::Jabber::Presence();
  1908. if (exists($args{signature}))
  1909. {
  1910. my $xSigned = $presence->NewX("jabber:x:signed");
  1911. $xSigned->SetSigned(signature=>delete($args{signature}));
  1912. }
  1913. $presence->SetPresence(%args);
  1914. $self->Send($presence,$ignoreActivity);
  1915. return $presence;
  1916. }
  1917. ###############################################################################
  1918. #
  1919. # PresenceProbe - Sends a presence probe to the server
  1920. #
  1921. ###############################################################################
  1922. sub PresenceProbe
  1923. {
  1924. my $self = shift;
  1925. my %args;
  1926. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  1927. delete($args{type});
  1928. my $presence = new Net::Jabber::Presence();
  1929. $presence->SetPresence(type=>"probe",
  1930. %args);
  1931. $self->Send($presence);
  1932. }
  1933. ###############################################################################
  1934. #
  1935. # Subscription - Sends a presence tag to perform the subscription on the
  1936. # specified JID.
  1937. #
  1938. ###############################################################################
  1939. sub Subscription
  1940. {
  1941. my $self = shift;
  1942. my $presence = new Net::Jabber::Presence();
  1943. $presence->SetPresence(@_);
  1944. $self->Send($presence);
  1945. }
  1946. ###############################################################################
  1947. #
  1948. # AgentsGet - Sends an empty IQ to the server/transport to request that the
  1949. # list of supported Agents be sent to them. Returns a hash
  1950. # containing the values for the agents.
  1951. #
  1952. ###############################################################################
  1953. sub AgentsGet
  1954. {
  1955. my $self = shift;
  1956. my $iq = new Net::Jabber::IQ();
  1957. $iq->SetIQ(@_);
  1958. $iq->SetIQ(type=>"get");
  1959. my $query = $iq->NewQuery("jabber:iq:agents");
  1960. $iq = $self->SendAndReceiveWithID($iq);
  1961. return unless defined($iq);
  1962. $query = $iq->GetQuery();
  1963. my @agents = $query->GetAgents();
  1964. my %agents;
  1965. my $count = 0;
  1966. foreach my $agent (@agents)
  1967. {
  1968. my $jid = $agent->GetJID();
  1969. $agents{$jid}->{name} = $agent->GetName();
  1970. $agents{$jid}->{description} = $agent->GetDescription();
  1971. $agents{$jid}->{transport} = $agent->GetTransport();
  1972. $agents{$jid}->{service} = $agent->GetService();
  1973. $agents{$jid}->{register} = $agent->DefinedRegister();
  1974. $agents{$jid}->{search} = $agent->DefinedSearch();
  1975. $agents{$jid}->{groupchat} = $agent->DefinedGroupChat();
  1976. $agents{$jid}->{agents} = $agent->DefinedAgents();
  1977. $agents{$jid}->{order} = $count++;
  1978. }
  1979. return %agents;
  1980. }
  1981. ###############################################################################
  1982. #
  1983. # AuthSend - This is a self contained function to send a login iq tag with
  1984. # an id. Then wait for a reply what the same id to come back
  1985. # and tell the caller what the result was.
  1986. #
  1987. ###############################################################################
  1988. sub AuthSend
  1989. {
  1990. my $self = shift;
  1991. my %args;
  1992. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  1993. carp("AuthSend requires a username arguement")
  1994. unless exists($args{username});
  1995. carp("AuthSend requires a password arguement")
  1996. unless exists($args{password});
  1997. if (exists($self->{SESSION}->{version}) &&
  1998. ($self->{SESSION}->{version} ne ""))
  1999. {
  2000. return $self->AuthSASL(%args);
  2001. }
  2002. carp("AuthSend requires a resource arguement")
  2003. unless exists($args{resource});
  2004. my $authType = "digest";
  2005. my $token;
  2006. my $sequence;
  2007. #--------------------------------------------------------------------------
  2008. # First let's ask the sever what all is available in terms of auth types.
  2009. # If we get an error, then all we can do is digest or plain.
  2010. #--------------------------------------------------------------------------
  2011. my $iqAuthProbe = new Net::Jabber::IQ();
  2012. $iqAuthProbe->SetIQ(type=>"get");
  2013. my $iqAuthProbeQuery = $iqAuthProbe->NewQuery("jabber:iq:auth");
  2014. $iqAuthProbeQuery->SetUsername($args{username});
  2015. $iqAuthProbe = $self->SendAndReceiveWithID($iqAuthProbe);
  2016. return unless defined($iqAuthProbe);
  2017. return ( $iqAuthProbe->GetErrorCode() , $iqAuthProbe->GetError() )
  2018. if ($iqAuthProbe->GetType() eq "error");
  2019. if ($iqAuthProbe->GetType() eq "error")
  2020. {
  2021. $authType = "digest";
  2022. }
  2023. else
  2024. {
  2025. $iqAuthProbeQuery = $iqAuthProbe->GetQuery();
  2026. $authType = "plain" if $iqAuthProbeQuery->DefinedPassword();
  2027. $authType = "digest" if $iqAuthProbeQuery->DefinedDigest();
  2028. $authType = "zerok" if ($iqAuthProbeQuery->DefinedSequence() &&
  2029. $iqAuthProbeQuery->DefinedToken());
  2030. $token = $iqAuthProbeQuery->GetToken() if ($authType eq "zerok");
  2031. $sequence = $iqAuthProbeQuery->GetSequence() if ($authType eq "zerok");
  2032. }
  2033. delete($args{digest});
  2034. delete($args{type});
  2035. #--------------------------------------------------------------------------
  2036. # 0k authenticaion (http://core.jabber.org/0k.html)
  2037. #
  2038. # Tell the server that we want to connect this way, the server sends back
  2039. # a token and a sequence number. We take that token + the password and
  2040. # SHA1 it. Then we SHA1 it sequence number more times and send that hash.
  2041. # The server SHA1s that hash one more time and compares it to the hash it
  2042. # stored last time. IF they match, we are in and it stores the hash we sent
  2043. # for the next time and decreases the sequence number, else, no go.
  2044. #--------------------------------------------------------------------------
  2045. if ($authType eq "zerok")
  2046. {
  2047. my $hashA = Digest::SHA1::sha1_hex(delete($args{password}));
  2048. $args{hash} = Digest::SHA1::sha1_hex($hashA.$token);
  2049. for (1..$sequence)
  2050. {
  2051. $args{hash} = Digest::SHA1::sha1_hex($args{hash});
  2052. }
  2053. }
  2054. #--------------------------------------------------------------------------
  2055. # If we have access to the SHA-1 digest algorithm then let's use it.
  2056. # Remove the password from the hash, create the digest, and put the
  2057. # digest in the hash instead.
  2058. #
  2059. # Note: Concat the Session ID and the password and then digest that
  2060. # string to get the server to accept the digest.
  2061. #--------------------------------------------------------------------------
  2062. if ($authType eq "digest")
  2063. {
  2064. my $password = delete($args{password});
  2065. $args{digest} = Digest::SHA1::sha1_hex($self->{SESSION}->{id}.$password);
  2066. }
  2067. #--------------------------------------------------------------------------
  2068. # Create a Net::Jabber::IQ object to send to the server
  2069. #--------------------------------------------------------------------------
  2070. my $iqLogin = new Net::Jabber::IQ();
  2071. $iqLogin->SetIQ(type=>"set");
  2072. my $iqAuth = $iqLogin->NewQuery("jabber:iq:auth");
  2073. $iqAuth->SetAuth(%args);
  2074. #--------------------------------------------------------------------------
  2075. # Send the IQ with the next available ID and wait for a reply with that
  2076. # id to be received. Then grab the IQ reply.
  2077. #--------------------------------------------------------------------------
  2078. $iqLogin = $self->SendAndReceiveWithID($iqLogin);
  2079. #--------------------------------------------------------------------------
  2080. # From the reply IQ determine if we were successful or not. If yes then
  2081. # return "". If no then return error string from the reply.
  2082. #--------------------------------------------------------------------------
  2083. return unless defined($iqLogin);
  2084. return ( $iqLogin->GetErrorCode() , $iqLogin->GetError() )
  2085. if ($iqLogin->GetType() eq "error");
  2086. return ("ok","");
  2087. }
  2088. ###############################################################################
  2089. #
  2090. # AuthSASL - This is a helper function to perform all of the required steps for
  2091. # doing SASL with the server.
  2092. #
  2093. ###############################################################################
  2094. sub AuthSASL
  2095. {
  2096. my $self = shift;
  2097. my (%args) = @_;
  2098. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2099. carp("AuthSASL requires a username arguement")
  2100. unless exists($args{username});
  2101. carp("AuthSASL requires a password arguement")
  2102. unless exists($args{password});
  2103. $args{resource} = "" unless exists($args{resource});
  2104. my $sid = $self->{SESSION}->{id};
  2105. my $status =
  2106. $self->{STREAM}->SASLClient($sid,
  2107. $args{username},
  2108. $args{password}
  2109. );
  2110. $args{timeout} = "120" unless exists($args{timeout});
  2111. my $endTime = time + $args{timeout};
  2112. while(!$self->{STREAM}->SASLClientDone($sid) && ($endTime >= time))
  2113. {
  2114. $self->{DEBUG}->Log1("AuthSASL: haven't authed yet... let's wait.");
  2115. return unless (defined($self->Process(1)));
  2116. &{$self->{CB}->{update}}() if exists($self->{CB}->{update});
  2117. }
  2118. if (!$self->{STREAM}->SASLClientDone($sid))
  2119. {
  2120. $self->{DEBUG}->Log1("AuthSASL: timed out...");
  2121. return( "system","SASL timed out authenticating");
  2122. }
  2123. if (!$self->{STREAM}->SASLClientAuthed($sid))
  2124. {
  2125. $self->{DEBUG}->Log1("AuthSASL: Authentication failed.");
  2126. return ( "error", $self->{STREAM}->SASLClientError($sid));
  2127. }
  2128. $self->{DEBUG}->Log1("AuthSASL: We authed!");
  2129. $self->{SESSION} = $self->{STREAM}->OpenStream($sid);
  2130. $sid = $self->{SESSION}->{id};
  2131. $self->{DEBUG}->Log1("AuthSASL: We got a new session. sid($sid)");
  2132. my $bind = $self->{STREAM}->GetStreamFeature($sid,"xmpp-bind");
  2133. if ($bind)
  2134. {
  2135. $self->{DEBUG}->Log1("AuthSASL: Binding to resource");
  2136. $self->BindResource($args{resource});
  2137. }
  2138. my $session = $self->{STREAM}->GetStreamFeature($sid,"xmpp-session");
  2139. if ($session)
  2140. {
  2141. $self->{DEBUG}->Log1("AuthSASL: Starting session");
  2142. $self->StartSession();
  2143. }
  2144. return ("ok","");
  2145. }
  2146. ##############################################################################
  2147. #
  2148. # BindResource - bind to a resource
  2149. #
  2150. ##############################################################################
  2151. sub BindResource
  2152. {
  2153. my $self = shift;
  2154. my $resource = shift;
  2155. $self->{DEBUG}->Log2("BindResource: Binding to resource");
  2156. my $iq = new Net::Jabber::IQ();
  2157. $iq->SetIQ(type=>"set");
  2158. my $bind = $iq->NewQuery(&XML::Stream::ConstXMLNS("xmpp-bind"));
  2159. if (defined($resource) && ($resource ne ""))
  2160. {
  2161. $self->{DEBUG}->Log2("BindResource: resource($resource)");
  2162. $bind->SetBind(resource=>$resource);
  2163. }
  2164. my $result = $self->SendAndReceiveWithID($iq);
  2165. }
  2166. ##############################################################################
  2167. #
  2168. # StartSession - Initialize a session
  2169. #
  2170. ##############################################################################
  2171. sub StartSession
  2172. {
  2173. my $self = shift;
  2174. my $iq = new Net::Jabber::IQ();
  2175. $iq->SetIQ(type=>"set");
  2176. my $session = $iq->NewQuery(&XML::Stream::ConstXMLNS("xmpp-session"));
  2177. my $result = $self->SendAndReceiveWithID($iq);
  2178. }
  2179. ###############################################################################
  2180. #
  2181. # BrowseRequest - requests the browse information from the specified JID.
  2182. #
  2183. ###############################################################################
  2184. sub BrowseRequest
  2185. {
  2186. my $self = shift;
  2187. my %args;
  2188. $args{mode} = "block";
  2189. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2190. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2191. my $iq = new Net::Jabber::IQ();
  2192. $iq->SetIQ(to=>$args{jid},
  2193. type=>"get");
  2194. my $query = $iq->NewQuery("jabber:iq:browse");
  2195. #--------------------------------------------------------------------------
  2196. # Send the IQ with the next available ID and wait for a reply with that
  2197. # id to be received. Then grab the IQ reply.
  2198. #--------------------------------------------------------------------------
  2199. if ($args{mode} eq "passthru")
  2200. {
  2201. my $id = $self->UniqueID();
  2202. $iq->SetIQ(id=>$id);
  2203. $self->Send($iq);
  2204. return $id;
  2205. }
  2206. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2207. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2208. #--------------------------------------------------------------------------
  2209. # Check if there was an error.
  2210. #--------------------------------------------------------------------------
  2211. return unless defined($iq);
  2212. if ($iq->GetType() eq "error")
  2213. {
  2214. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2215. return;
  2216. }
  2217. $query = $iq->GetQuery();
  2218. if (defined($query))
  2219. {
  2220. my %browse = %{$self->BrowseParse($query)};
  2221. return %browse;
  2222. }
  2223. else
  2224. {
  2225. return;
  2226. }
  2227. }
  2228. ###############################################################################
  2229. #
  2230. # BrowseParse - helper function for BrowseRequest to convert the object
  2231. # tree into a hash for better consumption.
  2232. #
  2233. ###############################################################################
  2234. sub BrowseParse
  2235. {
  2236. my $self = shift;
  2237. my $item = shift;
  2238. my %browse;
  2239. if ($item->DefinedCategory())
  2240. {
  2241. $browse{category} = $item->GetCategory();
  2242. }
  2243. else
  2244. {
  2245. $browse{category} = $item->GetTag();
  2246. }
  2247. $browse{type} = $item->GetType();
  2248. $browse{name} = $item->GetName();
  2249. $browse{jid} = $item->GetJID();
  2250. $browse{ns} = [ $item->GetNS() ];
  2251. foreach my $subitem ($item->GetItems())
  2252. {
  2253. my ($subbrowse) = $self->BrowseParse($subitem);
  2254. push(@{$browse{children}},$subbrowse);
  2255. }
  2256. return \%browse;
  2257. }
  2258. ###############################################################################
  2259. #
  2260. # BrowseDBDelete - delete the JID from the DB completely.
  2261. #
  2262. ###############################################################################
  2263. sub BrowseDBDelete
  2264. {
  2265. my $self = shift;
  2266. my ($jid) = @_;
  2267. my $indexJID = $jid;
  2268. $indexJID = $jid->GetJID() if (ref($jid) eq "Net::Jabber::JID");
  2269. return if !exists($self->{BROWSEDB}->{$indexJID});
  2270. delete($self->{BROWSEDB}->{$indexJID});
  2271. $self->{DEBUG}->Log1("BrowseDBDelete: delete ",$indexJID," from the DB");
  2272. }
  2273. ###############################################################################
  2274. #
  2275. # BrowseDBQuery - retrieve the last Net::Jabber::Browse received with
  2276. # the highest priority.
  2277. #
  2278. ###############################################################################
  2279. sub BrowseDBQuery
  2280. {
  2281. my $self = shift;
  2282. my %args;
  2283. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2284. $args{timeout} = 10 unless exists($args{timeout});
  2285. my $indexJID = $args{jid};
  2286. $indexJID = $args{jid}->GetJID() if (ref($args{jid}) eq "Net::Jabber::JID");
  2287. if ((exists($args{refresh}) && ($args{refresh} eq "1")) ||
  2288. (!exists($self->{BROWSEDB}->{$indexJID})))
  2289. {
  2290. my %browse = $self->BrowseRequest(jid=>$args{jid},
  2291. timeout=>$args{timeout});
  2292. $self->{BROWSEDB}->{$indexJID} = \%browse;
  2293. }
  2294. return %{$self->{BROWSEDB}->{$indexJID}};
  2295. }
  2296. ###############################################################################
  2297. #
  2298. # ByteStreamsProxyRequest - This queries a proxy server to get a list of
  2299. #
  2300. ###############################################################################
  2301. sub ByteStreamsProxyRequest
  2302. {
  2303. my $self = shift;
  2304. my %args;
  2305. $args{mode} = "block";
  2306. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2307. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2308. my $iq = new Net::Jabber::IQ();
  2309. $iq->SetIQ(to=>$args{jid},
  2310. type=>"get");
  2311. my $query = $iq->NewQuery("http://jabber.org/protocol/bytestreams");
  2312. #--------------------------------------------------------------------------
  2313. # Send the IQ with the next available ID and wait for a reply with that
  2314. # id to be received. Then grab the IQ reply.
  2315. #--------------------------------------------------------------------------
  2316. if ($args{mode} eq "passthru")
  2317. {
  2318. my $id = $self->UniqueID();
  2319. $iq->SetIQ(id=>$id);
  2320. $self->Send($iq);
  2321. return $id;
  2322. }
  2323. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2324. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2325. #--------------------------------------------------------------------------
  2326. # Check if there was an error.
  2327. #--------------------------------------------------------------------------
  2328. return unless defined($iq);
  2329. if ($iq->GetType() eq "error")
  2330. {
  2331. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2332. return;
  2333. }
  2334. $query = $iq->GetQuery();
  2335. if (defined($query))
  2336. {
  2337. my @hosts = @{$self->ByteStreamsProxyParse($query)};
  2338. return @hosts;
  2339. }
  2340. else
  2341. {
  2342. return;
  2343. }
  2344. }
  2345. ###############################################################################
  2346. #
  2347. # ByteStreamsProxyParse - helper function for ByteStreamProxyRequest to convert
  2348. # the object tree into a hash for better consumption.
  2349. #
  2350. ###############################################################################
  2351. sub ByteStreamsProxyParse
  2352. {
  2353. my $self = shift;
  2354. my $item = shift;
  2355. my @hosts;
  2356. foreach my $host ($item->GetStreamHosts())
  2357. {
  2358. my %host;
  2359. $host{jid} = $host->GetJID();
  2360. $host{host} = $host->GetHost() if $host->DefinedHost();
  2361. $host{port} = $host->GetPort() if $host->DefinedPort();
  2362. $host{zeroconf} = $host->GetZeroConf() if $host->DefinedZeroConf();
  2363. push(@hosts,\%host);
  2364. }
  2365. return \@hosts;
  2366. }
  2367. ###############################################################################
  2368. #
  2369. # ByteStreamsProxyActivate - This tells a proxy to activate the connection
  2370. #
  2371. ###############################################################################
  2372. sub ByteStreamsProxyActivate
  2373. {
  2374. my $self = shift;
  2375. my %args;
  2376. $args{mode} = "block";
  2377. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2378. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2379. my $iq = new Net::Jabber::IQ();
  2380. $iq->SetIQ(to=>$args{jid},
  2381. type=>"set");
  2382. my $query = $iq->NewQuery("http://jabber.org/protocol/bytestreams");
  2383. $query->SetByteStreams(sid=>$args{sid},
  2384. activate=>(ref($args{recipient}) eq "Net::Jabber::JID" ? $args{recipient}->GetJID("full") : $args{recipient})
  2385. );
  2386. #--------------------------------------------------------------------------
  2387. # Send the IQ with the next available ID and wait for a reply with that
  2388. # id to be received. Then grab the IQ reply.
  2389. #--------------------------------------------------------------------------
  2390. if ($args{mode} eq "passthru")
  2391. {
  2392. my $id = $self->UniqueID();
  2393. $iq->SetIQ(id=>$id);
  2394. $self->Send($iq);
  2395. return $id;
  2396. }
  2397. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2398. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2399. #--------------------------------------------------------------------------
  2400. # Check if there was an error.
  2401. #--------------------------------------------------------------------------
  2402. return unless defined($iq);
  2403. if ($iq->GetType() eq "error")
  2404. {
  2405. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2406. return;
  2407. }
  2408. return 1;
  2409. }
  2410. ###############################################################################
  2411. #
  2412. # ByteStreamsOffer - This offers a recipient a list of stream hosts to pick
  2413. # from.
  2414. #
  2415. ###############################################################################
  2416. sub ByteStreamsOffer
  2417. {
  2418. my $self = shift;
  2419. my %args;
  2420. $args{mode} = "block";
  2421. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2422. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2423. my $iq = new Net::Jabber::IQ();
  2424. $iq->SetIQ(to=>$args{jid},
  2425. type=>"set");
  2426. my $query = $iq->NewQuery("http://jabber.org/protocol/bytestreams");
  2427. $query->SetByteStreams(sid=>$args{sid});
  2428. foreach my $host (@{$args{streamhosts}})
  2429. {
  2430. $query->AddStreamHost(jid=>$host->{jid},
  2431. (exists($host->{host}) ? (host=>$host->{host}) : ()),
  2432. (exists($host->{port}) ? (port=>$host->{port}) : ()),
  2433. (exists($host->{zeroconf}) ? (zeroconf=>$host->{zeroconf}) : ()),
  2434. );
  2435. }
  2436. #--------------------------------------------------------------------------
  2437. # Send the IQ with the next available ID and wait for a reply with that
  2438. # id to be received. Then grab the IQ reply.
  2439. #--------------------------------------------------------------------------
  2440. if ($args{mode} eq "passthru")
  2441. {
  2442. my $id = $self->UniqueID();
  2443. $iq->SetIQ(id=>$id);
  2444. $self->Send($iq);
  2445. return $id;
  2446. }
  2447. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2448. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2449. #--------------------------------------------------------------------------
  2450. # Check if there was an error.
  2451. #--------------------------------------------------------------------------
  2452. return unless defined($iq);
  2453. if ($iq->GetType() eq "error")
  2454. {
  2455. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2456. return;
  2457. }
  2458. $query = $iq->GetQuery();
  2459. if (defined($query))
  2460. {
  2461. return $query->GetStreamHostUsedJID();
  2462. }
  2463. else
  2464. {
  2465. return;
  2466. }
  2467. }
  2468. ###############################################################################
  2469. #
  2470. # DiscoInfoRequest - requests the disco information from the specified JID.
  2471. #
  2472. ###############################################################################
  2473. sub DiscoInfoRequest
  2474. {
  2475. my $self = shift;
  2476. my %args;
  2477. $args{mode} = "block";
  2478. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2479. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2480. my $iq = new Net::Jabber::IQ();
  2481. $iq->SetIQ(to=>$args{jid},
  2482. type=>"get");
  2483. my $query = $iq->NewQuery("http://jabber.org/protocol/disco#info");
  2484. $query->SetDiscoInfo(node=>$args{node}) if exists($args{node});
  2485. #--------------------------------------------------------------------------
  2486. # Send the IQ with the next available ID and wait for a reply with that
  2487. # id to be received. Then grab the IQ reply.
  2488. #--------------------------------------------------------------------------
  2489. if ($args{mode} eq "passthru")
  2490. {
  2491. my $id = $self->UniqueID();
  2492. $iq->SetIQ(id=>$id);
  2493. $self->Send($iq);
  2494. return $id;
  2495. }
  2496. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2497. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2498. #--------------------------------------------------------------------------
  2499. # Check if there was an error.
  2500. #--------------------------------------------------------------------------
  2501. return unless defined($iq);
  2502. if ($iq->GetType() eq "error")
  2503. {
  2504. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2505. return;
  2506. }
  2507. return unless $iq->DefinedQuery();
  2508. $query = $iq->GetQuery();
  2509. return %{$self->DiscoInfoParse($query)};
  2510. }
  2511. ###############################################################################
  2512. #
  2513. # DiscoInfoParse - helper function for DiscoInfoRequest to convert the object
  2514. # tree into a hash for better consumption.
  2515. #
  2516. ###############################################################################
  2517. sub DiscoInfoParse
  2518. {
  2519. my $self = shift;
  2520. my $item = shift;
  2521. my %disco;
  2522. foreach my $ident ($item->GetIdentities())
  2523. {
  2524. my %identity;
  2525. $identity{category} = $ident->GetCategory();
  2526. $identity{name} = $ident->GetName();
  2527. $identity{type} = $ident->GetType();
  2528. push(@{$disco{identity}},\%identity);
  2529. }
  2530. foreach my $feat ($item->GetFeatures())
  2531. {
  2532. $disco{feature}->{$feat->GetVar()} = 1;
  2533. }
  2534. return \%disco;
  2535. }
  2536. ###############################################################################
  2537. #
  2538. # DiscoItemsRequest - requests the disco information from the specified JID.
  2539. #
  2540. ###############################################################################
  2541. sub DiscoItemsRequest
  2542. {
  2543. my $self = shift;
  2544. my %args;
  2545. $args{mode} = "block";
  2546. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2547. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2548. my $iq = new Net::Jabber::IQ();
  2549. $iq->SetIQ(to=>$args{jid},
  2550. type=>"get");
  2551. my $query = $iq->NewQuery("http://jabber.org/protocol/disco#items");
  2552. #--------------------------------------------------------------------------
  2553. # Send the IQ with the next available ID and wait for a reply with that
  2554. # id to be received. Then grab the IQ reply.
  2555. #--------------------------------------------------------------------------
  2556. if ($args{mode} eq "passthru")
  2557. {
  2558. my $id = $self->UniqueID();
  2559. $iq->SetIQ(id=>$id);
  2560. $self->Send($iq);
  2561. return $id;
  2562. }
  2563. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2564. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2565. #--------------------------------------------------------------------------
  2566. # Check if there was an error.
  2567. #--------------------------------------------------------------------------
  2568. return unless defined($iq);
  2569. if ($iq->GetType() eq "error")
  2570. {
  2571. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2572. return;
  2573. }
  2574. $query = $iq->GetQuery();
  2575. if (defined($query))
  2576. {
  2577. my %disco = %{$self->DiscoItemsParse($query)};
  2578. return %disco;
  2579. }
  2580. else
  2581. {
  2582. return;
  2583. }
  2584. }
  2585. ###############################################################################
  2586. #
  2587. # DiscoItemsParse - helper function for DiscoItemsRequest to convert the object
  2588. # tree into a hash for better consumption.
  2589. #
  2590. ###############################################################################
  2591. sub DiscoItemsParse
  2592. {
  2593. my $self = shift;
  2594. my $item = shift;
  2595. my %disco;
  2596. foreach my $item ($item->GetItems())
  2597. {
  2598. $disco{$item->GetJID()}->{$item->GetNode()} = $item->GetName();
  2599. }
  2600. return \%disco;
  2601. }
  2602. ###############################################################################
  2603. #
  2604. # FeatureNegRequest - requests a feature negotiation from the specified JID.
  2605. #
  2606. ###############################################################################
  2607. sub FeatureNegRequest
  2608. {
  2609. my $self = shift;
  2610. my %args;
  2611. $args{mode} = "block";
  2612. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2613. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2614. my $iq = new Net::Jabber::IQ();
  2615. $iq->SetIQ(to=>$args{jid},
  2616. type=>"get");
  2617. my $query = $self->FeatureNegQuery($args{features});
  2618. $iq->AddQuery($query);
  2619. #--------------------------------------------------------------------------
  2620. # Send the IQ with the next available ID and wait for a reply with that
  2621. # id to be received. Then grab the IQ reply.
  2622. #--------------------------------------------------------------------------
  2623. if ($args{mode} eq "passthru")
  2624. {
  2625. my $id = $self->UniqueID();
  2626. $iq->SetIQ(id=>$id);
  2627. $self->Send($iq);
  2628. return $id;
  2629. }
  2630. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2631. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2632. #--------------------------------------------------------------------------
  2633. # Check if there was an error.
  2634. #--------------------------------------------------------------------------
  2635. return unless defined($iq);
  2636. if ($iq->GetType() eq "error")
  2637. {
  2638. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2639. return;
  2640. }
  2641. $query = $iq->GetQuery();
  2642. if (defined($query))
  2643. {
  2644. my %feats = %{$self->FeatureNegParse($query)};
  2645. return %feats;
  2646. }
  2647. else
  2648. {
  2649. return;
  2650. }
  2651. }
  2652. #xxx fneg needs to reutrn a type='submit' on the x:data in a result
  2653. ###############################################################################
  2654. #
  2655. # FeatureNegQuery - given a feature hash, return a query that contains it.
  2656. #
  2657. ###############################################################################
  2658. sub FeatureNegQuery
  2659. {
  2660. my $self = shift;
  2661. my $features = shift;
  2662. my $tag = "query";
  2663. $tag = $Net::Jabber::Query::TAGS{'http://jabber.org/protocol/feature-neg'}
  2664. if exists($Net::Jabber::Query::TAGS{'http://jabber.org/protocol/feature-neg'});
  2665. my $query = new Net::Jabber::Query($tag);
  2666. $query->SetXMLNS("http://jabber.org/protocol/feature-neg");
  2667. my $xdata = $query->NewX("jabber:x:data");
  2668. foreach my $feature (keys(%{$features}))
  2669. {
  2670. my $field = $xdata->AddField(type=>"list-single",
  2671. var=>$feature);
  2672. foreach my $value (@{$features->{$feature}})
  2673. {
  2674. $field->AddOption(value=>$value);
  2675. }
  2676. }
  2677. return $query;
  2678. }
  2679. ###############################################################################
  2680. #
  2681. # FeatureNegParse - helper function for FeatureNegRequest to convert the object
  2682. # tree into a hash for better consumption.
  2683. #
  2684. ###############################################################################
  2685. sub FeatureNegParse
  2686. {
  2687. my $self = shift;
  2688. my $item = shift;
  2689. my %feats;
  2690. my $xdata = $item->GetX("jabber:x:data");
  2691. foreach my $field ($xdata->GetFields())
  2692. {
  2693. my @options;
  2694. foreach my $option ($field->GetOptions())
  2695. {
  2696. push(@options,$option->GetValue());
  2697. }
  2698. if ($#options == -1)
  2699. {
  2700. $feats{$field->GetVar()} = $field->GetValue();
  2701. }
  2702. else
  2703. {
  2704. $feats{$field->GetVar()} = \@options;
  2705. }
  2706. }
  2707. return \%feats;
  2708. }
  2709. #XXX - need a feature-neg answer function...
  2710. ###############################################################################
  2711. #
  2712. # FileTransferOffer - offer a file transfer JEP-95
  2713. #
  2714. ###############################################################################
  2715. sub FileTransferOffer
  2716. {
  2717. my $self = shift;
  2718. my %args;
  2719. $args{mode} = "block";
  2720. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2721. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2722. my $iq = new Net::Jabber::IQ();
  2723. $iq->SetIQ(to=>$args{jid},
  2724. type=>"set");
  2725. my $query = $iq->NewQuery("http://jabber.org/protocol/si");
  2726. my $profile = $query->NewQuery("http://jabber.org/protocol/si/profile/file-transfer");
  2727. # XXX support hashing via MD5
  2728. # XXX support date via JEP-82
  2729. my ($filename) = ($args{filename} =~ /\/?([^\/]+)$/);
  2730. $profile->SetFile(name=>$filename,
  2731. size=>(-s $args{filename})
  2732. );
  2733. $profile->SetFile(desc=>$args{desc}) if exists($args{desc});
  2734. $query->SetStream(mimetype=>(-B $args{filename} ?
  2735. "application/octect-stream" :
  2736. "text/plain"
  2737. ),
  2738. id=>$args{sid},
  2739. profile=>"http://jabber.org/protocol/si/profile/file-transfer"
  2740. );
  2741. if (!exists($args{skip_methods}))
  2742. {
  2743. if ($#{$args{methods}} == -1)
  2744. {
  2745. print STDERR "You did not provide any valid methods for file transfer.\n";
  2746. return;
  2747. }
  2748. my $fneg = $self->FeatureNegQuery({'stream-method'=>$args{methods}});
  2749. $query->AddQuery($fneg);
  2750. }
  2751. #--------------------------------------------------------------------------
  2752. # Send the IQ with the next available ID and wait for a reply with that
  2753. # id to be received. Then grab the IQ reply.
  2754. #--------------------------------------------------------------------------
  2755. if ($args{mode} eq "passthru")
  2756. {
  2757. my $id = $self->UniqueID();
  2758. $iq->SetIQ(id=>$id);
  2759. $self->Send($iq);
  2760. return $id;
  2761. }
  2762. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2763. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2764. #--------------------------------------------------------------------------
  2765. # Check if there was an error.
  2766. #--------------------------------------------------------------------------
  2767. return unless defined($iq);
  2768. if ($iq->GetType() eq "error")
  2769. {
  2770. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2771. return;
  2772. }
  2773. $query = $iq->GetQuery();
  2774. if (defined($query))
  2775. {
  2776. my @fneg = $query->GetQuery("http://jabber.org/protocol/feature-neg");
  2777. my @xdata = $fneg[0]->GetX("jabber:x:data");
  2778. my @fields = $xdata[0]->GetFields();
  2779. return $fields[0]->GetValue();
  2780. # XXX need better error handling
  2781. }
  2782. else
  2783. {
  2784. return;
  2785. }
  2786. }
  2787. ###############################################################################
  2788. #
  2789. # TreeTransferOffer - offer a file transfer JEP-95
  2790. #
  2791. ###############################################################################
  2792. sub TreeTransferOffer
  2793. {
  2794. my $self = shift;
  2795. my %args;
  2796. $args{mode} = "block";
  2797. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2798. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2799. my $iq = new Net::Jabber::IQ();
  2800. $iq->SetIQ(to=>$args{jid},
  2801. type=>"set");
  2802. my $query = $iq->NewQuery("http://jabber.org/protocol/si");
  2803. my $profile = $query->NewQuery("http://jabber.org/protocol/si/profile/tree-transfer");
  2804. my ($root) = ($args{directory} =~ /\/?([^\/]+)$/);
  2805. my $rootDir = $profile->AddDirectory(name=>$root);
  2806. my %tree;
  2807. $tree{counter} = 0;
  2808. $self->TreeTransferDescend($args{sidbase},
  2809. $args{directory},
  2810. $rootDir,
  2811. \%tree
  2812. );
  2813. $profile->SetTree(numfiles=>$tree{counter},
  2814. size=>$tree{size}
  2815. );
  2816. $query->SetStream(id=>$args{sidbase},
  2817. profile=>"http://jabber.org/protocol/si/profile/tree-transfer"
  2818. );
  2819. if ($#{$args{methods}} == -1)
  2820. {
  2821. print STDERR "You did not provide any valid methods for the tree transfer.\n";
  2822. return;
  2823. }
  2824. my $fneg = $self->FeatureNegQuery({'stream-method'=>$args{methods}});
  2825. $query->AddQuery($fneg);
  2826. #--------------------------------------------------------------------------
  2827. # Send the IQ with the next available ID and wait for a reply with that
  2828. # id to be received. Then grab the IQ reply.
  2829. #--------------------------------------------------------------------------
  2830. if ($args{mode} eq "passthru")
  2831. {
  2832. my $id = $self->UniqueID();
  2833. $iq->SetIQ(id=>$id);
  2834. $self->Send($iq);
  2835. $tree{id} = $id;
  2836. return %tree;
  2837. }
  2838. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2839. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2840. #--------------------------------------------------------------------------
  2841. # Check if there was an error.
  2842. #--------------------------------------------------------------------------
  2843. return unless defined($iq);
  2844. if ($iq->GetType() eq "error")
  2845. {
  2846. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  2847. return;
  2848. }
  2849. $query = $iq->GetQuery();
  2850. if (defined($query))
  2851. {
  2852. my @fneg = $query->GetQuery("http://jabber.org/protocol/feature-neg");
  2853. my @xdata = $fneg[0]->GetX("jabber:x:data");
  2854. my @fields = $xdata[0]->GetFields();
  2855. return $fields[0]->GetValue();
  2856. # XXX need better error handling
  2857. }
  2858. else
  2859. {
  2860. return;
  2861. }
  2862. }
  2863. ###############################################################################
  2864. #
  2865. # TreeTransferDescend - descend a directory structure and build the packet.
  2866. #
  2867. ###############################################################################
  2868. sub TreeTransferDescend
  2869. {
  2870. my $self = shift;
  2871. my $sidbase = shift;
  2872. my $path = shift;
  2873. my $parent = shift;
  2874. my $tree = shift;
  2875. $tree->{size} += (-s $path);
  2876. opendir(DIR, $path);
  2877. foreach my $file ( sort {$a cmp $b} readdir(DIR) )
  2878. {
  2879. next if ($file =~ /^\.\.?$/);
  2880. if (-d "$path/$file")
  2881. {
  2882. my $tempParent = $parent->AddDirectory(name=>$file);
  2883. $self->TreeTransferDescend($sidbase,
  2884. "$path/$file",
  2885. $tempParent,
  2886. $tree
  2887. );
  2888. }
  2889. else
  2890. {
  2891. $tree->{size} += (-s "$path/$file");
  2892. $tree->{tree}->{"$path/$file"}->{order} = $tree->{counter};
  2893. $tree->{tree}->{"$path/$file"}->{sid} =
  2894. $sidbase."-".$tree->{counter};
  2895. $tree->{tree}->{"$path/$file"}->{name} = $file;
  2896. $parent->AddFile(name=>$tree->{tree}->{"$path/$file"}->{name},
  2897. sid=>$tree->{tree}->{"$path/$file"}->{sid});
  2898. $tree->{counter}++;
  2899. }
  2900. }
  2901. closedir(DIR);
  2902. }
  2903. ###############################################################################
  2904. #
  2905. # LastQuery - Sends an iq:last query to either the server or the specified
  2906. # JID.
  2907. #
  2908. ###############################################################################
  2909. sub LastQuery
  2910. {
  2911. my $self = shift;
  2912. my %args;
  2913. $args{mode} = "passthru";
  2914. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2915. $args{waitforid} = 0 unless exists($args{waitforid});
  2916. my $waitforid = delete($args{waitforid});
  2917. $args{mode} = "block" if $waitforid;
  2918. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2919. my $iq = new Net::Jabber::IQ();
  2920. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  2921. $iq->SetIQ(type=>'get');
  2922. my $last = $iq->NewQuery("jabber:iq:last");
  2923. if ($args{mode} eq "passthru")
  2924. {
  2925. my $id = $self->UniqueID();
  2926. $iq->SetIQ(id=>$id);
  2927. $self->Send($iq);
  2928. return $id;
  2929. }
  2930. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  2931. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  2932. return unless defined($iq);
  2933. $last = $iq->GetQuery();
  2934. return unless defined($last);
  2935. return $last->GetLast();
  2936. }
  2937. ###############################################################################
  2938. #
  2939. # LastSend - sends an iq:last packet to the specified user.
  2940. #
  2941. ###############################################################################
  2942. sub LastSend
  2943. {
  2944. my $self = shift;
  2945. my %args;
  2946. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2947. $args{ignoreactivity} = 0 unless exists($args{ignoreactivity});
  2948. my $ignoreActivity = delete($args{ignoreactivity});
  2949. my $iq = new Net::Jabber::IQ();
  2950. $iq->SetIQ(to=>delete($args{to}),
  2951. type=>'result');
  2952. my $last = $iq->NewQuery("jabber:iq:last");
  2953. $last->SetLast(%args);
  2954. $self->Send($iq,$ignoreActivity);
  2955. }
  2956. ###############################################################################
  2957. #
  2958. # LastActivity - returns number of seconds since the last activity.
  2959. #
  2960. ###############################################################################
  2961. sub LastActivity
  2962. {
  2963. my $self = shift;
  2964. return (time - $self->{STREAM}->LastActivity($self->{SESSION}->{id}));
  2965. }
  2966. ###############################################################################
  2967. #
  2968. # RegisterRequest - This is a self contained function to send an iq tag
  2969. # an id that requests the target address to send back
  2970. # the required fields. It waits for a reply what the
  2971. # same id to come back and tell the caller what the
  2972. # fields are.
  2973. #
  2974. ###############################################################################
  2975. sub RegisterRequest
  2976. {
  2977. my $self = shift;
  2978. my %args;
  2979. $args{mode} = "block";
  2980. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  2981. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  2982. #--------------------------------------------------------------------------
  2983. # Create a Net::Jabber::IQ object to send to the server
  2984. #--------------------------------------------------------------------------
  2985. my $iq = new Net::Jabber::IQ();
  2986. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  2987. $iq->SetIQ(type=>"get");
  2988. my $query = $iq->NewQuery("jabber:iq:register");
  2989. #--------------------------------------------------------------------------
  2990. # Send the IQ with the next available ID and wait for a reply with that
  2991. # id to be received. Then grab the IQ reply.
  2992. #--------------------------------------------------------------------------
  2993. if ($args{mode} eq "passthru")
  2994. {
  2995. my $id = $self->UniqueID();
  2996. $iq->SetIQ(id=>$id);
  2997. $self->Send($iq);
  2998. return $id;
  2999. }
  3000. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  3001. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  3002. #--------------------------------------------------------------------------
  3003. # Check if there was an error.
  3004. #--------------------------------------------------------------------------
  3005. return unless defined($iq);
  3006. if ($iq->GetType() eq "error")
  3007. {
  3008. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  3009. return;
  3010. }
  3011. my %register;
  3012. #--------------------------------------------------------------------------
  3013. # From the reply IQ determine what fields are required and send a hash
  3014. # back with the fields and any values that are already defined (like key)
  3015. #--------------------------------------------------------------------------
  3016. $query = $iq->GetQuery();
  3017. $register{fields} = { $query->GetRegister() };
  3018. #--------------------------------------------------------------------------
  3019. # Get any forms so that we have the option of showing a nive dynamic form
  3020. # to the user and not just a bunch of fields.
  3021. #--------------------------------------------------------------------------
  3022. &ExtractForms(\%register,$query->GetX("jabber:x:data"));
  3023. #--------------------------------------------------------------------------
  3024. # Get any oobs so that we have the option of sending the user to the http
  3025. # form and not a dynamic one.
  3026. #--------------------------------------------------------------------------
  3027. &ExtractOobs(\%register,$query->GetX("jabber:x:oob"));
  3028. return %register;
  3029. }
  3030. ###############################################################################
  3031. #
  3032. # RegisterSend - This is a self contained function to send a registration
  3033. # iq tag with an id. Then wait for a reply what the same
  3034. # id to come back and tell the caller what the result was.
  3035. #
  3036. ###############################################################################
  3037. sub RegisterSend
  3038. {
  3039. my $self = shift;
  3040. my %args;
  3041. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3042. #--------------------------------------------------------------------------
  3043. # Create a Net::Jabber::IQ object to send to the server
  3044. #--------------------------------------------------------------------------
  3045. my $iq = new Net::Jabber::IQ();
  3046. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  3047. $iq->SetIQ(type=>"set");
  3048. my $iqRegister = $iq->NewQuery("jabber:iq:register");
  3049. $iqRegister->SetRegister(%args);
  3050. #--------------------------------------------------------------------------
  3051. # Send the IQ with the next available ID and wait for a reply with that
  3052. # id to be received. Then grab the IQ reply.
  3053. #--------------------------------------------------------------------------
  3054. $iq = $self->SendAndReceiveWithID($iq);
  3055. #--------------------------------------------------------------------------
  3056. # From the reply IQ determine if we were successful or not. If yes then
  3057. # return "". If no then return error string from the reply.
  3058. #--------------------------------------------------------------------------
  3059. return unless defined($iq);
  3060. return ( $iq->GetErrorCode() , $iq->GetError() )
  3061. if ($iq->GetType() eq "error");
  3062. return ("ok","");
  3063. }
  3064. ###############################################################################
  3065. #
  3066. # RegisterSendData - This is a self contained function to send a register iq
  3067. # tag with an id. It uses the jabber:x:data method to
  3068. # return the data.
  3069. #
  3070. ###############################################################################
  3071. sub RegisterSendData
  3072. {
  3073. my $self = shift;
  3074. my $to = shift;
  3075. my %args;
  3076. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3077. #--------------------------------------------------------------------------
  3078. # Create a Net::Jabber::IQ object to send to the server
  3079. #--------------------------------------------------------------------------
  3080. my $iq = new Net::Jabber::IQ();
  3081. $iq->SetIQ(to=>$to) if (defined($to) && ($to ne ""));
  3082. $iq->SetIQ(type=>"set");
  3083. my $iqRegister = $iq->NewQuery("jabber:iq:register");
  3084. my $xForm = $iqRegister->NewX("jabber:x:data");
  3085. foreach my $var (keys(%args))
  3086. {
  3087. next if ($args{$var} eq "");
  3088. $xForm->AddField(var=>$var,
  3089. value=>$args{$var}
  3090. );
  3091. }
  3092. #--------------------------------------------------------------------------
  3093. # Send the IQ with the next available ID and wait for a reply with that
  3094. # id to be received. Then grab the IQ reply.
  3095. #--------------------------------------------------------------------------
  3096. $iq = $self->SendAndReceiveWithID($iq);
  3097. #--------------------------------------------------------------------------
  3098. # From the reply IQ determine if we were successful or not. If yes then
  3099. # return "". If no then return error string from the reply.
  3100. #--------------------------------------------------------------------------
  3101. return unless defined($iq);
  3102. return ( $iq->GetErrorCode() , $iq->GetError() )
  3103. if ($iq->GetType() eq "error");
  3104. return ("ok","");
  3105. }
  3106. ###############################################################################
  3107. #
  3108. # RosterAdd - Takes the Jabber ID of the user to add to their Roster and
  3109. # sends the IQ packet to the server.
  3110. #
  3111. ###############################################################################
  3112. sub RosterAdd
  3113. {
  3114. my $self = shift;
  3115. my %args;
  3116. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3117. my $iq = new Net::Jabber::IQ();
  3118. $iq->SetIQ(type=>"set");
  3119. my $roster = $iq->NewQuery("jabber:iq:roster");
  3120. my $item = $roster->AddItem();
  3121. $item->SetItem(%args);
  3122. $self->{DEBUG}->Log1("RosterAdd: xml(",$iq->GetXML(),")");
  3123. $self->Send($iq);
  3124. }
  3125. ###############################################################################
  3126. #
  3127. # RosterAdd - Takes the Jabber ID of the user to remove from their Roster
  3128. # and sends the IQ packet to the server.
  3129. #
  3130. ###############################################################################
  3131. sub RosterRemove
  3132. {
  3133. my $self = shift;
  3134. my %args;
  3135. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3136. delete($args{subscription});
  3137. my $iq = new Net::Jabber::IQ();
  3138. $iq->SetIQ(type=>"set");
  3139. my $roster = $iq->NewQuery("jabber:iq:roster");
  3140. my $item = $roster->AddItem();
  3141. $item->SetItem(%args,
  3142. subscription=>"remove");
  3143. $self->Send($iq);
  3144. }
  3145. ###############################################################################
  3146. #
  3147. # RosterParse - Returns a hash of roster items.
  3148. #
  3149. ###############################################################################
  3150. sub RosterParse
  3151. {
  3152. my $self = shift;
  3153. my($iq) = @_;
  3154. my $query = $iq->GetQuery();
  3155. my @items = $query->GetItems();
  3156. my %roster;
  3157. foreach my $item (@items)
  3158. {
  3159. my $jid = $item->GetJID();
  3160. $roster{$jid}->{name} = $item->GetName();
  3161. $roster{$jid}->{subscription} = $item->GetSubscription();
  3162. $roster{$jid}->{ask} = $item->GetAsk();
  3163. $roster{$jid}->{groups} = [ $item->GetGroup() ];
  3164. }
  3165. return %roster;
  3166. }
  3167. ###############################################################################
  3168. #
  3169. # RosterGet - Sends an empty IQ to the server to request that the user's
  3170. # Roster be sent to them. Returns a hash of roster items.
  3171. #
  3172. ###############################################################################
  3173. sub RosterGet
  3174. {
  3175. my $self = shift;
  3176. my $iq = new Net::Jabber::IQ();
  3177. $iq->SetIQ(type=>"get");
  3178. my $query = $iq->NewQuery("jabber:iq:roster");
  3179. $iq = $self->SendAndReceiveWithID($iq);
  3180. return unless defined($iq);
  3181. return $self->RosterParse($iq);
  3182. }
  3183. ###############################################################################
  3184. #
  3185. # RosterRequest - Sends an empty IQ to the server to request that the user's
  3186. # Roster be sent to them, and return to let the user's program
  3187. # handle parsing the return packet.
  3188. #
  3189. ###############################################################################
  3190. sub RosterRequest
  3191. {
  3192. my $self = shift;
  3193. my $iq = new Net::Jabber::IQ();
  3194. $iq->SetIQ(type=>"get");
  3195. my $query = $iq->NewQuery("jabber:iq:roster");
  3196. $self->Send($iq);
  3197. }
  3198. ###############################################################################
  3199. #
  3200. # RosterDBParse - takes an iq packet that containsa roster, parses it, and puts
  3201. # the roster into the Roster DB.
  3202. #
  3203. ###############################################################################
  3204. sub RosterDBParse
  3205. {
  3206. my $self = shift;
  3207. my ($iq) = @_;
  3208. my $type = $iq->GetType();
  3209. return unless (($type eq "set") || ($type eq "result"));
  3210. my %newroster = $self->RosterParse($iq);
  3211. $self->RosterDBProcessParsed(%newroster);
  3212. }
  3213. ###############################################################################
  3214. #
  3215. # RosterDBProcessParsed - takes a parsed roster and puts it into the Roster DB.
  3216. #
  3217. ###############################################################################
  3218. sub RosterDBProcessParsed
  3219. {
  3220. my $self = shift;
  3221. my (%roster) = @_;
  3222. foreach my $jid (keys(%roster))
  3223. {
  3224. if ($roster{$jid}->{subscription} eq "remove")
  3225. {
  3226. $self->RosterDBRemove($jid);
  3227. }
  3228. else
  3229. {
  3230. $self->RosterDBAdd($jid, %{$roster{$jid}} );
  3231. }
  3232. }
  3233. }
  3234. ###############################################################################
  3235. #
  3236. # RosterDBAdd - adds the entry to the Roster DB.
  3237. #
  3238. ###############################################################################
  3239. sub RosterDBAdd
  3240. {
  3241. my $self = shift;
  3242. my ($jid,%item) = @_;
  3243. $self->{ROSTERDB}->{$jid} = \%item;
  3244. }
  3245. ###############################################################################
  3246. #
  3247. # RosterDBRemove - removes the JID from the Roster DB.
  3248. #
  3249. ###############################################################################
  3250. sub RosterDBRemove
  3251. {
  3252. my $self = shift;
  3253. my ($jid) = @_;
  3254. delete($self->{ROSTERDB}->{$jid}) if exists($self->{ROSTERDB}->{$jid});
  3255. }
  3256. ###############################################################################
  3257. #
  3258. # RosterDBQuery - allows you to get one of the pieces of info from the
  3259. # Roster DB.
  3260. #
  3261. ###############################################################################
  3262. sub RosterDBQuery
  3263. {
  3264. my $self = shift;
  3265. my ($jid,$key) = @_;
  3266. return unless exists($self->{ROSTERDB});
  3267. return unless exists($self->{ROSTERDB}->{$jid});
  3268. return unless exists($self->{ROSTERDB}->{$jid}->{$key});
  3269. return $self->{ROSTERDB}->{$jid}->{$key};
  3270. }
  3271. ###############################################################################
  3272. #
  3273. # RPCSetCallBacks - place to register a callback for RPC calls. This is
  3274. # used in conjunction with the default IQ callback.
  3275. #
  3276. ###############################################################################
  3277. sub RPCSetCallBacks
  3278. {
  3279. my $self = shift;
  3280. while($#_ >= 0) {
  3281. my $func = pop(@_);
  3282. my $method = pop(@_);
  3283. $self->{DEBUG}->Log2("RPCSetCallBacks: method($method) func($func)");
  3284. if (defined($func))
  3285. {
  3286. $self->{RPCCB}{$method} = $func;
  3287. }
  3288. else
  3289. {
  3290. delete($self->{RPCCB}{$method});
  3291. }
  3292. }
  3293. }
  3294. ###############################################################################
  3295. #
  3296. # RPCCall - Make an RPC call to the specified JID.
  3297. #
  3298. ###############################################################################
  3299. sub RPCCall
  3300. {
  3301. my $self = shift;
  3302. my %args;
  3303. $args{mode} = "block";
  3304. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3305. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  3306. my $iq = new Net::Jabber::IQ();
  3307. $iq->SetIQ(type=>"set",
  3308. to=>delete($args{to}));
  3309. $iq->AddQuery($self->RPCEncode(type=>"methodCall",
  3310. %args));
  3311. if ($args{mode} eq "passthru")
  3312. {
  3313. my $id = $self->UniqueID();
  3314. $iq->SetIQ(id=>$id);
  3315. $self->Send($iq);
  3316. return $id;
  3317. }
  3318. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  3319. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  3320. return unless defined($iq);
  3321. return $self->RPCParse($iq);
  3322. }
  3323. ###############################################################################
  3324. #
  3325. # RPCResponse - Send back an RPC response, or fault, to the specified JID.
  3326. #
  3327. ###############################################################################
  3328. sub RPCResponse
  3329. {
  3330. my $self = shift;
  3331. my %args;
  3332. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3333. my $iq = new Net::Jabber::IQ();
  3334. $iq->SetIQ(type=>"result",
  3335. to=>delete($args{to}));
  3336. $iq->AddQuery($self->RPCEncode(type=>"methodResponse",
  3337. %args));
  3338. $iq = $self->SendAndReceiveWithID($iq);
  3339. return unless defined($iq);
  3340. return $self->RPCParse($iq);
  3341. }
  3342. ###############################################################################
  3343. #
  3344. # RPCEncode - Returns a Net::Jabber::Query with the arguments encoded for the
  3345. # RPC packet.
  3346. #
  3347. ###############################################################################
  3348. sub RPCEncode
  3349. {
  3350. my $self = shift;
  3351. my %args;
  3352. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3353. my $query = new Net::Jabber::Query();
  3354. $query->SetXMLNS("jabber:iq:rpc");
  3355. my $source;
  3356. if ($args{type} eq "methodCall")
  3357. {
  3358. $source = $query->AddMethodCall();
  3359. $source->SetMethodName($args{methodname});
  3360. }
  3361. if ($args{type} eq "methodResponse")
  3362. {
  3363. $source = $query->AddMethodResponse();
  3364. }
  3365. if (exists($args{faultcode}) || exists($args{faultstring}))
  3366. {
  3367. my $struct = $source->AddFault()->AddValue()->AddStruct();
  3368. $struct->AddMember(name=>"faultCode")->AddValue(i4=>$args{faultcode});
  3369. $struct->AddMember(name=>"faultString")->AddValue(string=>$args{faultstring});
  3370. }
  3371. elsif (exists($args{params}))
  3372. {
  3373. my $params = $source->AddParams();
  3374. foreach my $param (@{$args{params}})
  3375. {
  3376. $self->RPCEncode_Value($params->AddParam(),$param);
  3377. }
  3378. }
  3379. return $query;
  3380. }
  3381. ###############################################################################
  3382. #
  3383. # RPCEncode_Value - Run through the value, and encode it into XML.
  3384. #
  3385. ###############################################################################
  3386. sub RPCEncode_Value
  3387. {
  3388. my $self = shift;
  3389. my $obj = shift;
  3390. my $value = shift;
  3391. if (ref($value) eq "ARRAY")
  3392. {
  3393. my $array = $obj->AddValue()->AddArray();
  3394. foreach my $data (@{$value})
  3395. {
  3396. $self->RPCEncode_Value($array->AddData(),$data);
  3397. }
  3398. }
  3399. elsif (ref($value) eq "HASH")
  3400. {
  3401. my $struct = $obj->AddValue()->AddStruct();
  3402. foreach my $key (keys(%{$value}))
  3403. {
  3404. $self->RPCEncode_Value($struct->AddMember(name=>$key),$value->{$key});
  3405. }
  3406. }
  3407. else
  3408. {
  3409. if ($value =~ /^(int|i4|boolean|string|double|datetime|base64):/i)
  3410. {
  3411. my $type = $1;
  3412. my($val) = ($value =~ /^$type:(.*)$/);
  3413. $obj->AddValue($type=>$val);
  3414. }
  3415. elsif ($value =~ /^[+-]?\d+$/)
  3416. {
  3417. $obj->AddValue(i4=>$value);
  3418. }
  3419. elsif ($value =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/)
  3420. {
  3421. $obj->AddValue(double=>$value);
  3422. }
  3423. else
  3424. {
  3425. $obj->AddValue(string=>$value);
  3426. }
  3427. }
  3428. }
  3429. ###############################################################################
  3430. #
  3431. # RPCParse - Returns an array of the params sent in the RPC packet.
  3432. #
  3433. ###############################################################################
  3434. sub RPCParse
  3435. {
  3436. my $self = shift;
  3437. my($iq) = @_;
  3438. my $query = $iq->GetQuery();
  3439. my $source;
  3440. $source = $query->GetMethodCall() if $query->DefinedMethodCall();
  3441. $source = $query->GetMethodResponse() if $query->DefinedMethodResponse();
  3442. if (defined($source))
  3443. {
  3444. if (($source->GetTag() eq "methodResponse") && ($source->DefinedFault()))
  3445. {
  3446. my %response =
  3447. $self->RPCParse_Struct($source->GetFault()->GetValue()->GetStruct());
  3448. return ("fault",\%response);
  3449. }
  3450. if ($source->DefinedParams())
  3451. {
  3452. #------------------------------------------------------------------
  3453. # The <param/>s part
  3454. #------------------------------------------------------------------
  3455. my @response;
  3456. foreach my $param ($source->GetParams()->GetParams())
  3457. {
  3458. push(@response,$self->RPCParse_Value($param->GetValue()));
  3459. }
  3460. return ("ok",\@response);
  3461. }
  3462. }
  3463. else
  3464. {
  3465. print "AAAAHHHH!!!!\n";
  3466. }
  3467. }
  3468. ###############################################################################
  3469. #
  3470. # RPCParse_Value - Takes a <value/> and returns the data it represents
  3471. #
  3472. ###############################################################################
  3473. sub RPCParse_Value
  3474. {
  3475. my $self = shift;
  3476. my($value) = @_;
  3477. if ($value->DefinedStruct())
  3478. {
  3479. my %struct = $self->RPCParse_Struct($value->GetStruct());
  3480. return \%struct;
  3481. }
  3482. if ($value->DefinedArray())
  3483. {
  3484. my @array = $self->RPCParse_Array($value->GetArray());
  3485. return \@array;
  3486. }
  3487. return $value->GetI4() if $value->DefinedI4();
  3488. return $value->GetInt() if $value->DefinedInt();
  3489. return $value->GetBoolean() if $value->DefinedBoolean();
  3490. return $value->GetString() if $value->DefinedString();
  3491. return $value->GetDouble() if $value->DefinedDouble();
  3492. return $value->GetDateTime() if $value->DefinedDateTime();
  3493. return $value->GetBase64() if $value->DefinedBase64();
  3494. return $value->GetValue();
  3495. }
  3496. ###############################################################################
  3497. #
  3498. # RPCParse_Struct - Takes a <struct/> and returns the hash of values.
  3499. #
  3500. ###############################################################################
  3501. sub RPCParse_Struct
  3502. {
  3503. my $self = shift;
  3504. my($struct) = @_;
  3505. my %struct;
  3506. foreach my $member ($struct->GetMembers())
  3507. {
  3508. $struct{$member->GetName()} = $self->RPCParse_Value($member->GetValue());
  3509. }
  3510. return %struct;
  3511. }
  3512. ###############################################################################
  3513. #
  3514. # RPCParse_Array - Takes a <array/> and returns the hash of values.
  3515. #
  3516. ###############################################################################
  3517. sub RPCParse_Array
  3518. {
  3519. my $self = shift;
  3520. my($array) = @_;
  3521. my @array;
  3522. foreach my $data ($array->GetDatas())
  3523. {
  3524. push(@array,$self->RPCParse_Value($data->GetValue()));
  3525. }
  3526. return @array;
  3527. }
  3528. ###############################################################################
  3529. #
  3530. # SearchRequest - This is a self contained function to send an iq tag
  3531. # an id that requests the target address to send back
  3532. # the required fields. It waits for a reply what the
  3533. # same id to come back and tell the caller what the
  3534. # fields are.
  3535. #
  3536. ###############################################################################
  3537. sub SearchRequest
  3538. {
  3539. my $self = shift;
  3540. my %args;
  3541. $args{mode} = "block";
  3542. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3543. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  3544. #--------------------------------------------------------------------------
  3545. # Create a Net::Jabber::IQ object to send to the server
  3546. #--------------------------------------------------------------------------
  3547. my $iq = new Net::Jabber::IQ();
  3548. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  3549. $iq->SetIQ(type=>"get");
  3550. my $query = $iq->NewQuery("jabber:iq:search");
  3551. $self->{DEBUG}->Log1("SearchRequest: sent(",$iq->GetXML(),")");
  3552. #--------------------------------------------------------------------------
  3553. # Send the IQ with the next available ID and wait for a reply with that
  3554. # id to be received. Then grab the IQ reply.
  3555. #--------------------------------------------------------------------------
  3556. if ($args{mode} eq "passthru")
  3557. {
  3558. my $id = $self->UniqueID();
  3559. $iq->SetIQ(id=>$id);
  3560. $self->Send($iq);
  3561. return $id;
  3562. }
  3563. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  3564. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  3565. $self->{DEBUG}->Log1("SearchRequest: received(",$iq->GetXML(),")")
  3566. if defined($iq);
  3567. #--------------------------------------------------------------------------
  3568. # Check if there was an error.
  3569. #--------------------------------------------------------------------------
  3570. return unless defined($iq);
  3571. if ($iq->GetType() eq "error")
  3572. {
  3573. $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
  3574. $self->{DEBUG}->Log1("SearchRequest: error(",$self->GetErrorCode(),")");
  3575. return;
  3576. }
  3577. my %search;
  3578. #--------------------------------------------------------------------------
  3579. # From the reply IQ determine what fields are required and send a hash
  3580. # back with the fields and any values that are already defined (like key)
  3581. #--------------------------------------------------------------------------
  3582. $query = $iq->GetQuery();
  3583. $search{fields} = { $query->GetSearch() };
  3584. #--------------------------------------------------------------------------
  3585. # Get any forms so that we have the option of showing a nive dynamic form
  3586. # to the user and not just a bunch of fields.
  3587. #--------------------------------------------------------------------------
  3588. &ExtractForms(\%search,$query->GetX("jabber:x:data"));
  3589. #--------------------------------------------------------------------------
  3590. # Get any oobs so that we have the option of sending the user to the http
  3591. # form and not a dynamic one.
  3592. #--------------------------------------------------------------------------
  3593. &ExtractOobs(\%search,$query->GetX("jabber:x:oob"));
  3594. return %search;
  3595. }
  3596. ###############################################################################
  3597. #
  3598. # SearchSend - This is a self contained function to send a search
  3599. # iq tag with an id. Then wait for a reply what the same
  3600. # id to come back and tell the caller what the result was.
  3601. #
  3602. ###############################################################################
  3603. sub SearchSend
  3604. {
  3605. my $self = shift;
  3606. my %args;
  3607. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3608. #--------------------------------------------------------------------------
  3609. # Create a Net::Jabber::IQ object to send to the server
  3610. #--------------------------------------------------------------------------
  3611. my $iq = new Net::Jabber::IQ();
  3612. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  3613. $iq->SetIQ(type=>"set");
  3614. my $iqSearch = $iq->NewQuery("jabber:iq:search");
  3615. $iqSearch->SetSearch(%args);
  3616. #--------------------------------------------------------------------------
  3617. # Send the IQ.
  3618. #--------------------------------------------------------------------------
  3619. $self->Send($iq);
  3620. }
  3621. ###############################################################################
  3622. #
  3623. # SearchSendData - This is a self contained function to send a search iq tag
  3624. # with an id. It uses the jabber:x:data method to return the
  3625. # data.
  3626. #
  3627. ###############################################################################
  3628. sub SearchSendData
  3629. {
  3630. my $self = shift;
  3631. my $to = shift;
  3632. my %args;
  3633. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3634. #--------------------------------------------------------------------------
  3635. # Create a Net::Jabber::IQ object to send to the server
  3636. #--------------------------------------------------------------------------
  3637. my $iq = new Net::Jabber::IQ();
  3638. $iq->SetIQ(to=>$to) if (defined($to) && ($to ne ""));
  3639. $iq->SetIQ(type=>"set");
  3640. my $iqSearch = $iq->NewQuery("jabber:iq:search");
  3641. my $xForm = $iqSearch->NewX("jabber:x:data");
  3642. foreach my $var (keys(%args))
  3643. {
  3644. next if ($args{$var} eq "");
  3645. $xForm->AddField(var=>$var,
  3646. value=>$args{$var}
  3647. );
  3648. }
  3649. #--------------------------------------------------------------------------
  3650. # Send the IQ.
  3651. #--------------------------------------------------------------------------
  3652. $self->Send($iq);
  3653. }
  3654. ###############################################################################
  3655. #
  3656. # TimeQuery - Sends an iq:time query to either the server or the specified
  3657. # JID.
  3658. #
  3659. ###############################################################################
  3660. sub TimeQuery
  3661. {
  3662. my $self = shift;
  3663. my %args;
  3664. $args{mode} = "passthru";
  3665. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3666. $args{waitforid} = 0 unless exists($args{waitforid});
  3667. my $waitforid = delete($args{waitforid});
  3668. $args{mode} = "block" if $waitforid;
  3669. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  3670. my $iq = new Net::Jabber::IQ();
  3671. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  3672. $iq->SetIQ(type=>'get',%args);
  3673. my $time = $iq->NewQuery("jabber:iq:time");
  3674. if ($args{mode} eq "passthru")
  3675. {
  3676. my $id = $self->UniqueID();
  3677. $iq->SetIQ(id=>$id);
  3678. $self->Send($iq);
  3679. return $id;
  3680. }
  3681. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  3682. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  3683. return unless defined($iq);
  3684. my $query = $iq->GetQuery();
  3685. return unless defined($query);
  3686. my %result;
  3687. $result{utc} = $query->GetUTC();
  3688. $result{display} = $query->GetDisplay();
  3689. $result{tz} = $query->GetTZ();
  3690. return %result;
  3691. }
  3692. ###############################################################################
  3693. #
  3694. # TimeSend - sends an iq:time packet to the specified user.
  3695. #
  3696. ###############################################################################
  3697. sub TimeSend
  3698. {
  3699. my $self = shift;
  3700. my %args;
  3701. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3702. my $iq = new Net::Jabber::IQ();
  3703. $iq->SetIQ(to=>delete($args{to}),
  3704. type=>'result');
  3705. my $time = $iq->NewQuery("jabber:iq:time");
  3706. $time->SetTime(%args);
  3707. $self->Send($iq);
  3708. }
  3709. ###############################################################################
  3710. #
  3711. # VersionQuery - Sends an iq:version query to either the server or the
  3712. # specified JID.
  3713. #
  3714. ###############################################################################
  3715. sub VersionQuery
  3716. {
  3717. my $self = shift;
  3718. my %args;
  3719. $args{mode} = "passthru";
  3720. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3721. $args{waitforid} = 0 unless exists($args{waitforid});
  3722. my $waitforid = delete($args{waitforid});
  3723. $args{mode} = "block" if $waitforid;
  3724. my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
  3725. my $iq = new Net::Jabber::IQ();
  3726. $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
  3727. $iq->SetIQ(type=>'get',%args);
  3728. my $version = $iq->NewQuery("jabber:iq:version");
  3729. if ($args{mode} eq "passthru")
  3730. {
  3731. my $id = $self->UniqueID();
  3732. $iq->SetIQ(id=>$id);
  3733. $self->Send($iq);
  3734. return $id;
  3735. }
  3736. return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
  3737. $iq = $self->SendAndReceiveWithID($iq,$timeout);
  3738. return unless defined($iq);
  3739. my $query = $iq->GetQuery();
  3740. return unless defined($query);
  3741. my %result;
  3742. $result{name} = $query->GetName();
  3743. $result{ver} = $query->GetVer();
  3744. $result{os} = $query->GetOS();
  3745. return %result;
  3746. }
  3747. ###############################################################################
  3748. #
  3749. # VersionSend - sends an iq:version packet to the specified user.
  3750. #
  3751. ###############################################################################
  3752. sub VersionSend
  3753. {
  3754. my $self = shift;
  3755. my %args;
  3756. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3757. my $iq = new Net::Jabber::IQ();
  3758. $iq->SetIQ(to=>delete($args{to}),
  3759. type=>'result');
  3760. my $version = $iq->NewQuery("jabber:iq:version");
  3761. $version->SetVersion(%args);
  3762. $self->Send($iq);
  3763. }
  3764. ###############################################################################
  3765. #
  3766. # MUCJoin - join a MUC room
  3767. #
  3768. ###############################################################################
  3769. sub MUCJoin
  3770. {
  3771. my $self = shift;
  3772. my %args;
  3773. while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
  3774. my $presence = new Net::Jabber::Presence();
  3775. $presence->SetTo($args{room}.'@'.$args{server}.'/'.$args{nick});
  3776. my $x = $presence->NewX("http://jabber.org/protocol/muc");
  3777. if (exists($args{password}) && ($args{password} ne ""))
  3778. {
  3779. $x->SetMUC(password=>$args{password});
  3780. }
  3781. return $presence->GetXML() if exists($args{'__netjabber__:test'});
  3782. $self->Send($presence);
  3783. }
  3784. ###############################################################################
  3785. #+-----------------------------------------------------------------------------
  3786. #|
  3787. #| Helper Functions
  3788. #|
  3789. #+-----------------------------------------------------------------------------
  3790. ###############################################################################
  3791. ###############################################################################
  3792. #
  3793. # ExtractForms - Helper function to make extracting jabber:x:data for forms
  3794. # more centrally definable.
  3795. #
  3796. ###############################################################################
  3797. sub ExtractForms
  3798. {
  3799. my ($target,@xForms) = @_;
  3800. my $tempVar = "1";
  3801. foreach my $xForm (@xForms) {
  3802. $target->{instructions} = $xForm->GetInstructions();
  3803. my $order = 0;
  3804. foreach my $field ($xForm->GetFields())
  3805. {
  3806. $target->{form}->[$order]->{type} = $field->GetType()
  3807. if $field->DefinedType();
  3808. $target->{form}->[$order]->{label} = $field->GetLabel()
  3809. if $field->DefinedLabel();
  3810. $target->{form}->[$order]->{desc} = $field->GetDesc()
  3811. if $field->DefinedDesc();
  3812. $target->{form}->[$order]->{var} = $field->GetVar()
  3813. if $field->DefinedVar();
  3814. $target->{form}->[$order]->{var} = "__netjabber__:tempvar:".$tempVar++
  3815. if !$field->DefinedVar();
  3816. if ($field->DefinedValue())
  3817. {
  3818. if ($field->GetType() eq "list-multi")
  3819. {
  3820. $target->{form}->[$order]->{value} = [ $field->GetValue() ];
  3821. }
  3822. else
  3823. {
  3824. $target->{form}->[$order]->{value} = ($field->GetValue())[0];
  3825. }
  3826. }
  3827. my $count = 0;
  3828. foreach my $option ($field->GetOptions())
  3829. {
  3830. $target->{form}->[$order]->{options}->[$count]->{value} =
  3831. $option->GetValue();
  3832. $target->{form}->[$order]->{options}->[$count]->{label} =
  3833. $option->GetLabel();
  3834. $count++;
  3835. }
  3836. $order++;
  3837. }
  3838. foreach my $reported ($xForm->GetReported())
  3839. {
  3840. my $order = 0;
  3841. foreach my $field ($reported->GetFields())
  3842. {
  3843. $target->{reported}->[$order]->{label} = $field->GetLabel();
  3844. $target->{reported}->[$order]->{var} = $field->GetVar();
  3845. $order++;
  3846. }
  3847. }
  3848. }
  3849. }
  3850. ###############################################################################
  3851. #
  3852. # ExtractOobs - Helper function to make extracting jabber:x:oob for forms
  3853. # more centrally definable.
  3854. #
  3855. ###############################################################################
  3856. sub ExtractOobs
  3857. {
  3858. my ($target,@xOobs) = @_;
  3859. foreach my $xOob (@xOobs)
  3860. {
  3861. $target->{oob}->{url} = $xOob->GetURL();
  3862. $target->{oob}->{desc} = $xOob->GetDesc();
  3863. }
  3864. }
  3865. ###############################################################################
  3866. #+-----------------------------------------------------------------------------
  3867. #|
  3868. #| Default CallBacks
  3869. #|
  3870. #+-----------------------------------------------------------------------------
  3871. ###############################################################################
  3872. ###############################################################################
  3873. #
  3874. # callbackInit - initialize the default callbacks
  3875. #
  3876. ###############################################################################
  3877. sub callbackInit
  3878. {
  3879. my $self = shift;
  3880. $self->SetCallBacks(iq=>sub{ $self->callbackIQ(@_) },
  3881. presence=>sub{ $self->callbackPresence(@_) },
  3882. message=>sub{ $self->callbackMessage(@_) },
  3883. );
  3884. $self->SetPresenceCallBacks(available=>sub{ $self->callbackPresenceAvailable(@_) },
  3885. subscribe=>sub{ $self->callbackPresenceSubscribe(@_) },
  3886. unsubscribe=>sub{ $self->callbackPresenceUnsubscribe(@_) },
  3887. subscribed=>sub{ $self->callbackPresenceSubscribed(@_) },
  3888. unsubscribed=>sub{ $self->callbackPresenceUnsubscribed(@_) },
  3889. );
  3890. $self->SetIQCallBacks("jabber:iq:last"=>
  3891. {
  3892. get=>sub{ $self->callbackGetIQLast(@_) },
  3893. result=>sub{ $self->callbackResultIQLast(@_) }
  3894. },
  3895. "jabber:iq:rpc"=>
  3896. {
  3897. set=>sub{ $self->callbackSetIQRPC(@_) },
  3898. },
  3899. "jabber:iq:time"=>
  3900. {
  3901. get=>sub{ $self->callbackGetIQTime(@_) },
  3902. result=>sub{ $self->callbackResultIQTime(@_) }
  3903. },
  3904. "jabber:iq:version"=>
  3905. {
  3906. get=>sub{ $self->callbackGetIQVersion(@_) },
  3907. result=>sub{ $self->callbackResultIQVersion(@_) }
  3908. },
  3909. );
  3910. }
  3911. ###############################################################################
  3912. #
  3913. # callbackMessage - default callback for <message/> packets.
  3914. #
  3915. ###############################################################################
  3916. sub callbackMessage
  3917. {
  3918. my $self = shift;
  3919. my $sid = shift;
  3920. my $message = shift;
  3921. my $type = "normal";
  3922. $type = $message->GetType() if $message->DefinedType();
  3923. if (exists($self->{CB}->{Mess}->{$type}) &&
  3924. (ref($self->{CB}->{Mess}->{$type}) eq "CODE"))
  3925. {
  3926. &{$self->{CB}->{Mess}->{$type}}($sid,$message);
  3927. }
  3928. }
  3929. ###############################################################################
  3930. #
  3931. # callbackPresence - default callback for <presence/> packets.
  3932. #
  3933. ###############################################################################
  3934. sub callbackPresence
  3935. {
  3936. my $self = shift;
  3937. my $sid = shift;
  3938. my $presence = shift;
  3939. my $type = "available";
  3940. $type = $presence->GetType() if $presence->DefinedType();
  3941. if (exists($self->{CB}->{Pres}->{$type}) &&
  3942. (ref($self->{CB}->{Pres}->{$type}) eq "CODE"))
  3943. {
  3944. &{$self->{CB}->{Pres}->{$type}}($sid,$presence);
  3945. }
  3946. }
  3947. ###############################################################################
  3948. #
  3949. # callbackIQ - default callback for <iq/> packets.
  3950. #
  3951. ###############################################################################
  3952. sub callbackIQ
  3953. {
  3954. my $self = shift;
  3955. my $sid = shift;
  3956. my $iq = shift;
  3957. return unless $iq->DefinedQuery();
  3958. my $query = $iq->GetQuery();
  3959. return unless defined($query);
  3960. my $type = $iq->GetType();
  3961. my $ns = $query->GetXMLNS();
  3962. if (exists($self->{CB}->{IQns}->{$ns}) &&
  3963. (ref($self->{CB}->{IQns}->{$ns}) eq "CODE"))
  3964. {
  3965. &{$self->{CB}->{IQns}->{$ns}}($sid,$iq);
  3966. } elsif (exists($self->{CB}->{IQns}->{$ns}->{$type}) &&
  3967. (ref($self->{CB}->{IQns}->{$ns}->{$type}) eq "CODE"))
  3968. {
  3969. &{$self->{CB}->{IQns}->{$ns}->{$type}}($sid,$iq);
  3970. }
  3971. }
  3972. ###############################################################################
  3973. #
  3974. # callbackPresenceAvailable - default callback for available packets.
  3975. #
  3976. ###############################################################################
  3977. sub callbackPresenceAvailable
  3978. {
  3979. my $self = shift;
  3980. my $sid = shift;
  3981. my $presence = shift;
  3982. my $reply = $presence->Reply();
  3983. $self->Send($reply,1);
  3984. }
  3985. ###############################################################################
  3986. #
  3987. # callbackPresenceSubscribe - default callback for subscribe packets.
  3988. #
  3989. ###############################################################################
  3990. sub callbackPresenceSubscribe
  3991. {
  3992. my $self = shift;
  3993. my $sid = shift;
  3994. my $presence = shift;
  3995. my $reply = $presence->Reply(type=>"subscribed");
  3996. $self->Send($reply,1);
  3997. $reply->SetType("subscribe");
  3998. $self->Send($reply,1);
  3999. }
  4000. ###############################################################################
  4001. #
  4002. # callbackPresenceUnsubscribe - default callback for unsubscribe packets.
  4003. #
  4004. ###############################################################################
  4005. sub callbackPresenceUnsubscribe
  4006. {
  4007. my $self = shift;
  4008. my $sid = shift;
  4009. my $presence = shift;
  4010. my $reply = $presence->Reply(type=>"unsubscribed");
  4011. $self->Send($reply,1);
  4012. }
  4013. ###############################################################################
  4014. #
  4015. # callbackPresenceSubscribed - default callback for subscribed packets.
  4016. #
  4017. ###############################################################################
  4018. sub callbackPresenceSubscribed
  4019. {
  4020. my $self = shift;
  4021. my $sid = shift;
  4022. my $presence = shift;
  4023. my $reply = $presence->Reply(type=>"subscribed");
  4024. $self->Send($reply,1);
  4025. }
  4026. ###############################################################################
  4027. #
  4028. # callbackPresenceUnsubscribed - default callback for unsubscribed packets.
  4029. #
  4030. ###############################################################################
  4031. sub callbackPresenceUnsubscribed
  4032. {
  4033. my $self = shift;
  4034. my $sid = shift;
  4035. my $presence = shift;
  4036. my $reply = $presence->Reply(type=>"unsubscribed");
  4037. $self->Send($reply,1);
  4038. }
  4039. ###############################################################################
  4040. #
  4041. # callbackSetIQRPC - callback to handle auto-replying to an iq:rpc by calling
  4042. # the user registered functions.
  4043. #
  4044. ###############################################################################
  4045. sub callbackSetIQRPC
  4046. {
  4047. my $self = shift;
  4048. my $sid = shift;
  4049. my $iq = shift;
  4050. my $query = $iq->GetQuery();
  4051. my $reply = $iq->Reply(type=>"result");
  4052. my $replyQuery = $reply->GetQuery();
  4053. if (!$query->DefinedMethodCall())
  4054. {
  4055. my $methodResponse = $replyQuery->AddMethodResponse();
  4056. my $struct = $methodResponse->AddFault()->AddValue()->AddStruct();
  4057. $struct->AddMember(name=>"faultCode")->AddValue(int=>400);
  4058. $struct->AddMember(name=>"faultString")->AddValue(string=>"Missing methodCall.");
  4059. $self->Send($reply,1);
  4060. return;
  4061. }
  4062. if (!$query->GetMethodCall()->DefinedMethodName())
  4063. {
  4064. my $methodResponse = $replyQuery->AddMethodResponse();
  4065. my $struct = $methodResponse->AddFault()->AddValue()->AddStruct();
  4066. $struct->AddMember(name=>"faultCode")->AddValue(int=>400);
  4067. $struct->AddMember(name=>"faultString")->AddValue(string=>"Missing methodName.");
  4068. $self->Send($reply,1);
  4069. return;
  4070. }
  4071. my $methodName = $query->GetMethodCall()->GetMethodName();
  4072. if (!exists($self->{RPCCB}->{$methodName}))
  4073. {
  4074. my $methodResponse = $replyQuery->AddMethodResponse();
  4075. my $struct = $methodResponse->AddFault()->AddValue()->AddStruct();
  4076. $struct->AddMember(name=>"faultCode")->AddValue(int=>404);
  4077. $struct->AddMember(name=>"faultString")->AddValue(string=>"methodName $methodName not defined.");
  4078. $self->Send($reply,1);
  4079. return;
  4080. }
  4081. my @params = $self->RPCParse($iq);
  4082. my @return = &{$self->{RPCCB}->{$methodName}}($iq,$params[1]);
  4083. if ($return[0] ne "ok") {
  4084. my $methodResponse = $replyQuery->AddMethodResponse();
  4085. my $struct = $methodResponse->AddFault()->AddValue()->AddStruct();
  4086. $struct->AddMember(name=>"faultCode")->AddValue(int=>$return[1]->{faultCode});
  4087. $struct->AddMember(name=>"faultString")->AddValue(string=>$return[1]->{faultString});
  4088. $self->Send($reply,1);
  4089. return;
  4090. }
  4091. $reply->RemoveQuery();
  4092. $reply->AddQuery($self->RPCEncode(type=>"methodResponse",
  4093. params=>$return[1]));
  4094. $self->Send($reply,1);
  4095. }
  4096. ###############################################################################
  4097. #
  4098. # callbackGetIQTime - callback to handle auto-replying to an iq:time get.
  4099. #
  4100. ###############################################################################
  4101. sub callbackGetIQTime
  4102. {
  4103. my $self = shift;
  4104. my $sid = shift;
  4105. my $iq = shift;
  4106. my $query = $iq->GetQuery();
  4107. my $reply = $iq->Reply(type=>"result");
  4108. my $replyQuery = $reply->GetQuery();
  4109. $replyQuery->SetTime();
  4110. $self->Send($reply,1);
  4111. }
  4112. ###############################################################################
  4113. #
  4114. # callbackResultIQTime - callback to handle formatting iq:time result into
  4115. # a message.
  4116. #
  4117. ###############################################################################
  4118. sub callbackResultIQTime
  4119. {
  4120. my $self = shift;
  4121. my $sid = shift;
  4122. my $iq = shift;
  4123. my $fromJID = $iq->GetFrom("jid");
  4124. my $query = $iq->GetQuery();
  4125. my $body = "UTC: ".$query->GetUTC()."\n";
  4126. $body .= "Time: ".$query->GetDisplay()."\n";
  4127. $body .= "Timezone: ".$query->GetTZ()."\n";
  4128. my $message = new Net::Jabber::Message();
  4129. $message->SetMessage(to=>$iq->GetTo(),
  4130. from=>$iq->GetFrom(),
  4131. subject=>"CTCP: Time",
  4132. body=>$body);
  4133. $self->CallBack($sid,$message);
  4134. }
  4135. ###############################################################################
  4136. #
  4137. # callbackGetIQVersion - callback to handle auto-replying to an iq:time
  4138. # get.
  4139. #
  4140. ###############################################################################
  4141. sub callbackGetIQVersion
  4142. {
  4143. my $self = shift;
  4144. my $sid = shift;
  4145. my $iq = shift;
  4146. my $query = $iq->GetQuery();
  4147. my $reply = $iq->Reply(type=>"result");
  4148. my $replyQuery = $reply->GetQuery();
  4149. $replyQuery->SetVersion(name=>$self->{INFO}->{name},
  4150. ver=>$self->{INFO}->{version},
  4151. os=>"");
  4152. $self->Send($reply,1);
  4153. }
  4154. ###############################################################################
  4155. #
  4156. # callbackResultIQVersion - callback to handle formatting iq:time result
  4157. # into a message.
  4158. #
  4159. ###############################################################################
  4160. sub callbackResultIQVersion
  4161. {
  4162. my $self = shift;
  4163. my $sid = shift;
  4164. my $iq = shift;
  4165. my $query = $iq->GetQuery();
  4166. my $body = "Program: ".$query->GetName()."\n";
  4167. $body .= "Version: ".$query->GetVer()."\n";
  4168. $body .= "OS: ".$query->GetOS()."\n";
  4169. my $message = new Net::Jabber::Message();
  4170. $message->SetMessage(to=>$iq->GetTo(),
  4171. from=>$iq->GetFrom(),
  4172. subject=>"CTCP: Version",
  4173. body=>$body);
  4174. $self->CallBack($sid,$message);
  4175. }
  4176. ###############################################################################
  4177. #
  4178. # callbackGetIQLast - callback to handle auto-replying to an iq:last get.
  4179. #
  4180. ###############################################################################
  4181. sub callbackGetIQLast
  4182. {
  4183. my $self = shift;
  4184. my $sid = shift;
  4185. my $iq = shift;
  4186. my $query = $iq->GetQuery();
  4187. my $reply = $iq->Reply(type=>"result");
  4188. my $replyQuery = $reply->GetQuery();
  4189. $replyQuery->SetLast(seconds=>$self->LastActivity());
  4190. $self->Send($reply,1);
  4191. }
  4192. ###############################################################################
  4193. #
  4194. # callbackResultIQLast - callback to handle formatting iq:last result into
  4195. # a message.
  4196. #
  4197. ###############################################################################
  4198. sub callbackResultIQLast
  4199. {
  4200. my $self = shift;
  4201. my $sid = shift;
  4202. my $iq = shift;
  4203. my $fromJID = $iq->GetFrom("jid");
  4204. my $query = $iq->GetQuery();
  4205. my $seconds = $query->GetSeconds();
  4206. my $lastTime = &Net::Jabber::GetTimeStamp("local",(time - $seconds),"long");
  4207. my $elapsedTime = &Net::Jabber::GetHumanTime($seconds);
  4208. my $body;
  4209. if ($fromJID->GetUserID() eq "")
  4210. {
  4211. $body = "Start Time: $lastTime\n";
  4212. $body .= "Up time: $elapsedTime\n";
  4213. $body .= "Message: ".$query->GetMessage()."\n"
  4214. if ($query->DefinedMessage());
  4215. }
  4216. elsif ($fromJID->GetResource() eq "")
  4217. {
  4218. $body = "Logout Time: $lastTime\n";
  4219. $body .= "Elapsed time: $elapsedTime\n";
  4220. $body .= "Message: ".$query->GetMessage()."\n"
  4221. if ($query->DefinedMessage());
  4222. }
  4223. else
  4224. {
  4225. $body = "Last activity: $lastTime\n";
  4226. $body .= "Elapsed time: $elapsedTime\n";
  4227. $body .= "Message: ".$query->GetMessage()."\n"
  4228. if ($query->DefinedMessage());
  4229. }
  4230. my $message = new Net::Jabber::Message();
  4231. $message->SetMessage(from=>$iq->GetFrom(),
  4232. subject=>"Last Activity",
  4233. body=>$body);
  4234. $self->CallBack($sid,$message);
  4235. }
  4236. 1;