@@ -191,15 +191,35 @@ int OAuthClient::httpRequest(const char* method, const char* path, const char* c
191191 url += _ip[3 ];
192192 }
193193
194- url += path;
194+ const char * queryParams = NULL ;
195+ const char * bodyParams = NULL ;
196+
197+ // split the path from query params if necessary
198+ char * questionMark = strchr (path, ' ?' );
199+ if (questionMark != NULL ) {
200+ queryParams = (questionMark + 1 );
201+
202+ const char * temp = path;
203+
204+ while (temp != questionMark) {
205+ url += *temp++;
206+ }
207+ } else {
208+ url += path;
209+ }
210+
211+ if (strcmp (contentType, " application/x-www-form-urlencoded" ) == 0 ) {
212+ // only use the body as params if the body is URL encoded
213+ bodyParams = body;
214+ }
195215
196216 unsigned long time = 0 ;
197217
198218 if (_onGetTimeCallback) {
199219 time = _onGetTimeCallback ();
200220 }
201221
202- String signature = calculateSignature (method, url.c_str (), time, body );
222+ String signature = calculateSignature (method, url.c_str (), time, queryParams, bodyParams );
203223 String authorization = calculateOauthAuthorization (signature, time);
204224
205225 _httpClient.beginRequest ();
@@ -237,29 +257,120 @@ String OAuthClient::createNonce() {
237257 return n;
238258}
239259
240- String OAuthClient::calculateSignature (const char * method, const char * url, unsigned long time, const char * body)
260+ static int strcmp_pointer (const void * a, const void * b) {
261+ return strcmp (*(const char **)a, *(const char **)b);
262+ }
263+
264+ String OAuthClient::calculateSignature (const char * method, const char * url, unsigned long time, const char * queryParams, const char * bodyParams)
241265{
266+ // This function is long due to the complexity of the OAuth signature.
267+ // It must collect all the parameters from the oauth, query, and body params,
268+ // then sort the param key values lexographically. After these steps the
269+ // signature can be calculated.
270+
271+ // calculate the OAuth params
272+ String oauthParams;
273+
274+ oauthParams += " oauth_consumer_key=" ;
275+ oauthParams += _consumerKey;
276+ oauthParams += " &oauth_nonce=" ;
277+ oauthParams += _nonce;
278+ oauthParams += " &oauth_signature_method=HMAC-SHA1&oauth_timestamp=" ;
279+ oauthParams += String (time);
280+ oauthParams += " &oauth_token=" ;
281+ oauthParams += _accessToken;
282+ oauthParams += " &oauth_version=1.0" ;
283+
284+ // calculate the length of all of the params
285+ int paramsLength = oauthParams.length ();
286+ int queryParamsLength = strlen (queryParams);
287+ int bodyParamsLength = strlen (bodyParams);
288+
289+ if (queryParams) {
290+ paramsLength += (1 + queryParamsLength);
291+ }
292+
293+ if (bodyParams) {
294+ paramsLength += (1 + bodyParamsLength);
295+ }
296+
297+ // copy the parameters to a buffer
298+ char params[paramsLength + 1 ];
299+ char * temp = params;
300+
301+ temp = strcpy (temp, oauthParams.c_str ());
302+ temp += oauthParams.length ();
303+
304+ if (queryParams) {
305+ *temp++ = ' &' ;
306+ strcpy (temp, queryParams);
307+ temp += queryParamsLength;
308+ }
309+
310+ if (bodyParams) {
311+ *temp++ = ' &' ;
312+ strcpy (temp, bodyParams);
313+ temp += bodyParamsLength;
314+ }
315+
316+ *temp = ' \0 ' ;
317+
318+ // caculate the number of parameters
319+ int numParams = 0 ;
320+ for (int i = 0 ; i < paramsLength; i++) {
321+ if (params[i] == ' =' ) {
322+ numParams++;
323+ }
324+ }
325+
326+ // collect the keys of the parameters to an array
327+ // and also replace the = and & characters with \0
328+ // this will help with the sorting later
329+ const char * paramKeys[numParams];
330+ int paramIndex = 0 ;
331+ const char * lastKey = params;
332+
333+ temp = params;
334+ while (1 ) {
335+ char c = *temp;
336+
337+ if (c == ' \0 ' ) {
338+ break ;
339+ } else if (c == ' =' ) {
340+ paramKeys[paramIndex++] = lastKey;
341+
342+ *temp = ' \0 ' ;
343+ } else if (c == ' &' ) {
344+ lastKey = (temp + 1 );
345+
346+ *temp = ' \0 ' ;
347+ }
348+
349+ temp++;
350+ }
351+
352+ // sort the param keys
353+ qsort (paramKeys, numParams, sizeof (uintptr_t ), strcmp_pointer);
354+
355+ // calculate the signature
242356 SHA1.beginHmac (_signingKey);
243357 SHA1.print (method);
244358 SHA1.print (" &" );
245359 SHA1.print (URLEncoder.encode (url));
246360 SHA1.print (" &" );
361+ for (int i = 0 ; i < numParams; i++) {
362+ const char * paramKey = paramKeys[i];
363+ int keyLength = strlen (paramKey);
364+ const char * paramValue = paramKey + keyLength + 1 ;
365+
366+ SHA1.print (URLEncoder.encode (paramKey));
367+ SHA1.print (URLEncoder.encode (" =" ));
368+ SHA1.print (URLEncoder.encode (paramValue));
247369
248- SHA1.print (URLEncoder.encode (" oauth_consumer_key=" ));
249- SHA1.print (URLEncoder.encode (_consumerKey));
250- SHA1.print (URLEncoder.encode (" &" ));
251- SHA1.print (URLEncoder.encode (" oauth_nonce=" ));
252- SHA1.print (URLEncoder.encode (_nonce));
253- SHA1.print (URLEncoder.encode (" &" ));
254- SHA1.print (URLEncoder.encode (" oauth_signature_method=HMAC-SHA1&" ));
255- SHA1.print (URLEncoder.encode (" oauth_timestamp=" ));
256- SHA1.print (URLEncoder.encode (String (time)));
257- SHA1.print (URLEncoder.encode (" &" ));
258- SHA1.print (URLEncoder.encode (" oauth_token=" ));
259- SHA1.print (URLEncoder.encode (_accessToken));
260- SHA1.print (URLEncoder.encode (" &" ));
261- SHA1.print (URLEncoder.encode (" oauth_version=1.0&" ));
262- SHA1.print (URLEncoder.encode (body));
370+ if ((i + 1 ) < numParams) {
371+ SHA1.print (URLEncoder.encode (" &" ));
372+ }
373+ }
263374 SHA1.endHmac ();
264375
265376 int rawSignatureLength = SHA1.available ();
@@ -283,21 +394,15 @@ String OAuthClient::calculateOauthAuthorization(const String& signature, unsigne
283394 authorization += " OAuth " ;
284395 authorization += " oauth_consumer_key=\" " ;
285396 authorization += _consumerKey;
286- authorization += " \" ," ;
287- authorization += " oauth_nonce=\" " ;
397+ authorization += " \" ,oauth_nonce=\" " ;
288398 authorization += _nonce;
289- authorization += " \" ," ;
290- authorization += " oauth_signature=\" " ;
399+ authorization += " \" ,oauth_signature=\" " ;
291400 authorization += signature;
292- authorization += " \" ," ;
293- authorization += " oauth_signature_method=\" HMAC-SHA1\" ," ;
294- authorization += " oauth_timestamp=\" " ;
401+ authorization += " \" ,oauth_signature_method=\" HMAC-SHA1\" ,oauth_timestamp=\" " ;
295402 authorization += timestamp;
296- authorization += " \" ," ;
297- authorization += " oauth_token=\" " ;
403+ authorization += " \" ,oauth_token=\" " ;
298404 authorization += _accessToken;
299- authorization += " \" ," ;
300- authorization += " oauth_version=\" 1.0\" " ;
405+ authorization += " \" ,oauth_version=\" 1.0\" " ;
301406
302407 return authorization;
303408}
0 commit comments