Sending an SMS

by Erik Möller, June 2009

If you've ever written an application that is expected to run 24-7 such as a game server (which was what I wrote) it can be really useful to have the application report its status to you or the support team at regular intervals. Maybe it's important enough that in case of a crash you want to get a message so you can drag yourself out of bed and look through callstacks in the middle of the night.

The easy solution to this is to have the app or some monitoring daemon text your cell. This is by no means a general solution but I thought I'd show just how easy it was for me to add that functionality to a daemon. The provider I used was mo-sms which is a Swedish company. I created an account, filled it with 100 SEK (about $12) wrote a short snippet of code and away we went. The servers were texting support staff after just a few hours work.

The source is included below. It's been compiled with Visual Studio 2005 and uses windows specific API calls, but it should be a breeze to port to any other platform. Besides, your provider will probably use a slightly different method, the point I wanted to make was that it's simple!


Source listing SMS.h

			      
#ifndef SMS_H
#define SMS_H

#include <string>;

//
// Sends an SMS message to the specified phone number.
// The phone number should be in the following format:
// (Swedish mobile) +46706100338
//
bool SendSMS(const std::string &message, const std::string &phone, std::string &errMsg);

#endif //SMS_H
  		      
			    

Source listing SMS.cpp

			      
#include <windows.h>
#include <wininet.h>
#include "SMS.h"

#pragma comment(lib, "wininet.lib")

//
// URL encode the string passed in.
//
std::string URLEncode(std::string str)
{
  // A list of unsafe characters that has to be hex encoded.
  const std::string unsafeString = "\"<>%\\^[]`+$,@:;/!#?=&";
  std::string res;
  
  // Loop over the string and check if it's an allowed character.
  for(std::string::iterator ch = str.begin(); ch != str.end(); ++ch)
    if(unsafeString.find(*ch) == std::string::npos && *ch > ' ' && *ch < '{')
      res += *ch;
    else
    {
      // If not, we'll have to print it as % followed by a two digit hex code.
      const size_t kBufSize = 8;
      char buf[kBufSize] = "";
      sprintf_s(buf, kBufSize, "%%%02X", *ch);
      res += buf;
    }
    return res;
}

//
// Sends an SMS message to the specified phone number. The phone number should be in
// the following format: (Swedish mobile) +46706100338
//
bool SendSMS(const std::string &message, const std::string &phone, std::string &errMsg)
{
  // Set up some strings with the details to send with the fury account.
  std::string username = "user",
              password = "pass",
              url = "http://www.mosms.com/se/sms-send.php",
              type = "text",
              data = URLEncode(message),
              callString = url + "?username=" + username + "&password=" + password + "&nr=" + phone + "&type=" + type + "&data=" + data;

  // Open an internet session.
  HINTERNET session = InternetOpenA("Fury-SMS", PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
  if (session == NULL)
  {
    errMsg = "Failed to open internet connection.";
    return false;
  }
  else
  {
    // Do the actual request to send the message.
    HINTERNET request = InternetOpenUrlA(session, callString.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (request == NULL)
    {
      errMsg = "Failed to process request.";
      return false;
    }
    else
    {
      // Read back the result.
      const size_t kBufSize = 1024;
      char buffer[kBufSize];
      DWORD bytesRead = 0;
      if (!InternetReadFile(request, buffer, kBufSize - 1, &bytesRead))
      {
        errMsg = "Failed to read response.";
        return false;
      }
      else
      {
        // The expected result on success is a single 0. If there's a failure the
        // provider will send back an error message.
        buffer[bytesRead] = 0;
        if (std::string(buffer) == "0")
          return true;
        else
          errMsg = "Call failed. Provider msg: " + std::string(buffer);
      }
    }
  }
  return false;
}