Commits

Patrick Cloke committed effebed

Add oscar code.

  • Participants
  • Parent commits 2d594a0

Comments (0)

Files changed (2)

+diff -r ded093a82746 chat/modules/socket.jsm
+--- a/chat/modules/socket.jsm	Thu Sep 27 01:40:36 2012 +0200
++++ b/chat/modules/socket.jsm	Fri Oct 12 08:11:30 2012 -0400
+@@ -32,7 +32,7 @@
+  *   onBadCertificate(AString aNSSErrorMessage)
+  *   onConnectionClosed()
+  *   onDataReceived(data)
+- *   onBinaryDataReceived(ArrayBuffer data, int length)
++ *   size = onBinaryDataReceived(byte array data)
+  *   onTransportStatus(nsISocketTransport transport, nsresult status,
+  *                     unsigned long progress, unsigned longprogressMax)
+  *   log(message)
+@@ -300,20 +300,39 @@
+       this._incomingDataBuffer = this._incomingDataBuffer
+                                      .concat(this._binaryInputStream
+                                                  .readByteArray(aCount));
+-
+-      let size = this.inputSegmentSize || this._incomingDataBuffer.length;
+-      this.log(size + " " + this._incomingDataBuffer.length);
+-      while (this._incomingDataBuffer.length >= size) {
++      
++      // Fixed size data: send all the received packets
++      if (this.inputSegmentSize) {
++        while (this._incomingDataBuffer.length >= this.inputSegmentSize) {
++          // Create a new ArrayBuffer.
++          let buffer = new ArrayBuffer(size);
++          let uintArray = new Uint8Array(buffer);
++          
++            // Set the data into the array while saving the extra data
++            uintArray.set(
++              this._incomingDataBuffer.splice(0, this.inputSegmentSize));
++          
++          // Notify we've received data.
++          this.onBinaryDataReceived(buffer);
++        }
++      }
++      // Variable data size, the callee must return how much data was
++      // handled.
++      else {
++        let size = this._incomingDataBuffer.length;
++        
++        // Create a new ArrayBuffer.
+         let buffer = new ArrayBuffer(size);
+-
+-        // Create a new ArraybufferView
+         let uintArray = new Uint8Array(buffer);
+-
++        
+         // Set the data into the array while saving the extra data
+-        uintArray.set(this._incomingDataBuffer.splice(0, size));
+-
+-        // Notify we've received data
+-        this.onBinaryDataReceived(buffer);
++        uintArray.set(this._incomingDataBuffer);
++        
++        // Notify we've received data.
++        size = this.onBinaryDataReceived(buffer);
++        
++        // Remove the handled data.
++        this._incomingDataBuffer.splice(0, size);
+       }
+     } else {
+       if (this.delimiter) {
+@@ -477,25 +496,31 @@
+    ********************* Methods for subtypes to override **********************
+    *****************************************************************************
+    */
+-  log: function(aString) { },
++  log: function(aString) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+   // Called when a connection is established.
+-  onConnection: function() { },
++  onConnection: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+   // Called when a socket is accepted after listening.
+-  onConnectionHeard: function() { },
++  onConnectionHeard: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+   // Called when a connection times out.
+-  onConnectionTimedOut: function() { },
++  onConnectionTimedOut: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+   // Called when a socket request's network is reset.
+-  onConnectionReset: function() { },
++  onConnectionReset: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+   // Called when the certificate provided by the server didn't satisfy NSS.
+-  onBadCertificate: function(aNSSErrorMessage) { },
++  onBadCertificate: function(aNSSErrorMessage) {
++    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
++  },
+   // Called when the other end has closed the connection.
+-  onConnectionClosed: function() { },
++  onConnectionClosed: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
+ 
+   // Called when ASCII data is available.
+-  onDataReceived: function(/* string */ aData) { },
++  onDataReceived: function(/* string */ aData) {
++    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
++  },
+ 
+   // Called when binary data is available.
+-  onBinaryDataReceived: function(/* ArrayBuffer */ aData) { },
++  onBinaryDataReceived: function(/* ArrayBuffer */ aData) {
++    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
++  },
+ 
+   /* QueryInterface and nsIInterfaceRequestor implementations */
+   _interfaces: [Ci.nsIServerSocketListener, Ci.nsIStreamListener,
+diff -r ded093a82746 chat/protocols/oscar/aim.js
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/chat/protocols/oscar/aim.js	Fri Oct 12 08:11:30 2012 -0400
+@@ -0,0 +1,27 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++Components.utils.import("resource:///modules/imXPCOMUtils.jsm");
++Components.utils.import("resource:///modules/jsProtoHelper.jsm");
++
++function aimProtocol() { }
++aimProtocol.prototype = {
++  __proto__: GenericProtocolPrototype,
++  get name() "AIM",
++  options: {
++    "server": {label: "Server",    default: "slogin.oscar.aol.com"},
++    "port" : {label: "Port", default: 5190},
++    "encryption": {label: "Connection Security",  default: "option2",
++             listValues: {"option1": "Use encryption if available",
++                          "option2": "Require encryption",
++                          "option3": "Don't use encryption"}},
++    "clientLogin": {label: "Use clientLogin", default: false},
++    "proxyServer": {label: "Always use AIM/ICQ proxy server for file tansfer and direct IM (slower, but does not reveal your IP address)", default: false},
++    "simultaneousLogins": {label: "Allow multiple simultaneous logins", default: true}
++  },
++  getAccount: function(aImAccount) new oscarAccount(this, aImAccount),
++  classID: Components.ID("{c6489a4b-6a04-4102-9d5f-ca74952981ca}"),
++};
++
++const NSGetFactory = XPCOMUtils.generateNSGetFactory([aimProtocol]);
+\ No newline at end of file
+diff -r ded093a82746 chat/protocols/oscar/oscar.js
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/chat/protocols/oscar/oscar.js	Fri Oct 12 08:11:30 2012 -0400
+@@ -0,0 +1,76 @@
++// XXX This needs DataView to do endianess.
++
++/*
++ * A TLV (Type, Length and Value) data structure:
++ *  Unsigned Short  type    Describes what the value represents.
++ *  Unsigned Short  length  The length of the data block.
++ *  Bytes           value   The raw payload.
++ *
++ * The overall length of a TlvBlock is length + 4.
++ *
++ * The inputs to this are:
++ *  type    The type of the TLV Block.
++ *  value   An ArrayBuffer containing the data.
++ */
++function TlvBlock(aType, aValue) {
++    this.type = aType;
++    this.value = aValue;
++}
++TlvBlock.prototype = {
++    type: null,
++    get length() this.value.byteLength,
++    value: new ArrayBuffer(0),
++    
++    toBytes: function() {
++        let data = new ArrayBuffer(this.value.byteLength + 4);
++        // The first two bytes are unsigned shorts.
++        let view = new Uint16Array(data, 0, 2);
++        view[0] = this.type;
++        view[1] = this.value.byteLength;
++        
++        // The rest just gets the data copied into it.
++        view = new Uint8Array(data, 4);
++        view.set(new Uint8Array(this.value));
++        
++        return data;
++    }
++}
++TlvBlock.fromBytes = function(data) {
++    let view = new Uint16Array(data);
++    print(data)
++    return new TlvBlock(view[0], data.slice(4, view[1]));
++}
++
++/*
++ * Convert a JavaScript string into the ASCII representation as an
++ * ArrayBuffer.
++ */
++function AsciiString(aString) {
++    let data = new ArrayBuffer(aString.length);
++    let view = new Uint8Array(data);
++    for (let i = 0; i < aString.length; ++i)
++        view[i] = aString[i].charCodeAt();
++    return data;
++}
++
++let data = new ArrayBuffer(4);
++let view = Uint8Array(data);
++for (let i = 0; i < view.length; i++)
++    view[i] = i;
++
++let block = new TlvBlock(1, data);
++
++function printBlock(block) {
++view = Uint8Array(block.toBytes());
++let bytes = [];
++for (let i = 0; i < view.length; i++)
++    bytes.push(view[i]);
++print(bytes)
++}
++printBlock(block);
++
++//let newBlock = TlvBlock.fromBytes(block.toBytes());
++//print(newBlock.type)
++
++block = new TlvBlock(0x04, AsciiString("This is a test."));
++printBlock(block)
+\ No newline at end of file
+diff -r ded093a82746 chat/protocols/oscar/oscar.jsm
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/chat/protocols/oscar/oscar.jsm	Fri Oct 12 08:11:30 2012 -0400
+@@ -0,0 +1,120 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++const EXPORTED_SYMBOLS = ["oscarAccount", "oscarProtocol"];
++
++const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
++
++Cu.import("resource:///modules/imXPCOMUtils.jsm");
++Cu.import("resource:///modules/jsProtoHelper.jsm");
++Cu.import("resource:///modules/oscarFlapConnection.jsm");
++Cu.import("resource:///modules/oscarUtils.jsm");
++
++function Conversation(aAccount)
++{
++  this._init(aAccount);
++}
++Conversation.prototype = {
++  _disconnected: false,
++  _setDisconnected: function() {
++    this._disconnected = true;
++  },
++  close: function() {
++    if (!this._disconnected)
++      this.account.disconnect(true);
++  },
++  sendMsg: function (aMsg) {
++    if (this._disconnected) {
++      this.writeMessage("jstest", "This message could not be sent because the conversation is no longer active: " + aMsg, {system: true, error: true});
++      return;
++    }
++
++    this.writeMessage("You", aMsg, {outgoing: true});
++    this.writeMessage("/dev/null", "Thanks! I appreciate your attention.",
++                      {incoming: true, autoResponse: true});
++  },
++
++  get name() "/dev/null",
++};
++Conversation.prototype.__proto__ = GenericConvIMPrototype;
++
++function Account(aProtoInstance, aImAccount)
++{
++  this._init(aProtoInstance, aImAccount);
++}
++Account.prototype = {
++  _flapConnection: null,
++  _loginCookie: new ArrayBuffer(0);
++  connect: function() {
++    this.reportConnecting();
++    
++    this._flapConnection = oscarFlapConnection(this, true);
++    this._flapConnection.connect(this._server, this._port, this._ssl ? ["ssl"] : []);
++  },
++  _conv: null,
++  disconnect: function(aSilent) {
++    this.reportDisconnecting(Components.interfaces.prplIAccount.NO_ERROR, "");
++    if (!aSilent)
++      this._conv.writeMessage("jstest", "You have disconnected.", {system: true});
++    if (this._conv) {
++      this._conv._setDisconnected();
++      delete this._conv;
++    }
++    this.reportDisconnected();
++  },
++
++  get canJoinChat() true,
++  chatRoomFields: {
++    channel: {label: "_Channel Field", required: true},
++    channelDefault: {label: "_Field with default", default: "Default Value"},
++    password: {label: "_Password Field", default: "", isPassword: true,
++               required: false},
++    sampleIntField: {label: "_Int Field", default: 4, min: 0, max: 10,
++                     required: true}
++  },
++  
++  handleFlapPacket: function(aConnection, aChannel, aData) {
++    if (aChannel == kFlapChannels.LOGIN) {
++        let view = Uint32Array(aData);
++        if (view[0] != kFlapVersion) {
++            // XXX Disconnect.
++            return;
++        }
++        if (aData.byteLength > 4) {
++            // XXX Unexpected data.
++            return;
++        }
++        
++      // Initial authentication server.
++      let isInitialAuthentication = aConnection.host == this._server;
++      
++        let data;
++        if (isInitialAuthentication)
++            data = new ArrayBuffer(4);
++        else
++            data = newArrayBuffer(...);
++        let view = new Uint32Array(data);
++        view[0] = kFlapVersion;
++        
++        if (!isInitialAuthentication) {
++            // XXX Add the cookie
++            let loginCookie = new TlvBlock(0x0006, this._loginCookie);
++            copyBytes(data, loginCookie, 4, 0);
++        }
++        
++        aConnection.sendFlapPacket(kFlapChannels.LOGIN, data);
++        
++        // On the initial authentication, request 
++        if (isInitialAuthentication) {
++          aConnection.sendFlapPacket(kFlapChannels.LOGIN, data);
++        }
++        
++      if (isInitialAuthentication) {
++        // Request a cookie.
++        
++      }
++    }
++  }
++};
++Account.prototype.__proto__ = GenericAccountPrototype;
+\ No newline at end of file
+diff -r ded093a82746 chat/protocols/oscar/oscarFlapConnection.jsm
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/chat/protocols/oscar/oscarFlapConnection.jsm	Fri Oct 12 08:11:30 2012 -0400
+@@ -0,0 +1,137 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++const EXPORTED_SYMBOLS = ["flapConnection", "kFlapChannels", "kFlapVersion"];
++
++const kFlapConstant = 0x2A;
++// The length of a FLAP packet's header, in bytes.
++const kFlapHeaderLength = 6;
++
++// The possible options for a channel.
++const kFlapChannels = {
++  // Initiating a FLAP connection.
++  "LOGIN": 1,
++  // Most packets contain SNAC commands.
++  "SNAC": 2,
++  // FLAP level errors.
++  "ERROR": 3,
++  // Indicate why a connection is going to be closed.
++  "CLOSED": 4,
++  // Unknown usage.
++  "UNKNOWN": 5
++};
++
++const kFlapVersion = 1;
++
++/*
++ * A FLAP connection has only FLAP packets sent over it. Each packet has a
++ * sequence number to ensure proper ordering and connections have channels
++ * assigned to them.
++ */
++function flapConnection(aAccount, aIsInitialLoginServer) {
++  this._account = aAccount;
++  this.isInitialLoginServer = aIsInitialLoginServer;
++}
++flapConnection.prototype = {
++  __proto__: Socket,
++  binary: true,
++  connectTimeout: 60, // Failure to connect after 1 minute
++  readWriteTimeout: 300, // Failure when no data for 5 minutes
++
++  sequenceNumber: 0,
++  isInitialLoginServer: false,
++
++  // Parse the flap packet.
++  onBinaryDataReceived: function(aData) {
++    // There's definitely not a full packet.
++    if (aData.byteLength < kFlapHeaderLength)
++      return 0;
++    
++    let view = Uint8Array(aData);
++    if (view[0] == kFlapConstant) {
++      // XXX Disconnect here.
++    }
++    let channel = view[1];
++    view = Uint16Array(aData, 2, 2);
++    let sequenceNumber = view[0];
++    let length = view[1];
++    // The full payload has not been received.
++    if (aData.byteLength - kFlapHeaderLength < length)
++      return 0;
++    
++    let data = aData.slice(kFlapHeaderLength, length);
++    
++    this._account.handleFlapPacket(channel, data);
++    
++    // The total number of bytes handled is the payload length + 6.
++    let handledLength = kFlapHeaderLength + length;
++    // Recursively handle each incoming FLAP Packet if multiple are
++    // available.
++    return handledLength +
++           this.onBinaryDataReceived(aData.slice(handledLength));
++  },
++  
++  /*
++   * A FLAP packet has 5 fields:
++   *  UnsignedByte  0x2A
++   *  UnsignedByte  channel
++   *  UnsignedShort seqnum
++   *  UnsignedShort length
++   *  Bytes         data
++   */
++  sendFlapPacket: function(aChannel, aData) {
++    let packet = new ArrayBuffer(aData.byteLength + kFlapHeaderLength);
++    // Set the first two unsigned bytes.
++    let view = new Uint8Array(packet, 0, 2);
++    view[0] = kFlapConstant;
++    view[1] = aChannel;
++    // Set the next two unsigned shorts.
++    view = newUint16Array(packet, 2, 2);
++    view[0] = this.sequenceNumber++; // Increment the sequence number.
++    view[1] = aData.byteLength;
++    // Copy the data into the packet.
++    copyBytes(packet, aData, kFlapHeaderLength, 0);
++    
++    this.sendBinaryData(packet);
++  },
++  
++  // When a connection is started, immediately send the 
++  onConnection: function() {
++    let data;
++    // For the initial connection, there is no cookie to send.
++    if (this.isInitialLoginServer)
++      data = new ArrayBuffer(0);
++    else
++      data = new ArrayBuffer(40);
++    
++    
++  },
++
++  // Throw errors if the socket has issues.
++  onConnectionClosed: function () {
++    if (!this._account.imAccount || this._account.disconnecting ||
++        this._account.disconnected)
++      return;
++
++    ERROR("Connection closed by server.");
++    this._account.gotDisconnected(Ci.prplIAccount.ERROR_NETWORK_ERROR,
++                                  _("connection.error.lost"));
++  },
++  onConnectionReset: function () {
++    ERROR("Connection reset.");
++    this._account.gotDisconnected(Ci.prplIAccount.ERROR_NETWORK_ERROR,
++                                  _("connection.error.lost"));
++  },
++  onConnectionTimedOut: function() {
++    ERROR("Connection timed out.");
++    this._account.gotDisconnected(Ci.prplIAccount.ERROR_NETWORK_ERROR,
++                                  _("connection.error.timeOut"));
++  },
++  onBadCertificate: function(aNSSErrorMessage) {
++    ERROR("bad certificate: " + aNSSErrorMessage);
++    this._account.gotDisconnected(Ci.prplIAccount.ERROR_CERT_OTHER_ERROR,
++                                  aNSSErrorMessage);
++  },
++  log: LOG
++};
+\ No newline at end of file
+diff -r ded093a82746 chat/protocols/oscar/oscarUtils.jsm
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/chat/protocols/oscar/oscarUtils.jsm	Fri Oct 12 08:11:30 2012 -0400
+@@ -0,0 +1,15 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++const EXPORTED_SYMBOLS = ["copyBytes"];
++
++/*
++ * aTarget / aSource are ArrayBuffers
++ */
++function copyBytes(aTarget, aSource, aTargetOffset, aSourceOffset, aLength) {
++    let length = aLength || aSource.byteLength;
++    // The rest just gets the data copied into it.
++    let view = new Uint8Array(aTarget, aTargetOffset);
++    view.set(new Uint8Array(aSource, aSourceOffset, length));
++}
+\ No newline at end of file
 irc-isupport
 irc-cap
 irc-minor
+oscar
 sipe-mozconfig
 sipe-deps
 sipe