.NET code for SMS

HI all.

I am working on some code to send/receive SMS messages through Zeep Mobile (http://zeepmobile.com/developers/).

I have looked at their google groups and even contacted their support but they've not been very good at communicating back and I am really flying blind now.

I have to get integrated with them (work has required it) and I'm not sure why my code is not working. So, I'm wondering if anyone out there has any C# .Net code that they won't mind sharing so that I may integrate that into my app.

Of course, this is all dependent on whether you have experience with Zeep or not. If you want me post my code, I can do that too. Let me know.

Thanks and I really appreciate the help.

**

EDIT: I HAVE ADDED SOURCE CODE HERE IN CASE ANYONE CAN HELP WITH THIS!

**

Please forgive the sloppy code. Its just something I threw together to test Zeep and I'm hoping someone can give it a shot. (.Net 3.5 console app in case you want to build it).

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Web;
using System.Web.Handlers;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;


namespace ConsoleApplication1
{


    class Program
    {
        public static string API_KEY = "MY_API_KEY";
        public static string SECRET_ACCESS_KEY = "MY_SECRET_KEY";
        public static string PATTERN_RFC1123 = "ddd, dd MMM yyyy HH:mm:ss " + "GMT";

        static void Main(string[] args)
        {
            // URL for sending message - 
            //      send_message = "https://api.zeepmobile.com/messaging/2008-07-14/send_message";
            //      blast_message = "https://api.zeepmobile.com/messaging/2008-07-14/blast_message";
            string apiurl = "https://api.zeepmobile.com/messaging/2008-07-14/blast_message";


            // FORMAT must be Sun, 06 Nov 1994 08:49:37 GMT
            string http_date = DateTime.UtcNow.ToString("r");
            // Text to send
            string body = HttpUtility.UrlEncode("Test message.", System.Text.Encoding.UTF8);
            // NOTE: Use 'user_id=22&body=' instead of just 'body=' when sending a message to a user.
            // 22 is a user I have previously registered with ZEEP and is used for testing purposes.
            string parameters = "body=" + body; 
            // String that will be converted into a signature.
            string canonicalString = API_KEY + http_date + parameters;


            //------------START HASH COMPUTATION---------------------
            // Compute the Base64 HMACSHA1 value
            HMACSHA1 hmacsha1 = new HMACSHA1(SECRET_ACCESS_KEY.ToByteArray());

            // Compute the hash of the input file.
            byte[] hashValue = hmacsha1.ComputeHash(canonicalString.ToByteArray());

            String b64Mac = hashValue.ToBase64String();
            String authentication = String.Format("Zeep {0}:{1}", API_KEY, b64Mac);
            //-----------END HASH COMPUTATION------------------------


            // We are using TCPClient instead of an HTTPWebRequest because we need to manually
            // set the "Headers" such as Date, Authorization etc which cannot easily be done with HTTPWebRequest.
            Uri reqUrl = new Uri(apiurl);
            TcpClient client = new TcpClient(reqUrl.Host, reqUrl.Port);
            NetworkStream netStream = client.GetStream();
            // SSLStream is used for secure communication. ZEEP requires the use of SSL to send and SMS.
            System.Net.Security.SslStream sslStream = new System.Net.Security.SslStream(
                netStream, 
                false, 
                new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCertificate));
            sslStream.AuthenticateAsClient(reqUrl.Host);


            // POST content we are going to transmit over the SSL channel. 
            // See. http://zeepmobile.com/developers/documentation/messaging/2008-07-14/rest_api#send_message
            System.IO.StreamWriter s = new System.IO.StreamWriter(sslStream);
            s.WriteLine(String.Format("POST {0} HTTP/1.1", "/api/blast"));
            s.WriteLine(String.Format("Host: {0}", "api.zeepmobile.com"));
            s.WriteLine(String.Format("Authorization: Zeep {0}:{1}", API_KEY, b64Mac));
            s.WriteLine(String.Format("Date: {0}", http_date));
            s.WriteLine(String.Format("Content-Type: {0}", "application/x-www-form-urlencoded"));
            s.WriteLine(String.Format("Content-Length: {0}", parameters.Length));
            s.WriteLine(String.Format("{0}", parameters));
            s.Flush();


            System.IO.StreamReader r = new StreamReader(sslStream);
            string resp = r.ReadToEnd();
            Console.WriteLine(resp);
            r.Close();

        }

        // The following method is invoked by the RemoteCertificateValidationDelegate.
        // We want to make sure the SSL has no Policy errors and is safe.
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }

    }

    public static class Extensions
    {
        public static byte[] ToByteArray(this string input)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            return encoding.GetBytes(input);
        }

        public static string ToBase64String(this byte[] input)
        {
            return Convert.ToBase64String(input);
        }
    }
}
Error

What is happening when I run this code is the error shown in the following image.

Answers


SOLUTION:

OK. I worked on it all last night and I made some progress. I used Fiddler to construct the POST message to see any discrepancies between what I had above and what the server expected.

I have got it sending the message and returning a HTTP 200 OK response. Again, this code is not production ready, its just something to test and see if I can get Zeep working. Thank you all who responded and if your looking for ZEEP code, I hope this can help you.


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Web;
using System.Web.Handlers;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;


namespace ConsoleApplication1
{
    class Program
    {
        public static string API_KEY = "YOUR_API_KEY_GOES_HERE! INCLUDE DASHES!";
        public static string SECRET_ACCESS_KEY = "YOUR_SECRET_KEY_GOES_HERE!";

        static void Main(string[] args)
        {
            Console.WriteLine("BLAST - \r\n\r\n");
            BlastTcpPost();

            Console.WriteLine("SEND - \r\n\r\n");
            SendTcpPost();
        }

        /// <summary>
        /// Send a BLAST to all users in your ZEEP account.
        /// </summary>
        public static void BlastTcpPost()
        {
            SendSMS(
                "https://api.zeepmobile.com/messaging/2008-07-14/blast_message",    // URL for Send_Message 
                "You are on blast",                                                 // Message to send
                string.Empty                                                        // No UserId to send.
                );
        }

        /// <summary>
        /// Send a single message to a user in your ZEEP account.
        /// </summary>
        public static void SendTcpPost()
        {
            // Note:- 22 I use for the UserId is just a user I have signed up. Yours may be different and you 
            // might want to pass that in as a parameter.

            SendSMS(
                "https://api.zeepmobile.com/messaging/2008-07-14/send_message",     // URL for Send_Message
                "You are a user...good job!",                                       // Message to send
                "22"                                                                // User Id in your system.
                );

        }

        /// <summary>
        /// Uses a TCPClient and SSLStream to perform a POST.
        /// </summary>
        /// <param name="requestUrl">URL that the POST must be directed to.</param>
        /// <param name="body">Message that is to be sent.</param>
        /// <param name="user">UserId in your Zeep System. Only required if your sending a Single Message to a User. 
        /// Otherwise, just send a string.Empty.</param>
        /// <returns>Response from the server. (although it will write the response to console)</returns>
        public static string SendSMS(string requestUrl, string body, string user)
        {
            string parameters = "";
            string requestHeaders = "";
            string responseData = "";

            // FORMAT must be Sun, 06 Nov 1994 08:49:37 GMT
            string http_date = DateTime.UtcNow.ToString("r");

            // Clean the text to send
            body = HttpUtility.UrlEncode(body, System.Text.Encoding.UTF8);

            if (user.Length > 0) parameters += "user_id=" + user + "&";
            if (body.Length > 0) parameters += "body=" + body;


            // String that will be converted into a signature.
            string canonicalString = API_KEY + http_date + parameters;


            //------------START HASH COMPUTATION---------------------
            // Compute the Base64 HMACSHA1 value
            HMACSHA1 hmacsha1 = new HMACSHA1(SECRET_ACCESS_KEY.ToByteArray());

            // Compute the hash of the input file.
            byte[] hashValue = hmacsha1.ComputeHash(canonicalString.ToByteArray());

            String b64Mac = hashValue.ToBase64String();
            String authentication = String.Format("Zeep {0}:{1}", API_KEY, b64Mac);
            //-----------END HASH COMPUTATION------------------------


            string auth = String.Format("Zeep {0}:{1}", API_KEY, b64Mac);


            System.Uri uri = new Uri(requestUrl);
            System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(uri.Host, uri.Port);
            string requestMethod = "POST " + uri.LocalPath + " HTTP/1.1\r\n";

            // Set Headers for the POST message
            requestHeaders += "Host: api.zeepmobile.com\r\n";
            requestHeaders += "Authorization: " + auth + "\r\n";
            requestHeaders += "Date: " + DateTime.UtcNow.ToString("r") + "\r\n";
            requestHeaders += "Content-Type: application/x-www-form-urlencoded\r\n";
            requestHeaders += "Content-Length: " + parameters.ToByteArray().Length + "\r\n";
            requestHeaders += "\r\n";


            // Get the data to be sent as a byte array.
            Byte[] data = System.Text.Encoding.UTF8.GetBytes(requestMethod + requestHeaders + parameters + "\r\n");
            // Send the message to the connected TcpServer.
            NetworkStream stream = client.GetStream();


            // SSL Authentication is used because the Server requires https.
            System.Net.Security.SslStream sslStream = new System.Net.Security.SslStream(
                stream,
                false,
                new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCertificate));
            sslStream.AuthenticateAsClient(uri.Host);

            // Send the data over the SSL stream.
            sslStream.Write(data, 0, data.Length);
            sslStream.Flush();


            // Receive the TcpServer.response.
            for (int i = 0; i < 100; i++)
            {
                if (stream.DataAvailable)
                {
                    break;
                }
                System.Threading.Thread.Sleep(100);
            }

            Byte[] bytes = new byte[1024];
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            while (stream.DataAvailable)
            {
                int count = sslStream.Read(bytes, 0, 1024);
                if (count == 0)
                {
                    break;
                }
                sb.Append(System.Text.Encoding.UTF8.GetString(bytes, 0, count));
            }

            responseData = sb.ToString();
            Console.WriteLine(responseData);
            // Close everything.
            client.Close();

            return responseData;
        }



        // The following method is invoked by the RemoteCertificateValidationDelegate.
        // We want to make sure the SSL has no Policy errors and is safe.
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            // Somehow the cert always has PolicyErrors so I am returning true regardless.
            return true;
            //if (sslPolicyErrors == SslPolicyErrors.None)
            //    return true;

            //Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            //// Do not allow this client to communicate with unauthenticated servers.
            //return false;
        }
    }

    public static class Extensions
    {
        public static byte[] ToByteArray(this string input)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            return encoding.GetBytes(input);
        }

        public static string ToBase64String(this byte[] input)
        {
            return Convert.ToBase64String(input);
        }
    }
}

Are you sure that you need to manually implement the HTTP protocol in code using a TcpClient and an SslStream to communicatie with Zeep? I'd try to just use a HttpWebRequest object and let the .NET Framework handle the HTTP specific protocol bits for you. Especially since the error message you get back seems to point in the direction of the request you are sending not being able to be understood by the remote server.

(Wasn't there an extra empty line needed between HTTP header and actual content? I never really remember, and I don't have to if I just do the correct .NET Framework WebRequest...)

Good luck!


Need Your Help

What is “Hadoop” - the definition of Hadoop?

hadoop hbase hdfs apache-spark yarn

It is kind of obvious and we will all agree that we can call HDFS + YARN + MapReduce as Hadoop. But what happens with different other combinations and other products in the Hadoop ecosystem?

Circular buffer without modulo operation

c buffer code-review c99 circular-buffer

In Wikipedia it states that a modulo operation is required to check the available space in a circular buffer. However in my implementation I simply do:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.