PageRenderTime 37ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/javascript/opensrf_xmpp.js

https://github.com/evergreen-library-system/OpenSRF
JavaScript | 440 lines | 227 code | 117 blank | 96 comment | 29 complexity | 512773b5b27413ebab15febbc1369f85 MD5 | raw file
  1. /**
  2. * XXX
  3. * XXX For reference only until this code is updated to match new opensrf.js layout XXX
  4. * XXX
  5. */
  6. // ------------------------------------------------------------------
  7. // Houses the jabber transport code
  8. //
  9. // 1. jabber_connection - high level jabber component
  10. // 2. jabber_message - message class
  11. // 3. jabber_socket - socket handling code (shouldn't have to
  12. // use this class directly)
  13. //
  14. // Requires oils_utils.js
  15. // ------------------------------------------------------------------
  16. // ------------------------------------------------------------------
  17. // JABBER_CONNECTION
  18. // High level transport code
  19. // ------------------------------------------------------------------
  20. // Constructor
  21. // ------------------------------------------------------------------
  22. jabber_connection.prototype = new transport_connection();
  23. jabber_connection.prototype.constructor = jabber_connection;
  24. jabber_connection.baseClass = transport_connection.prototype.constructor;
  25. /** Initializes a jabber_connection object */
  26. function jabber_connection( username, password, resource ) {
  27. this.username = username;
  28. this.password = password;
  29. this.resource = resource;
  30. this.socket = new jabber_socket();
  31. this.host = "";
  32. }
  33. /** Connects to the Jabber server. 'timeout' is the connect timeout
  34. * in milliseconds
  35. */
  36. jabber_connection.prototype.connect = function( host, port, timeout ) {
  37. this.host = host;
  38. return this.socket.connect(
  39. this.username, this.password, this.resource, host, port, timeout );
  40. };
  41. /** Sends a message to 'recipient' with the provided message
  42. * thread and body
  43. */
  44. jabber_connection.prototype.send = function( recipient, thread, body ) {
  45. var jid = this.username+"@"+this.host+"/"+this.resource;
  46. var msg = new jabber_message( jid, recipient, thread, body );
  47. return this.socket.tcp_send( msg.to_string() );
  48. };
  49. /** This method will wait at most 'timeout' milliseconds
  50. * for a Jabber message to arrive. If one arrives
  51. * it is returned to the caller, other it returns null
  52. */
  53. jabber_connection.prototype.recv = function( timeout ) {
  54. return this.socket.recv( timeout );
  55. };
  56. /** Disconnects from the jabber server */
  57. jabber_connection.prototype.disconnect = function() {
  58. return this.socket.disconnect();
  59. };
  60. /** Returns true if we are currently connected to the
  61. * Jabber server
  62. */
  63. jabber_connection.prototype.connected = function() {
  64. return this.socket.connected();
  65. };
  66. // ------------------------------------------------------------------
  67. // JABBER_MESSAGE
  68. // High level message handling code
  69. jabber_message.prototype = new transport_message();
  70. jabber_message.prototype.constructor = jabber_message;
  71. jabber_message.prototype.baseClass = transport_message.prototype.constructor;
  72. /** Builds a jabber_message object */
  73. function jabber_message( sender, recipient, thread, body ) {
  74. if( sender == null || recipient == null || recipient.length < 1 ) { return; }
  75. this.doc = new DOMParser().parseFromString("<message></message>", "text/xml");
  76. this.root = this.doc.documentElement;
  77. this.root.setAttribute( "from", sender );
  78. this.root.setAttribute( "to", recipient );
  79. var body_node = this.doc.createElement("body");
  80. body_node.appendChild( this.doc.createTextNode( body ) );
  81. var thread_node = this.doc.createElement("thread");
  82. thread_node.appendChild( this.doc.createTextNode( thread ) );
  83. this.root.appendChild( body_node );
  84. this.root.appendChild( thread_node );
  85. }
  86. /** Builds a new message from raw xml.
  87. * If the message is a Jabber error message, then msg.is_error_msg
  88. * is set to true;
  89. */
  90. jabber_message.prototype.from_xml = function( xml ) {
  91. var msg = new jabber_message();
  92. msg.doc = new DOMParser().parseFromString( xml, "text/xml" );
  93. msg.root = msg.doc.documentElement;
  94. if( msg.root.getAttribute( "type" ) == "error" ) {
  95. msg.is_error_msg = true;
  96. } else {
  97. this.is_error_msg = false;
  98. }
  99. return msg;
  100. };
  101. /** Returns the 'from' field of the message */
  102. jabber_message.prototype.get_sender = function() {
  103. return this.root.getAttribute( "from" );
  104. };
  105. /** Returns the jabber thread */
  106. jabber_message.prototype.get_thread = function() {
  107. var nodes = this.root.getElementsByTagName( "thread" );
  108. var thread_node = nodes.item(0);
  109. return thread_node.firstChild.nodeValue;
  110. };
  111. /** Returns the message body */
  112. jabber_message.prototype.get_body = function() {
  113. var nodes = this.root.getElementsByTagName( "body" );
  114. var body_node = nodes.item(0);
  115. new Logger().transport( "Get Body returning:\n" + body_node.textContent, Logger.DEBUG );
  116. return body_node.textContent;
  117. };
  118. /** Returns the message as a whole as an XML string */
  119. jabber_message.prototype.to_string = function() {
  120. return new XMLSerializer().serializeToString(this.root);
  121. };
  122. // ------------------------------------------------------------------
  123. // TRANSPORT_SOCKET
  124. /** Initializes a new jabber_socket object */
  125. function jabber_socket() {
  126. this.is_connected = false;
  127. this.outstream = "";
  128. this.instream = "";
  129. this.buffer = "";
  130. this.socket = "";
  131. }
  132. /** Connects to the jabber server */
  133. jabber_socket.prototype.connect =
  134. function( username, password, resource, host, port, timeout ) {
  135. var starttime = new Date().getTime();
  136. // there has to be at least some kind of timeout
  137. if( ! timeout || timeout < 100 ) { timeout = 1000; }
  138. try {
  139. this.xpcom_init( host, port );
  140. this.tcp_send( "<stream:stream to='"+host
  141. +"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>" );
  142. if( !this.tcp_recv( timeout ) ) { throw 1; }
  143. } catch( E ) {
  144. throw new oils_ex_transport( "Could not open a socket to the transport server\n"
  145. + "Server: " + host + " Port: " + port );
  146. }
  147. // Send the auth packet
  148. this.tcp_send( "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'><username>"
  149. + username + "</username><password>" + password +
  150. "</password><resource>" + resource + "</resource></query></iq>" );
  151. var cur = new Date().getTime();
  152. var remaining = timeout - ( cur - starttime );
  153. this.tcp_recv( remaining );
  154. if( ! this.connected() ) {
  155. throw new oils_ex_transport( "Connection to transport server timed out" );
  156. }
  157. return true;
  158. };
  159. /** Sets up all of the xpcom components */
  160. jabber_socket.prototype.xpcom_init = function( host, port ) {
  161. var transportService =
  162. Components.classes["@mozilla.org/network/socket-transport-service;1"]
  163. .getService(Components.interfaces.nsISocketTransportService);
  164. this.transport = transportService.createTransport( null, 0, host, port, null);
  165. // ------------------------------------------------------------------
  166. // Build the stream objects
  167. // ------------------------------------------------------------------
  168. this.outstream = this.transport.openOutputStream(0,0,0);
  169. var stream = this.transport.openInputStream(0,0,0);
  170. this.instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
  171. .createInstance(Components.interfaces.nsIScriptableInputStream);
  172. this.instream.init(stream);
  173. };
  174. /** Send data to the TCP pipe */
  175. jabber_socket.prototype.tcp_send = function( data ) {
  176. new Logger().transport( "Sending Data: \n" + data, Logger.INFO );
  177. this.outstream.write(data,data.length);
  178. };
  179. /** Accepts data coming directly from the socket. If we're not
  180. * connected, we pass it off to procecc_connect(). Otherwise,
  181. * this method adds the data to the local buffer.
  182. */
  183. jabber_socket.prototype.process_data = function( data ) {
  184. new Logger().transport( "Received TCP data: " + data, Logger.DEBUG );
  185. if( ! this.connected() ) {
  186. this.process_connect( data );
  187. return;
  188. }
  189. this.buffer += data;
  190. };
  191. /** Processes connect data to verify we are logged in correctly */
  192. jabber_socket.prototype.process_connect = function( data ) {
  193. var reg = /type=["\']result["\']/;
  194. var err = /error/;
  195. if( reg.exec( data ) ) {
  196. this.is_connected = true;
  197. } else {
  198. if( err.exec( data ) ) {
  199. //throw new oils_ex_transport( "Server returned: \n" + data );
  200. throw new oils_ex_jabber_auth( "Server returned: \n" + data );
  201. // Throw exception, return something...
  202. }
  203. }
  204. };
  205. /** Waits up to at most 'timeout' milliseconds for data to arrive
  206. * in the TCP buffer. If there is at least one byte of data
  207. * in the buffer, then all of the data that is in the buffer is sent
  208. * to the process_data method for processing and the method returns.
  209. */
  210. jabber_socket.prototype.tcp_recv = function( timeout ) {
  211. var count = this.instream.available();
  212. var did_receive = false;
  213. // ------------------------------------------------------------------
  214. // If there is any data in the tcp buffer, process it and return
  215. // ------------------------------------------------------------------
  216. if( count > 0 ) {
  217. did_receive = true;
  218. while( count > 0 ) {
  219. new Logger().transport(
  220. "before process data", Logger.DEBUG );
  221. this.process_data( this.instream.read( count ) );
  222. new Logger().transport(
  223. "after process data", Logger.DEBUG );
  224. count = this.instream.available();
  225. new Logger().transport(
  226. "received " + count + " bytes" , Logger.DEBUG );
  227. }
  228. } else {
  229. // ------------------------------------------------------------------
  230. // Do the timeout dance
  231. // ------------------------------------------------------------------
  232. // ------------------------------------------------------------------
  233. // If there is no data in the buffer, wait up to timeout seconds
  234. // for some data to arrive. Once it arrives, suck it all out
  235. // and send it on for processing
  236. // ------------------------------------------------------------------
  237. var now, then;
  238. now = new Date().getTime();
  239. then = now;
  240. // ------------------------------------------------------------------
  241. // Loop and poll for data every 50 ms.
  242. // ------------------------------------------------------------------
  243. while( ((now-then) <= timeout) && count <= 0 ) {
  244. sleep(50);
  245. count = this.instream.available();
  246. now = new Date().getTime();
  247. }
  248. // ------------------------------------------------------------------
  249. // if we finally get some data, process it.
  250. // ------------------------------------------------------------------
  251. if( count > 0 ) {
  252. did_receive = true;
  253. while( count > 0 ) { // pull in all of the data there is
  254. this.process_data( this.instream.read( count ) );
  255. count = this.instream.available();
  256. }
  257. }
  258. }
  259. return did_receive;
  260. };
  261. /** If a message is already sitting in the queue, it is returned.
  262. * If not, this method waits till at most 'timeout' milliseconds
  263. * for a full jabber message to arrive and then returns that.
  264. * If none ever arrives, returns null.
  265. */
  266. jabber_socket.prototype.recv = function( timeout ) {
  267. var now, then;
  268. now = new Date().getTime();
  269. then = now;
  270. var first_pass = true;
  271. while( ((now-then) <= timeout) ) {
  272. if( this.buffer.length == 0 || !first_pass ) {
  273. if( ! this.tcp_recv( timeout ) ) {
  274. return null;
  275. }
  276. }
  277. first_pass = false;
  278. //new Logger().transport( "\n\nTCP Buffer Before: \n" + this.buffer, Logger.DEBUG );
  279. var buf = this.buffer;
  280. this.buffer = "";
  281. new Logger().transport( "CURRENT BUFFER\n" + buf,
  282. Logger.DEBUG );
  283. buf = buf.replace( /\n/g, '' ); // remove pesky newlines
  284. var reg = /<message.*?>.*?<\/message>/;
  285. var iqr = /<iq.*?>.*?<\/iq>/;
  286. var out = reg.exec(buf);
  287. if( out ) {
  288. var msg_xml = out[0];
  289. this.buffer = buf.substring( msg_xml.length, buf.length );
  290. new Logger().transport( "Building Jabber message\n\n" + msg_xml, Logger.DEBUG );
  291. var jab_msg = new jabber_message().from_xml( msg_xml );
  292. if( jab_msg.is_error_msg ) {
  293. new Logger().transport( "Received Jabber error message \n\n" + msg_xml, Logger.ERROR );
  294. }
  295. return jab_msg;
  296. } else {
  297. out = iqr.exec(buf);
  298. if( out ) {
  299. var msg_xml = out[0];
  300. this.buffer = buf.substring( msg_xml.length, buf.length );
  301. process_iq_data( msg_xml );
  302. return;
  303. } else {
  304. this.buffer = buf;
  305. }
  306. }
  307. now = new Date().getTime();
  308. }
  309. return null;
  310. };
  311. jabber_socket.prototype.process_iq_data = function( data ) {
  312. new Logger().transport( "IQ Packet received... Not Implemented\n" + data, Logger.ERROR );
  313. };
  314. /** Disconnects from the jabber server and closes down shop */
  315. jabber_socket.prototype.disconnect = function() {
  316. this.tcp_send( "</stream:stream>" );
  317. this.instream.close();
  318. this.outstream.close();
  319. };
  320. /** True if connected */
  321. jabber_socket.prototype.connected = function() {
  322. return this.is_connected;
  323. };