2
votes

Using agsXMPP to connect to Google Cloud Messaging XMPP API for the purpose of sending notification to Android devices.

The connection is established OK, but on SASL start, after sending the PLAIN auth element, the server stops responding, and closes the connection after a further 20 seconds.

Base64 decoding the auth example from the documentation page (http://developer.android.com/google/gcm/ccs.html) shows login values of:

[email protected]@projects-ga-.android.comAIzaSyB3rcZNkfnqKdFb9mhzCBiYpORDA2JWWtw

Where as agsXMPP is (correctly I think) encoding the string, to give something like:

[ProjectID]\40gcm.googleapis.com[**API*KEY*PASSWORD**]

Note the \40 in my version instead of the @ in the Google example - could this make a difference?

I'm expecting either a success or failure message, no response at all is difficult to debug. Could this at character be responsible for some failure, or does Google's implementation of XMPP just not provide the correct responses.

UPDATED:

I answered below, essentially, yes, Google can't handled the encoded @ character because it doesn't support that XMPP extension.

1

1 Answers

2
votes

After some more testing, I added a new SaslFactory mechanism in agsXMPP and bound it to use the username without encoding (part of extension http://xmpp.org/extensions/xep-0106.html, which Google doesn't support), and then on SaslStartEvent - specify that I want to use that mechanism instead of the inbuilt plain one. - and now the connection will continue normally.

xmpp = new XmppClientConnection();
xmpp.UseSSL = true;
xmpp.UseStartTLS = false;
xmpp.Server = "gcm.googleapis.com";
xmpp.ConnectServer = "gcm.googleapis.com";
xmpp.Port = 5235;
/* Other connection settings /*

SaslFactory.AddMechanism("MyPLAINMechanism", typeof(MyPlainMechanismClass));

xmpp.OnSaslStart += (sender, args) =>
                                {
                                   args.Auto = false;
                                   args.Mechanism = "MyPLAINMechanism";
                                   args.ExtentedData = new GcmPlainSaslExtendedData
                                                          {
                                                             Username = "MY UNENCODED USERNAME"
                                                          };
                                };

Then we define the MyPlainMechanismClass which inherits from the Mechanism in agsXMPP, the source code is the same as the original PlainSaslMechanism except the line where the username is input - you can pass in an unencoded username using the ExtendedData property on args.

public class MyPlainMechanismClass: Mechanism
   {
      private XmppClientConnection m_XmppClient = null;

      public GcmPlainSaslMechanism()
      {
      }

      public override void Init(XmppClientConnection con)
      {
         m_XmppClient = con;

         // <auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">$Message</auth>
         m_XmppClient.Send(new agsXMPP.protocol.sasl.Auth(agsXMPP.protocol.sasl.MechanismType.PLAIN, Message()));
      }

      public override void Parse(Node e)
      {
         // not needed here in PLAIN mechanism
      }


      private string Message()
      {
         // NULL Username NULL Password
         StringBuilder sb = new StringBuilder();

         //sb.Append( (char) 0 );
         //sb.Append(this.m_XmppClient.MyJID.Bare);

         sb.Append((char)0);
         //sb.Append(this.Username);
         sb.Append(((GcmPlainSaslExtendedData) this.ExtentedData).Username);
         sb.Append((char)0);
         sb.Append(this.Password);

         byte[] msg = Encoding.UTF8.GetBytes(sb.ToString());
         return Convert.ToBase64String(msg, 0, msg.Length);
      }
   }

Our custom ExtendedData object which we use to pass in custom arguments, such as an unencoded username in this case.

       public class GcmPlainSaslExtendedData : agsXMPP.Sasl.ExtendedData
       {
          public string Username { get; set; }
       }