00001 /** 00002 00003 */ 00004 00005 00006 /*! 00007 \mainpage 00008 00009 This is a small library that provides Amazon Web Services binding 00010 for C programs. 00011 00012 The AWS4C leverages CURL and OPENSSL libraries for HTTP transfer and 00013 cryptographic functions. 00014 00015 The \ref todo list is here. 00016 00017 The \ref bug list is here. 00018 00019 */ 00020 00021 /// \todo Document .awsAuth file 00022 /// \todo Include license 00023 /// \todo Include regression testing 00024 /// \todo Run thing through valgrind 00025 00026 #include <stdlib.h> 00027 #include <stdio.h> 00028 #include <stdarg.h> 00029 #include <string.h> 00030 #include <sys/stat.h> 00031 #include <curl/curl.h> 00032 #include <openssl/hmac.h> 00033 #include <openssl/evp.h> 00034 #include <openssl/bio.h> 00035 #include <openssl/buffer.h> 00036 00037 #include "aws4c.h" 00038 00039 00040 /*! 00041 \defgroup internal Internal Functions 00042 \{ 00043 */ 00044 00045 static int debug = 0; /// <flag to control debugging options 00046 static char * ID = NULL; /// <Current ID 00047 static char * awsKeyID = NULL; /// <AWS Key ID 00048 static char * awsKey = NULL; /// <AWS Key Material 00049 static char * S3Host = "s3.amazonaws.com"; /// <AWS S3 host 00050 static char * SQSHost = "queue.amazonaws.com"; /// <AWS SQS host 00051 static char * Bucket = NULL; 00052 00053 static void __debug ( char *fmt, ... ) ; 00054 static char * __aws_get_iso_date (); 00055 static char * __aws_get_httpdate (); 00056 static FILE * __aws_getcfg (); 00057 static int s3_do_get ( IOBuf *b, char * const signature, 00058 char * const date, char * const resource ); 00059 static int s3_do_put ( IOBuf *b, char * const signature, 00060 char * const date, char * const resource ); 00061 static char* __aws_sign ( char * const str ); 00062 static void __chomp ( char * str ); 00063 00064 00065 /// Decode base64 into binary 00066 /// \param input base64 text 00067 /// \param length length of the input text 00068 /// \return decoded data in newly allocated buffer 00069 /// \internal 00070 /// 00071 /// This function allocates a buffer of the same size as the input 00072 /// buffer and then decodes the given base64 encoded text into 00073 /// binary. The result is placed into the allocated buffer. It is 00074 /// the caller's responsibility to free this buffer 00075 static char *unbase64(unsigned char *input, int length) 00076 { 00077 BIO *b64, *bmem; 00078 00079 /// Allocate and zero the buffer 00080 char *buffer = (char *)malloc(length); 00081 memset(buffer, 0, length); 00082 00083 /// Decode the input into the newly allocated buffer 00084 /// \todo Check for errors during decode 00085 b64 = BIO_new(BIO_f_base64()); 00086 bmem = BIO_new_mem_buf(input, length); 00087 bmem = BIO_push(b64, bmem); 00088 00089 BIO_read(bmem, buffer, length); 00090 00091 BIO_free_all(bmem); 00092 00093 /// Return the decoded text 00094 return buffer; 00095 } 00096 00097 /// Encode a binary into base64 buffer 00098 /// \param input binary data text 00099 /// \param length length of the input text 00100 /// \internal 00101 /// \return a newly allocated buffer with base64 encoded data 00102 static char *__b64_encode(const unsigned char *input, int length) 00103 { 00104 BIO *bmem, *b64; 00105 BUF_MEM *bptr; 00106 00107 b64 = BIO_new(BIO_f_base64()); 00108 bmem = BIO_new(BIO_s_mem()); 00109 b64 = BIO_push(b64, bmem); 00110 BIO_write(b64, input, length); 00111 BIO_flush(b64); 00112 BIO_get_mem_ptr(b64, &bptr); 00113 00114 char *buff = (char *)malloc(bptr->length); 00115 memcpy(buff, bptr->data, bptr->length-1); 00116 buff[bptr->length-1] = 0; 00117 00118 BIO_free_all(b64); 00119 00120 return buff; 00121 } 00122 00123 /// Chomp (remove the trailing '\n' from the string 00124 /// \param str string 00125 static void __chomp ( char * str ) 00126 { 00127 if ( str[0] == 0 ) return; 00128 int ln = strlen(str); 00129 ln--; 00130 if ( str[ln] == '\n' ) str[ln] = 0; 00131 if ( ln == 0 ) return ; 00132 ln--; 00133 if ( str[ln] == '\r' ) str[ln] = 0; 00134 } 00135 00136 /// Handles reception of the data 00137 /// \param ptr pointer to the incoming data 00138 /// \param size size of the data member 00139 /// \param nmemb number of data memebers 00140 /// \param stream pointer to I/O buffer 00141 /// \return number of bytes processed 00142 static size_t writefunc ( void * ptr, size_t size, size_t nmemb, void * stream ) 00143 { 00144 __debug ( "DATA RCVD %d items of size %d ", nmemb, size ); 00145 aws_iobuf_append ( stream, ptr, nmemb*size ); 00146 return nmemb * size; 00147 } 00148 00149 /// Handles sending of the data 00150 /// \param ptr pointer to the incoming data 00151 /// \param size size of the data member 00152 /// \param nmemb number of data memebers 00153 /// \param stream pointer to I/O buffer 00154 /// \return number of bytes written 00155 static size_t readfunc ( void * ptr, size_t size, size_t nmemb, void * stream ) 00156 { 00157 static int x = 101; 00158 00159 char * Ln = ptr; 00160 int sz = aws_iobuf_getline ( stream, ptr, size*nmemb); 00161 __debug ( "Sent[%3d] %s", sz, Ln ); 00162 return sz; 00163 } 00164 00165 /// Process incming header 00166 /// \param ptr pointer to the incoming data 00167 /// \param size size of the data member 00168 /// \param nmemb number of data memebers 00169 /// \param stream pointer to I/O buffer 00170 /// \return number of bytes processed 00171 static size_t header ( void * ptr, size_t size, size_t nmemb, void * stream ) 00172 { 00173 IOBuf * b = stream; 00174 00175 if (!strncmp ( ptr, "HTTP/1.1", 8 )) 00176 { 00177 b->result = strdup ( ptr + 9 ); 00178 __chomp(b->result); 00179 b->code = atoi ( ptr + 9 ); 00180 } 00181 else if ( !strncmp ( ptr, "ETag: ", 6 )) 00182 { 00183 b->eTag = strdup ( ptr + 6 ); 00184 __chomp(b->eTag); 00185 } 00186 else if ( !strncmp ( ptr, "Last-Modified: ", 14 )) 00187 { 00188 b->lastMod = strdup ( ptr + 15 ); 00189 __chomp(b->lastMod); 00190 } 00191 else if ( !strncmp ( ptr, "Content-Length: ", 15 )) 00192 { 00193 b->len = atoi ( ptr + 16 ); 00194 } 00195 00196 return nmemb * size; 00197 } 00198 00199 00200 /// Get Data for authentication of SQS request 00201 /// \return date in ISO format 00202 static char * __aws_get_iso_date () 00203 { 00204 static char dTa[256]; 00205 time_t t = time(NULL); 00206 struct tm * gTime = gmtime ( & t ); 00207 00208 memset ( dTa, 0 , sizeof(dTa)); 00209 strftime ( dTa, sizeof(dTa), "%FT%H:%M:%SZ", gTime ); 00210 __debug ( "Request Time: %s", dTa ); 00211 return dTa; 00212 } 00213 00214 00215 /// Dump current state 00216 /// \internal 00217 static void Dump () 00218 { 00219 printf ( "----------------------------------------\n"); 00220 printf ( "ID : %-40s \n", ID ); 00221 printf ( "KeyID : %-40s \n", awsKeyID ); 00222 printf ( "Key : %-40s \n", awsKey ); 00223 printf ( "S3 Host : %-40s \n", S3Host ); 00224 printf ( "SQS Host : %-40s \n", SQSHost ); 00225 printf ( "Bucket : %-40s \n", Bucket ); 00226 printf ( "----------------------------------------\n"); 00227 } 00228 00229 00230 /// Print debug output 00231 /// \internal 00232 /// \param fmt printf like formating string 00233 static void __debug ( char *fmt, ... ) { 00234 /// If debug flag is not set we won't print anything 00235 if ( ! debug ) return ; 00236 /// Otherwise process the arguments and print the result 00237 va_list args; 00238 va_start( args, fmt ); 00239 fprintf( stderr, "DBG: " ); 00240 vfprintf( stderr, fmt, args ); 00241 fprintf( stderr, "\n" ); 00242 va_end( args ); 00243 } 00244 00245 00246 /// Get Request Date 00247 /// \internal 00248 /// \return date in HTTP format 00249 static char * __aws_get_httpdate () 00250 { 00251 static char dTa[256]; 00252 time_t t = time(NULL); 00253 struct tm * gTime = gmtime ( & t ); 00254 memset ( dTa, 0 , sizeof(dTa)); 00255 strftime ( dTa, sizeof(dTa), "%a, %d %b %Y %H:%M:%S +0000", gTime ); 00256 __debug ( "Request Time: %s", dTa ); 00257 return dTa; 00258 } 00259 00260 /// Internal function to get configuration file 00261 static FILE * __aws_getcfg () 00262 { 00263 int rv; 00264 char ConfigFile[256] = ""; 00265 /// Compose FileName and check 00266 snprintf ( ConfigFile, sizeof(ConfigFile) - 3, "%s/.awsAuth", 00267 getenv("HOME")); 00268 __debug ( "Config File %s", ConfigFile ); 00269 00270 struct stat sBuf; 00271 rv = stat ( ConfigFile, &sBuf ); 00272 if ( rv == -1 ) return NULL; 00273 00274 00275 if ( sBuf.st_mode & 066 || 00276 sBuf.st_uid != getuid () ) 00277 { 00278 fprintf ( stderr, "I refuse to read your credentials from %s as this " 00279 "file is readable by, writable by or owned by someone else." 00280 "Try chmod 600 %s", ConfigFile, ConfigFile ); 00281 return NULL; 00282 } 00283 00284 return fopen ( ConfigFile, "r" ); 00285 00286 } 00287 00288 00289 /// Get S3 Request signature 00290 /// \internal 00291 /// \param resource -- URI of the object 00292 /// \param resSize -- size of the resoruce buffer 00293 /// \param date -- HTTP date 00294 /// \param method -- HTTP method 00295 /// \param bucket -- bucket 00296 /// \param file -- file 00297 /// \return fills up resource and date parameters, also 00298 /// returns request signature to be used with Authorization header 00299 static char * GetStringToSign ( char * resource, int resSize, 00300 char ** date, 00301 char * const method, 00302 char * const bucket, 00303 char * const file ) 00304 { 00305 char reqToSign[2048]; 00306 00307 * date = __aws_get_httpdate(); 00308 00309 memset ( resource,0,resSize); 00310 if ( bucket != NULL ) 00311 snprintf ( resource, resSize,"%s/%s", bucket, file ); 00312 else 00313 snprintf ( resource, resSize,"%s", file ); 00314 00315 00316 snprintf ( reqToSign, sizeof(reqToSign),"%s\n\n\n%s\n/%s", 00317 method, *date, resource ); 00318 return __aws_sign(reqToSign); 00319 00320 } 00321 00322 static int __aws_urlencode ( char * src, char * dest, int nDest ) 00323 { 00324 int i; 00325 int n; 00326 memset ( dest, 0, nDest ); 00327 __debug ( "Encoding: %s", src ); 00328 const char * badChrs = " \n$&+,/:;=?@"; 00329 const char * hexDigit = "0123456789ABCDEF"; 00330 00331 n = 0; 00332 for ( i = 0 ; src[i] ; i ++ ) 00333 { 00334 if ( n + 5 > nDest ) 00335 { puts ( "URLEncode:: Dest buffer to small.. can't continue \n" ); exit(0); } 00336 if ( strchr ( badChrs, src[i] )) 00337 { 00338 unsigned char c = src[i]; 00339 dest[n++] = '%'; 00340 dest[n++] = hexDigit [(c >> 4 ) & 0xF ]; 00341 dest[n++] = hexDigit [c & 0xF ]; 00342 } 00343 else dest[n++] = src[i]; 00344 } 00345 __debug ( "Encoded To: %s", dest ); 00346 } 00347 00348 static int SQSRequest ( IOBuf *b, char * verb, char * const url ) 00349 { 00350 char Buf[1024]; 00351 00352 00353 CURL* ch = curl_easy_init( ); 00354 struct curl_slist *slist=NULL; 00355 00356 00357 curl_easy_setopt ( ch, CURLOPT_URL, url ); 00358 curl_easy_setopt ( ch, CURLOPT_HEADERDATA, b ); 00359 curl_easy_setopt ( ch, CURLOPT_VERBOSE, debug ); 00360 curl_easy_setopt ( ch, CURLOPT_INFILESIZE, b->len ); 00361 curl_easy_setopt ( ch, CURLOPT_POST, 1 ); 00362 curl_easy_setopt ( ch, CURLOPT_POST, CURLOPT_POSTFIELDSIZE , 0 ); 00363 curl_easy_setopt ( ch, CURLOPT_HEADERFUNCTION, header ); 00364 curl_easy_setopt ( ch, CURLOPT_WRITEFUNCTION, writefunc ); 00365 curl_easy_setopt ( ch, CURLOPT_WRITEDATA, b ); 00366 curl_easy_setopt ( ch, CURLOPT_READFUNCTION, readfunc ); 00367 curl_easy_setopt ( ch, CURLOPT_READDATA, b ); 00368 00369 int sc = curl_easy_perform(ch); 00370 /** \todo check the return code */ 00371 __debug ( "Return Code: %d ", sc ); 00372 00373 curl_slist_free_all(slist); 00374 00375 return sc; 00376 } 00377 static char * SQSSign ( char * str ) 00378 { 00379 char RealSign[1024]; 00380 char * signature = __aws_sign(str); 00381 int c = 0; 00382 int n = strlen(signature); 00383 00384 __aws_urlencode ( signature, RealSign, sizeof(RealSign)); 00385 00386 free ( signature ); 00387 return strdup(RealSign); 00388 00389 } 00390 00391 00392 00393 /*! 00394 \} 00395 */ 00396 00397 00398 /*! 00399 \defgroup Conf Configuration Functions 00400 \{ 00401 */ 00402 00403 /// Initialize the library 00404 void aws_init () { curl_global_init (CURL_GLOBAL_ALL); } 00405 00406 /// Set debuging output 00407 /// \param d when non-zero causes debugging output to be printed 00408 void aws_set_debug (int d) 00409 { 00410 debug = d; 00411 } 00412 00413 /// \brief Set AWS account ID to be read from .awsAuth file 00414 /// \param str account ID 00415 void aws_set_id ( char * const str ) 00416 { ID = str == NULL ? NULL : strdup(str); } 00417 00418 /// Set AWS account access key 00419 void aws_set_key ( char * const str ) 00420 { awsKey = str == NULL ? NULL : strdup(str); } 00421 00422 /// Set AWS account access key ID 00423 void aws_set_keyid ( char * const str ) 00424 { awsKeyID = str == NULL ? NULL : strdup(str);} 00425 00426 /// Read AWS authentication records 00427 /// \param id user ID 00428 int aws_read_config ( char * const id ) 00429 { 00430 aws_set_id ( id ); 00431 aws_set_keyid ( NULL ); 00432 aws_set_key ( NULL ); 00433 00434 /// Open File 00435 /// Make sure that file permissions are set right 00436 __debug ( "Reading Config File ID[%s]", ID ); 00437 FILE * f = __aws_getcfg(); 00438 if ( f == NULL ) { perror ("Error opening config file"); exit(1); } 00439 00440 00441 /// Read Lines 00442 char line[1024]; 00443 int ln = 0; 00444 while ( !feof(f)) 00445 { 00446 ln++; 00447 memset (line,0,sizeof(line)); 00448 fgets ( line, sizeof(line), f ); 00449 00450 /// Skip Comments 00451 if ( line[0] == '#' ) continue; 00452 if ( line[0] == 0 ) continue; 00453 00454 __chomp ( line ); 00455 00456 00457 /// Split the line on ':' 00458 char * keyID = strchr(line,':'); 00459 if ( keyID == NULL ) 00460 { printf ( "Syntax error in credentials file line %d, no keyid\n", ln ); 00461 exit(1); 00462 } 00463 *keyID = 0; keyID ++; 00464 00465 char * key = strchr(keyID,':'); 00466 if ( key == NULL ) 00467 { printf ( "Syntax error in credentials file line %d, no key\n", ln ); 00468 exit(1); 00469 } 00470 *key = 0; key ++; 00471 00472 00473 /// If the line is correct Set the IDs 00474 if ( !strcmp(line,id)) 00475 { 00476 aws_set_keyid ( keyID ); 00477 aws_set_key ( key ); 00478 break; 00479 } 00480 00481 } 00482 /// Return error if not found 00483 if ( awsKeyID == NULL ) return -1; 00484 return 0; 00485 } 00486 00487 /*! 00488 \} 00489 */ 00490 00491 00492 00493 00494 /*! 00495 \defgroup S3 S3 interface functions 00496 \{ 00497 */ 00498 00499 00500 /// Select current S3 bucket 00501 /// \param str bucket ID 00502 void s3_set_bucket ( char * const str ) 00503 { Bucket = str == NULL ? NULL : strdup(str); } 00504 00505 /// Set S3 host 00506 void s3_set_host ( char * const str ) 00507 { S3Host = str == NULL ? NULL : strdup(str); } 00508 00509 00510 00511 /// Upload the file into currently selected bucket 00512 /// \param b I/O buffer 00513 /// \param file filename 00514 int s3_put ( IOBuf * b, char * const file ) 00515 { 00516 char * const method = "PUT"; 00517 char resource [1024]; 00518 char * date = NULL; 00519 00520 char * signature = GetStringToSign ( resource, sizeof(resource), 00521 &date, method, Bucket, file ); 00522 int sc = s3_do_put( b, signature, date, resource ); 00523 free ( signature ); 00524 return sc; 00525 00526 } 00527 00528 00529 /// Download the file from the current bucket 00530 /// \param b I/O buffer 00531 /// \param file filename 00532 int s3_get ( IOBuf * b, char * const file ) 00533 { 00534 char * const method = "GET"; 00535 00536 char resource [1024]; 00537 char * date = NULL; 00538 00539 00540 char * signature = GetStringToSign ( resource, sizeof(resource), 00541 &date, method, Bucket, file ); 00542 int sc = s3_do_get( b, signature, date, resource ); 00543 free ( signature ); 00544 return sc; 00545 } 00546 00547 00548 00549 static int s3_do_put ( IOBuf *b, char * const signature, 00550 char * const date, char * const resource ) 00551 { 00552 char Buf[1024]; 00553 00554 CURL* ch = curl_easy_init( ); 00555 struct curl_slist *slist=NULL; 00556 00557 00558 snprintf ( Buf, sizeof(Buf), "Date: %s", date ); 00559 slist = curl_slist_append(slist, Buf ); 00560 snprintf ( Buf, sizeof(Buf), "Authorization: AWS %s:%s", awsKeyID, signature ); 00561 slist = curl_slist_append(slist, Buf ); 00562 00563 snprintf ( Buf, sizeof(Buf), "http://%s/%s", S3Host , resource ); 00564 00565 curl_easy_setopt ( ch, CURLOPT_HTTPHEADER, slist); 00566 curl_easy_setopt ( ch, CURLOPT_URL, Buf ); 00567 curl_easy_setopt ( ch, CURLOPT_READDATA, b ); 00568 curl_easy_setopt ( ch, CURLOPT_READFUNCTION, readfunc ); 00569 curl_easy_setopt ( ch, CURLOPT_HEADERFUNCTION, header ); 00570 curl_easy_setopt ( ch, CURLOPT_HEADERDATA, b ); 00571 curl_easy_setopt ( ch, CURLOPT_VERBOSE, debug ); 00572 curl_easy_setopt ( ch, CURLOPT_UPLOAD, 1 ); 00573 curl_easy_setopt ( ch, CURLOPT_INFILESIZE, b->len ); 00574 00575 int sc = curl_easy_perform(ch); 00576 /** \todo check the return code */ 00577 __debug ( "Return Code: %d ", sc ); 00578 00579 curl_slist_free_all(slist); 00580 00581 return sc; 00582 00583 } 00584 00585 00586 static int s3_do_get ( IOBuf *b, char * const signature, 00587 char * const date, char * const resource ) 00588 { 00589 char Buf[1024]; 00590 00591 CURL* ch = curl_easy_init( ); 00592 struct curl_slist *slist=NULL; 00593 00594 slist = curl_slist_append(slist, "If-Modified-Since: Tue, 26 May 2009 18:58:55 GMT" ); 00595 slist = curl_slist_append(slist, "ETag: \"6ea58533db38eca2c2cc204b7550aab6\""); 00596 00597 snprintf ( Buf, sizeof(Buf), "Date: %s", date ); 00598 slist = curl_slist_append(slist, Buf ); 00599 snprintf ( Buf, sizeof(Buf), "Authorization: AWS %s:%s", awsKeyID, signature ); 00600 slist = curl_slist_append(slist, Buf ); 00601 00602 snprintf ( Buf, sizeof(Buf), "http://%s/%s", S3Host, resource ); 00603 00604 curl_easy_setopt ( ch, CURLOPT_HTTPHEADER, slist); 00605 curl_easy_setopt ( ch, CURLOPT_URL, Buf ); 00606 curl_easy_setopt ( ch, CURLOPT_WRITEFUNCTION, writefunc ); 00607 curl_easy_setopt ( ch, CURLOPT_WRITEDATA, b ); 00608 curl_easy_setopt ( ch, CURLOPT_HEADERFUNCTION, header ); 00609 curl_easy_setopt ( ch, CURLOPT_HEADERDATA, b ); 00610 curl_easy_setopt ( ch, CURLOPT_VERBOSE, debug ); 00611 00612 int sc = curl_easy_perform(ch); 00613 /** \todo check the return code */ 00614 __debug ( "Return Code: %d ", sc ); 00615 00616 curl_slist_free_all(slist); 00617 00618 return sc; 00619 00620 } 00621 00622 00623 00624 00625 00626 00627 static char* __aws_sign ( char * const str ) 00628 { 00629 int result; 00630 HMAC_CTX ctx; 00631 unsigned char MD[256]; 00632 unsigned len; 00633 00634 __debug("StrToSign:%s", str ); 00635 00636 HMAC_CTX_init(&ctx); 00637 HMAC_Init(&ctx, awsKey, strlen(awsKey), EVP_sha1()); 00638 HMAC_Update(&ctx,(unsigned char*)str, strlen(str)); 00639 HMAC_Final(&ctx,(unsigned char*)MD,&len); 00640 HMAC_CTX_cleanup(&ctx); 00641 00642 char * b64 = __b64_encode (MD,len); 00643 __debug("Signature: %s", b64 ); 00644 00645 return b64; 00646 } 00647 /*! 00648 \} 00649 */ 00650 00651 00652 00653 #define SQS_REQ_TAIL "&Signature=%s" "&SignatureVersion=1" "&Timestamp=%s" "&Version=2009-02-01" 00654 /*! 00655 \defgroup SQS SQS interface functions 00656 \{ 00657 */ 00658 00659 00660 /// Create SQS queue 00661 /// \param b I/O buffer 00662 /// \param name queue name 00663 /// \return on success return 0, otherwise error code 00664 int sqs_create_queue ( IOBuf *b, char * const name ) 00665 { 00666 __debug ( "Creating Que: %s\n", name ); 00667 00668 00669 const char * method = "POST"; 00670 char resource [1024]; 00671 char customSign [1024]; 00672 char * date = NULL; 00673 char * signature = NULL; 00674 00675 00676 00677 char * Req = 00678 "http://queue.amazonaws.com/" 00679 "?Action=CreateQueue" 00680 "&QueueName=%s" 00681 "&AWSAccessKeyId=%s" 00682 SQS_REQ_TAIL ; 00683 00684 char * Sign = "ActionCreateQueue" 00685 "AWSAccessKeyId%s" 00686 "QueueName%s" 00687 "SignatureVersion1" 00688 "Timestamp%sVersion2009-02-01"; 00689 00690 date = __aws_get_iso_date (); 00691 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, name, date ); 00692 signature = SQSSign ( customSign ); 00693 00694 snprintf ( resource, sizeof(resource), Req , name, awsKeyID, signature, date ); 00695 00696 int sc = SQSRequest( b, "POST", resource ); 00697 free ( signature ); 00698 return sc; 00699 00700 } 00701 00702 /// Retrieve URL of the queue 00703 /// \param b I/O buffer 00704 /// \param prefix queue prefix. better use the whole name 00705 /// \return on success return 0, otherwise error code 00706 /// 00707 /// URL is placed into the I/O buffer. User get_line to retrieve it 00708 int sqs_list_queues ( IOBuf *b, char * const prefix ) 00709 { 00710 __debug ( "Listing Queues PFX: %s\n", prefix ); 00711 00712 const char * method = "POST"; 00713 char resource [1024]; 00714 char customSign [1024]; 00715 char * date = NULL; 00716 char * signature = NULL; 00717 00718 00719 00720 char * Req = 00721 "http://queue.amazonaws.com/" 00722 "?Action=ListQueues" 00723 "&QueueNamePrefix=%s" 00724 "&AWSAccessKeyId=%s" 00725 SQS_REQ_TAIL ; 00726 00727 char * Sign = "ActionListQueues" 00728 "AWSAccessKeyId%s" 00729 "QueueNamePrefix%s" 00730 "SignatureVersion1" 00731 "Timestamp%sVersion2009-02-01"; 00732 00733 date = __aws_get_iso_date (); 00734 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, prefix, date ); 00735 signature = SQSSign ( customSign ); 00736 00737 snprintf ( resource, sizeof(resource), Req , prefix, awsKeyID, 00738 signature, date ); 00739 00740 IOBuf *nb = aws_iobuf_new(); 00741 int sc = SQSRequest( nb, "POST", resource ); 00742 free ( signature ); 00743 00744 b-> result = strdup(nb->result); 00745 b-> code = nb->code; 00746 00747 /// \todo This only retrieves just one line in the string.. 00748 /// make that all URLs are returned 00749 00750 if ( b->code == 200 ) 00751 { 00752 /// Parse Out the List Of Queues 00753 while(-1) 00754 { 00755 char Ln[1024]; 00756 int sz = aws_iobuf_getline ( nb, Ln, sizeof(Ln)); 00757 if ( Ln[0] == 0 ) break; 00758 char *q = strstr ( Ln, "<QueueUrl>" ); 00759 if ( q != 0 ) 00760 { 00761 q += 10; 00762 char * end = NULL; 00763 end = strstr ( q, "</QueueUrl>" ); 00764 if ( *end != 0 ) 00765 { 00766 * end = 0; 00767 aws_iobuf_append ( b, q, strlen(q )); 00768 aws_iobuf_append ( b, "\n", 1 ); 00769 } 00770 } 00771 } 00772 } 00773 aws_iobuf_free ( nb ); 00774 00775 return sc; 00776 } 00777 00778 00779 /// Retrieve queue attributes 00780 /// \param b I/O buffer 00781 /// \param url queue url. Use sqs_list_queues to retrieve 00782 /// \param timeOut queue visibility timeout 00783 /// \param nMesg approximate number of messages in the queue 00784 /// \return on success return 0, otherwise error code 00785 int sqs_get_queueattributes ( IOBuf *b, char * url, int *timeOut, int *nMesg ) 00786 { 00787 __debug ( "Getting Que Attributes\n" ); 00788 00789 const char * method = "POST"; 00790 char resource [1024]; 00791 char customSign [1024]; 00792 char * date = NULL; 00793 char * signature = NULL; 00794 00795 char * Req = 00796 "%s/" 00797 "?Action=GetQueueAttributes" 00798 "&AttributeName.1=VisibilityTimeout" 00799 "&AttributeName.2=ApproximateNumberOfMessages" 00800 "&AWSAccessKeyId=%s" 00801 SQS_REQ_TAIL ; 00802 00803 char * Sign = 00804 "ActionGetQueueAttributes" 00805 "AttributeName.1VisibilityTimeout" 00806 "AttributeName.2ApproximateNumberOfMessages" 00807 "AWSAccessKeyId%s" 00808 "SignatureVersion1" 00809 "Timestamp%s" 00810 "Version2009-02-01"; 00811 00812 date = __aws_get_iso_date (); 00813 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, date ); 00814 signature = SQSSign ( customSign ); 00815 00816 snprintf ( resource, sizeof(resource), Req , url, awsKeyID, signature, date ); 00817 00818 const char *pfxVisTO = "<Name>VisibilityTimeout</Name><Value>"; 00819 const char *pfxQLen = "<Name>ApproximateNumberOfMessages</Name><Value>"; 00820 00821 00822 int sc = SQSRequest( b, "POST", resource ); 00823 while(-1) 00824 { 00825 char Ln[1024]; 00826 int sz = aws_iobuf_getline ( b, Ln, sizeof(Ln)); 00827 if ( Ln[0] == 0 ) break; 00828 00829 char *q; 00830 q = strstr ( Ln, pfxVisTO ); 00831 if ( q != 0 ) { *timeOut = atoi(q+strlen(pfxVisTO)); } 00832 q = strstr ( Ln, pfxQLen ); 00833 if ( q != 0 ) { *nMesg = atoi(q+strlen(pfxQLen)); } 00834 } 00835 00836 free ( signature ); 00837 return sc; 00838 } 00839 00840 /// Set queue visibility timeout 00841 /// \param b I/O buffer 00842 /// \param url queue url. Use sqs_list_queues to retrieve 00843 /// \param sec queue visibility timeout 00844 /// \return on success return 0, otherwise error code 00845 int sqs_set_queuevisibilitytimeout ( IOBuf *b, char * url, int sec ) 00846 { 00847 __debug ( "Setting Visibility Timeout : %d\n", sec ); 00848 00849 const char * method = "POST"; 00850 char resource [1024]; 00851 char customSign [1024]; 00852 char * date = NULL; 00853 char * signature = NULL; 00854 00855 char * Req = 00856 "%s/" 00857 "?Action=SetQueueAttributes" 00858 "&Attribute.1.Name=VisibilityTimeout" 00859 "&Attribute.1.Value=%d" 00860 "&AWSAccessKeyId=%s" 00861 SQS_REQ_TAIL ; 00862 00863 char * Sign = 00864 "ActionSetQueueAttributes" 00865 "Attribute.1.NameVisibilityTimeout" 00866 "Attribute.1.Value%d" 00867 "AWSAccessKeyId%s" 00868 "SignatureVersion1" 00869 "Timestamp%s" 00870 "Version2009-02-01"; 00871 00872 date = __aws_get_iso_date (); 00873 snprintf ( customSign, sizeof(customSign), Sign, sec, awsKeyID, date ); 00874 signature = SQSSign ( customSign ); 00875 00876 snprintf ( resource, sizeof(resource), Req , 00877 url, sec, awsKeyID, signature, date ); 00878 00879 int sc = SQSRequest( b, "POST", resource ); 00880 free ( signature ); 00881 return sc; 00882 } 00883 00884 /// Send a message to the queue 00885 /// \param b I/O buffer 00886 /// \param url queue url. Use sqs_list_queues to retrieve 00887 /// \param msg a message to send 00888 /// \return on success return 0, otherwise error code 00889 int sqs_send_message ( IOBuf *b, char * const url, char * const msg ) 00890 { 00891 __debug ( "Sending Message to the queue %s\n[%s]", 00892 url, msg ); 00893 00894 const char * method = "POST"; 00895 char resource [10900]; 00896 char customSign [10900]; 00897 char * date = NULL; 00898 char * signature = NULL; 00899 char encodedMsg[8192]; 00900 00901 00902 int i,n; 00903 n = 0; 00904 __aws_urlencode ( msg, encodedMsg, sizeof(encodedMsg)); 00905 __debug ( "Encoded MSG %s", encodedMsg ); 00906 00907 char * Req = 00908 "%s/" 00909 "?Action=SendMessage" 00910 "&MessageBody=%s" 00911 "&AWSAccessKeyId=%s" 00912 SQS_REQ_TAIL ; 00913 00914 char * Sign = 00915 "ActionSendMessage" 00916 "AWSAccessKeyId%s" 00917 "MessageBody%s" 00918 "SignatureVersion1" 00919 "Timestamp%s" 00920 "Version2009-02-01"; 00921 00922 date = __aws_get_iso_date (); 00923 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, msg, date ); 00924 signature = SQSSign ( customSign ); 00925 00926 snprintf ( resource, sizeof(resource), Req , 00927 url, encodedMsg, awsKeyID, signature, date ); 00928 00929 int sc = SQSRequest( b, "POST", resource ); 00930 free ( signature ); 00931 return sc; 00932 } 00933 00934 /// Retrieve a message from the queue 00935 /// \param b I/O buffer 00936 /// \param url queue url. Use sqs_list_queues to retrieve 00937 /// \param id Message receipt handle. 00938 /// \return on success return 0, otherwise error code 00939 /// 00940 /// Message contents are placed into I/O buffer 00941 /// Caller has to allocate enough memory for the receipt handle 00942 /// 1024 bytes should be enough 00943 int sqs_get_message ( IOBuf * b, char * const url, char * id ) 00944 { 00945 __debug ( "Retieving message from: %s", url ); 00946 00947 const char * method = "POST"; 00948 char resource [10900]; 00949 char customSign [10900]; 00950 char * date = NULL; 00951 char * signature = NULL; 00952 char encodedMsg[8192]; 00953 00954 00955 00956 char * Req = 00957 "%s/" 00958 "?Action=ReceiveMessage" 00959 "&AWSAccessKeyId=%s" 00960 SQS_REQ_TAIL ; 00961 00962 char * Sign = 00963 "ActionReceiveMessage" 00964 "AWSAccessKeyId%s" 00965 "SignatureVersion1" 00966 "Timestamp%s" 00967 "Version2009-02-01"; 00968 00969 date = __aws_get_iso_date (); 00970 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, date ); 00971 signature = SQSSign ( customSign ); 00972 00973 snprintf ( resource, sizeof(resource), Req , 00974 url, awsKeyID, signature, date ); 00975 free ( signature ); 00976 00977 IOBuf * bf = aws_iobuf_new(); 00978 int sc = SQSRequest( bf, "POST", resource ); 00979 00980 b->code = bf->code; 00981 b->result = strdup(bf->result); 00982 00983 if ( bf->code != 200 ) { aws_iobuf_free(bf); return sc; } 00984 00985 /// \todo This is really bad. Must get a real message parser 00986 int inBody = 0; 00987 while(-1) 00988 { 00989 char Ln[1024]; 00990 int sz = aws_iobuf_getline ( bf, Ln, sizeof(Ln)); 00991 if ( Ln[0] == 0 ) break; 00992 00993 __debug ( "%s|%s|", inBody ? ">>": "", Ln ); 00994 00995 char *q; 00996 char *e; 00997 00998 /// Handle a body already being processed.. 00999 if ( inBody ) 01000 { 01001 e = strstr ( Ln, "</Body>" ); 01002 if ( e ) { *e = 0; inBody = 0; } 01003 aws_iobuf_append (b,Ln,strlen(Ln)); 01004 if ( ! inBody ) break; 01005 continue; 01006 } 01007 01008 q = strstr ( Ln, "<ReceiptHandle>" ); 01009 if ( q != 0 ) 01010 { 01011 q += 15; 01012 e = strstr ( Ln, "</ReceiptHandle>" ); 01013 *e = 0; 01014 strcpy ( id, q ); 01015 q = e+1; 01016 q = strstr ( q, "<Body>" ); 01017 if ( q != 0 ) 01018 { 01019 q += 6; 01020 e = strstr ( q, "</Body>" ); 01021 if ( e ) *e = 0; else inBody = 1; 01022 aws_iobuf_append (b,q,strlen(q)); 01023 } 01024 } 01025 } 01026 01027 01028 return sc; 01029 } 01030 01031 /// Delete processed message from the queue 01032 /// \param bf I/O buffer 01033 /// \param url queue url. Use sqs_list_queues to retrieve 01034 /// \param receipt Message receipt handle. 01035 /// \return on success return 0, otherwise error code 01036 /// 01037 int sqs_delete_message ( IOBuf * bf, char * const url, char * receipt ) 01038 { 01039 const char * method = "POST"; 01040 char resource [10900]; 01041 char customSign [10900]; 01042 char * date = NULL; 01043 char * signature = NULL; 01044 char encodedMsg[8192]; 01045 01046 01047 01048 char * Req = 01049 "%s/" 01050 "?Action=DeleteMessage" 01051 "&ReceiptHandle=%s" 01052 "&AWSAccessKeyId=%s" 01053 SQS_REQ_TAIL ; 01054 01055 char * Sign = 01056 "ActionDeleteMessage" 01057 "AWSAccessKeyId%s" 01058 "ReceiptHandle%s" 01059 "SignatureVersion1" 01060 "Timestamp%s" 01061 "Version2009-02-01"; 01062 01063 date = __aws_get_iso_date (); 01064 snprintf ( customSign, sizeof(customSign), Sign, awsKeyID, receipt, date ); 01065 signature = SQSSign ( customSign ); 01066 01067 char encReceipt[1024]; 01068 __aws_urlencode ( receipt, encReceipt, sizeof(encReceipt)); 01069 01070 snprintf ( resource, sizeof(resource), Req , url, encReceipt, awsKeyID, signature, date ); 01071 free ( signature ); 01072 01073 int sc = SQSRequest( bf, "POST", resource ); 01074 return sc; 01075 } 01076 01077 /*! 01078 \} 01079 */ 01080 01081 01082 01083 /*! 01084 \defgroup IOBuf I/O Buffer functions 01085 \{ 01086 */ 01087 01088 /// \todo Place sentinels at the begining of the buffer 01089 01090 /// Create a new I/O buffer 01091 /// \return a newly allocated I/O buffer 01092 IOBuf * aws_iobuf_new () 01093 { 01094 IOBuf * bf = malloc(sizeof(IOBuf)); 01095 01096 bf->first = NULL; 01097 bf->current = NULL; 01098 bf->pos = NULL; 01099 01100 bf->result = NULL; 01101 bf->lastMod = NULL; 01102 bf->eTag = NULL; 01103 bf->len = 0; 01104 bf->code = 0; 01105 01106 return bf; 01107 } 01108 01109 01110 /// Append data to I/O buffer 01111 /// \param B I/O buffer 01112 /// \param d pointer to the data to be appended 01113 /// \param len length of the data to be appended 01114 void aws_iobuf_append ( IOBuf *B, char * d, int len ) 01115 { 01116 01117 IOBufNode * N = malloc(sizeof(IOBufNode)); 01118 N->next = NULL; 01119 N->buf = malloc(len+1); 01120 memcpy(N->buf,d,len); 01121 N->buf[len] = 0; 01122 B->len += len; 01123 01124 if ( B->first == NULL ) 01125 { 01126 B->first = N; 01127 B->current = N; 01128 B->pos = N->buf; 01129 } 01130 else 01131 { 01132 // Find the last block 01133 IOBufNode * D = B->first; 01134 while(D->next != NULL ) D = D->next; 01135 D->next = N; 01136 } 01137 } 01138 01139 /// Read the next line from the buffer 01140 /// \param B I/O buffer 01141 /// \param Line character array to store the read line in 01142 /// \param size size of the character array Line 01143 /// \return number of characters read or 0 01144 int aws_iobuf_getline ( IOBuf * B, char * Line, int size ) 01145 { 01146 int ln = 0; 01147 memset ( Line, 0, size ); 01148 01149 if ( B->current == NULL ) return 0; 01150 01151 while ( size - ln > 1 ) 01152 { 01153 if (* B->pos == '\n' ) { B->pos++; Line[ln] = '\n'; ln++; break; } 01154 if ( *B->pos == 0 ) 01155 { 01156 B->current = B->current->next; 01157 if ( B->current == NULL ) break; 01158 B->pos = B->current->buf; 01159 continue; 01160 } 01161 Line[ln] = * B->pos; 01162 ln++; 01163 01164 B->pos++; 01165 // At the end of the block switch again 01166 } 01167 B->len -= ln; 01168 return ln; 01169 } 01170 01171 /// Release IO Buffer 01172 /// \param bf I/O buffer to be deleted 01173 void aws_iobuf_free ( IOBuf * bf ) 01174 { 01175 /// Release Things 01176 IOBufNode * N = bf->first; 01177 if ( bf->result != NULL ) free ( bf->result ); 01178 if ( bf->lastMod != NULL ) free ( bf->lastMod ); 01179 if ( bf->eTag != NULL ) free ( bf->eTag ); 01180 free (bf); 01181 01182 if ( N == NULL ) return; 01183 01184 /// Walk down the list and release blocks 01185 while ( N->next != NULL ) 01186 { 01187 IOBufNode * NN = N->next; 01188 free(N); 01189 N = NN; 01190 } 01191 if ( N != NULL ) free ( N ); 01192 } 01193 01194 /*! 01195 \} 01196 */ 01197 01198
Generated by Doxygen