diff --git a/js/lib/connection-xpcom.js b/js/lib/connection-xpcom.js --- a/js/lib/connection-xpcom.js +++ b/js/lib/connection-xpcom.js @@ -224,67 +224,103 @@ function CBSConnection (binary) CBSConnection.prototype.workingBinaryStreams = -1; CBSConnection.prototype.connect = function bc_connect(host, port, config, observer) { this.host = host.toLowerCase(); this.port = port; - /* The APIs below want either host:port or host and port seperately. For - * the combined case, we must preserve the square brackets around IPv6 - * literals. For the split case, we must strip them. + /* The APIs below want host:port. Later on, we also reformat the host to + * strip IPv6 literal brackets. */ var hostPort = host + ":" + port; - if (host[0] == '[' && host[host.length - 1] == ']') - host = host.substr(1, host.length - 2); - if (typeof config != "object") + if (!config) config = {}; + if (!("proxyInfo" in config)) + { // Lets get a transportInfo for this var pps = getService("@mozilla.org/network/protocol-proxy-service;1", "nsIProtocolProxyService"); - if (!pps) - throw ("Couldn't get protocol proxy service"); + var ios = getService("@mozilla.org/network/io-service;1", + "nsIIOService"); - var ios = getService("@mozilla.org/network/io-service;1", "nsIIOService"); - - function getProxyFor(uri) - { - uri = ios.newURI(uri, null, null); - // As of 2005-03-25, 'examineForProxy' was replaced by 'resolve'. - if ("resolve" in pps) - return pps.resolve(uri, 0); - if ("examineForProxy" in pps) - return pps.examineForProxy(uri); - return null; - }; - - var proxyInfo = null; - var usingHTTPCONNECT = false; - if ("proxy" in config) - { /* Force Necko to supply the HTTP proxy info if desired. For none, * force no proxy. Other values will get default treatment. */ + var uri = "irc://" + hostPort; + if ("proxy" in config) + { if (config.proxy == "http") - proxyInfo = getProxyFor("http://" + hostPort); - else if (config.proxy != "none") - proxyInfo = getProxyFor("irc://" + hostPort); + uri = "http://" + hostPort; + else if (config.proxy == "none") + uri = ""; + } + + var self = this; + function continueWithProxy(proxyInfo) + { + config.proxyInfo = proxyInfo; + try + { + self.connect(host, port, config, observer); + } + catch (ex) + { + if ("onSocketConnection" in observer) + observer.onSocketConnection(host, port, config, ex); + return; + } + if ("onSocketConnection" in observer) + observer.onSocketConnection(host, port, config); + } + + if (uri) + { + uri = ios.newURI(uri, null, null); + if ("asyncResolve" in pps) + { + pps.asyncResolve(uri, 0, { + onProxyAvailable: function(request, uri, proxyInfo, status) { + continueWithProxy(proxyInfo); + } + }); + } + else if ("resolve" in pps) + { + continueWithProxy(pps.resolve(uri, 0)); + } + else if ("examineForProxy" in pps) + { + continueWithProxy(pps.examineForProxy(uri)); + } + else + { + throw "Unable to find method to resolve proxies"; + } + } + else + { + continueWithProxy(null); + } + return true; + } + + // Strip the IPv6 literal brackets; all the APIs below don't want them. + if (host[0] == '[' && host[host.length - 1] == ']') + host = host.substr(1, host.length - 2); /* Since the proxy info is opaque, we need to check that we got * something for our HTTP proxy - we can't just check proxyInfo.type. */ - usingHTTPCONNECT = ((config.proxy == "http") && proxyInfo); - } - else - { - proxyInfo = getProxyFor("irc://" + hostPort); - } + var proxyInfo = config.proxyInfo || null; + var usingHTTPCONNECT = ("proxy" in config) && (config.proxy == "http") + && proxyInfo; if (proxyInfo && ("type" in proxyInfo) && (proxyInfo.type == "unknown")) throw JSIRC_ERR_PAC_LOADING; if (jsenv.HAS_STREAM_PROVIDER) { if (("isSecure" in config) && config.isSecure) { @@ -297,17 +333,17 @@ function bc_connect(host, port, config, this._transport = this._sockService. createTransport(host, port, proxyInfo, 0, 0); } if (!this._transport) throw ("Error creating transport."); if (jsenv.HAS_NSPR_EVENTQ) { /* we've got an event queue, so start up an async write */ - this._streamProvider = new StreamProvider (observer); + this._streamProvider = new StreamProvider(); this._write_req = this._transport.asyncWrite (this._streamProvider, this, 0, -1, 0); } else { /* no nspr event queues in this environment, we can't use async * calls, so set up the streams. */ @@ -366,19 +402,17 @@ function bc_connect(host, port, config, this.binaryMode); } this.connectDate = new Date(); this.isConnected = true; // Bootstrap the connection if we're proxying via an HTTP proxy. if (usingHTTPCONNECT) - { this.sendData("CONNECT " + hostPort + " HTTP/1.1\r\n\r\n"); - } return true; } CBSConnection.prototype.listen = function bc_listen(port, observer) { diff --git a/js/lib/dcc.js b/js/lib/dcc.js --- a/js/lib/dcc.js +++ b/js/lib/dcc.js @@ -526,25 +526,20 @@ function CIRCDCCChat(parent, user, port) this.user = user; this.localIP = this.parent.localIP; this.remoteIP = user.remoteIP; this.port = port; this.unicodeName = user.unicodeName; this.viewName = "DCC: " + user.unicodeName; // Set up the initial state. - this.state = DCC_STATE_INIT; - this.dir = DCC_DIR_UNKNOWN; this.requested = null; this.connection = null; - this.savedLine = ""; - this.state = new CIRCDCCState(parent, this, "dcc-chat"); - this.parent.chats.push(this); // Give ourselves a "me" object for the purposes of displaying stuff. this.me = this.parent.addUser(this.user.netUser.parent.me, "0.0.0.0"); if ("onInit" in this) this.onInit(); @@ -603,32 +598,39 @@ function dchat_request() // Call to make this end accept DCC Chat with target user. CIRCDCCChat.prototype.accept = function dchat_accept() { this.state.sendAccept(); this.connection = new CBSConnection(); - if (this.connection.connect(this.remoteIP, this.port)) + this.connection.connect(this.remoteIP, this.port, null, this); + + return true; +} + +// This may be called synchronously or asynchronously by CBSConnection.connect. +CIRCDCCChat.prototype.onSocketConnection = +function dchat_onsocketconnection(host, port, config, exception) +{ + if (!exception) { this.state.socketConnected(); if (jsenv.HAS_NSPR_EVENTQ) this.connection.startAsyncRead(this); else this.eventPump.addEvent(new CEvent("dcc-chat", "poll", this, "onPoll")); } else { this.state.failed(); } - - return (this.state == DCC_STATE_ACCEPTED); } // Call to make this end decline DCC Chat with target user. CIRCDCCChat.prototype.decline = function dchat_decline() { this.state.sendDecline(); @@ -920,23 +922,19 @@ function CIRCDCCFileTransfer(parent, use this.remoteIP = user.remoteIP; this.port = port; this.filename = file; this.size = size; this.unicodeName = user.unicodeName; this.viewName = "File: " + this.filename; // Set up the initial state. - this.state = DCC_STATE_INIT; - this.dir = DCC_DIR_UNKNOWN; this.requested = null; this.connection = null; - this.state = new CIRCDCCState(parent, this, "dcc-file"); - this.parent.files.push(this); // Give ourselves a "me" object for the purposes of displaying stuff. this.me = this.parent.addUser(this.user.netUser.parent.me, "0.0.0.0"); if ("onInit" in this) this.onInit(); @@ -1035,36 +1033,42 @@ function dfile_accept(localFile) this.localFile = new LocalFile(localFile, ">"); this.localPath = localFile.path; this.filestream = Components.classes["@mozilla.org/binaryoutputstream;1"]; this.filestream = this.filestream.createInstance(nsIBinaryOutputStream); this.filestream.setOutputStream(this.localFile.outputStream); + this.position = 0; this.connection = new CBSConnection(true); - this.position = 0; + this.connection.connect(this.remoteIP, this.port, null, this); - if (this.connection.connect(this.remoteIP, this.port)) + return true; +} + +// This may be called synchronously or asynchronously by CBSConnection.connect. +CIRCDCCFileTransfer.prototype.onSocketConnection = +function dfile_onsocketconnection(host, port, config, exception) +{ + if (!exception) { this.state.socketConnected(); if (jsenv.HAS_NSPR_EVENTQ) this.connection.startAsyncRead(this); else this.eventPump.addEvent(new CEvent("dcc-file", "poll", this, "onPoll")); } else { this.state.failed(); this.dispose(); } - - return (this.state == DCC_STATE_ACCEPTED); } // Call to make this end decline DCC File from target user. CIRCDCCFileTransfer.prototype.decline = function dfile_decline() { this.state.sendDecline(); diff --git a/js/lib/irc.js b/js/lib/irc.js --- a/js/lib/irc.js +++ b/js/lib/irc.js @@ -289,24 +289,27 @@ function net_cancel() if (this.state == NET_ONLINE) { this.quit(); } // We're waiting for the 001, too late to throw a reconnect, or... else if (this.state == NET_CONNECTING) { this.state = NET_CANCELLING; + if ("primServ" in this && this.primServ.isConnected) + { this.primServ.connection.disconnect(); - // Throw the necessary error events: - ev = new CEvent ("network", "error", this, "onError"); - ev.server = this; - ev.debug = "Connect sequence was cancelled."; + + var ev = new CEvent("network", "error", this, "onError"); + ev.server = this.primServ; + ev.debug = "Connect sequence was canceled."; ev.errorCode = JSIRC_ERR_CANCELLED; this.eventPump.addEvent(ev); } + } // We're waiting for onDoConnect, so try a reconnect (which will fail us) else if (this.state == NET_WAITING) { this.state = NET_CANCELLING; // onDoConnect will throw the error events for us, as it will fail this.immediateConnect(); } else @@ -327,24 +330,24 @@ function net_doconnect(e) clearTimeout(this.reconnectTimer); delete this.reconnectTimer; } var ev; if (this.state == NET_CANCELLING) { - if ("primServ" in this && this.primServ.connection) + if ("primServ" in this && this.primServ.isConnected) this.primServ.connection.disconnect(); else this.state = NET_OFFLINE; - ev = new CEvent ("network", "error", this, "onError"); - ev.server = this; - ev.debug = "Connect sequence was cancelled."; + ev = new CEvent("network", "error", this, "onError"); + ev.server = this.primServ; + ev.debug = "Connect sequence was canceled."; ev.errorCode = JSIRC_ERR_CANCELLED; this.eventPump.addEvent(ev); return false; } if ("primServ" in this && this.primServ.isConnected) return true; @@ -372,21 +375,17 @@ function net_doconnect(e) ev.port = this.serverList[host].port; ev.server = this.serverList[host]; ev.connectAttempt = this.connectAttempt; ev.reconnectDelayMs = this.getReconnectDelayMs(); this.eventPump.addEvent (ev); try { - if (!this.serverList[host].connect(null)) - { - /* connect failed, try again */ - this.delayedConnect(); - } + this.serverList[host].connect(); } catch(ex) { this.state = NET_OFFLINE; ev = new CEvent("network", "error", this, "onError"); ev.server = this; ev.debug = "Exception opening socket: " + ex; @@ -663,56 +662,67 @@ function chan_getchannel(name) if (tname in this.channels) return this.channels[tname]; return null; } CIRCServer.prototype.connect = -function serv_connect (password) +function serv_connect() { - try - { - this.connection = new CBSConnection(); - } - catch (ex) - { - ev = new CEvent ("server", "error", this, "onError"); - ev.server = this; - ev.debug = "Couldn't create socket :" + ex; - ev.errorCode = JSIRC_ERR_NO_SOCKET; - ev.exception = ex; - this.parent.eventPump.addEvent (ev); - return false; - } + if (this.connection != null) + throw "Server already has a connection pending or established"; var config = { isSecure: this.isSecure }; if (this.parent.PROXY_TYPE_OVERRIDE) config.proxy = this.parent.PROXY_TYPE_OVERRIDE; - if (this.connection.connect(this.hostname, this.port, config)) + this.connection = new CBSConnection(); + this.connection.connect(this.hostname, this.port, config, this); +} + +// This may be called synchronously or asynchronously by CBSConnection.connect. +CIRCServer.prototype.onSocketConnection = +function serv_onsocketconnection(host, port, config, exception) +{ + if (this.parent.state == NET_CANCELLING) + { + this.connection.disconnect(); + this.connection = null; + this.parent.state = NET_OFFLINE; + + var ev = new CEvent("network", "error", this.parent, "onError"); + ev.server = this; + ev.debug = "Connect sequence was canceled."; + ev.errorCode = JSIRC_ERR_CANCELLED; + this.parent.eventPump.addEvent(ev); + } + else if (!exception) { var ev = new CEvent("server", "connect", this, "onConnect"); - - if (password) - this.password = password; - ev.server = this; - this.parent.eventPump.addEvent (ev); + this.parent.eventPump.addEvent(ev); this.isConnected = true; if (jsenv.HAS_NSPR_EVENTQ) this.connection.startAsyncRead(this); else this.parent.eventPump.addEvent(new CEvent("server", "poll", this, "onPoll")); } - - return true; + else + { + var ev = new CEvent("server", "disconnect", this, "onDisconnect"); + ev.server = this; + ev.reason = "error"; + ev.exception = exception; + ev.disconnectStatus = NS_ERROR_ABORT; + this.parent.eventPump.addEvent(ev); + } } /* * What to do when the client connects to it's primary server */ CIRCServer.prototype.onConnect = function serv_onconnect (e) { diff --git a/locales/en-US/chrome/chatzilla.properties b/locales/en-US/chrome/chatzilla.properties --- a/locales/en-US/chrome/chatzilla.properties +++ b/locales/en-US/chrome/chatzilla.properties @@ -1103,17 +1103,17 @@ msg.dcc.err.notdcc = Must specify # /dcc-list words and phrases. msg.dcclist.dir.in = incoming msg.dcclist.dir.out = outgoing (offer) msg.dcclist.to = to msg.dcclist.from = from ## Params: index, state, direction (incoming/outgoing), DCC type, direction (to/from), user (ip:port), commands. msg.dcclist.line = %S: %S %S DCC %S %S %S (%S:%S) %S ## Params: waiting, running, done. -msg.dcclist.summary = DCC sessions: %S pending, %S connected, %S finished. +msg.dcclist.summary = DCC sessions: %S pending, %S connected, %S failed. msg.dccaccept.disabled = Currently not auto-accepting DCC on this network. msg.dccaccept.list = Currently auto-accepting DCC on this network from [%S]. msg.dccaccept.add = Now auto-accepting DCC on this network from %S. msg.dccaccept.del = No longer auto-accepting DCC on this network from %S. msg.dccaccept.adderr = You are already auto-accepting DCC on this network from %S. msg.dccaccept.delerr = %S not found on your DCC auto-accept list for this network. diff --git a/xul/content/handlers.js b/xul/content/handlers.js --- a/xul/content/handlers.js +++ b/xul/content/handlers.js @@ -3156,17 +3156,17 @@ function my_autoperform() CIRCDCCChat.prototype.onInit = function my_dccinit(e) { } CIRCDCCChat.prototype._getParams = function my_dccgetparams() { - return [this.user.unicodeName, this.localIP, this.port]; + return [this.unicodeName, this.remoteIP, this.port]; } CIRCDCCChat.prototype.onPrivmsg = function my_dccprivmsg(e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.displayHere(toUnicode(e.line, this), "PRIVMSG", e.user, "ME!"); client.munger.getRule(".mailto").enabled = false; @@ -3228,18 +3228,18 @@ function my_dccfilegetparams() var dir = MSG_UNKNOWN; if (this.state.dir == DCC_DIR_GETTING) dir = MSG_DCCLIST_FROM; if (this.state.dir == DCC_DIR_SENDING) dir = MSG_DCCLIST_TO; - return [this.filename, dir, this.user.unicodeName, - this.localIP/*FIXME*/, this.port/*FIXME?*/]; + return [this.filename, dir, this.unicodeName, + this.remoteIP, this.port]; } CIRCDCCFileTransfer.prototype.onConnect = function my_dccfileconnect(e) { this.displayHere(getMsg(MSG_DCCFILE_OPENED, this._getParams()), "DCC-FILE"); this.busy = true; this.speed = 0;