Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added C# Async API.

  • Loading branch information...
commit 3260465ba59cbdbe57cabd0d033bb00be340d141 1 parent d061e84
Stephen L. Blum authored July 03, 2011
BIN  csharp/Pubnub.exe
Binary file not shown
BIN  csharp/Pubnub.exe.mdb
Binary file not shown
1,007  csharp/PubnubAsync.cs
... ...
@@ -0,0 +1,1007 @@
  1
+using System;
  2
+using System.IO;
  3
+using System.Net;
  4
+using System.Text;
  5
+using System.Collections;
  6
+using System.Collections.Generic;
  7
+using System.Security.Cryptography;
  8
+using System.Runtime.Serialization;
  9
+using System.Runtime.Serialization.Json;
  10
+using System.ComponentModel;
  11
+
  12
+namespace Pubnub
  13
+{
  14
+    // INotifyPropertyChanged provides a standard event for objects to notify clients that one of its properties has changed
  15
+    public class PubnubClass : INotifyPropertyChanged
  16
+    {
  17
+        // Common property changed event
  18
+        public event PropertyChangedEventHandler PropertyChanged;
  19
+        public void RaisePropertyChanged(string propertyName)
  20
+        {
  21
+            var handler = PropertyChanged;
  22
+            if (handler != null)
  23
+            {
  24
+                handler(this, new PropertyChangedEventArgs(propertyName));
  25
+            }
  26
+        }
  27
+
  28
+        // Message that is published through PubNub
  29
+        private object _ReturnMessage = new object();
  30
+        public object ReturnMessage
  31
+        {
  32
+            get
  33
+            {
  34
+                return _ReturnMessage;
  35
+            }
  36
+            set
  37
+            {
  38
+                _ReturnMessage = value; RaisePropertyChanged("ReturnMessage");
  39
+            }
  40
+        }
  41
+
  42
+        // Publish
  43
+        private List<object> _Publish = new List<object>();
  44
+        public List<object> Publish
  45
+        {
  46
+            get
  47
+            {
  48
+                return _Publish;
  49
+            }
  50
+            set
  51
+            {
  52
+                _Publish = value;
  53
+                RaisePropertyChanged("Publish");
  54
+            }
  55
+        }
  56
+
  57
+        // History of Messages
  58
+        private List<object> _History = new List<object>();
  59
+        public List<object> History { get { return _History; } set { _History = value; RaisePropertyChanged("History"); } }
  60
+
  61
+        // Subscribe
  62
+        private List<object> _Subscribe = new List<object>();
  63
+        public List<object> Subscribe
  64
+        {
  65
+            get
  66
+            {
  67
+                return _Subscribe;
  68
+            }
  69
+            set
  70
+            {
  71
+                _Subscribe = value;
  72
+                if (Int64.Parse(value[1].ToString()) > 0)
  73
+                    _subscribe(value[2].ToString(), Int64.Parse(value[1].ToString()) - 1);
  74
+                RaisePropertyChanged("Subscribe");
  75
+            }
  76
+        }
  77
+
  78
+        // Time stamp
  79
+        private List<object> _Time = new List<object>();
  80
+        public List<object> Time { get { return _Time; } set { _Time = value; RaisePropertyChanged("Time"); } }
  81
+
  82
+        private string ORIGIN = "pubsub.pubnub.com";
  83
+        private int LIMIT = 1800;
  84
+        private string PUBLISH_KEY = "";
  85
+        private string SUBSCRIBE_KEY = "";
  86
+        private string SECRET_KEY = "";
  87
+        private bool SSL = false;
  88
+
  89
+        public delegate bool Procedure(object message);
  90
+
  91
+        /**
  92
+         * PubNub 3.0
  93
+         *
  94
+         * Prepare PubNub Class State.
  95
+         *
  96
+         * @param string Publish Key.
  97
+         * @param string Subscribe Key.
  98
+         * @param string Secret Key.
  99
+         * @param bool SSL Enabled.
  100
+         */
  101
+        public PubnubClass(string publish_key, string subscribe_key, string secret_key, bool ssl_on)
  102
+        {
  103
+            this.init(publish_key, subscribe_key, secret_key, ssl_on);
  104
+        }
  105
+
  106
+        /**
  107
+         * PubNub 2.0 Compatibility
  108
+         *
  109
+         * Prepare PubNub Class State.
  110
+         *
  111
+         * @param string Publish Key.
  112
+         * @param string Subscribe Key.
  113
+         */
  114
+        public PubnubClass( string publish_key, string subscribe_key)
  115
+        {
  116
+            this.init(publish_key, subscribe_key, "", false);
  117
+        }
  118
+
  119
+        /**
  120
+         * PubNub 3.0 without SSL
  121
+         *
  122
+         * Prepare PubNub Class State.
  123
+         *
  124
+         * @param string Publish Key.
  125
+         * @param string Subscribe Key.
  126
+         * @param string Secret Key.
  127
+         */
  128
+        public PubnubClass( string publish_key, string subscribe_key, string secret_key)
  129
+        {
  130
+            this.init(publish_key, subscribe_key, secret_key, false);
  131
+        }
  132
+
  133
+        /**
  134
+         * Init
  135
+         *
  136
+         * Prepare PubNub Class State.
  137
+         *
  138
+         * @param string Publish Key.
  139
+         * @param string Subscribe Key.
  140
+         * @param string Secret Key.
  141
+         * @param bool SSL Enabled.
  142
+         */
  143
+        public void init(string publish_key, string subscribe_key, string secret_key, bool ssl_on)
  144
+        {
  145
+            this.PUBLISH_KEY = publish_key;
  146
+            this.SUBSCRIBE_KEY = subscribe_key;
  147
+            this.SECRET_KEY = secret_key;
  148
+            this.SSL = ssl_on;
  149
+
  150
+            // SSL On?
  151
+            if (this.SSL)
  152
+            {
  153
+                this.ORIGIN = "https://" + this.ORIGIN;
  154
+            }
  155
+            else
  156
+            {
  157
+                this.ORIGIN = "http://" + this.ORIGIN;
  158
+            }
  159
+        }
  160
+
  161
+        /**
  162
+         * History
  163
+         *
  164
+         * Load history from a channel.
  165
+         *
  166
+         * @param String channel name.
  167
+         * @param int limit history count response.
  168
+         * @return ListArray of history.
  169
+         */
  170
+        public void history(string channel, int limit)
  171
+        {
  172
+            List<string> url = new List<string>();
  173
+
  174
+            url.Add("history");
  175
+            url.Add(this.SUBSCRIBE_KEY);
  176
+            url.Add(channel);
  177
+            url.Add("0");
  178
+            url.Add(limit.ToString());
  179
+
  180
+            _request(url, ResponseType.History);
  181
+        }
  182
+
  183
+        /**
  184
+         * Publish
  185
+         *
  186
+         * Send a message to a channel.
  187
+         *
  188
+         * @param String channel name.
  189
+         * @param List<object> info.
  190
+         * @return bool false on fail.
  191
+         */
  192
+        public void publish(string channel, object message)
  193
+        {
  194
+            // Generate String to Sign
  195
+            string signature = "0";
  196
+            if (this.SECRET_KEY.Length > 0)
  197
+            {
  198
+                StringBuilder string_to_sign = new StringBuilder();
  199
+                string_to_sign
  200
+                    .Append(this.PUBLISH_KEY)
  201
+                    .Append('/')
  202
+                    .Append(this.SUBSCRIBE_KEY)
  203
+                    .Append('/')
  204
+                    .Append(this.SECRET_KEY)
  205
+                    .Append('/')
  206
+                    .Append(channel)
  207
+                    .Append('/')
  208
+                    .Append(SerializeToJsonString(message)); // 1
  209
+                    ;
  210
+                // Sign Message
  211
+                signature = md5(string_to_sign.ToString());
  212
+            }
  213
+
  214
+            // Build URL
  215
+            List<string> url = new List<string>();
  216
+            url.Add("publish");
  217
+            url.Add(this.PUBLISH_KEY);
  218
+            url.Add(this.SUBSCRIBE_KEY);
  219
+            url.Add(signature);
  220
+            url.Add(channel);
  221
+            url.Add("0");
  222
+            url.Add(SerializeToJsonString(message));
  223
+            
  224
+            _request(url, ResponseType.Publish);
  225
+        }
  226
+
  227
+        /**
  228
+         * Subscribe
  229
+         *
  230
+         * This function is BLOCKING.
  231
+         * Listen for a message on a channel.
  232
+         *
  233
+         * @param string channel name.
  234
+         * @param Procedure function callback.
  235
+         */
  236
+        public void subscribe(string channel)
  237
+        {
  238
+            this._subscribe(channel, 0);
  239
+        }
  240
+
  241
+        /**
  242
+         * Subscribe - Private Interface
  243
+         *
  244
+         * @param string channel name.
  245
+         * @param Procedure function callback.
  246
+         * @param string timetoken.
  247
+         */
  248
+        private void _subscribe(string channel, object timetoken)
  249
+        {
  250
+            // Begin Recusive Subscribe
  251
+            try
  252
+            {
  253
+                // Build URL
  254
+                List<string> url = new List<string>();
  255
+                url.Add("subscribe");
  256
+                url.Add(this.SUBSCRIBE_KEY);
  257
+                url.Add(channel);
  258
+                url.Add("0");
  259
+                url.Add(timetoken.ToString());
  260
+
  261
+                // Wait for Message
  262
+                _request(url, ResponseType.Subscribe);
  263
+
  264
+                if (Subscribe.Count > 0)
  265
+                {
  266
+                    // Update TimeToken
  267
+                    if (Subscribe[1].ToString().Length > 0)
  268
+                        timetoken = (object)Subscribe[1];
  269
+                }
  270
+            }
  271
+            catch
  272
+            {
  273
+                System.Threading.Thread.Sleep(1000);
  274
+                this._subscribe(channel, timetoken);
  275
+            }
  276
+        }
  277
+
  278
+        /**
  279
+         * Time
  280
+         *
  281
+         * Timestamp from PubNub Cloud.
  282
+         *
  283
+         * @return object timestamp.
  284
+         */
  285
+        public void time()
  286
+        {
  287
+            List<string> url = new List<string>();
  288
+
  289
+            url.Add("time");
  290
+            url.Add("0");
  291
+
  292
+            _request(url, ResponseType.Time);
  293
+        }
  294
+
  295
+        /**
  296
+         * Request URL
  297
+         *
  298
+         * @param List<string> request of url directories.
  299
+         * @return List<object> from JSON response.
  300
+         */
  301
+        private void _request(List<string> url_components, ResponseType type)
  302
+        {
  303
+            List<object> result = new List<object>();
  304
+            StringBuilder url = new StringBuilder();
  305
+
  306
+            // Add Origin To The Request
  307
+            url.Append(this.ORIGIN);
  308
+
  309
+            // Generate URL with UTF-8 Encoding
  310
+            foreach (string url_bit in url_components)
  311
+            {
  312
+                url.Append("/");
  313
+                url.Append(_encodeURIcomponent(url_bit));
  314
+            }
  315
+
  316
+            // Fail if string too long
  317
+            if (url.Length > this.LIMIT)
  318
+            {
  319
+                result.Add(0);
  320
+                result.Add("Message Too Long.");
  321
+                //return result;
  322
+            }
  323
+
  324
+            // Create Request
  325
+            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.ToString());
  326
+
  327
+            // Make request with the following inline Async Call back
  328
+            //IAsyncResult result1 = request.BeginGetResponse(new AsyncCallback((asynchronousResult) =>
  329
+            request.BeginGetResponse(new AsyncCallback((asynchronousResult) =>
  330
+              {
  331
+                  //System.Deployment.Current.Dispatcher.BeginInvoke(delegate()
  332
+                  //{
  333
+                      HttpWebRequest aRequest = (HttpWebRequest)asynchronousResult.AsyncState;
  334
+                      HttpWebResponse aResponse = (HttpWebResponse)aRequest.EndGetResponse(asynchronousResult);
  335
+                      using (StreamReader streamReader = new StreamReader(aResponse.GetResponseStream()))
  336
+                      {
  337
+                          // Deserialize the result
  338
+                          result = DeserializeToListOfObject(streamReader.ReadToEnd());
  339
+                          if (result[0] is object[])
  340
+                          {
  341
+                              foreach (object message in (object[])result[0])
  342
+                              {
  343
+                                  this.ReturnMessage = message;
  344
+                              }
  345
+                          }
  346
+                          switch (type)
  347
+                          {
  348
+                              case ResponseType.Publish:
  349
+                                  result.Add(url_components[4]);
  350
+                                  Publish = result;
  351
+                                  break;
  352
+                              case ResponseType.History:
  353
+                                  History = result;
  354
+                                  break;
  355
+                              case ResponseType.Time:
  356
+                                  Time = result;
  357
+                                  break;
  358
+                              default:
  359
+                                  result.Add(url_components[2]);
  360
+                                  Subscribe = result;
  361
+                                  break;
  362
+                          }
  363
+
  364
+                      }
  365
+                  //});
  366
+              }), request);
  367
+        }
  368
+
  369
+        
  370
+        /// <summary>
  371
+        /// Serialize the given object into JSON
  372
+        /// </summary>
  373
+        /// <param name="objectToSerialize"></param>
  374
+        /// <returns></returns>
  375
+        public static string SerializeToJsonString(object objectToSerialize)
  376
+        {
  377
+            using (MemoryStream ms = new MemoryStream())
  378
+            {
  379
+                DataContractJsonSerializer serializer = new DataContractJsonSerializer(objectToSerialize.GetType());
  380
+                serializer.WriteObject(ms, objectToSerialize);
  381
+                ms.Position = 0;
  382
+
  383
+                using (StreamReader reader = new StreamReader(ms))
  384
+                {
  385
+                    return reader.ReadToEnd();
  386
+                }
  387
+            }
  388
+        }
  389
+
  390
+        /// <summary>
  391
+        /// Deserialize JSON string into List of Objects
  392
+        /// </summary>
  393
+        /// <param name="jsonString"></param>
  394
+        /// <returns></returns>
  395
+        public static List<object> DeserializeToListOfObject(string jsonString)
  396
+        {
  397
+            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
  398
+            {
  399
+                DataContractJsonSerializer serializer =
  400
+                        new DataContractJsonSerializer(typeof(List<object>));
  401
+
  402
+                return (List<object>)serializer.ReadObject(ms);
  403
+            }
  404
+        }
  405
+
  406
+        private string _encodeURIcomponent(string s)
  407
+        {
  408
+            StringBuilder o = new StringBuilder();
  409
+            foreach (char ch in s.ToCharArray())
  410
+            {
  411
+                if (isUnsafe(ch))
  412
+                {
  413
+                    o.Append('%');
  414
+                    o.Append(toHex(ch / 16));
  415
+                    o.Append(toHex(ch % 16));
  416
+                }
  417
+                else o.Append(ch);
  418
+            }
  419
+            return o.ToString();
  420
+        }
  421
+
  422
+        private char toHex(int ch)
  423
+        {
  424
+            return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10);
  425
+        }
  426
+
  427
+        private bool isUnsafe(char ch)
  428
+        {
  429
+            return " ~`!@#$%^&*()+=[]\\{}|;':\",./<>?".IndexOf(ch) >= 0;
  430
+        }
  431
+
  432
+        public static string md5(string text)
  433
+        {
  434
+            MD5 md5 = new MD5CryptoServiceProvider();
  435
+            byte[] data = Encoding.Unicode.GetBytes(text);
  436
+            byte[] hash = md5.ComputeHash(data);
  437
+            string hexaHash = "";
  438
+            foreach (byte b in hash) hexaHash += String.Format("{0:x2}", b);
  439
+            return hexaHash;
  440
+        }
  441
+
  442
+        enum ResponseType
  443
+        {
  444
+            Publish,
  445
+            History,
  446
+            Time,
  447
+            Subscribe
  448
+        }
  449
+    }
  450
+
  451
+    public class MD5CryptoServiceProvider : MD5
  452
+    {
  453
+        public MD5CryptoServiceProvider()
  454
+            : base()
  455
+        {
  456
+        }
  457
+    }
  458
+    /// <summary>
  459
+    /// Summary description for MD5.
  460
+    /// </summary>
  461
+    public class MD5 : IDisposable
  462
+    {
  463
+        static public MD5 Create(string hashName)
  464
+        {
  465
+            if (hashName == "MD5")
  466
+                return new MD5();
  467
+            else
  468
+                throw new NotSupportedException();
  469
+        }
  470
+
  471
+        static public String GetMd5String(String source)
  472
+        {
  473
+            MD5 md = MD5CryptoServiceProvider.Create();
  474
+            byte[] hash;
  475
+
  476
+            //Create a new instance of ASCIIEncoding to 
  477
+            //convert the string into an array of Unicode bytes.
  478
+            UTF8Encoding enc = new UTF8Encoding();
  479
+            //            ASCIIEncoding enc = new ASCIIEncoding();
  480
+
  481
+            //Convert the string into an array of bytes.
  482
+            byte[] buffer = enc.GetBytes(source);
  483
+
  484
+            //Create the hash value from the array of bytes.
  485
+            hash = md.ComputeHash(buffer);
  486
+
  487
+            StringBuilder sb = new StringBuilder();
  488
+            foreach (byte b in hash)
  489
+                sb.Append(b.ToString("x2"));
  490
+            return sb.ToString();
  491
+        }
  492
+
  493
+        static public MD5 Create()
  494
+        {
  495
+            return new MD5();
  496
+        }
  497
+
  498
+        #region base implementation of the MD5
  499
+        #region constants
  500
+        private const byte S11 = 7;
  501
+        private const byte S12 = 12;
  502
+        private const byte S13 = 17;
  503
+        private const byte S14 = 22;
  504
+        private const byte S21 = 5;
  505
+        private const byte S22 = 9;
  506
+        private const byte S23 = 14;
  507
+        private const byte S24 = 20;
  508
+        private const byte S31 = 4;
  509
+        private const byte S32 = 11;
  510
+        private const byte S33 = 16;
  511
+        private const byte S34 = 23;
  512
+        private const byte S41 = 6;
  513
+        private const byte S42 = 10;
  514
+        private const byte S43 = 15;
  515
+        private const byte S44 = 21;
  516
+        static private byte[] PADDING = new byte[] {
  517
+														0x80, 0, 0, 0, 0, 0, 
  518
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  519
+														0, 0, 0, 0, 0, 0, 0, 
  520
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  521
+														0, 0, 0, 0, 0, 0, 0, 
  522
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  523
+													};
  524
+        #endregion
  525
+
  526
+        #region F, G, H and I are basic MD5 functions.
  527
+        static private uint F(uint x, uint y, uint z)
  528
+        {
  529
+            return (((x) & (y)) | ((~x) & (z)));
  530
+        }
  531
+        static private uint G(uint x, uint y, uint z)
  532
+        {
  533
+            return (((x) & (z)) | ((y) & (~z)));
  534
+        }
  535
+        static private uint H(uint x, uint y, uint z)
  536
+        {
  537
+            return ((x) ^ (y) ^ (z));
  538
+        }
  539
+        static private uint I(uint x, uint y, uint z)
  540
+        {
  541
+            return ((y) ^ ((x) | (~z)));
  542
+        }
  543
+        #endregion
  544
+
  545
+        #region rotates x left n bits.
  546
+        /// <summary>
  547
+        /// rotates x left n bits.
  548
+        /// </summary>
  549
+        /// <param name="x"></param>
  550
+        /// <param name="n"></param>
  551
+        /// <returns></returns>
  552
+        static private uint ROTATE_LEFT(uint x, byte n)
  553
+        {
  554
+            return (((x) << (n)) | ((x) >> (32 - (n))));
  555
+        }
  556
+        #endregion
  557
+
  558
+        #region FF, GG, HH, and II transformations
  559
+        /// FF, GG, HH, and II transformations 
  560
+        /// for rounds 1, 2, 3, and 4.
  561
+        /// Rotation is separate from addition to prevent recomputation.
  562
+        static private void FF(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
  563
+        {
  564
+            (a) += F((b), (c), (d)) + (x) + (uint)(ac);
  565
+            (a) = ROTATE_LEFT((a), (s));
  566
+            (a) += (b);
  567
+        }
  568
+        static private void GG(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
  569
+        {
  570
+            (a) += G((b), (c), (d)) + (x) + (uint)(ac);
  571
+            (a) = ROTATE_LEFT((a), (s));
  572
+            (a) += (b);
  573
+        }
  574
+        static private void HH(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
  575
+        {
  576
+            (a) += H((b), (c), (d)) + (x) + (uint)(ac);
  577
+            (a) = ROTATE_LEFT((a), (s));
  578
+            (a) += (b);
  579
+        }
  580
+        static private void II(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
  581
+        {
  582
+            (a) += I((b), (c), (d)) + (x) + (uint)(ac);
  583
+            (a) = ROTATE_LEFT((a), (s));
  584
+            (a) += (b);
  585
+        }
  586
+        #endregion
  587
+
  588
+        #region context info
  589
+        /// <summary>
  590
+        /// state (ABCD)
  591
+        /// </summary>
  592
+        uint[] state = new uint[4];
  593
+
  594
+        /// <summary>
  595
+        /// number of bits, modulo 2^64 (lsb first)
  596
+        /// </summary>
  597
+        uint[] count = new uint[2];
  598
+
  599
+        /// <summary>
  600
+        /// input buffer
  601
+        /// </summary>
  602
+        byte[] buffer = new byte[64];
  603
+        #endregion
  604
+
  605
+        internal MD5()
  606
+        {
  607
+            Initialize();
  608
+        }
  609
+
  610
+        /// <summary>
  611
+        /// MD5 initialization. Begins an MD5 operation, writing a new context.
  612
+        /// </summary>
  613
+        /// <remarks>
  614
+        /// The RFC named it "MD5Init"
  615
+        /// </remarks>
  616
+        public virtual void Initialize()
  617
+        {
  618
+            count[0] = count[1] = 0;
  619
+
  620
+            // Load magic initialization constants.
  621
+            state[0] = 0x67452301;
  622
+            state[1] = 0xefcdab89;
  623
+            state[2] = 0x98badcfe;
  624
+            state[3] = 0x10325476;
  625
+        }
  626
+
  627
+        /// <summary>
  628
+        /// MD5 block update operation. Continues an MD5 message-digest
  629
+        /// operation, processing another message block, and updating the
  630
+        /// context.
  631
+        /// </summary>
  632
+        /// <param name="input"></param>
  633
+        /// <param name="offset"></param>
  634
+        /// <param name="count"></param>
  635
+        /// <remarks>The RFC Named it MD5Update</remarks>
  636
+        protected virtual void HashCore(byte[] input, int offset, int count)
  637
+        {
  638
+            int i;
  639
+            int index;
  640
+            int partLen;
  641
+
  642
+            // Compute number of bytes mod 64
  643
+            index = (int)((this.count[0] >> 3) & 0x3F);
  644
+
  645
+            // Update number of bits
  646
+            if ((this.count[0] += (uint)((uint)count << 3)) < ((uint)count << 3))
  647
+                this.count[1]++;
  648
+            this.count[1] += ((uint)count >> 29);
  649
+
  650
+            partLen = 64 - index;
  651
+
  652
+            // Transform as many times as possible.
  653
+            if (count >= partLen)
  654
+            {
  655
+                Buffer.BlockCopy(input, offset, this.buffer, index, partLen);
  656
+                Transform(this.buffer, 0);
  657
+
  658
+                for (i = partLen; i + 63 < count; i += 64)
  659
+                    Transform(input, offset + i);
  660
+
  661
+                index = 0;
  662
+            }
  663
+            else
  664
+                i = 0;
  665
+
  666
+            // Buffer remaining input 
  667
+            Buffer.BlockCopy(input, offset + i, this.buffer, index, count - i);
  668
+        }
  669
+
  670
+        /// <summary>
  671
+        /// MD5 finalization. Ends an MD5 message-digest operation, writing the
  672
+        /// the message digest and zeroizing the context.
  673
+        /// </summary>
  674
+        /// <returns>message digest</returns>
  675
+        /// <remarks>The RFC named it MD5Final</remarks>
  676
+        protected virtual byte[] HashFinal()
  677
+        {
  678
+            byte[] digest = new byte[16];
  679
+            byte[] bits = new byte[8];
  680
+            int index, padLen;
  681
+
  682
+            // Save number of bits
  683
+            Encode(bits, 0, this.count, 0, 8);
  684
+
  685
+            // Pad out to 56 mod 64.
  686
+            index = (int)((uint)(this.count[0] >> 3) & 0x3f);
  687
+            padLen = (index < 56) ? (56 - index) : (120 - index);
  688
+            HashCore(PADDING, 0, padLen);
  689
+
  690
+            // Append length (before padding)
  691
+            HashCore(bits, 0, 8);
  692
+
  693
+            // Store state in digest 
  694
+            Encode(digest, 0, state, 0, 16);
  695
+
  696
+            // Zeroize sensitive information.
  697
+            count[0] = count[1] = 0;
  698
+            state[0] = 0;
  699
+            state[1] = 0;
  700
+            state[2] = 0;
  701
+            state[3] = 0;
  702
+
  703
+            // initialize again, to be ready to use
  704
+            Initialize();
  705
+
  706
+            return digest;
  707
+        }
  708
+
  709
+        /// <summary>
  710
+        /// MD5 basic transformation. Transforms state based on 64 bytes block.
  711
+        /// </summary>
  712
+        /// <param name="block"></param>
  713
+        /// <param name="offset"></param>
  714
+        private void Transform(byte[] block, int offset)
  715
+        {
  716
+            uint a = state[0], b = state[1], c = state[2], d = state[3];
  717
+            uint[] x = new uint[16];
  718
+            Decode(x, 0, block, offset, 64);
  719
+
  720
+            // Round 1
  721
+            FF(ref a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
  722
+            FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
  723
+            FF(ref c, d, a, b, x[2], S13, 0x242070db); /* 3 */
  724
+            FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
  725
+            FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
  726
+            FF(ref d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
  727
+            FF(ref c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
  728
+            FF(ref b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
  729
+            FF(ref a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
  730
+            FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
  731
+            FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  732
+            FF(ref b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  733
+            FF(ref a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  734
+            FF(ref d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  735
+            FF(ref c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  736
+            FF(ref b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
  737
+
  738
+            // Round 2
  739
+            GG(ref a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
  740
+            GG(ref d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
  741
+            GG(ref c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  742
+            GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
  743
+            GG(ref a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
  744
+            GG(ref d, a, b, c, x[10], S22, 0x2441453); /* 22 */
  745
+            GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  746
+            GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
  747
+            GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
  748
+            GG(ref d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  749
+            GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
  750
+            GG(ref b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
  751
+            GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  752
+            GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
  753
+            GG(ref c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
  754
+            GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
  755
+
  756
+            // Round 3
  757
+            HH(ref a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
  758
+            HH(ref d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
  759
+            HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  760
+            HH(ref b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  761
+            HH(ref a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
  762
+            HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
  763
+            HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
  764
+            HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  765
+            HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  766
+            HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
  767
+            HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
  768
+            HH(ref b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
  769
+            HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
  770
+            HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  771
+            HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  772
+            HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
  773
+
  774
+            // Round 4
  775
+            II(ref a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
  776
+            II(ref d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
  777
+            II(ref c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  778
+            II(ref b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
  779
+            II(ref a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  780
+            II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
  781
+            II(ref c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  782
+            II(ref b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
  783
+            II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
  784
+            II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  785
+            II(ref c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
  786
+            II(ref b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  787
+            II(ref a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
  788
+            II(ref d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  789
+            II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
  790
+            II(ref b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
  791
+
  792
+            state[0] += a;
  793
+            state[1] += b;
  794
+            state[2] += c;
  795
+            state[3] += d;
  796
+
  797
+            // Zeroize sensitive information.
  798
+            for (int i = 0; i < x.Length; i++)
  799
+                x[i] = 0;
  800
+        }
  801
+
  802
+        /// <summary>
  803
+        /// Encodes input (uint) into output (byte). Assumes len is
  804
+        ///  multiple of 4.
  805
+        /// </summary>
  806
+        /// <param name="output"></param>
  807
+        /// <param name="outputOffset"></param>
  808
+        /// <param name="input"></param>
  809
+        /// <param name="inputOffset"></param>
  810
+        /// <param name="count"></param>
  811
+        private static void Encode(byte[] output, int outputOffset, uint[] input, int inputOffset, int count)
  812
+        {
  813
+            int i, j;
  814
+            int end = outputOffset + count;
  815
+            for (i = inputOffset, j = outputOffset; j < end; i++, j += 4)
  816
+            {
  817
+                output[j] = (byte)(input[i] & 0xff);
  818
+                output[j + 1] = (byte)((input[i] >> 8) & 0xff);
  819
+                output[j + 2] = (byte)((input[i] >> 16) & 0xff);
  820
+                output[j + 3] = (byte)((input[i] >> 24) & 0xff);
  821
+            }
  822
+        }
  823
+
  824
+        /// <summary>
  825
+        /// Decodes input (byte) into output (uint). Assumes len is
  826
+        /// a multiple of 4.
  827
+        /// </summary>
  828
+        /// <param name="output"></param>
  829
+        /// <param name="outputOffset"></param>
  830
+        /// <param name="input"></param>
  831
+        /// <param name="inputOffset"></param>
  832
+        /// <param name="count"></param>
  833
+        static private void Decode(uint[] output, int outputOffset, byte[] input, int inputOffset, int count)
  834
+        {
  835
+            int i, j;
  836
+            int end = inputOffset + count;
  837
+            for (i = outputOffset, j = inputOffset; j < end; i++, j += 4)
  838
+                output[i] = ((uint)input[j]) | (((uint)input[j + 1]) << 8) | (((uint)input[j + 2]) << 16) | (((uint)input[j + 3]) <<
  839
+24);
  840
+        }
  841
+        #endregion
  842
+
  843
+        #region expose the same interface as the regular MD5 object
  844
+
  845
+        protected byte[] HashValue;
  846
+        protected int State;
  847
+        public virtual bool CanReuseTransform
  848
+        {
  849
+            get
  850
+            {
  851
+                return true;
  852
+            }
  853
+        }
  854
+
  855
+        public virtual bool CanTransformMultipleBlocks
  856
+        {
  857
+            get
  858
+            {
  859
+                return true;
  860
+            }
  861
+        }
  862
+        public virtual byte[] Hash
  863
+        {
  864
+            get
  865
+            {
  866
+                if (this.State != 0)
  867
+                    throw new InvalidOperationException();
  868
+                return (byte[])HashValue.Clone();
  869
+            }
  870
+        }
  871
+        public virtual int HashSize
  872
+        {
  873
+            get
  874
+            {
  875
+                return HashSizeValue;
  876
+            }
  877
+        }
  878
+        protected int HashSizeValue = 128;
  879
+
  880
+        public virtual int InputBlockSize
  881
+        {
  882
+            get
  883
+            {
  884
+                return 1;
  885
+            }
  886
+        }
  887
+        public virtual int OutputBlockSize
  888
+        {
  889
+            get
  890
+            {
  891
+                return 1;
  892
+            }
  893
+        }
  894
+
  895
+        public void Clear()
  896
+        {
  897
+            Dispose(true);
  898
+        }
  899
+
  900
+        public byte[] ComputeHash(byte[] buffer)
  901
+        {
  902
+            return ComputeHash(buffer, 0, buffer.Length);
  903
+        }
  904
+        public byte[] ComputeHash(byte[] buffer, int offset, int count)
  905
+        {
  906
+            Initialize();
  907
+            HashCore(buffer, offset, count);
  908
+            HashValue = HashFinal();
  909
+            return (byte[])HashValue.Clone();
  910
+        }
  911
+
  912
+        public byte[] ComputeHash(Stream inputStream)
  913
+        {
  914
+            Initialize();
  915
+            int count;
  916
+            byte[] buffer = new byte[4096];
  917
+            while (0 < (count = inputStream.Read(buffer, 0, 4096)))
  918
+            {
  919
+                HashCore(buffer, 0, count);
  920
+            }
  921
+            HashValue = HashFinal();
  922
+            return (byte[])HashValue.Clone();
  923
+        }
  924
+
  925
+        public int TransformBlock(
  926
+            byte[] inputBuffer,
  927
+            int inputOffset,
  928
+            int inputCount,
  929
+            byte[] outputBuffer,
  930
+            int outputOffset
  931
+            )
  932
+        {
  933
+            if (inputBuffer == null)
  934
+            {
  935
+                throw new ArgumentNullException("inputBuffer");
  936
+            }
  937
+            if (inputOffset < 0)
  938
+            {
  939
+                throw new ArgumentOutOfRangeException("inputOffset");
  940
+            }
  941
+            if ((inputCount < 0) || (inputCount > inputBuffer.Length))
  942
+            {
  943
+                throw new ArgumentException("inputCount");
  944
+            }
  945
+            if ((inputBuffer.Length - inputCount) < inputOffset)
  946
+            {
  947
+                throw new ArgumentOutOfRangeException("inputOffset");
  948
+            }
  949
+            if (this.State == 0)
  950
+            {
  951
+                Initialize();
  952
+                this.State = 1;
  953
+            }
  954
+
  955
+            HashCore(inputBuffer, inputOffset, inputCount);
  956
+            if ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))
  957
+            {
  958
+                Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
  959
+            }
  960
+            return inputCount;
  961
+        }
  962
+        public byte[] TransformFinalBlock(
  963
+            byte[] inputBuffer,
  964
+            int inputOffset,
  965
+            int inputCount
  966
+            )
  967
+        {
  968
+            if (inputBuffer == null)
  969
+            {
  970
+                throw new ArgumentNullException("inputBuffer");
  971
+            }
  972
+            if (inputOffset < 0)
  973
+            {
  974
+                throw new ArgumentOutOfRangeException("inputOffset");
  975
+            }
  976
+            if ((inputCount < 0) || (inputCount > inputBuffer.Length))
  977
+            {
  978
+                throw new ArgumentException("inputCount");
  979
+            }
  980
+            if ((inputBuffer.Length - inputCount) < inputOffset)
  981
+            {
  982
+                throw new ArgumentOutOfRangeException("inputOffset");
  983
+            }
  984
+            if (this.State == 0)
  985
+            {
  986
+                Initialize();
  987
+            }
  988
+            HashCore(inputBuffer, inputOffset, inputCount);
  989
+            HashValue = HashFinal();
  990
+            byte[] buffer = new byte[inputCount];
  991
+            Buffer.BlockCopy(inputBuffer, inputOffset, buffer, 0, inputCount);
  992
+            this.State = 0;
  993
+            return buffer;
  994
+        }
  995
+        #endregion
  996
+
  997
+        protected virtual void Dispose(bool disposing)
  998
+        {
  999
+            if (!disposing)
  1000
+                Initialize();
  1001
+        }
  1002
+        public void Dispose()
  1003
+        {
  1004
+            Dispose(true);
  1005
+        }
  1006
+    }
  1007
+}
392  csharp/PubnubCustomer.cs
... ...
@@ -0,0 +1,392 @@
  1
+using System;
  2
+using System.IO;
  3
+using System.Net;
  4
+using System.Text;
  5
+using System.Collections;
  6
+using System.Collections.Generic;
  7
+using System.Security.Cryptography;
  8
+using System.Web.Script.Serialization;
  9
+
  10
+/**
  11
+ * PubNub Customer API TEST CLASS
  12
+ */
  13
+public class PubnubTEST {
  14
+    static public void Main() {
  15
+        // ------------------------------------------------
  16
+        // USE MASTER CUSTOMER PUB/SUB/SEC Keys
  17
+        // ------------------------------------------------
  18
+        PubnubCustomer pubnub_customer = new PubnubCustomer(
  19
+            "",  // Master Account PUBLISH_KEY
  20
+            "",  // Master Account SUBSCRIBE_KEY
  21
+            ""   // Master Account SECRET_KEY
  22
+        );
  23
+
  24
+        // ===================================================================
  25
+        // Customer Create /w Custom Data
  26
+        // ===================================================================
  27
+        Dictionary<object,object> data = new Dictionary<object,object>();
  28
+
  29
+        data.Add( "internal_uid", "123456" );
  30
+        data.Add( "anything", "anything" );
  31
+
  32
+        Dictionary<object,object> new_customer = pubnub_customer.Create(data);
  33
+
  34
+        if ((int)new_customer["status"] != 200) {
  35
+            Console.WriteLine("Error, Unalbe to Create Customer:");
  36
+            Console.WriteLine(new_customer["message"]);
  37
+            return;
  38
+        }
  39
+
  40
+        Console.WriteLine("================================================");
  41
+        Console.WriteLine("NEW CUSTOMER:");
  42
+        Console.WriteLine("status: "        + new_customer["status"]);
  43
+        Console.WriteLine("uid: "           + new_customer["uid"]);
  44
+        Console.WriteLine("publish_key: "   + new_customer["publish_key"]);
  45
+        Console.WriteLine("subscribe_key: " + new_customer["subscribe_key"]);
  46
+        Console.WriteLine("secret_key: "    + new_customer["secret_key"]);
  47
+        Console.WriteLine("CUSTOM VALUES:");
  48
+        Console.WriteLine("------------------------------------------------");
  49
+        Console.WriteLine("internal_uid: "  + new_customer["internal_uid"]);
  50
+        Console.WriteLine("anything: "      + new_customer["anything"]);
  51
+        Console.WriteLine("================================================");
  52
+
  53
+        // ===================================================================
  54
+        // Customer Update
  55
+        // ===================================================================
  56
+        Dictionary<object,object> updates = new Dictionary<object,object>();
  57
+
  58
+        updates.Add( "anything", "something else" );
  59
+        updates.Add( "more-data", "more custom data" );
  60
+
  61
+        Dictionary<object,object> updated_customer = pubnub_customer.Update(
  62
+            (string)new_customer["uid"], // CUSTOMER'S UID
  63
+            updates                      // CUSTOM VALUE UPDATES
  64
+        );
  65
+
  66
+        if ((int)updated_customer["status"] != 200) {
  67
+            Console.WriteLine("Error, Unalbe to Update Customer:");
  68
+            Console.WriteLine(updated_customer["message"]);
  69
+            return;
  70
+        }
  71
+
  72
+        Console.WriteLine("================================================");
  73
+        Console.WriteLine("UPDATED CUSTOMER:");
  74
+        Console.WriteLine("status: "       + updated_customer["status"]);
  75
+        Console.WriteLine("UPDATED VALUES:");
  76
+        Console.WriteLine("------------------------------------------------");
  77
+        Console.WriteLine("internal_uid: " + updated_customer["internal_uid"]);
  78
+        Console.WriteLine("anything: "     + updated_customer["anything"]);
  79
+        Console.WriteLine("more-data: "    + updated_customer["more-data"]);
  80
+        Console.WriteLine("================================================");
  81
+
  82
+        // ===================================================================
  83
+        // Customer Get
  84
+        // ===================================================================
  85
+        Dictionary<object,object> get_customer = pubnub_customer.Get(
  86
+            (string)updated_customer["uid"] // CUSTOMER'S UID
  87
+        );
  88
+
  89
+        if ((int)get_customer["status"] != 200) {
  90
+            Console.WriteLine("Error, Unalbe to Get Customer:");
  91
+            Console.WriteLine(get_customer["message"]);
  92
+            return;
  93
+        }
  94
+
  95
+        Console.WriteLine("================================================");
  96
+        Console.WriteLine("GET CUSTOMER:");
  97
+        Console.WriteLine("status: "        + get_customer["status"]);
  98
+        Console.WriteLine("uid: "           + get_customer["uid"]);
  99
+        Console.WriteLine("publish_key: "   + get_customer["publish_key"]);
  100
+        Console.WriteLine("subscribe_key: " + get_customer["subscribe_key"]);
  101
+        Console.WriteLine("secret_key: "    + get_customer["secret_key"]);
  102
+        Console.WriteLine("------------------------------------------------");
  103
+        Console.WriteLine("BALANCE VALUES:");
  104
+        Console.WriteLine("------------------------------------------------");
  105
+        Console.WriteLine("balance: " + get_customer["balance"]);
  106
+        Console.WriteLine("free_credits_used: " + get_customer["free_credits_used"]);
  107
+        Console.WriteLine("total_credits_used: " + get_customer["total_credits_used"]);
  108
+        Console.WriteLine("------------------------------------------------");
  109
+        Console.WriteLine("CUSTOM VALUES:");
  110
+        Console.WriteLine("------------------------------------------------");
  111
+        Console.WriteLine("internal_uid: "  + get_customer["internal_uid"]);
  112
+        Console.WriteLine("anything: "      + get_customer["anything"]);
  113
+        Console.WriteLine("more-data: "     + get_customer["more-data"]);
  114
+        Console.WriteLine("================================================");
  115
+
  116
+        // ===================================================================
  117
+        // Disable Customer
  118
+        // ===================================================================
  119
+        Dictionary<object,object> disable_customer = pubnub_customer.Disable(
  120
+            (string)updated_customer["uid"] // CUSTOMER'S UID
  121
+        );
  122
+
  123
+        Console.WriteLine("================================================");
  124
+        Console.WriteLine("DISABLE CUSTOMER:");
  125
+        Console.WriteLine("status: "   + disable_customer["status"]);
  126
+        Console.WriteLine("message: "  + disable_customer["message"]);
  127
+        Console.WriteLine("================================================");
  128
+
  129
+        // ===================================================================
  130
+        // Enable Customer
  131
+        // ===================================================================
  132
+        Dictionary<object,object> enable_customer = pubnub_customer.Enable(
  133
+            (string)updated_customer["uid"] // CUSTOMER'S UID
  134
+        );
  135
+
  136
+        Console.WriteLine("================================================");
  137
+        Console.WriteLine("ENABLE CUSTOMER:");
  138
+        Console.WriteLine("status: "   + enable_customer["status"]);
  139
+        Console.WriteLine("message: "  + enable_customer["message"]);
  140
+        Console.WriteLine("================================================");
  141
+    }
  142
+}
  143
+
  144
+
  145
+/**
  146
+ * PubNub Customer API
  147
+ *
  148
+ * @author Stephen Blum
  149
+ * @package PubnubCustomer
  150
+ */
  151
+public class PubnubCustomer {
  152
+    private string ORIGIN        = "http://pubnub-prod.appspot.com/";
  153
+    private string PUBLISH_KEY   = "";
  154
+    private string SUBSCRIBE_KEY = "";
  155
+    private string SECRET_KEY    = "";
  156
+
  157
+    /**
  158
+     * Constructor
  159
+     *
  160
+     * Prepare PubNub Class State.
  161
+     *
  162
+     * @param string Publish Key.
  163
+     * @param string Subscribe Key.
  164
+     * @param string Secret Key.
  165
+     */
  166
+    public PubnubCustomer(
  167