Fork me on GitHub
» Making the world a better place, one byte at a time…

#commands on elggVoices

March 22nd, 2008 by Marcus Povey

Building on the @ commands I talked about in a previous posting, I would like to introduce elggVoices #commands!

These commands offer a plugable framework for third parties to add channel specific information oriented commands.

To see this in action, text or post one of the following to a channel (assumes you have validated either a phone number or an email address previously).

  • #weather <location> – Receive the weather forecast for the location.
  • #stock <stock code> – Receive a stock quote.

I have found #weather very useful. Contact Curverider directly if you want to talk about adding your own commands!

Connecting to elggVoices with C

February 28th, 2008 by Marcus Povey

The other week I posted a PHP example for connecting to the elggVoices API. For those of you interested in developing desktop applications, here is some example code for doing the same thing in C/C++.

This code is designed to be built into a library (either a static or dynamic) but could probably be directly included without too much trouble. It is tested and works fine on Linux, but should also build as a Windows DLL without too much trouble. Windows doesn’t support gettimeofday, so this has been rewritten.

You will also need to install libcurl and cryptopp, on Debian/Ubuntu these are apt-gettable.

searunner.h

/**
* @file searunner.h Searunner C++ Client library.
* This is the main include file for the Linux/Win32 client api for the
* searunner social networking platform.
* @author Marcus Povey <marcus@dushka.co.uk>
*/
#ifndef SEARUNNER_H_
#define SEARUNNER_H_

/** Some definitions */
#define VERSION "0.1.0"

#ifdef WIN32
// Windows Only

#ifdef LIBSEARUNNERW32_EXPORTS
#define LIBSEARUNNERW32_API __declspec(dllexport)
#else
#define LIBSEARUNNERW32_API __declspec(dllimport)
#endif

#include <winsock2.h>

// Map snprintf to _snprintf
#define snprintf _snprintf

#endif

#ifdef LINUX
// Linux

#define LIBSEARUNNERW32_API

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>

#endif

/* Include crypto++ lib from www.cryptopp.com */
#include <crypto++/cryptlib.h>

/**
* Create the configuration structure.
*/
typedef struct {
char apikey[33];
char secret[33];
char endpoint[255];
char hmac_algo[8];
char hash_algo[8];
} SearunnerConfig;


extern LIBSEARUNNERW32_API SearunnerConfig hSearunnerConfig;

/** Get call */
#define SEARUNNER_CALLMODE_GET 0
/** Post call */
#define SEARUNNER_CALLMODE_POST 1

/** CURL Write function callback type */
typedef size_t (*CURLOPT_WRITEFUNCTION_CALLBACK)( void *ptr, size_t size, size_t nmemb, void *stream);

/**
* Initialisation the API client.
* This function initialises the api client configuration structure ready for use.
* @param api_key The API key you have been provided with.
* @param secret The secret key you have been provided with.
* @param endpoint_url The API Endpoint.
*/
LIBSEARUNNERW32_API int searunner_initialise_api(const char * api_key, const char * secret, const char * endpoint_url);

/**
* Raw POST request.
* @param parameters List of parameters.
* @param postData Optional post data in post call or null if this is a GET call.
* @param postdata_length The length of the data being sent.
* @param content_type Optional content type or null if this is a GET call.
* @param buffer The buffer to write the result into [OUT]
* @param buffer_size The size of the buffer
* @return The amount actually written.
*/
LIBSEARUNNERW32_API int __searunner_post_call(const char * parameters, unsigned char * postData, int postdata_length, char * content_type, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func);

/**
* Raw GET request.
* @param parameters List of parameters.
* @param buffer The buffer to write the result into [OUT]
* @param buffer_size The size of the buffer
* @return The amount actually written.
*/
LIBSEARUNNERW32_API int __searunner_get_call(const char * parameters, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func);

/**
* Make a searunner call.
* @param method Method call.
* @param parameters List of parameters.
* @param postData Optional post data in post call or null if this is a GET call.
* @param postdata_length The length of the data being sent.
* @param content_type Optional content type or null if this is a GET call.
* @param buffer The buffer to write the result into [OUT]
* @param buffer_size The size of the buffer
* @return The amount actually written.
*/
LIBSEARUNNERW32_API int __searunner_call(int method, const char * parameters, unsigned char * postData, int postdata_length, char * content_type, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func);

#endif /*SEARUNNER_H_*/

searunner.cpp

/**
* @file searunner.cpp
* Main functions and Win32 DLL entrypoint.
* @author Marcus Povey <marcus@dushka.co.uk>
*/

#include "searunner.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crypto++/sha.h>
#include <crypto++/hmac.h>
#include <crypto++/hex.h>
#include <crypto++/filters.h>
#include <curl/curl.h>
#include <time.h>

/* Configuration structure */
LIBSEARUNNERW32_API SearunnerConfig hSearunnerConfig;

/* Utility functions used internally */
#ifdef WIN32

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif

struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};

/**
* Replacement gettimeofday function for windows.
* Obtained from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/
*/
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
unsigned __int64 tmpres = 0;
static int tzflag;

if (NULL != tv)
{
GetSystemTimeAsFileTime(&ft);

tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;

/*converting file time to unix epoch*/
tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tv->tv_sec = (long)(tmpres / 1000000UL);
tv->tv_usec = (long)(tmpres % 1000000UL);
}

if (NULL != tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}

return 0;
}

#endif

#define MICRO_IN_SEC 1000000.00

/**
* Micro time function.
*
* Example usage:
*
* char oo[100];
* microtime(oo);
* printf(oo);
*
* @param buffer Where to output variables.
*/
int microtime(char * buffer)
{
struct timeval tp;
long sec = 0L;
double msec = 0.0;
char ret[100];

if (gettimeofday((struct timeval *) &tp, (NULL)) == 0)
{
msec = (double) (tp.tv_usec / MICRO_IN_SEC);
sec = tp.tv_sec;

if (msec >= 1.0) msec -= (long) msec;

int msec2 = (int)(msec*100);

snprintf(ret, 100, "%ld.%d", sec, msec2);
strcpy(buffer, ret);
return 0;
}

return -1;
}

/**
* Calculate a hmac for the given data.
*
* Example usage:
*
* char oo [2*CryptoPP::SHA1::DIGESTSIZE+1];
* calculate_hmac(&hSearunnerConfig, "1", "2", "3", oo, 2*CryptoPP::SHA1::DIGESTSIZE+1);
* printf(oo);
*
* @param time String representation of time
* @param get_variables String of parameters
* @param post_hash The hash
* @param buffer The buffer
* @param bugger_size Size of the buffer
*/
int calculate_hmac(
const char * time,
const char * get_variables,
const char * post_hash,

char * buffer, int buffer_size)
{
memset(buffer,0,buffer_size);

if (strcmp(hSearunnerConfig.hmac_algo, "sha1")==0)
{
// Sha1

// Check the buffer size before we do anything
if (buffer_size<2*CryptoPP::SHA1::DIGESTSIZE + 1) return -1;

// Initialise memory
unsigned char tmp[ 2*CryptoPP::SHA1::DIGESTSIZE + 1 ];
memset(tmp,0,2*CryptoPP::SHA1::DIGESTSIZE + 1);

// Compute HMAC
CryptoPP::HMAC<CryptoPP::SHA1> hmac((const byte*)hSearunnerConfig.secret, strlen(hSearunnerConfig.secret));
hmac.Update((const byte*)time, strlen(time));
hmac.Update((const byte*)hSearunnerConfig.apikey, strlen(hSearunnerConfig.apikey));
hmac.Update((const byte*)get_variables, strlen(get_variables));
if (post_hash) hmac.Update((const byte*)post_hash, strlen(post_hash));

hmac.Final((byte*)tmp);

// hex encode
CryptoPP::HexEncoder encoder;
encoder.Attach(new CryptoPP::ArraySink((byte*)buffer, 2*CryptoPP::SHA1::DIGESTSIZE));
encoder.Put(tmp, sizeof(tmp));
encoder.MessageEnd();

// Convert to lower case
for (int n = 0; n<buffer_size; n++)
if (buffer[n]!=0) buffer[n] = tolower(buffer[n]);

return 0;
}

return -1;
}

/*
* Calculate the hash of some POST data.
* TODO: Tidy this up
* TODO: Support more algorithms
*
* Example usage:
*
* char oo [2*CryptoPP::MD5::DIGESTSIZE+1];
* calculate_POST_hash(&hSearunnerConfig, (unsigned char*)"test", 4, oo, 2*CryptoPP::MD5::DIGESTSIZE+1);
* printf(oo);
*
* @param postdata The data
* @param postlength The length
* @param outputbuffer The buffer
* @param outputbuffer_size The size of the output buffer
*/
int calculate_POST_hash(const unsigned char * postdata, int postlength, char * outputbuffer, int outputbuffer_size)
{
memset(outputbuffer,0,outputbuffer_size);

if (strcmp(hSearunnerConfig.hash_algo, "sha1")==0)
{
// Check the buffer size before we do anything
if (outputbuffer_size<2*CryptoPP::SHA1::DIGESTSIZE + 1) return -1;


// Initialise memory
unsigned char tmp[ 2*CryptoPP::SHA1::DIGESTSIZE + 1 ];
memset(tmp,0,2*CryptoPP::SHA1::DIGESTSIZE + 1);

// Calculate the digest
CryptoPP::SHA1 hash;
hash.CalculateDigest((byte*)tmp, (const byte*)postdata, postlength);

// hex encode
CryptoPP::HexEncoder encoder;
encoder.Attach(new CryptoPP::ArraySink((byte*)outputbuffer, 2*CryptoPP::SHA1::DIGESTSIZE));
encoder.Put(tmp, sizeof(tmp));
encoder.MessageEnd();

// Convert to lower case
for (int n = 0; n<outputbuffer_size; n++)
if (outputbuffer[n]!=0) outputbuffer[n] = tolower(outputbuffer[n]);

return 0;
}

return -1;
}

/*************************************/

LIBSEARUNNERW32_API int searunner_initialise_api(const char * api_key, const char * secret, const char * endpoint_url)
{
strcpy(hSearunnerConfig.apikey, api_key);
strcpy(hSearunnerConfig.secret, secret);
strcpy(hSearunnerConfig.endpoint, endpoint_url);
strcpy(hSearunnerConfig.hmac_algo, "sha1");
strcpy(hSearunnerConfig.hash_algo, "sha1");
return 0;
}

LIBSEARUNNERW32_API int __searunner_call(int method,
const char * parameters,
unsigned char * postData,
int postdata_length,
char * content_type,
CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)
{
char url[2048] = {""};
char posthash[256] = {""};
char hmac[256] = {""};
char tmp[100] = {""};

struct curl_slist *headerlist = NULL;

// Get the microtime for now
char time[100];
microtime(time);

// Construct URL
strcpy(url, hSearunnerConfig.endpoint);
strcat(url, "?");
strcat(url, parameters);

// If post data, hash it
if (postdata_length > 0)
calculate_POST_hash(postData, postdata_length, posthash, 256);

// Constract the hmac
calculate_hmac(time, parameters, posthash, hmac, 256);

// Initialise CURL
CURL * curl = curl_easy_init();
if(curl) {

// Set headers
sprintf(tmp, "X-Searunner-apikey: %s", hSearunnerConfig.apikey);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "X-Searunner-time: %s", time);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "X-Searunner-hmac-algo: %s", hSearunnerConfig.hmac_algo);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "X-Searunner-hmac: %s", hmac);
headerlist = curl_slist_append(headerlist, tmp);

// Set options
if (method == SEARUNNER_CALLMODE_POST)
{
curl_easy_setopt(curl, CURLOPT_POST, 1); // Use POST

sprintf(tmp, "X-Searunner-posthash: %s", posthash);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "X-Searunner-posthash-algo: %s", hSearunnerConfig.hash_algo);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "Content-type: %s", content_type);
headerlist = curl_slist_append(headerlist, tmp);

sprintf(tmp, "Content-Length: %d", postdata_length);
headerlist = curl_slist_append(headerlist, tmp);

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData); // Set the post data
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postdata_length);
}

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); // Set the headers
curl_easy_setopt(curl,CURLOPT_URL, url); // Set the url
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_read_func); // Use our own read function

// Make call
CURLcode res = curl_easy_perform(curl);

// Clean up
curl_easy_cleanup(curl);

return res;
}

return 0;
}

LIBSEARUNNERW32_API int __searunner_post_call(const char * parameters, unsigned char * postData, int postdata_length, char * content_type, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)
{
return __searunner_call(SEARUNNER_CALLMODE_POST, parameters, postData, postdata_length, content_type, curl_read_func);
}

LIBSEARUNNERW32_API int __searunner_get_call(const char * parameters, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)
{
return __searunner_call(SEARUNNER_CALLMODE_GET, parameters, NULL, 0, NULL, curl_read_func);
}

#ifdef WIN32

/*
Windows Only DLL entrypoint.
*/

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


#endif

@commands on elggVoices

February 26th, 2008 by Marcus Povey

Some of you who have been using elggVoices recently, may have noticed some rather odd looking shouts appearing in the channels.

Shouts beginning with “@” can be used to execute commands in a channel – especially useful when used remotely via SMS.

I thought it might be useful to give you a quick run down of the commands currently available. I say “currently”, since we are adding to this all the time.

SMS and Website

  • @status message: Updates your status on a channel.
  • @sleep on|off: Set your sleep status on and off, when @sleep is on you won’t receive SMS messages.
  • @location city: Updates your location.
  • @country country: Updates your country.

SMS only

  • @signup desired_username your_email: Send this to a channel’s SMS number and you will create a elggVoices account and automatically be signed up to the channel. Your password will be emailed to your email address.
  • @join: Send this to a channel’s SMS number will join the channel.

Just a few commands for now, but there will be more to come.

Next Page »
All content is © Copyright Marcus Povey 2008-2012 and released under a Creative Commons licence unless otherwise stated.