Latest Tweet:
  • Loading...

I haven't blogged too much about my studies, but some of you might know that I'm graduating from the Norwegian University of Technology and Science with a bachelors degree in Informatics this summer. I think the coolest course I've taken through out the three years at the university is the "microcontroller system design" course. The course is all about embedded hardware and building software for devices. The course is really practical, so you get's lots of hands on assignments.

We're using the STK1000 development kit from the Trondheim based chip manufacture Atmel to learn microcontroller programming. This Friday Håvard Sørbø and Daugur Johanssonn and I delivered our third assignment in the course. It's a PONG clone running on the STK1000. The task was to install a custom Linux distribution on the device, write a kernel driver to handle buttons and lights, and to implement the PONG game. Håvard did the kernel driver for the LEDs and buttons and lights, Dagur did most of the game engine, while I did the graphics and some pair programming with Dagur on the actual game. The entire project is written in C. To make the game more fun we decided to base the theme of the game on Super Mario Brus on the NES.

  

I found this course really interesting since it's so far away from my everyday job as a .NET developer, working in such an high level of abstraction. On this course you get a chance to do really low level stuff, like writing C and assembly to the device, not depending on an operating system. You get to draw on the screen, one pixel at the time, and not depend on some pre-built graphics library. In all the course has been a great learning experience. I've included a YouTube video showing off the gameplay.

This post is the second in a series of posts covering how to send free SMS messages using the Ung1881 web site. The first post discussed how to use screen scraping to wrap the Ung1881 web site into a .NET library we could use to send messages from the command line. In this post I'm going to discuss how to create a Vista Sidebar Gadget that allows us to use the .NET library to send free messages.

 Now that we have a nice and clean SMS library at our disposal it's time to do something more fun (and useful) than sending messages from a Console application. The next step is to create a Vista Sidebar Gadget that uses the .NET library to send SMS messages. I'm not going to walk you through the basics of a Sidebar Gadget in this post. There is an excellent article for absolute beginners up on MSDN. The focus of this post is going to be .NET interop, and how to read phone numbers from your Vista address book.

Throughout the development cycle of Vista we've seen various versions of the sidebar. I still remember seeing some awesome WPF gadgets up on Channel9 when Vista still was known as Longhorn, and all we had was early CTP builds. However, as most of you know the final version of the Sidebar is limited to HTML, CSS and JavaScript. To many (including me) this was quite a disappointment…

To be honest, it's not fair to say that Vista Gadgets can't run .NET code. You can expose your .NET code as COM object, and it's fairly easy to create COM instances from script languages.

The real problem is to register your .NET code for COM interop. This involves modifying your code to become COM compatible and to create a MSI installation package to register and distribute your code. This is to much hassle for a simple gadget.

Thankfully Tyler (he doesn't include his full name) have found a really elegant solution to .NET Interop in Gadgets, and have posted an excellent article on this on "The Code Project". You should definitely check out the article. His solution to the problems involves creating a general purpose GadgetAdapter, which is a .NET assembly, that supports COM interop, that can load other .NET assemblies dynamically using reflection. The GadgetAdapter assembly is registered on the end users machine using shell scripting from JavaScript. So the first time you use the interop layer the GadgetAdapter gets registered on the clients machine. Once the GadgetAdapter is registered you can create an instance of it from your own gadget script and load up any .NET class you want from your own assembly.

So to backup for a second, your Gadget folder structure looks something like this:

In your Gadget Script file all you have to do to create a new instance of a .NET type is this:

// Instance of the GadgetBuilder to load/unload .NET assemblies. See GadgetInterop.js.
var builder = new GadgetBuilder();         
    
// Initialize the adapter to call .NET assemblies.
builder.Initialize();

// Use the builder to add the username and password as constructor argument values.
builder.AddConstructorParam(username);
builder.AddConstructorParam(password);
    
// Load the Ung1881.dll assembly and create an instance of the Ung1881.Ung1881Client type.
ung1881Client = builder.LoadType(System.Gadget.path + "\\bin\\Ung1881.dll", "Ung1881.Ung1881Client");
    
//Get values from the UI.
var number = txtPhoneNumber.value;
var message = txtMessage.innerText;    
    
//Sending the message
var status = ung1881Client.SendMessage(number, message);

The Ung1881Client is a new class I added to the library to make it even easier to use it from a Gadget. The class takes care of wrapping long messages into smaller chunks, and it returns a status code instead of throwing exceptions when something goes wrong.

During testing of the gadget I discovered one bug/issue with the GadgetInterop JavaScript library provider by Tyler. If you turn of UAC (the "cancel or allow" stuff in Vista), or right click and run Sidebar.exe as administrator, the gadget stops working. After doing some debugging I figured out that I was unable to load the GadgetAdapter. After digging some more I located the bug in the RegisterGadgetInterop function in the GadgetInterop.js file. The problem is that if you run with out UAC, or run sidebar.exe as an administrator, you can't write to HKCU (Current User). Instead you have to register the GadgetAdapter under HKLM (Local Machine). I have notified Tyler on the Code Project message board, but article isn't updated. I've included the updated version of the RegisterGadgetInterop function below:

////////////////////////////////////////////////////////////////////////////////
//
// Add the Gadget.Interop dll to the registry so it can be used by COM and
// created in javascript as an ActiveX object
//
////////////////////////////////////////////////////////////////////////////////
function RegisterGadgetInterop()
{
    try
    {
        // Full path to the Gadget.Interop.dll assembly
        var fullPath = System.Gadget.path + assemblyStore;
        var asmPath = fullPath + assemblyName;
            
        // Register the interop assembly under the Current User registry key
        RegAsmInstall("HKCU", progID, "Gadget.Interop.GadgetAdapter", guid,
            "Gadget.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9389e9f4d8844504",
            "1.0.0.0", asmPath);
            
        if(InteropRegistered() == false)
        {
            // Try Register the interop assembly under the Local Machine registry key
            RegAsmInstall("HKLM", progID, "Gadget.Interop.GadgetAdapter", guid,
                "Gadget.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9389e9f4d8844504",
                "1.0.0.0", asmPath);
         }            
    }
    catch(e)
    {
        System.Debug.outputString("RegisterGadgetInterop fails: " + e.message);
    }    
}

Another interesting feature of the gadget is the Vista Contacts integration. Since this is an SMS gadget you obviously want to access your address book to look up mobile phone numbers of your friends and family. This is fairly straight forward, since contact support is included in the Vista Sidebar Object Model. The function reading the address book and adding contacts who have a mobile phone to a drop down list is included below:

///////////////////////////////////////////////////////////////
//
// Load contacts from the Vista address book.
//
///////////////////////////////////////////////////////////////
function loadContacts()
{
    var contactMgr = System.ContactManager;
    var contacts = contactMgr.Contacts;
    var contact = null;
    var element = null;
        
    for(var i=0; i < contacts.count; i++)
    {
        contact = contacts.item(i);        
        if(contact.mobilePhone != "")
        {            
            element = document.createElement("option");
            element.text = contact.name;
            element.value = contact.mobilePhone;
            ddlContacts.add(element, ddlContacts.options.length);
        }
    }
    
    element = document.createElement("option");
    element.value = "";
    
    if(ddlContacts.options.length > 0)
    {
        element.text = "<Velg kontakt>";
        ddlContacts.disabled = false;
    }
    else
    {
        element.text = "<Ingen kontakter>";    
        ddlContacts.disabled = true;
    }
    ddlContacts.add(element, 0);    
    ddlContacts.selectedIndex = 0;
}

Gadget floating on desktop showing contacts

I've gotten a few requests from friends who want their Outlook 2007 contacts to show up in the gadget. Since this isn't supported directly by the Vista Sidebar Object Model I'd have to do the Outlook 2007 interop my self. I assume this wouldn't be too hard, but I haven't had time to figure this out yet. Drop me a comment if you have any good resources on how to read the Outlook 2007 address book from script. However, if you're running Outlook 2007 I have something far cooler up my sleeves for the next part of this series on how to make the most fun out the SMS library!

The download link points to the Windows Live Gallery site, so if you download and like the gadget please rate it. Feel free to post any comments, suggestions, questions or feedback.

Hege, my girlfriend, finally have a new site! Well, the site has actually been online for a couple of weeks now, and have been ready for almost a year! The site is a customized "Personal Web Page Starter Kit", with a blog module, a "chat box", a guestbook, and some cool Flickr! Integration. The site is available up on http://www.hege.rokenes.com/, so be sure to pay it a visit and drop her a note in the guestbook/chat box.

The Flickr! integration we've done is basically a mobile blog. When Hege takes a picture she can send it as an MMS message to her secret Flickr! e-mail address. All message received to this address is posted to her Flickr! site with a custom "mobile" tag. By using an RSS feed with all "mobile" tagged pictures, the XmlDataSource and the DataList control we managed to display mobile photos directly on her blog.

The reason it took so long to get the site online, was partly that we needed to get our virtual "personal project" server up and running on the new GreIT AS (my old company) server. Another "obstacle" was learning how to manage and deploy SQL 2005 databases.

Just before Christmas the SQL Servaer team released the "SQL Server Hosting Toolkit" on Codeplex. This toolkit consists of two major parts, a client side wizard and a server side Web Service. So you as an and user can install the toolkit and get a wizard that integrates nicely with all Visual Studio versions. The Wizard lets you generate both create and insert scripts for a database, and at the end of the wizard you can choose to save the file as a .SQL file and execute it your self, or you can choose to submit the query to a server running the "SQL Server Hosting Toolkit". The server part is a web service that accepts the SQL script and executes it against the server. This way hosting companies can offer their end users an easy way to upload both schema and data with out opening ports in the firewall.

My personal experience with the SQL Server Hosting Toolkit have been great. The script executed perfectly against the server database, and all the data (including binary data) was inserted as expected. If you want to learn more about how you can use the toolkit Scott Guthrie has a good walkthrough.

Another interesting aspect of the toolkit is the licensing. The application is licensed under the Microsoft Permissive License (Ms-PL), one of Microsofts new licenses that's part of their shared source initiative. Microsoft currently have three licenses under this initiative:

The Ms-PL is the least restrictive license and let's you create derivatives of the code, change it, distribute it etc. For instance is the client side JavaScript libraries in Microsoft AJAX framework licensed under the Ms-PL license. Mono guru Miquel de Icaza have some comments on this over at his blog. Another important AJAX technology is the AJAX Control Toolkit, which is licensed under the Ms-CL license. This license let's you create derivative works, but if you distribute them in source form you have to distribute them under the Ms-CL license.

The Ms-RL is the most restrictive, which basically just lets you read the code for reference, but not compile or alter it in any way.

The second thing that makes the SQL Server Hosting Toolkit interesting is that it shows how Microsoft is becoming more agile in meeting customer and developer demands. Instead of backing this toolkit into the official SQL Server release cycle, and having us wait a long time to get the functionality, they release it as a separate open source project. The same can be said about Microsoft AJAX framework. They released a fully supported 1.0 release now, instead of waiting for Visual Studio Orcas.

This blog post is the first one in a series of posts covering how to send free SMS messages using the Ung1881 web site. This first post discuss how to sign into a "Forms Authentication" protected ASP.NET web site, store an authentication cookie, and programmatically post web forms. Even if you're not interested in the free SMS messages you might still find the article interesting. The next part will be on how to use the .NET Class Library in a Windows Vista Gadget.

Unlimited free SMS messages, exposed as a .NET Class Library… Sounds to good to be true? Well, It's not, but as with everything else that's free (except Open Source?) it has a catch.  You can only send messages containing 130 characters. The last 30 characters are reserved for commercials. The good thing is that you can send as many messages as you want, and your own number show up as the sender. The service is offered by "Opplysningen 1881"  (Norwegian Number Enquiry) through their new portal "Ung1881", a page aimed at young people. In order to use their SMS service you need to be a have a Norwegian social security number, and a phone number. They need your social to do a lookup and verify that you're a real person. Even though you might not be able to register for an account at "Ung1881" you might still find the code and article useful since the concepts apply for any web application.

I got interested in the "Ung1881" after Håvard pointed me to it some weeks back. After doing some re-search it turned out someone had written a Mac OSX Widget letting you send SMS messages directly from your Mac desktop. My immediate idea was to port this widget to the Windows Vista Sidebar, but after looking at the JavaScript code of the widget I soon realized this was a bad idea. The Mac OSX Widget is depending on a third party server doing the screen scrapping server side, so all your SMS messages (including your username and password) are sent through a third party using HTTP GET like this: http://www.theretard.net/smss.pl?u=username&p=password&n=number&m=message. I don't know the guys behind theretard.net, but there's no way I'm trusting them with my username, password and every SMS message I ever send. Just imagine the password harvesting possibilities! On top of this they don't even use HTTPS for communication.

I decided I had to write a .NET Assembly wrapping the "Ung1881" service, so that I could re-use the functionality in other applications, like a Windows Vista Sidebar Gadget, a console application, Outlook 2007, PowerShell etc.

The technique of accessing web sites programmatically is commonly refereed to as  screen scrapping. Since the Ung1881 portal doesn't offer any kind of programming interface, you need to tap in to it at the HTTP level, and "pretend" that you're a regular user accessing the page with a browser. Since you're depending on the actual HTML structure page you're application might become unstable, and if the owner of the source you're scrapping change their HTML it might break your application. You also need to be careful that you're not violating copyright regulations, by for instance downloading weather information and displaying it as your own. In the case of "Ung1881" terms don't say anything about "automatic access", and as long as you have a valid account they still get their 30 character commercials included in every SMS message you send. I don't think you'll get into trouble by using this code, but if you do don't blame me!

The "Ung1881" portal is running on the .NET-based Content Management System EPiServer, and uses ASP.NET Forms Authentication to authenticate users on the portal. Forms Authentication normally works by using an authentication cookie. When you logon the server authenticates your username and password. If your credentials are valid, it issues a cookie your browser sends with subsequent requests to the site. So in order to wrap the SMS service in a C# class you basically need to write a "non visual" Web Browser. You need to make a request to their login page, use HTTP POST to post a username and password to the server and store the authentication cookie sent back in the response. On subsequent requests you need to attach the authentication cookie in order to access protected pages (like the send SMS page).

The first thing I did when implementing the "Ung1881" proxy class was to analyze the HTTP traffic going between server and client. There are several tools you can use to monitor network traffic at different levels. My preferred HTTP monitor is Fiddler. Fiddler act as a HTTP Proxy, so all you're HTTP requests are routed through Fiddler. Using Fiddler you can look at the raw requests, the headers, the posted form values etc. New in Fiddler v2.0 is support for HTTPS, which is excellent since the "Ung1881" portal is using HTTPS for secure communication. Another excellent tool for network monitoring is oSpy, written by Ole André Vadla Ravnås - a brilliant programmer, reverse engineer and in general a really nice guy!

The above screenshot shows all the HTTP traffic between Internet Explorer and the "Ung1881" web server. As you can see in the left column the site is quite "chatty" because of all the pictures, advertisements, script, style sheets etc. If you enter your username, password and hit the login button, and look at the request made to the URL https://www.ung1881.no/default____3.aspx you can figure out which form elements that are being posted to the server. The site contains quite a few form elements, but the interesting parts are the following:

Content-Disposition: form-data; name="defaultframework:login:tbxUsername"
myusername

Content-Disposition: form-data; name="defaultframework:login:tbxPassword"
mypassword

Content-Disposition: form-data; name="defaultframework:login:LoginButton.x"
0

Content-Disposition: form-data; name="defaultframework:login:LoginButton.y"
0

Content-Disposition: form-data; name="__VIEWSTATE"
dDw5NDE0MjMxMzc7O2w8ZGVmYXVsdGZyYW1ld29yazpsb2dpbjpMb2dpbk
J1dHRvbjtkZWZhdWx0ZnJhbWV3b3JrOnNlbmRzbXM6YnRuU2VuZDs+Pp5c
kvQYH64y3L+4/9ebcQzi7GmX

I've highlighted the names of the form elements. You could have figured out most of this just by looking at the HTML source, but I find it easier to use Fiddler. The thing that might not be too obvious just by looking at the HTML source, is what form value get posted when you click the login image button. The HTML fragment looks like this:

<input type="image" name="defaultframework:login:LoginButton" id="defaultframework_login_LoginButton" class="..." src="..." alt="" />

If you look at the HTTP POST values above you can see that there are two values posted back to the server when you click the login button, defaultframework:login:LoginButton.x and defaultframework:login:LoginButton.y. Both have the value 0. I took a few seconds before I figured what was happening, but apparently the HTML specifications says that <input type="image"> should post back the x and y values with the coordinates of where the user clicked the image. For accessibility reasons this is deprecated, and if you need x and y values from an image you should use an <imgemap> element instead. In order to confirm with the HTML standard the browser posts an x and y value back to the server.

Another important thing to note is that "Ung1881" is an ASP.NET application, and therefore is depending on the "__VIEWSTATE" form element. This hidden form value holds an encoded string representing the state of the application on the server. If we don't include this value in our scrapper the ASP.NET application can't figure out what state it's in, and how to process the request correctly.

If you look at the response sent back from the server you see that the server issues three cookies:

ASP.NET_SessionId=jnq5hh45zuuu0xyigc1jtm45; path=/

.EPiServerLogin=
6E1DBAC98D4073F956DA2000EBF122056E335441CCC7756F98B2
A229387EC47E446701A29
D36C4497553A5409F40322142C84B140E93C25CFC468C81
5B25CCC35648CE9B03C96XXXXXXXXXXXX; path=/;HttpOnly

ung1881.no=3568924299.20480.0000; expires=Sun, 25-Mar-2007 16:22:15 GMT; path=/

The interesting part is the .EPiServerLogin-cookie which is the authentication cookie. By adding this cookie to my subsequent requests, the server can tell that you're an authenticated user.

Now that we know what's going on when login into the page it's time to send an SMS message. I click the "Send SMS" link on the page, which takes me to the following URL: https://www.ung1881.no/Templates/SMS____24.aspx. I enter a phone number and message, and hit the button to send the message. The requests gets picked up by Fiddler, and by analyzing the request I figure out which form elements I need to post to the server in order to send a message:

Content-Disposition: form-data; name="defaultframework:_ctl2:Smssend:txtPhonenumber"
myNumber

Content-Disposition: form-data; name="defaultframework:_ctl2:Smssend:txtText"
myMessage

Content-Disposition: form-data; name="defaultframework:_ctl2:Smssend:butSend.x"
46

Content-Disposition: form-data; name="defaultframework:_ctl2:Smssend:butSend.y"
9

Content-Disposition: form-data; name="__VIEWSTATE"
*LARGE CHUNK OF STATE*

So after analyzing the application I now know the following about the communication between browser and server:

  • The login URL is: https://www.ung1881.no/default____3.aspx
  • The username form element is named: defaultframework:login:tbxUsername
  • The password form element is named: defaultframework:login:tbxPassword
  • The login button posts two values: defaultframework:login:LoginButton.x and defaultframework:login:LoginButton.y
  • The server issues an authentication cookie named .EPiServerLogin
  • The send SMS URL is: https://www.ung1881.no/Templates/SMS____24.aspx
  • The number form element is named: defaultframework:_ctl2:Smssend:txtPhonenumber
  • The message form element is named: defaultframework:_ctl2:Smssend:txtText
  • The send SMS button posts two values: defaultframework:_ctl2:Smssend:butSend.x and defaultframework:_ctl2:Smssend:butSend.y

Now that I know everything I need to know about the form elements It's time to write a C# class wrapping the site. The code is fairly well commented, so I won't describe it in detail. The key classes involved in the scrapper are the HttpWebRequest, HttpWebResponse, CookieContainer, StreamReader and StreamWriter classes. Another interesting part of the code is the private GetViewState method which extracts the view state of the page so that you can post it back to the server.

 

// Copyright (c) 2007, Jonas Follesø
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Jonas Follesø nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY Jonas Follesø ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Jonas Follesø BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Security.Authentication;

namespace Ung1881
{
    /// <summary>
    /// Proxy class wrapping the Ung1881 site for free SMS messaging.
    /// </summary>
    public class Ung1881Proxy
    {
        /// <summary>
        /// Variable used to store the authentication cookie.
        /// </summary>
        private CookieContainer cookies;

        /// <summary>
        /// Variable used to store the base uri of the page.
        /// </summary>
        private string baseUri = "https://www.ung1881.no/";

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Ung1881Proxy()
        {
            cookies = new CookieContainer();
        }

        /// <summary>
        /// Login on Ung1881 using username and password.
        /// Authenticates against the site and keeps the auth cookie for next request.
        /// </summary>
        /// <param name="username">Username used to logon.</param>
        /// <param name="password">Password used to logon.</param>
        public void Login(string username, string password)
        {
            //Validate arguments.
            if (username == null || username.Length == 0)
                throw new ArgumentException("You must provide a username!", "username");

            if (password == null || password.Length == 0)
                throw new ArgumentException("You must provide a password!", "password");
            
            string loginUri = baseUri + "default____3.aspx";

            // Perform the first http request against the asp.net application login site.
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(loginUri);

            // Get the response object, so that we may get the session cookie.
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // Populate the cookie container.
            request.CookieContainer = cookies;
            response.Cookies = request.CookieContainer.GetCookies(request.RequestUri);            

            // Read the incoming stream containing the login dialog page.
            StreamReader reader = new StreamReader(response.GetResponseStream());
            string loginDlgPage = reader.ReadToEnd();
            reader.Close();            

            // Extract the viewstate value from the login dialog page. 
            // We need to post this back, along with the username and password
            string viewState = GetViewState(loginDlgPage);

            // Build postback string.
            // This string will vary depending on the page. The best way to find out what your postback 
            // should look like is to monitor a normal login using a utility like Fiddler.
            string postback = String.Format("__VIEWSTATE={0}&defaultframework:login:tbxUsername={1}" +
                                            "&defaultframework:login:tbxPassword={2}" + 
                                            "&defaultframework:login:LoginButton.x=0&defaultframework:login:LoginButton.y=0",
                                            viewState, username, password);


            // Our second request is the POST of the username / password data.
            request = (HttpWebRequest)WebRequest.Create(loginUri);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.CookieContainer = cookies;            

            // Write our postback data into the request stream
            StreamWriter writer = new StreamWriter(request.GetRequestStream());
            writer.Write(postback);
            writer.Close();

            reader = new StreamReader(request.GetResponse().GetResponseStream());
            loginDlgPage = reader.ReadToEnd();
            reader.Close();

            int index = loginDlgPage.IndexOf("Logg ut", StringComparison.OrdinalIgnoreCase);

            if (!CheckAuthenticationCookies() || index == -1)
            {
                throw new AuthenticationException("Unable to authenticate user. Check your username and password.");
            }            
        }

        /// <summary>
        /// Method sending a new SMS message trough Ung1881.
        /// </summary>
        /// <param name="phoneNumber">The phone number of the receiver.</param>
        /// <param name="message">The message to send.</param>
        /// <returns>True if the message was sendt sucsessfully.</returns>
        public void SendSms(string phoneNumber, string message)
        {
            //Validate arguments
            if (phoneNumber == null || phoneNumber.Length == 0)
                throw new ArgumentException("You need to provide a phone number!");

            if (message == null || message.Length == 0)
                throw new ArgumentException("You need to provide a message!");

            //Check that we have an authentication cookie.
            if (!CheckAuthenticationCookies())
                throw new AuthenticationException("User not authenticated. Call Login first!");

            //our third request is for the actual webpage after the login. 
            string smsUrl = baseUri + "/Templates/SMS____24.aspx";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(smsUrl);
            request.CookieContainer = cookies;

            //and read the response
            StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream());
            string page = reader.ReadToEnd();
            string viewState = GetViewState(page);

            string postback = String.Format("__VIEWSTATE={0}&defaultframework:_ctl2:Smssend:txtPhonenumber={1}" +
                                            "&defaultframework:_ctl2:Smssend:txtText={2}&defaultframework:_ctl2:Smssend:butSend.x=0" +
                                            "&defaultframework:_ctl2:Smssend:butSend.y=0", viewState, phoneNumber, message);

            reader.Close();

            request = (HttpWebRequest)WebRequest.Create(smsUrl);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.CookieContainer = cookies;

            //Write our postback data into the request stream
            StreamWriter writer = new StreamWriter(request.GetRequestStream());
            writer.Write(postback);
            writer.Close();

            //Execute the request and read the response.
            reader = new StreamReader(request.GetResponse().GetResponseStream());
            page = reader.ReadToEnd();
            reader.Close();

            //Verify that the message was sent.
            if (!page.Contains("SMS sendt"))
            {
                throw new Ung1881Exception("Unable to send SMS message. Unknown error.");
            }            
        }

        /// <summary>
        /// Check if the user is authenticated.
        /// </summary>
        /// <returns>True if the user is authenticated.</returns>
        private bool CheckAuthenticationCookies()
        {
            bool authenticated = false;

            //Check that we have cookies.
            if (cookies != null)
            {
                //Check all cookies for the EpiServerLogin cookie.
                foreach (Cookie cookie in cookies.GetCookies(new Uri(baseUri)))
                {
                    if (cookie.Name != null && 
                        cookie.Name.Equals(".EPiServerLogin", StringComparison.OrdinalIgnoreCase))
                        authenticated = true;
                }
            }
            return authenticated;
        }

        /// <summary>
        /// Extract the viewstate data from a page.
        /// </summary>
        /// <param name="aspxPage">The raw HTML of the page.</param>
        /// <returns>Thew viewstate data of the page.</returns>
        private string GetViewState(string aspxPage)
        {
            Regex regex = new Regex("(?<=(__viewstate\".value.\")).*(?=\"./>)", RegexOptions.IgnoreCase);
            Match match = regex.Match(aspxPage);
            return System.Web.HttpUtility.UrlEncode(match.Value);
        }
    }
}

To test the class I've created a simple console application accepting a username, password, number and message as it's argument. Using the console application you can send SMS messages by typing the command: "SMS username password number message". If you add the folder containing the .EXE to your environment path you can send SMS messages from any command prompt.

 

// Copyright (c) 2007, Jonas Follesø
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Jonas Follesø nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY Jonas Follesø ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Jonas Follesø BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Ung1881;
namespace ConsoleScraper
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //Check that we have at least four arguments (username, password, number, message)
                if (args.Length < 4)
                {
                    Console.WriteLine("Usage: SMS username password number message");
                }
                else
                {
                    //Extract variables from arguments.
                    string username = args[0];
                    string password = args[1];
                    string number = args[2];
                    string message = string.Empty;

                    //Build up the message.
                    for (int i = 3; i < args.Length; ++i)
                    {
                        message += args[i];

                        //Add space if this isn't the last word of the message.
                        message += (i == args.Length - 1) ? string.Empty : " ";
                    }

                    Console.WriteLine("Sending message \"{0}\" to number {1}", message, number);

                    //Send the message.
                    Ung1881Client client = new Ung1881Client(username, password);
                    client.SendMessage(number, message);                    
                    Console.WriteLine("Message \"{0}\" sent to number {1}", message, number);
                }
            }
            catch (Exception ex)
            {
                //Simple exception handling.
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

So that's about it. A simple C# library allowing you to send unlimited free SMS messages through "Ung1881". You can choose to either download the compiled binaries or the source code. In the next part of this article/post I'll discuss how to use the library in a Windows Vista Sidebar Gadget.

If you have any questions, comments or bugs, or find the library useful, please drop me a comment!

<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910