1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is JSIRC Library.
18 * The Initial Developer of the Original Code is
19 * New Dimensions Consulting, Inc.
20 * Portions created by the Initial Developer are Copyright (C) 1999
21 * the Initial Developer. All Rights Reserved.
24 * Robert Ginda, rginda@ndcico.com, original author
25 * Peter Van der Beken, peter.vanderbeken@pandora.be, necko-only version
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 const NS_ERROR_MODULE_NETWORK
= 2152398848;
43 const NS_ERROR_UNKNOWN_HOST
= NS_ERROR_MODULE_NETWORK
+ 30;
44 const NS_ERROR_CONNECTION_REFUSED
= NS_ERROR_MODULE_NETWORK
+ 13;
45 const NS_ERROR_NET_TIMEOUT
= NS_ERROR_MODULE_NETWORK
+ 14;
46 const NS_ERROR_NET_RESET
= NS_ERROR_MODULE_NETWORK
+ 20;
47 const NS_ERROR_UNKNOWN_PROXY_HOST
= NS_ERROR_MODULE_NETWORK
+ 42;
48 const NS_ERROR_PROXY_CONNECTION_REFUSED
= NS_ERROR_MODULE_NETWORK
+ 72;
50 // Offline error constants:
51 const NS_ERROR_BINDING_ABORTED
= NS_ERROR_MODULE_NETWORK
+ 2;
52 const NS_ERROR_ABORT
= 0x80004004;
54 const NS_NET_STATUS_RESOLVING_HOST
= NS_ERROR_MODULE_NETWORK
+ 3;
55 const NS_NET_STATUS_CONNECTED_TO
= NS_ERROR_MODULE_NETWORK
+ 4;
56 const NS_NET_STATUS_SENDING_TO
= NS_ERROR_MODULE_NETWORK
+ 5;
57 const NS_NET_STATUS_RECEIVING_FROM
= NS_ERROR_MODULE_NETWORK
+ 6;
58 const NS_NET_STATUS_CONNECTING_TO
= NS_ERROR_MODULE_NETWORK
+ 7;
60 // Security Constants.
61 const STATE_IS_BROKEN
= 1;
62 const STATE_IS_SECURE
= 2;
63 const STATE_IS_INSECURE
= 3;
65 const STATE_SECURE_LOW
= 1;
66 const STATE_SECURE_HIGH
= 2;
68 const nsIScriptableInputStream
= Components
.interfaces
.nsIScriptableInputStream
;
70 const nsIBinaryInputStream
= Components
.interfaces
.nsIBinaryInputStream
;
71 const nsIBinaryOutputStream
= Components
.interfaces
.nsIBinaryOutputStream
;
73 function toSInputStream(stream
, binary
)
79 sstream
= Components
.classes
["@mozilla.org/binaryinputstream;1"];
80 sstream
= sstream
.createInstance(nsIBinaryInputStream
);
81 sstream
.setInputStream(stream
);
85 sstream
= Components
.classes
["@mozilla.org/scriptableinputstream;1"];
86 sstream
= sstream
.createInstance(nsIScriptableInputStream
);
93 function toSOutputStream(stream
, binary
)
99 sstream
= Components
.classes
["@mozilla.org/binaryoutputstream;1"];
100 sstream
= sstream
.createInstance(Components
.interfaces
.nsIBinaryOutputStream
);
101 sstream
.setOutputStream(stream
);
111 function CBSConnection (binary
)
113 /* Since 2003-01-17 18:14, Mozilla has had this contract ID for the STS.
114 * Prior to that it didn't have one, so we also include the CID for the
115 * STS back then - DO NOT UPDATE THE ID if it changes in Mozilla.
117 const sockClassByName
=
118 Components
.classes
["@mozilla.org/network/socket-transport-service;1"];
119 const sockClassByID
=
120 Components
.classesByID
["{c07e81e0-ef12-11d2-92b6-00105a1b0d64}"];
122 var sockServiceClass
= (sockClassByName
|| sockClassByID
);
124 if (!sockServiceClass
)
125 throw ("Couldn't get socket service class.");
127 var sockService
= sockServiceClass
.getService();
129 throw ("Couldn't get socket service.");
131 this._sockService
= sockService
.QueryInterface
132 (Components
.interfaces
.nsISocketTransportService
);
134 /* Note: as part of the mess from bug 315288 and bug 316178, ChatZilla now
135 * uses the *binary* stream interfaces for all network
138 * However, these interfaces do not exist prior to 1999-11-05. To
139 * make matters worse, an incompatible change to the "readBytes"
140 * method of this interface was made on 2003-03-13; luckly, this
141 * change also added a "readByteArray" method, which we will check
142 * for below, to determin if we can use the binary streams.
145 // We want to check for working binary streams only the first time.
146 if (CBSConnection
.prototype.workingBinaryStreams
== -1)
148 CBSConnection
.prototype.workingBinaryStreams
= false;
150 if (typeof nsIBinaryInputStream
!= "undefined")
152 var isCls
= Components
.classes
["@mozilla.org/binaryinputstream;1"];
153 var inputStream
= isCls
.createInstance(nsIBinaryInputStream
);
154 if ("readByteArray" in inputStream
)
155 CBSConnection
.prototype.workingBinaryStreams
= true;
159 this.wrappedJSObject
= this;
160 if (typeof binary
!= "undefined")
161 this.binaryMode
= binary
;
163 this.binaryMode
= this.workingBinaryStreams
;
165 if (!ASSERT(!this.binaryMode
|| this.workingBinaryStreams
,
166 "Unable to use binary streams in this build."))
168 throw ("Unable to use binary streams in this build.");
172 CBSConnection
.prototype.workingBinaryStreams
= -1;
174 CBSConnection
.prototype.connect
=
175 function bc_connect(host
, port
, config
, observer
)
177 this.host
= host
.toLowerCase();
180 if (typeof config
!= "object")
183 // Lets get a transportInfo for this
184 var pps
= getService("@mozilla.org/network/protocol-proxy-service;1",
185 "nsIProtocolProxyService");
187 throw ("Couldn't get protocol proxy service");
189 var ios
= getService("@mozilla.org/network/io-service;1", "nsIIOService");
191 function getProxyFor(uri
)
193 uri
= ios
.newURI(uri
, null, null);
194 // As of 2005-03-25, 'examineForProxy' was replaced by 'resolve'.
195 if ("resolve" in pps
)
196 return pps
.resolve(uri
, 0);
197 if ("examineForProxy" in pps
)
198 return pps
.examineForProxy(uri
);
202 var proxyInfo
= null;
203 var usingHTTPCONNECT
= false;
204 if ("proxy" in config
)
206 /* Force Necko to supply the HTTP proxy info if desired. For none,
207 * force no proxy. Other values will get default treatment.
209 if (config
.proxy
== "http")
210 proxyInfo
= getProxyFor("http://" + host
+ ":" + port
);
211 else if (config
.proxy
!= "none")
212 proxyInfo
= getProxyFor("irc://" + host
+ ":" + port
);
214 /* Since the proxy info is opaque, we need to check that we got
215 * something for our HTTP proxy - we can't just check proxyInfo.type.
217 usingHTTPCONNECT
= ((config
.proxy
== "http") && proxyInfo
);
221 proxyInfo
= getProxyFor("irc://" + host
+ ":" + port
);
224 if (jsenv
.HAS_STREAM_PROVIDER
)
226 if (("isSecure" in config
) && config
.isSecure
)
228 this._transport
= this._sockService
.
229 createTransportOfType("ssl", host
, port
,
234 this._transport
= this._sockService
.
235 createTransport(host
, port
, proxyInfo
, 0, 0);
237 if (!this._transport
)
238 throw ("Error creating transport.");
240 if (jsenv
.HAS_NSPR_EVENTQ
)
241 { /* we've got an event queue, so start up an async write */
242 this._streamProvider
= new StreamProvider (observer
);
244 this._transport
.asyncWrite (this._streamProvider
, this,
249 /* no nspr event queues in this environment, we can't use async
250 * calls, so set up the streams. */
251 this._outputStream
= this._transport
.openOutputStream(0, -1, 0);
252 if (!this._outputStream
)
253 throw "Error getting output stream.";
254 this._sOutputStream
= toSOutputStream(this._outputStream
,
257 this._inputStream
= this._transport
.openInputStream(0, -1, 0);
258 if (!this._inputStream
)
259 throw "Error getting input stream.";
260 this._sInputStream
= toSInputStream(this._inputStream
,
266 /* use new necko interfaces */
267 if (("isSecure" in config
) && config
.isSecure
)
269 this._transport
= this._sockService
.
270 createTransport(["ssl"], 1, host
, port
,
275 this._transport
= this._sockService
.
276 createTransport(null, 0, host
, port
, proxyInfo
);
278 if (!this._transport
)
279 throw ("Error creating transport.");
281 /* if we don't have an event queue, then all i/o must be blocking */
283 if (jsenv.HAS_NSPR_EVENTQ)
286 openFlags = Components.interfaces.nsITransport.OPEN_BLOCKING;
288 /* no limit on the output stream buffer */
290 this._transport
.openOutputStream(openFlags
, 4096, -1);
291 if (!this._outputStream
)
292 throw "Error getting output stream.";
293 this._sOutputStream
= toSOutputStream(this._outputStream
,
296 this._inputStream
= this._transport
.openInputStream(openFlags
, 0, 0);
297 if (!this._inputStream
)
298 throw "Error getting input stream.";
299 this._sInputStream
= toSInputStream(this._inputStream
,
303 this.connectDate
= new Date();
304 this.isConnected
= true;
306 // Bootstrap the connection if we're proxying via an HTTP proxy.
307 if (usingHTTPCONNECT
)
309 this.sendData("CONNECT " + host
+ ":" + port
+ " HTTP/1.1\r\n\r\n");
312 return this.isConnected
;
316 CBSConnection
.prototype.listen
=
317 function bc_listen(port
, observer
)
319 var serverSockClass
=
320 Components
.classes
["@mozilla.org/network/server-socket;1"];
322 if (!serverSockClass
)
323 throw ("Couldn't get server socket class.");
325 var serverSock
= serverSockClass
.createInstance();
327 throw ("Couldn't get server socket.");
329 this._serverSock
= serverSock
.QueryInterface
330 (Components
.interfaces
.nsIServerSocket
);
332 this._serverSock
.init(port
, false, -1);
334 this._serverSockListener
= new SocketListener(this, observer
);
336 this._serverSock
.asyncListen(this._serverSockListener
);
338 this.port
= this._serverSock
.port
;
343 CBSConnection
.prototype.accept
=
344 function bc_accept(transport
, observer
)
346 this._transport
= transport
;
347 this.host
= this._transport
.host
.toLowerCase();
348 this.port
= this._transport
.port
;
350 if (jsenv
.HAS_STREAM_PROVIDER
)
352 if (jsenv
.HAS_NSPR_EVENTQ
)
353 { /* we've got an event queue, so start up an async write */
354 this._streamProvider
= new StreamProvider (observer
);
356 this._transport
.asyncWrite (this._streamProvider
, this,
361 /* no nspr event queues in this environment, we can't use async
362 * calls, so set up the streams. */
363 this._outputStream
= this._transport
.openOutputStream(0, -1, 0);
364 if (!this._outputStream
)
365 throw "Error getting output stream.";
366 this._sOutputStream
= toSOutputStream(this._outputStream
,
369 //this._scriptableInputStream =
370 this._inputStream
= this._transport
.openInputStream(0, -1, 0);
371 if (!this._inputStream
)
372 throw "Error getting input stream.";
373 this._sInputStream
= toSInputStream(this._inputStream
,
379 /* if we don't have an event queue, then all i/o must be blocking */
381 if (jsenv.HAS_NSPR_EVENTQ)
384 openFlags = Components.interfaces.nsITransport.OPEN_BLOCKING;
386 /* no limit on the output stream buffer */
388 this._transport
.openOutputStream(openFlags
, 4096, -1);
389 if (!this._outputStream
)
390 throw "Error getting output stream.";
391 this._sOutputStream
= toSOutputStream(this._outputStream
,
394 this._inputStream
= this._transport
.openInputStream(openFlags
, 0, 0);
395 if (!this._inputStream
)
396 throw "Error getting input stream.";
397 this._sInputStream
= toSInputStream(this._inputStream
,
401 this.connectDate
= new Date();
402 this.isConnected
= true;
404 // Clean up listening socket.
407 return this.isConnected
;
410 CBSConnection
.prototype.close
=
413 if ("_serverSock" in this && this._serverSock
)
414 this._serverSock
.close();
417 CBSConnection
.prototype.disconnect
=
418 function bc_disconnect()
420 if ("_inputStream" in this && this._inputStream
)
421 this._inputStream
.close();
422 if ("_outputStream" in this && this._outputStream
)
423 this._outputStream
.close();
424 this.isConnected
= false;
426 this._streamProvider.close();
427 if (this._streamProvider.isBlocked)
428 this._write_req.resume();
432 CBSConnection
.prototype.sendData
=
433 function bc_senddata(str
)
435 if (!this.isConnected
)
436 throw "Not Connected.";
438 if (jsenv
.HAS_NSPR_EVENTQ
&& jsenv
.HAS_STREAM_PROVIDER
)
439 this.asyncWrite (str
);
441 this.sendDataNow (str
);
444 CBSConnection
.prototype.readData
=
445 function bc_readdata(timeout
, count
)
447 if (!this.isConnected
)
448 throw "Not Connected.";
452 if (!("_sInputStream" in this)) {
453 this._sInputStream
= toSInputStream(this._inputStream
);
454 dump("OMG, setting up _sInputStream!\n");
460 if (typeof count
== "undefined")
461 count
= this._sInputStream
.available();
463 rv
= this._sInputStream
.readBytes(count
);
465 rv
= this._sInputStream
.read(count
);
469 dd ("*** Caught " + ex
+ " while reading.");
477 CBSConnection
.prototype.startAsyncRead
=
478 function bc_saread (observer
)
480 if (jsenv
.HAS_STREAM_PROVIDER
)
482 this._transport
.asyncRead (new StreamListener (observer
),
487 var cls
= Components
.classes
["@mozilla.org/network/input-stream-pump;1"];
488 var pump
= cls
.createInstance(Components
.interfaces
.nsIInputStreamPump
);
489 pump
.init(this._inputStream
, -1, -1, 0, 0, false);
490 pump
.asyncRead(new StreamListener(observer
), this);
494 CBSConnection
.prototype.asyncWrite
=
495 function bc_awrite (str
)
497 this._streamProvider
.pendingData
+= str
;
498 if (this._streamProvider
.isBlocked
)
500 this._write_req
.resume();
501 this._streamProvider
.isBlocked
= false;
505 CBSConnection
.prototype.hasPendingWrite
=
506 function bc_haspwrite ()
508 if (jsenv
.HAS_STREAM_PROVIDER
)
509 return (this._streamProvider
.pendingData
!= "");
511 return false; /* data already pushed to necko */
514 CBSConnection
.prototype.sendDataNow
=
515 function bc_senddatanow(str
)
522 this._sOutputStream
.writeBytes(str
, str
.length
);
524 this._sOutputStream
.write(str
, str
.length
);
529 dd ("*** Caught " + ex
+ " while sending.");
537 /* getSecurityState returns an array containing information about the security
538 * of the connection. The array always has at least one item, which contains a
539 * value from the STATE_IS_* enumeration at the top of this file. Iff this is
540 * STATE_IS_SECURE, the array has a second item indicating the level of
541 * security - a value from the STATE_SECURE_* enumeration.
543 * STATE_IS_BROKEN is returned if any errors occur, and STATE_IS_INSECURE is
544 * returned for disconnected sockets.
546 CBSConnection
.prototype.getSecurityState
=
547 function bc_getsecuritystate()
549 if (!this.isConnected
|| !this._transport
.securityInfo
)
550 return [STATE_IS_INSECURE
];
554 var sslSp
= Components
.interfaces
.nsISSLStatusProvider
;
555 var sslStatus
= Components
.interfaces
.nsISSLStatus
;
557 // Get the actual SSL Status
558 sslSp
= this._transport
.securityInfo
.QueryInterface(sslSp
);
559 sslStatus
= sslSp
.SSLStatus
.QueryInterface(sslStatus
);
560 // Store appropriate status
561 if (!("keyLength" in sslStatus
) || !sslStatus
.keyLength
)
562 return [STATE_IS_BROKEN
];
563 else if (sslStatus
.keyLength
>= 90)
564 return [STATE_IS_SECURE
, STATE_SECURE_HIGH
];
566 return [STATE_IS_SECURE
, STATE_SECURE_LOW
];
570 // Something goes wrong -> broken security icon
571 dd("Exception getting certificate for connection: " + ex
.message
);
572 return [STATE_IS_BROKEN
];
576 CBSConnection
.prototype.getCertificate
=
577 function bc_getcertificate()
579 if (!this.isConnected
|| !this._transport
.securityInfo
)
582 var sslSp
= Components
.interfaces
.nsISSLStatusProvider
;
583 var sslStatus
= Components
.interfaces
.nsISSLStatus
;
585 // Get the actual SSL Status
586 sslSp
= this._transport
.securityInfo
.QueryInterface(sslSp
);
587 sslStatus
= sslSp
.SSLStatus
.QueryInterface(sslStatus
);
589 // return the certificate
590 return sslStatus
.serverCert
;
595 throw "Not Implemented.";
598 if (!jsenv
.HAS_NSPR_EVENTQ
)
600 CBSConnection
.prototype.startAsyncRead
= _notimpl
;
601 CBSConnection
.prototype.asyncWrite
= _notimpl
;
603 else if (jsenv
.HAS_STREAM_PROVIDER
)
605 CBSConnection
.prototype.sendDataNow
= _notimpl
;
609 CBSConnection
.prototype.asyncWrite
= _notimpl
;
614 function StreamProvider(observer
)
616 this._observer
= observer
;
619 StreamProvider
.prototype.pendingData
= "";
620 StreamProvider
.prototype.isBlocked
= true;
622 StreamProvider
.prototype.close
=
625 this.isClosed
= true;
628 StreamProvider
.prototype.onDataWritable
=
629 function sp_datawrite (request
, ctxt
, ostream
, offset
, count
)
631 //dd ("StreamProvider.prototype.onDataWritable");
633 if ("isClosed" in this && this.isClosed
)
634 throw Components
.results
.NS_BASE_STREAM_CLOSED
;
636 if (!this.pendingData
)
638 this.isBlocked
= true;
640 /* this is here to support pre-XPCDOM builds (0.9.0 era), which
641 * don't have this result code mapped. */
642 if (!Components
.results
.NS_BASE_STREAM_WOULD_BLOCK
)
645 throw Components
.results
.NS_BASE_STREAM_WOULD_BLOCK
;
648 var len
= ostream
.write (this.pendingData
, this.pendingData
.length
);
649 this.pendingData
= this.pendingData
.substr (len
);
652 StreamProvider
.prototype.onStartRequest
=
653 function sp_startreq (request
, ctxt
)
655 //dd ("StreamProvider::onStartRequest: " + request + ", " + ctxt);
659 StreamProvider
.prototype.onStopRequest
=
660 function sp_stopreq (request
, ctxt
, status
)
662 //dd ("StreamProvider::onStopRequest: " + request + ", " + ctxt + ", " +
665 this._observer
.onStreamClose(status
);
668 function StreamListener(observer
)
670 this._observer
= observer
;
673 StreamListener
.prototype.onStartRequest
=
674 function sl_startreq (request
, ctxt
)
676 //dd ("StreamListener::onStartRequest: " + request + ", " + ctxt);
679 StreamListener
.prototype.onStopRequest
=
680 function sl_stopreq (request
, ctxt
, status
)
682 //dd ("StreamListener::onStopRequest: " + request + ", " + ctxt + ", " +
685 this._observer
.onStreamClose(status
);
688 StreamListener
.prototype.onDataAvailable
=
689 function sl_dataavail (request
, ctxt
, inStr
, sourceOffset
, count
)
691 ctxt
= ctxt
.wrappedJSObject
;
694 dd ("*** Can't get wrappedJSObject from ctxt in " +
695 "StreamListener.onDataAvailable ***");
699 if (!("_sInputStream" in ctxt
))
700 ctxt
._sInputStream
= toSInputStream(inStr
, false);
703 this._observer
.onStreamDataAvailable(request
, inStr
, sourceOffset
,
707 function SocketListener(connection
, observer
)
709 this._connection
= connection
;
710 this._observer
= observer
;
713 SocketListener
.prototype.onSocketAccepted
=
714 function sl_onSocketAccepted(socket
, transport
)
716 this._observer
.onSocketAccepted(socket
, transport
);
718 SocketListener
.prototype.onStopListening
=
719 function sl_onStopListening(socket
, status
)
721 delete this._connection
._serverSockListener
;
722 delete this._connection
._serverSock
;