<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Marcus Povey &#187; elggvoices</title>
	<atom:link href="http://www.marcus-povey.co.uk/tag/elggvoices/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.marcus-povey.co.uk</link>
	<description>Making the world a better place, one byte at a time...</description>
	<lastBuildDate>Mon, 06 Feb 2012 19:13:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.1</generator>
	<atom:link rel='hub' href='http://www.marcus-povey.co.uk/?pushpress=hub'/>
		<item>
		<title>#commands on elggVoices</title>
		<link>http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/</link>
		<comments>http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/#comments</comments>
		<pubDate>Sat, 22 Mar 2008 11:55:08 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[elggvoices]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[commands]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>Building on the @ commands I talked about in <a href="http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/">a previous posting</a>, I would like to introduce elggVoices <strong>#commands!</strong></p>
<p>These commands offer a plugable framework for third parties to add channel specific information oriented commands.</p>
<p>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).</p>
<ul>
<li><code>#weather &lt;location&gt;</code> &#8211; Receive the weather forecast for the location.</li>
<li><code>#stock &lt;stock code&gt;</code> &#8211; Receive a stock quote.</li>
</ul>
<p>I have found #weather very useful. Contact <a href="http://www.curverider.co.uk/">Curverider</a> directly if you want to talk about adding your own commands!</p>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F03%2F22%2Fcommands-on-elggvoices-2%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/03/22/commands-on-elggvoices-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Connecting to elggVoices with C</title>
		<link>http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/#comments</comments>
		<pubDate>Thu, 28 Feb 2008 16:34:52 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[elggvoices]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>The other week I posted a <a href="http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/">PHP example for connecting to the elggVoices API</a>. For those of you interested in developing desktop applications, here is some example code for doing the same thing in C/C++.</p>
<p>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&#8217;t support <code>gettimeofday</code>, so this has been rewritten.</p>
<p>You will also need to install <a href="http://curl.haxx.se/libcurl/">libcurl</a> and <a href="http://www.cryptopp.com/">cryptopp</a>, on Debian/Ubuntu these are apt-gettable.</p>
<p><strong>searunner.h</strong></p>
<blockquote><p><code>/**<br />
* @file searunner.h Searunner C++ Client library.<br />
* This is the main include file for the Linux/Win32 client api for the<br />
* searunner social networking platform.<br />
* @author Marcus Povey &lt;marcus@dushka.co.uk&gt;<br />
*/<br />
#ifndef SEARUNNER_H_<br />
#define SEARUNNER_H_</code></p>
<p><code>/** Some definitions */<br />
#define VERSION     "0.1.0"</code></p>
<p><code>#ifdef WIN32<br />
// Windows Only</code></p>
<p><code>#ifdef LIBSEARUNNERW32_EXPORTS<br />
#define LIBSEARUNNERW32_API __declspec(dllexport)<br />
#else<br />
#define LIBSEARUNNERW32_API __declspec(dllimport)<br />
#endif</code></p>
<p><code>#include &lt;winsock2.h&gt;</code></p>
<p><code>// Map snprintf to _snprintf<br />
#define snprintf _snprintf</code></p>
<p><code>#endif</code></p>
<p><code>#ifdef LINUX<br />
// Linux</code></p>
<p><code>#define LIBSEARUNNERW32_API</code></p>
<p><code>#include &lt;sys/types.h&gt;<br />
#include &lt;sys/socket.h&gt;<br />
#include &lt;sys/time.h&gt;<br />
#include &lt;netinet/in.h&gt;<br />
#include &lt;unistd.h&gt;<br />
#include &lt;netdb.h&gt;</code></p>
<p><code>#endif</code></p>
<p><code>/* Include crypto++ lib from www.cryptopp.com */<br />
#include &lt;crypto++/cryptlib.h&gt;</code></p>
<p><code>/**<br />
* Create the configuration structure.<br />
*/<br />
typedef struct {<br />
char apikey[33];<br />
char secret[33];<br />
char endpoint[255];<br />
char hmac_algo[8];<br />
char hash_algo[8];<br />
} SearunnerConfig;</code><br />
<code><br />
extern LIBSEARUNNERW32_API SearunnerConfig hSearunnerConfig;</code></p>
<p><code>/** Get call */<br />
#define SEARUNNER_CALLMODE_GET         0<br />
/** Post call */<br />
#define SEARUNNER_CALLMODE_POST        1</code></p>
<p><code>/** CURL Write function callback type */<br />
typedef size_t (*CURLOPT_WRITEFUNCTION_CALLBACK)( void *ptr, size_t size, size_t nmemb, void *stream);</code></p>
<p><code>/**<br />
* Initialisation the API client.<br />
* This function initialises the api client configuration structure ready for use.<br />
* @param api_key The API key you have been provided with.<br />
* @param secret The secret key you have been provided with.<br />
* @param endpoint_url The API Endpoint.<br />
*/<br />
LIBSEARUNNERW32_API int searunner_initialise_api(const char * api_key, const char * secret, const char * endpoint_url);</code></p>
<p><code>/**<br />
* Raw POST request.<br />
* @param parameters List of parameters.<br />
* @param postData Optional post data in post call or null if this is a GET call.<br />
* @param postdata_length The length of the data being sent.<br />
* @param content_type Optional content type or null if this is a GET call.<br />
* @param buffer The buffer to write the result into [OUT]<br />
* @param buffer_size The size of the buffer<br />
* @return The amount actually written.<br />
*/<br />
LIBSEARUNNERW32_API int __searunner_post_call(const char * parameters, unsigned char * postData, int postdata_length, char * content_type, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func);</code></p>
<p><code>/**<br />
* Raw GET request.<br />
* @param parameters List of parameters.<br />
* @param buffer The buffer to write the result into [OUT]<br />
* @param buffer_size The size of the buffer<br />
* @return The amount actually written.<br />
*/<br />
LIBSEARUNNERW32_API int __searunner_get_call(const char * parameters, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func);</code></p>
<p><code>/**<br />
* Make a searunner call.<br />
* @param method Method call.<br />
* @param parameters List of parameters.<br />
* @param postData Optional post data in post call or null if this is a GET call.<br />
* @param postdata_length The length of the data being sent.<br />
* @param content_type Optional content type or null if this is a GET call.<br />
* @param buffer The buffer to write the result into [OUT]<br />
* @param buffer_size The size of the buffer<br />
* @return The amount actually written.<br />
*/<br />
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);</code></p>
<p><code>#endif /*SEARUNNER_H_*/</code></p></blockquote>
<p><strong>searunner.cpp</strong></p>
<blockquote><p> <code>/**<br />
* @file searunner.cpp<br />
* Main functions and Win32 DLL entrypoint.<br />
* @author Marcus Povey &lt;marcus@dushka.co.uk&gt;<br />
*/</code></p>
<p><code>#include "searunner.h"<br />
#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;string.h&gt;<br />
#include &lt;crypto++/sha.h&gt;<br />
#include &lt;crypto++/hmac.h&gt;<br />
#include &lt;crypto++/hex.h&gt;<br />
#include &lt;crypto++/filters.h&gt;<br />
#include &lt;curl/curl.h&gt;<br />
#include &lt;time.h&gt;<br />
</code></p>
<p><code>/* Configuration structure */<br />
LIBSEARUNNERW32_API SearunnerConfig hSearunnerConfig;</code></p>
<p><code>/* Utility functions used internally */<br />
#ifdef WIN32</code></p>
<p><code>#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)<br />
#define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64<br />
#else<br />
#define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL<br />
#endif</code></p>
<p><code>struct timezone<br />
{<br />
int  tz_minuteswest; /* minutes W of Greenwich */<br />
int  tz_dsttime;     /* type of dst correction */<br />
};</code></p>
<p><code>/**<br />
* Replacement gettimeofday function for windows.<br />
* Obtained from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/<br />
*/<br />
int gettimeofday(struct timeval *tv, struct timezone *tz)<br />
{<br />
FILETIME ft;<br />
unsigned __int64 tmpres = 0;<br />
static int tzflag;</code></p>
<p><code>if (NULL != tv)<br />
{<br />
GetSystemTimeAsFileTime(&amp;ft);</code></p>
<p><code>tmpres |= ft.dwHighDateTime;<br />
tmpres &lt;&lt;= 32;<br />
tmpres |= ft.dwLowDateTime;</code></p>
<p><code>/*converting file time to unix epoch*/<br />
tmpres /= 10;  /*convert into microseconds*/<br />
tmpres -= DELTA_EPOCH_IN_MICROSECS;<br />
tv-&gt;tv_sec = (long)(tmpres / 1000000UL);<br />
tv-&gt;tv_usec = (long)(tmpres % 1000000UL);<br />
}</code></p>
<p><code>if (NULL != tz)<br />
{<br />
if (!tzflag)<br />
{<br />
_tzset();<br />
tzflag++;<br />
}<br />
tz-&gt;tz_minuteswest = _timezone / 60;<br />
tz-&gt;tz_dsttime = _daylight;<br />
}</code></p>
<p><code>return 0;<br />
}</code></p>
<p><code>#endif</code></p>
<p><code>#define MICRO_IN_SEC 1000000.00</code></p>
<p><code>/**<br />
* Micro time function.<br />
*<br />
* Example usage:<br />
*<br />
* char oo[100];<br />
* microtime(oo);<br />
* printf(oo);<br />
*<br />
* @param buffer Where to output variables.<br />
*/<br />
int microtime(char * buffer)<br />
{<br />
struct timeval tp;<br />
long sec = 0L;<br />
double msec = 0.0;<br />
char ret[100];</code></p>
<p><code>if (gettimeofday((struct timeval *) &amp;tp, (NULL)) == 0)<br />
{<br />
msec = (double) (tp.tv_usec / MICRO_IN_SEC);<br />
sec = tp.tv_sec;</code></p>
<p><code>if (msec &gt;= 1.0) msec -= (long) msec;</code><br />
<code><br />
int msec2 = (int)(msec*100);</code></p>
<p><code>snprintf(ret, 100, "%ld.%d",  sec, msec2);<br />
strcpy(buffer, ret);<br />
return 0;<br />
}</code></p>
<p><code>return -1;<br />
}</code></p>
<p><code>/**<br />
* Calculate a hmac for the given data.<br />
*<br />
* Example usage:<br />
*<br />
* char oo [2*CryptoPP::SHA1::DIGESTSIZE+1];<br />
* calculate_hmac(&amp;hSearunnerConfig, "1", "2", "3", oo, 2*CryptoPP::SHA1::DIGESTSIZE+1);<br />
* printf(oo);<br />
*<br />
* @param time String representation of time<br />
* @param get_variables String of parameters<br />
* @param post_hash The hash<br />
* @param buffer The buffer<br />
* @param bugger_size Size of the buffer<br />
*/<br />
int calculate_hmac(<br />
const char * time,<br />
const char * get_variables,<br />
const char * post_hash,</code></p>
<p><code>char * buffer, int buffer_size)<br />
{<br />
memset(buffer,0,buffer_size);</code></p>
<p><code>if (strcmp(hSearunnerConfig.hmac_algo, "sha1")==0)<br />
{<br />
// Sha1</code></p>
<p><code>// Check the buffer size before we do anything<br />
if (buffer_size&lt;2*CryptoPP::SHA1::DIGESTSIZE + 1) return -1;</code></p>
<p><code>// Initialise memory<br />
unsigned char tmp[ 2*CryptoPP::SHA1::DIGESTSIZE + 1 ];<br />
memset(tmp,0,2*CryptoPP::SHA1::DIGESTSIZE + 1);</code></p>
<p><code>// Compute HMAC<br />
CryptoPP::HMAC&lt;CryptoPP::SHA1&gt; hmac((const byte*)hSearunnerConfig.secret, strlen(hSearunnerConfig.secret));<br />
hmac.Update((const byte*)time, strlen(time));<br />
hmac.Update((const byte*)hSearunnerConfig.apikey, strlen(hSearunnerConfig.apikey));<br />
hmac.Update((const byte*)get_variables, strlen(get_variables));<br />
if (post_hash) hmac.Update((const byte*)post_hash, strlen(post_hash));</code></p>
<p><code>hmac.Final((byte*)tmp);</code></p>
<p><code>// hex encode<br />
CryptoPP::HexEncoder encoder;<br />
encoder.Attach(new CryptoPP::ArraySink((byte*)buffer, 2*CryptoPP::SHA1::DIGESTSIZE));<br />
encoder.Put(tmp, sizeof(tmp));<br />
encoder.MessageEnd();</code></p>
<p><code>// Convert to lower case<br />
for (int n = 0; n&lt;buffer_size; n++)<br />
if (buffer[n]!=0) buffer[n] = tolower(buffer[n]);</code></p>
<p><code>return 0;<br />
}</code></p>
<p><code>return -1;<br />
}</code></p>
<p><code>/*<br />
* Calculate the hash of some POST data.<br />
* TODO: Tidy this up<br />
* TODO: Support more algorithms<br />
*<br />
* Example usage:<br />
*<br />
* char oo [2*CryptoPP::MD5::DIGESTSIZE+1];<br />
* calculate_POST_hash(&amp;hSearunnerConfig, (unsigned char*)"test", 4, oo, 2*CryptoPP::MD5::DIGESTSIZE+1);<br />
* printf(oo);<br />
*<br />
* @param postdata The data<br />
* @param postlength The length<br />
* @param outputbuffer The buffer<br />
* @param outputbuffer_size The size of the output buffer<br />
*/<br />
int calculate_POST_hash(const unsigned char * postdata, int postlength, char * outputbuffer, int outputbuffer_size)<br />
{<br />
memset(outputbuffer,0,outputbuffer_size);</code></p>
<p><code>if (strcmp(hSearunnerConfig.hash_algo, "sha1")==0)<br />
{<br />
// Check the buffer size before we do anything<br />
if (outputbuffer_size&lt;2*CryptoPP::SHA1::DIGESTSIZE + 1) return -1;</code><br />
<code><br />
// Initialise memory<br />
unsigned char tmp[ 2*CryptoPP::SHA1::DIGESTSIZE + 1 ];<br />
memset(tmp,0,2*CryptoPP::SHA1::DIGESTSIZE + 1);</code></p>
<p><code>// Calculate the digest<br />
CryptoPP::SHA1 hash;<br />
hash.CalculateDigest((byte*)tmp, (const byte*)postdata, postlength);</code></p>
<p><code>// hex encode<br />
CryptoPP::HexEncoder encoder;<br />
encoder.Attach(new CryptoPP::ArraySink((byte*)outputbuffer, 2*CryptoPP::SHA1::DIGESTSIZE));<br />
encoder.Put(tmp, sizeof(tmp));<br />
encoder.MessageEnd();</code></p>
<p><code>// Convert to lower case<br />
for (int n = 0; n&lt;outputbuffer_size; n++)<br />
if (outputbuffer[n]!=0) outputbuffer[n] = tolower(outputbuffer[n]);</code></p>
<p><code>return 0;<br />
}</code></p>
<p><code>return -1;<br />
}</code></p>
<p><code>/*************************************/</code></p>
<p><code>LIBSEARUNNERW32_API int searunner_initialise_api(const char * api_key, const char * secret, const char * endpoint_url)<br />
{<br />
strcpy(hSearunnerConfig.apikey, api_key);<br />
strcpy(hSearunnerConfig.secret, secret);<br />
strcpy(hSearunnerConfig.endpoint, endpoint_url);<br />
strcpy(hSearunnerConfig.hmac_algo, "sha1");<br />
strcpy(hSearunnerConfig.hash_algo, "sha1");<br />
return 0;<br />
}</code></p>
<p><code>LIBSEARUNNERW32_API int __searunner_call(int method,<br />
const char * parameters,<br />
unsigned char * postData,<br />
int postdata_length,<br />
char * content_type,<br />
CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)<br />
{<br />
char url[2048] = {""};<br />
char posthash[256] = {""};<br />
char hmac[256] = {""};<br />
char tmp[100] = {""};</code></p>
<p><code>struct curl_slist *headerlist = NULL;</code></p>
<p><code>// Get the microtime for now<br />
char time[100];<br />
microtime(time);</code></p>
<p><code>// Construct URL<br />
strcpy(url, hSearunnerConfig.endpoint);<br />
strcat(url, "?");<br />
strcat(url, parameters);</code></p>
<p><code>// If post data, hash it<br />
if (postdata_length &gt; 0)<br />
calculate_POST_hash(postData, postdata_length, posthash, 256);</code></p>
<p><code>// Constract the hmac<br />
calculate_hmac(time, parameters, posthash, hmac, 256);</code></p>
<p><code>// Initialise CURL<br />
CURL * curl = curl_easy_init();<br />
if(curl) {</code></p>
<p><code>// Set headers<br />
sprintf(tmp, "X-Searunner-apikey: %s", hSearunnerConfig.apikey);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "X-Searunner-time: %s", time);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "X-Searunner-hmac-algo: %s", hSearunnerConfig.hmac_algo);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "X-Searunner-hmac: %s", hmac);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>// Set options<br />
if (method == SEARUNNER_CALLMODE_POST)<br />
{<br />
curl_easy_setopt(curl, CURLOPT_POST, 1); // Use POST</code></p>
<p>s<code>printf(tmp, "X-Searunner-posthash: %s", posthash);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "X-Searunner-posthash-algo: %s", hSearunnerConfig.hash_algo);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "Content-type: %s", content_type);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>sprintf(tmp, "Content-Length: %d", postdata_length);<br />
headerlist = curl_slist_append(headerlist, tmp);</code></p>
<p><code>curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData); // Set the post data<br />
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postdata_length);<br />
}</code></p>
<p><code>curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); // Set the headers<br />
curl_easy_setopt(curl,CURLOPT_URL, url); // Set the url<br />
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_read_func); // Use our own read function</code></p>
<p><code>// Make call<br />
CURLcode res = curl_easy_perform(curl);</code></p>
<p><code>// Clean up<br />
curl_easy_cleanup(curl);</code></p>
<p><code>return res;<br />
}</code></p>
<p><code>return 0;<br />
}</code></p>
<p><code>LIBSEARUNNERW32_API int __searunner_post_call(const char * parameters, unsigned char * postData, int postdata_length, char * content_type, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)<br />
{<br />
return __searunner_call(SEARUNNER_CALLMODE_POST, parameters, postData, postdata_length, content_type, curl_read_func);<br />
}</code></p>
<p><code>LIBSEARUNNERW32_API int __searunner_get_call(const char * parameters, CURLOPT_WRITEFUNCTION_CALLBACK curl_read_func)<br />
{<br />
return __searunner_call(SEARUNNER_CALLMODE_GET, parameters, NULL, 0,  NULL, curl_read_func);<br />
}</code></p>
<p><code>#ifdef WIN32</code></p>
<p><code>/*<br />
Windows Only DLL entrypoint.<br />
*/</code></p>
<p><code>BOOL APIENTRY DllMain( HANDLE hModule,<br />
DWORD  ul_reason_for_call,<br />
LPVOID lpReserved<br />
)<br />
{<br />
switch (ul_reason_for_call)<br />
{<br />
case DLL_PROCESS_ATTACH:<br />
case DLL_THREAD_ATTACH:<br />
case DLL_THREAD_DETACH:<br />
case DLL_PROCESS_DETACH:<br />
break;<br />
}<br />
return TRUE;<br />
}</code><br />
<code><br />
#endif</code></p></blockquote>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F28%2Fconnecting-to-elggvoices-with-c%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/28/connecting-to-elggvoices-with-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>@commands on elggVoices</title>
		<link>http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/#comments</comments>
		<pubDate>Tue, 26 Feb 2008 22:19:57 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[commands]]></category>
		<category><![CDATA[elggvoices]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/</guid>
		<description><![CDATA[Some of you who have been using elggVoices recently, may have noticed some rather odd looking shouts appearing in the channels. Shouts beginning with &#8220;@&#8221; can be used to execute commands in a channel &#8211; especially useful when used remotely via SMS. I thought it might be useful to give you a quick run down [...]]]></description>
			<content:encoded><![CDATA[<p>Some of you who have been using <a href="http://www.elggvoices.com">elggVoices</a> recently, may have noticed some rather odd looking shouts appearing in the channels.</p>
<p>Shouts beginning with &#8220;@&#8221; can be used to execute commands in a channel &#8211; especially useful when used remotely via SMS.</p>
<p>I thought it might be useful to give you a quick run down of the commands currently available. I say &#8220;currently&#8221;, since we are adding to this all the time.</p>
<p><strong>SMS and Website</strong></p>
<ul>
<li><code>@status <em>message</em></code>: Updates your status on a channel.</li>
<li><code>@sleep <em>on|off</em></code>: Set your sleep status on and off, when @sleep is on you won&#8217;t receive SMS messages.</li>
<li><code>@location <em>city</em></code>: Updates your location.</li>
<li><code>@country <em>country</em></code>: Updates your country.</li>
</ul>
<p><strong>SMS only</strong></p>
<ul>
<li><code>@signup <em>desired_username your_email</em></code>: Send this to a channel&#8217;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.</li>
<li><code>@join</code>: Send this to a channel&#8217;s SMS number will join the channel.</li>
</ul>
<p>Just a few commands for now, but there will be more to come.</p>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F26%2Fcommands-on-elggvoices%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/26/commands-on-elggvoices/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with the elggVoices API (part 4 &#8211; Putting it all together)</title>
		<link>http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/#comments</comments>
		<pubDate>Mon, 25 Feb 2008 12:35:48 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[elggvoices]]></category>
		<category><![CDATA[howto]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/</guid>
		<description><![CDATA[In yesterday&#8217;s article, we took a look at the server&#8217;s reply format. You now have all the information you need to construct the core of a library and start making server requests. Here is some sample PHP code for making POST and GET style queries. This library will let you make a query, and return [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/">yesterday&#8217;s article</a>, we took a look at the server&#8217;s reply format. You now have all the information you need to construct the core of a library and start making server requests.</p>
<p>Here is some sample PHP code for making POST and GET style queries. This library will let you make a query, and return a <code>stdClass</code> object containing the serialised fields as described yesterday, from this you can build wrapper functions for the methods.</p>
<blockquote><p>  <code>$API_CLIENT = new stdClass;</code></p>
<p><code>// Configure your client<br />
$API_CLIENT-&gt;api_key = "<strong>your api key</strong>";<br />
$API_CLIENT-&gt;secret = "<strong>your secret key</strong>";<br />
$API_CLIENT-&gt;api_endpoint = "http://elggvoices.com/api/v1/";<br />
$API_CLIENT-&gt;hmac_algo = 'sha256';<br />
$API_CLIENT-&gt;postdata_hash_algo = 'md5';</code></p>
<p><code>// Status variables we can query later<br />
$LAST_CALL = null;<br />
$LAST_CALL_RAW = "";<br />
$LAST_ERROR = null;</code></p>
<p><code>/**<br />
* Generate our HMAC.<br />
*/<br />
function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "")<br />
{<br />
$ctx = hash_init($algo, HASH_HMAC, $secret_key);<br />
hash_update($ctx, trim($time));<br />
hash_update($ctx, trim($api_key));<br />
hash_update($ctx, trim($get_variables));<br />
if (trim($post_hash)!="") hash_update($ctx, trim($post_hash));<br />
return hash_final($ctx);<br />
}</code></p>
<p><code>/**<br />
* Generate our POST hash.<br />
*/<br />
function calculate_posthash($postdata, $algo)<br />
{<br />
$ctx = hash_init($algo);<br />
hash_update($ctx, $postdata);<br />
return hash_final($ctx);<br />
}</code></p>
<p><code>/**<br />
* Serialise HTTP headers.<br />
*/<br />
function serialise_headers(array $headers)<br />
{<br />
$headers_str = "";<br />
foreach ($headers as $k =&gt; $v)<br />
$headers_str .= trim($k) . ": " . trim($v) . "\r\n";<br />
return trim($headers_str);<br />
}</code></p>
<p><code>/**<br />
* Make a raw call.<br />
* @param array $method Method call parameters.<br />
* @param string $postdata Optional POST data.<br />
* @param string $content_type The content type.<br />
* @return stdClass<br />
*/<br />
function call(array $method, $postdata = "", $content_type = 'application/octet-stream')<br />
{</code><code><br />
// Get the config<br />
global $API_CLIENT, $LAST_CALL, $LAST_CALL_RAW, $LAST_ERROR; </code></p>
<p><code>$headers = array();<br />
$encoded_params = array();</code></p>
<p><code>$time = microtime(true); // Get the current time in microseconds<br />
$request = ($postdata!="" ? "POST" : "GET"); // Get the request method, either post or get</code></p>
<p><code>// Hard code the format - we're using PHP, so lets use PHP serialisation.<br />
$method['format'] = "php";</code></p>
<p><code>// URL encode all the parameters<br />
foreach ($method as $k =&gt; $v){<br />
$encoded_params[] = urlencode($k).'='.urlencode($v);<br />
}<br />
</code><br />
<code>$params = implode('&amp;', $encoded_params);</code></p>
<p><code>// Put together the query string<br />
$url = $API_CLIENT-&gt;api_endpoint."?". $params;</code></p>
<p><code>// Construct headers<br />
$posthash = "";<br />
if ($request=='POST')<br />
{<br />
$posthash = calculate_posthash($postdata, $API_CLIENT-&gt;postdata_hash_algo);<br />
$headers['X-Searunner-posthash'] = $posthash;<br />
$headers['X-Searunner-posthash-algo'] = $API_CLIENT-&gt;postdata_hash_algo;<br />
$headers['Content-type'] = $content_type;<br />
$headers['Content-Length'] = strlen($postdata);<br />
}</code></p>
<p><code>$headers['X-Searunner-apikey'] = $API_CLIENT-&gt;api_key;<br />
$headers['X-Searunner-time'] = $time;<br />
$headers['X-Searunner-hmac-algo'] = $API_CLIENT-&gt;hmac_algo;<br />
$headers['X-Searunner-hmac'] = calculate_hmac($API_CLIENT-&gt;hmac_algo,<br />
$time,<br />
$API_CLIENT-&gt;api_key,<br />
$API_CLIENT-&gt;secret,<br />
$params,<br />
$posthash<br />
);</code></p>
<p><code>// Configure stream options<br />
$opts = array(<br />
'http'=&gt;array(<br />
'method'=&gt; $request,<br />
'header'=&gt; serialise_headers($headers)<br />
)<br />
);</code></p>
<p><code>// If this is a post request then set the content<br />
if ($request=='POST')<br />
$opts['http']['content'] = $postdata; </code></p>
<p><code>// Set stream options<br />
$context = stream_context_create($opts);</code></p>
<p><code>// Send the query and get the result and decode.<br />
$LAST_CALL_RAW = file_get_contents($url, false, $context);<br />
$LAST_CALL = unserialize($LAST_CALL_RAW);</code></p>
<p><code>if (($LAST_CALL) &amp;&amp; ($LAST_CALL-&gt;status!=0)) // Check to see if this was an error<br />
$LAST_ERROR = $LAST_CALL;</code></p>
<p><code>return $LAST_CALL; // Return a stdClass containing the API result<br />
}</code></p>
<p><code>// Example GET call<br />
$get = call(<br />
array (<br />
'method' =&gt; 'test.test',<br />
'variable1' =&gt; 1,<br />
'variable2' =&gt; "test string"<br />
)<br />
);<br />
</code><code><br />
// Example POST call<br />
$post = call(<br />
array (<br />
'method' =&gt; 'test.test',<br />
'variable1' =&gt; 1,<br />
'variable2' =&gt; "test string"<br />
),<br />
"Some post data"<br />
);</code></p>
<p><code>// Output<br />
echo "Example GET result: \n";<br />
print_r($get);</code></p>
<p><code>echo "Example POST result: \n";<br />
print_r($post);<br />
</code></p></blockquote>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F25%2Fgetting-started-with-the-elggvoices-api-part-4-putting-it-all-together%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/25/getting-started-with-the-elggvoices-api-part-4-putting-it-all-together/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with the elggVoices API (part 3 &#8211; The reply)</title>
		<link>http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/#comments</comments>
		<pubDate>Sun, 24 Feb 2008 18:55:54 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[elggvoices]]></category>
		<category><![CDATA[howto]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/</guid>
		<description><![CDATA[On Friday we discussed how to construct an API request, today we will discuss how to handle the response that the server returns. Server replies The elggVoices server will respond with a result packet containing a number of fields. The format of this reply depends on the format requested. From the manual: &#8216;status&#8217;: The status [...]]]></description>
			<content:encoded><![CDATA[<p>On Friday we discussed how to <a href="http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/">construct an API request</a>, today we will discuss how to handle the response that the server returns.</p>
<h2>Server replies</h2>
<p>The <a href="http://www.elggvoices.com">elggVoices</a> server will respond with a result packet containing a number of fields. The format of this reply depends on the format requested. From the manual:</p>
<blockquote>
<ul>
<li><strong>&#8216;status&#8217;</strong>: The status code, 0 for success or a non-zero error code.</li>
<li><strong>&#8216;message&#8217;</strong>: If the status code is non-zero this contains a human readable<br />
explanation.</li>
<li><strong>&#8216;result&#8217;</strong>: The mixed result of the execution, this may be a simple type<br />
(int, string etc), an array or even an object, depending on the API being<br />
called (see also result objects).</li>
<li><strong>&#8216;runtime_errors&#8217;</strong>: If there have been any runtime errors picked up by<br />
the internal error handler during the execution of the command, these<br />
are given here in an array.</li>
</ul>
</blockquote>
<p>The server result will be returned, serialised in the requested format (xml,php or json).</p>
<p>Errors will often give details pointing to what went wrong, these can be helpful when debugging your application or when looking for support on the forums.</p>
<p>Have a look at this XML formatted error message, XML success messages will be of a similar format:</p>
<blockquote><p><code>&lt;SearunnerResult&gt;<br />
&lt;status type="integer"&gt;-1&lt;/status&gt;<br />
&lt;message type="string"&gt;Missing X-Searunner-apikey HTTP header&lt;/message&gt;<br />
&lt;result type="string"&gt;exception 'APIException' with message 'Missing X-Searunner-apikey HTTP header' in /home/liveshouts/html/lib/api/validation.php:109<br />
Stack trace:<br />
#0 /home/liveshouts/html/api/endpoints/rest.php(19): get_and_validate_api_headers()<br />
#1 {main}&lt;/result&gt;<br />
&lt;runtime_errors type="array"&gt;<br />
&lt;array_item name="0" type="string" &gt;DEBUG: 2008-02-24 18:38:19 (UTC): &amp; quot;Undefined index:  method&amp; quot;<br />
in file /home/liveshouts/html/lib/engine/input.php (line 30)&lt;/array_item&gt;<br />
&lt;array_item name="1" type="string"&gt;DEBUG: 2008-02-24 18:38:19 (UTC): &amp;quot;Undefined index:  HTTP_X_SEARUNNER_APIKEY&amp;quot; in file /home/liveshouts/html/lib/api/validation.php (line 107)&lt;/array_item&gt;<br />
&lt;/runtime_errors&gt;<br />
&lt;/SearunnerResult&gt;</code></p></blockquote>
<blockquote>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F24%2Fgetting-started-with-the-elggvoices-api-part-3-the-reply%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/24/getting-started-with-the-elggvoices-api-part-3-the-reply/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with the elggVoices API (part 2 &#8211; The request)</title>
		<link>http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/#comments</comments>
		<pubDate>Fri, 22 Feb 2008 10:28:41 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[elggvoices]]></category>
		<category><![CDATA[howto]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/</guid>
		<description><![CDATA[HTTP Headers In addition to the method call, format and variables sent on the URL line (discussed in yesterday&#8217;s article), you are required to send some extra information in the headers of the outgoing HTTP request. From the API Documentation: X-Searunner-apikey Your public API key you were given when signing up for a developer account. [...]]]></description>
			<content:encoded><![CDATA[<h2>HTTP Headers</h2>
<p>In addition to the method call, format and variables sent on the URL line (discussed in <a href="http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/">yesterday&#8217;s article</a>), you are required to send some extra information in the <a href="http://en.wikipedia.org/wiki/HTTP_headers">headers of the outgoing HTTP request</a>.</p>
<p>From the API Documentation:</p>
<blockquote>
<ul>
<li><strong>X-Searunner-apikey</strong> Your public API key you were given when signing<br />
up for a developer account.</li>
<li><strong>X-Searunner-time </strong>A float representation of “now” including<br />
milliseconds (like that produced by the php function microtime(true))</li>
<li><strong>X-Searunner-hmac-algo</strong> The algorithm used for generating the HMAC,<br />
like sha1, sha256, MD5 etc.</li>
<li><strong>X-Searunner-hmac</strong> The HEX representation of the HMAC signing the<br />
request.</li>
</ul>
<p>The following headers are only required when sending POST data:</p>
<ul>
<li><strong>X-Searunner-posthash</strong> A hash of the POST data packet.</li>
<li><strong>X-Searunner-posthash-algo</strong> The algorithm used to create the POST<br />
hash, eg md5, sha1 etc.</li>
<li><strong>Content-type</strong> The content type of the data (default:<br />
application/octet-stream)</li>
<li><strong> Content-Length</strong> The length of the POST data in bytes.</li>
</ul>
</blockquote>
<p>Initially, this may look like rather a lot to take in, but it is actually quite simple.</p>
<p>The two algorithm selection fields <strong><code>X-Searunner-hmac-algo</code></strong> and <strong><code>X-Searunner-posthash-algo</code></strong> give the name of the algorithm used &#8211; <code>md5</code>, <code>sha1</code>, <code>sha256</code> etc. Letting the client specify the algorithm used lets us get around issues with buggy/missing algorithm implementations. For example, we use MD5 for the Java library as this removes the need to install a third party JCE.</p>
<p>Where possible you should probably try and use &#8220;<code>sha256</code>&#8221; for <a href="http://en.wikipedia.org/wiki/Hmac">HMAC</a> and &#8220;<code>sha1</code>&#8221; for hashing your post data, since over time we will likely &#8220;retire&#8221; the weaker algorithms.</p>
<p><strong><code>X-Searunner-time</code></strong> timestamps the request, while <strong><code>Content-type</code></strong> and <strong><code>Content-Length</code></strong> (for POST requests) are pretty much self explanatory.</p>
<p>The field <strong><code>X-Searunner-posthash</code></strong> ensures that the POST body can not be modified, and it is a <strong>lower case</strong> <a href="http://en.wikipedia.org/wiki/Hexadecimal">hexadecimal</a> representation of the desired HASH algorithm, taken over all the data in the POST body, take a look at this pseudo code:</p>
<blockquote><p><code>hash = algorithmFactory( <strong>//X-Searunner-posthash-algo//</strong> );<br />
hash.update( <strong>//POST DATA//</strong> );<br />
return hex_encode(hash.final());</code></p></blockquote>
<p>Finally, the field <strong><code>X-Searunner-hmac</code></strong> contains a signature which lets the <a href="http://www.elggvoices.com">elggVoices</a> server guarantee that it was you that sent the request (rather than someone pretending to be you) and that the contents of the request have not been modified. This is where your <strong>secret key</strong> comes in, and is the reason why it is important that you keep it safe!</p>
<p>From the manual:</p>
<blockquote><p>A HMAC is a simple cryptographic signature, in this case is formed over the<br />
following information:</p>
<ul>
<li>The time header eg, <code>12345678.90</code></li>
<li>Your API key</li>
<li>The GET query string, eg &#8220;<code>format=xml&amp;method.example&amp;variable=foo</code>&#8220;. It is<br />
important to note two things, firstly that the preceding “?” character has<br />
been dropped, and second that the order is identical to the order they<br />
appear on the URL line.</li>
<li>If this is a POST request, the hash of the POST data.</li>
</ul>
</blockquote>
<p>Take a look at this pseudo code:</p>
<blockquote><p><code>hmac_cipher = algorithmFactory( <strong>//X-Searunner-hmac-algo//</strong> );<br />
hmac_cipher.init(<strong>//SECRET KEY//</strong>);<br />
hmac_cipher.update( <strong>//X-Searunner-time//</strong> );<br />
hmac_cipher.update( <strong>//X-Searunner-apikey//</strong> );<br />
hmac_cipher.update( <strong>//GET VARIABLES IN CALL ORDER//</strong> );<br />
if (POST CALL) hmac_cipher.update( <strong>//X-Searunner-posthash//</strong> );</code></p>
<p><code></code><code>return hex_encode(hmac_cipher.final());</code></p></blockquote>
<p>To form the HMAC, each variable&#8217;s string representation is turned into a string of bytes. This byte string is fed in turn into the HMAC algorithm which spits out a HMAC signature which is then converted into a lower case hexadecimal string. These are Octet strings, ensure that when adding the strings to the cipher that your language doesn&#8217;t convert it into unicode!</p>
<p>Note that the order in which you add each field is VERY IMPORTANT.</p>
<p>Next comes the server response&#8230;</p>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F22%2Fgetting-started-with-the-elggvoices-api-part-2-the-request%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/22/getting-started-with-the-elggvoices-api-part-2-the-request/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with the elggVoices API (part 1 &#8211; API Basics)</title>
		<link>http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/#comments</comments>
		<pubDate>Thu, 21 Feb 2008 15:23:59 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[elggvoices]]></category>
		<category><![CDATA[howto]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/</guid>
		<description><![CDATA[Over the past few weeks I have been helping Curverider develop the next generation of Elgg products, the first of which to be released is elggVoices – a cross platform short messaging tool. One of the key features of elggVoices which I have been heavily involved in developing is a powerful and fully featured API. [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past few weeks I have been helping <a href="http://www.curverider.co.uk">Curverider</a> develop the next generation of <a href="http://elgg.net/">Elgg</a> products, the first of which to be released is <a href="http://www.elggvoices.com">elggVoices</a> – a cross platform short messaging tool.</p>
<p>One of the key features of elggVoices which I have been heavily involved in developing is a powerful and fully featured API. This API lets you read and post messages (shouts), search for channels, find all shouts on a given subject etc.</p>
<h2>Getting the resources</h2>
<p>Over the next few weeks we will be building this out with example code, client libraries etc. The first step is to get yourself over to the <a href="http://elggvoices.com/pages/developer.php">developer centre</a> and take a look at the resources available. At the very least you should download the API documentation, this explains what API calls are available and what parameters they take.</p>
<h3>API Keys</h3>
<p>In order to be able to use the API, you will require two keys – a public API key, and a secret key. To obtain your keys, you can sign up for some at the developer centre.</p>
<p>The public API key is a way to identify your client with the elggVoices server, while the secret key is used to sign your api call and should be kept safe.</p>
<h2>Making an API call</h2>
<p>Once you have got your keys, you can begin making elggVoices API calls by using the REST endpoint at:</p>
<p><strong><code>http://elggvoices.com/api/v1/</code></strong></p>
<p>By far the easiest way to do this is to use one of the client libraries, however in the event a library hasn&#8217;t been written for your preferred language (or you fancy writing one), you are going to have to construct a raw query, as follows&#8230;</p>
<p>An API call is either a <strong>GET</strong> or a <strong>POST</strong> request, with API variables passed as a URL encoded string on the GET line. The POST data should be used for queries which expect large data blocks (file transfer etc).<br />
An example api call might look like this:</p>
<p><code>http://elggvoices.com/api/v1/?<strong>method</strong>=example.method&amp;<strong>format</strong>=xml&amp;foovar=bar</code></p>
<p>Where <strong><code>method</code></strong> is the method call you are making, and <strong><code>format</code></strong> is the desired reply format (<code>xml</code>,<code>php</code>,<code>json</code>). These required variables can then be followed by an arbitrary list of other parameters expected by the API call.</p>
<p>Tomorrow I will show you how to construct a HTTP request using custom request headers, and to construct the all important HMAC signature.</p>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F21%2Fgetting-started-with-the-elggvoices-api-part-1-api-basics%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/21/getting-started-with-the-elggvoices-api-part-1-api-basics/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Elgg and ElggVoices</title>
		<link>http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/</link>
		<comments>http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 18:59:45 +0000</pubDate>
		<dc:creator>Marcus Povey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[elgg]]></category>
		<category><![CDATA[elggvoices]]></category>

		<guid isPermaLink="false">http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/</guid>
		<description><![CDATA[With the appropriate head nod to Ben and Dave, I wanted to make a quick post to draw your attention to Elgg and ElggVoices. I&#8217;ve been doing a lot of the development on these projects over the past few weeks. It is still a little hush hush so I can&#8217;t go into a lot of [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.elggvoices.com/graphics/elgg_icon.gif" align="left" height="241" width="241" />With the appropriate head nod to <a href="http://ben.elgg.com/?action=comments&amp;postid=36">Ben</a> and <a href="http://dave.elgg.com/?action=comments&amp;postid=7">Dave</a>, I wanted to make a quick post to draw your attention to <a href="http://elgg.com">Elgg</a> and <a href="http://elggvoices.com">ElggVoices</a>.</p>
<p>I&#8217;ve been doing a lot of the development on these projects over the past few weeks.</p>
<p>It is still a little hush hush so I can&#8217;t go into a lot of detail, but they&#8217;ve been exciting projects to work on and I shall no doubt be writing about this some more.</p>
<div class="wsbuttons">
	<div class="shareblob facebook">
		<div class="fb-like" data-href="http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/" data-send="false" data-layout="box_count" data-width="60" data-show-faces="false" data-colorscheme="light"></div>
	</div>

	<div class="shareblob google">
		<div class="g-plusone" data-size="tall" data-href="http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/"></div>
	</div>

	<div class="shareblob twitter">
		<div class="twitter">
			<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.marcus-povey.co.uk%2F2008%2F02%2F15%2Felgg-and-elggvoices%2F&count=vertical" class="twitter-share-button" data-lang="en">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
		</div>
	</div>

</div>
	]]></content:encoded>
			<wfw:commentRss>http://www.marcus-povey.co.uk/2008/02/15/elgg-and-elggvoices/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

