00001
00002
00003
00004
00005 #define LIBNFSNAPI_BUILDING_LIB
00006
00007 #include <curl/curl.h>
00008 #include <limits.h>
00009 #include <openssl/sha.h>
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 #include <string.h>
00013 #include <sys/time.h>
00014 #include <time.h>
00015 #include "config.h"
00016 #include "error.h"
00017 #include "mgr.h"
00018
00019
00020 static size_t libnfsnapi_mgr_write_header(void *buffer, size_t size, size_t nmemb, void *userp);
00021 static size_t libnfsnapi_mgr_write_data(void *buffer, size_t size, size_t nmemb, void *userp);
00022
00023
00024 libnfsnapi_mgr_t *libnfsnapi_mgr_create(const char *login, const char *api_key,
00025 int *error_out)
00026 {
00027 libnfsnapi_mgr_t *mgr;
00028 struct timeval tp;
00029 int err = 0;
00030
00031 mgr = (libnfsnapi_mgr_t *) malloc(sizeof(libnfsnapi_mgr_t));
00032 mgr->error = 0;
00033 if (!mgr) {
00034 if (error_out)
00035 *error_out = LIBNFSNAPI_ERROR_MEMORY;
00036 return NULL;
00037 }
00038
00039 mgr->login = strdup(login);
00040 if (!mgr->login) {
00041 if (error_out)
00042 *error_out = LIBNFSNAPI_ERROR_MEMORY;
00043 free(mgr);
00044 return NULL;
00045 }
00046 mgr->api_key = strdup(api_key);
00047 if (!mgr->api_key) {
00048 if (error_out)
00049 *error_out = LIBNFSNAPI_ERROR_MEMORY;
00050 free(mgr->login);
00051 free(mgr);
00052 return NULL;
00053 }
00054
00055 mgr->nudge = 0;
00056 mgr->buf = NULL;
00057 mgr->buf_read = mgr->buf_sz = 0;
00058 mgr->headers = NULL;
00059
00060
00061
00062 mgr->curl = NULL;
00063 if (curl_global_init(CURL_GLOBAL_SSL) != 0) {
00064 libnfsnapi_mgr_destroy(mgr);
00065 if (error_out)
00066 *error_out = LIBNFSNAPI_ERROR_LIBCURL;
00067 return NULL;
00068 }
00069 mgr->curl = curl_easy_init();
00070 if (!mgr->curl) {
00071 curl_global_cleanup();
00072 libnfsnapi_mgr_destroy(mgr);
00073 if (error_out)
00074 *error_out = LIBNFSNAPI_ERROR_LIBCURL;
00075 return NULL;
00076 }
00077
00078 err += curl_easy_setopt(mgr->curl, CURLOPT_HEADER, 0);
00079 err += curl_easy_setopt(mgr->curl, CURLOPT_NOPROGRESS, 1);
00080
00081 err += curl_easy_setopt(mgr->curl, CURLOPT_USERAGENT,
00082 "libNFSNapi/" PACKAGE_VERSION);
00083
00084
00085 err += curl_easy_setopt(mgr->curl, CURLOPT_SSL_VERIFYPEER, 0);
00086 err += curl_easy_setopt(mgr->curl, CURLOPT_SSL_VERIFYHOST, 0);
00087
00088 err += curl_easy_setopt(mgr->curl, CURLOPT_WRITEFUNCTION,
00089 libnfsnapi_mgr_write_data);
00090 err += curl_easy_setopt(mgr->curl, CURLOPT_WRITEDATA, mgr);
00091 err += curl_easy_setopt(mgr->curl, CURLOPT_HEADERFUNCTION,
00092 libnfsnapi_mgr_write_header);
00093 err += curl_easy_setopt(mgr->curl, CURLOPT_HEADERDATA, mgr);
00094
00095 if (err != 0) {
00096
00097 libnfsnapi_mgr_destroy(mgr);
00098 if (error_out)
00099 *error_out = LIBNFSNAPI_ERROR_LIBCURL;
00100 return NULL;
00101 }
00102
00103
00104 gettimeofday(&tp, NULL);
00105 srandom(tp.tv_usec);
00106
00107
00108
00109 return mgr;
00110 }
00111
00112 void libnfsnapi_mgr_destroy(libnfsnapi_mgr_t *mgr)
00113 {
00114 free(mgr->login);
00115 free(mgr->api_key);
00116 if (mgr->buf)
00117 free(mgr->buf);
00118 if (mgr->headers)
00119 curl_slist_free_all(mgr->headers);
00120
00121 if (mgr->curl) {
00122 curl_easy_cleanup(mgr->curl);
00123 curl_global_cleanup();
00124 }
00125
00126 free(mgr);
00127 }
00128
00129 int libnfsnapi_mgr_error(libnfsnapi_mgr_t *mgr)
00130 {
00131 return mgr->error;
00132 }
00133
00134 const char *libnfsnapi_mgr_strerror(libnfsnapi_mgr_t *mgr)
00135 {
00136 return libnfsnapi_strerror(mgr->error);
00137 }
00138
00139 void libnfsnapi_mgr_set_timenudge(libnfsnapi_mgr_t *mgr, int nudge)
00140 {
00141 mgr->nudge = nudge;
00142 }
00143
00144
00145
00146
00147 static void libnfsnapi_mgr_gensalt(char *salt_dest)
00148 {
00149 static const char charset[62] =
00150 "abcdefghijklmnopqrstuvwxyz"
00151 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00152 "0123456789";
00153 int i;
00154
00155 for (i = 0; i < 16; ++i)
00156 salt_dest[i] = charset[random() % sizeof(charset)];
00157 salt_dest[17] = '\0';
00158 }
00159
00160
00161
00162
00163 static void libnfsnapi_mgr_sha1(const char *input, char *sha1_dest)
00164 {
00165 unsigned char sha1_raw[20];
00166 int i;
00167 size_t len = input ? strlen(input) : 0;
00168
00169
00170 SHA1((const unsigned char *) input, len, sha1_raw);
00171
00172
00173 for (i = 0; i < 20; ++i)
00174 sprintf(&sha1_dest[i * 2], "%02x", sha1_raw[i]);
00175 sha1_dest[40] = '\0';
00176 }
00177
00178
00179 static char *libnfsnapi_mgr_genauthheader(libnfsnapi_mgr_t *mgr,
00180 const char *request_uri, const char *body)
00181 {
00182 time_t timestamp;
00183 char salt[17], hash[41], *check_str, *auth_str;
00184
00185 timestamp = time(NULL);
00186 timestamp += mgr->nudge;
00187 libnfsnapi_mgr_gensalt(salt);
00188
00189
00190 libnfsnapi_mgr_sha1(body, hash);
00191
00192
00193 asprintf(&check_str, "%s;%lu;%s;%s;%s;%s",
00194 mgr->login, (unsigned long) timestamp, salt,
00195 mgr->api_key, request_uri, hash);
00196 if (!check_str)
00197 return NULL;
00198 libnfsnapi_mgr_sha1(check_str, hash);
00199 free(check_str);
00200
00201 asprintf(&auth_str, "X-NFSN-Authentication: %s;%lu;%s;%s",
00202 mgr->login, (unsigned long) timestamp, salt, hash);
00203
00204 return auth_str;
00205 }
00206
00207
00208 static size_t libnfsnapi_mgr_write_header(void *buffer, size_t size, size_t nmemb, void *userp)
00209 {
00210 libnfsnapi_mgr_t *mgr = (libnfsnapi_mgr_t *) userp;
00211 size_t num_bytes = size * nmemb;
00212
00213 char *hdr = (char *) malloc(num_bytes);
00214 if (!hdr) {
00215
00216 mgr->error = LIBNFSNAPI_ERROR_MEMORY;
00217 return 0;
00218 }
00219 memcpy(hdr, buffer, num_bytes);
00220 hdr[num_bytes] = '\0';
00221
00222
00223 while ((hdr[0] != '\0') && strchr("\r\n", hdr[strlen(hdr) - 1]))
00224 hdr[strlen(hdr) - 1] = '\0';
00225
00226 mgr->headers = curl_slist_append(mgr->headers, hdr);
00227
00228 if (strncmp(hdr, "HTTP/", 5) == 0)
00229 printf("HTTP header (%d bytes): %s\n", num_bytes, hdr);
00230 free(hdr);
00231
00232 return num_bytes;
00233 }
00234
00235 static size_t libnfsnapi_mgr_write_data(void *buffer, size_t size, size_t nmemb, void *userp)
00236 {
00237 libnfsnapi_mgr_t *mgr = (libnfsnapi_mgr_t *) userp;
00238 char *nbuf;
00239 size_t num_bytes = size * nmemb, new_sz;
00240
00241
00242
00243
00244
00245
00246 if ((mgr->buf_sz - mgr->buf_read) <= num_bytes) {
00247 new_sz = (mgr->buf_sz + 1) * 2;
00248 while ((new_sz - mgr->buf_read - 1) < num_bytes)
00249 new_sz *= 2;
00250 nbuf = (char *) malloc(new_sz);
00251 if (!nbuf)
00252 return 0;
00253 if (mgr->buf) {
00254 memcpy(nbuf, mgr->buf, mgr->buf_read);
00255 free(mgr->buf);
00256 }
00257 mgr->buf = nbuf;
00258 mgr->buf_sz = new_sz;
00259 }
00260
00261 memcpy(&mgr->buf[mgr->buf_read], buffer, num_bytes);
00262 mgr->buf_read += num_bytes;
00263
00264 return num_bytes;
00265 }
00266
00267
00268 int libnfsnapi_mgr_do(libnfsnapi_mgr_t *mgr, int method,
00269 const char *request_uri, const char *data)
00270 {
00271 struct curl_slist *headers = NULL;
00272 char *full_uri, *auth_header;
00273 int err = 0;
00274 size_t i;
00275
00276
00277 mgr->buf_read = 0;
00278 if (mgr->headers) {
00279 curl_slist_free_all(mgr->headers);
00280 mgr->headers = NULL;
00281 }
00282
00283 asprintf(&full_uri, "https://api.nearlyfreespeech.net%s", request_uri);
00284 if (!full_uri) {
00285 mgr->error = LIBNFSNAPI_ERROR_MEMORY;
00286 return mgr->error;
00287 }
00288 err += curl_easy_setopt(mgr->curl, CURLOPT_URL, full_uri);
00289
00290 switch (method) {
00291 case LIBNFSNAPI_MGR_HTTP_GET:
00292 err += curl_easy_setopt(mgr->curl, CURLOPT_HTTPGET, 1);
00293 break;
00294 case LIBNFSNAPI_MGR_HTTP_PUT:
00295 mgr->error = LIBNFSNAPI_ERROR_NOTIMPLEMENTED;
00296 goto cleanup;
00297 err += curl_easy_setopt(mgr->curl, CURLOPT_UPLOAD, 1);
00298 break;
00299 case LIBNFSNAPI_MGR_HTTP_POST:
00300 err += curl_easy_setopt(mgr->curl, CURLOPT_POST, 1);
00301 err += curl_easy_setopt(mgr->curl, CURLOPT_POSTFIELDS,
00302 data);
00303 break;
00304 }
00305
00306 if (err != 0) {
00307 mgr->error = LIBNFSNAPI_ERROR_LIBCURL;
00308 goto cleanup;
00309 }
00310
00311
00312 auth_header = libnfsnapi_mgr_genauthheader(mgr, request_uri, data);
00313 if (!auth_header) {
00314 mgr->error = LIBNFSNAPI_ERROR_MEMORY;
00315 goto cleanup;
00316 }
00317 headers = curl_slist_append(headers, auth_header);
00318 free(auth_header);
00319 if (!headers) {
00320 mgr->error = LIBNFSNAPI_ERROR_MEMORY;
00321 goto cleanup;
00322 }
00323 if (curl_easy_setopt(mgr->curl, CURLOPT_HTTPHEADER, headers)) {
00324 mgr->error = LIBNFSNAPI_ERROR_LIBCURL;
00325 goto cleanup;
00326 }
00327
00328
00329 if ((err = curl_easy_perform(mgr->curl)) != 0) {
00330
00331
00332 switch (err) {
00333 case CURLE_COULDNT_RESOLVE_PROXY:
00334 case CURLE_COULDNT_RESOLVE_HOST:
00335 case CURLE_COULDNT_CONNECT:
00336 mgr->error = LIBNFSNAPI_ERROR_NETWORK;
00337 break;
00338 default:
00339 printf("--> libcurl error is %d\n", err);
00340 mgr->error = LIBNFSNAPI_ERROR_LIBCURL;
00341 }
00342 goto cleanup;
00343 }
00344
00345 if (!mgr->headers) {
00346 mgr->error = LIBNFSNAPI_ERROR_PROTOCOL;
00347 goto cleanup;
00348 }
00349
00350 mgr->buf[mgr->buf_read] = '\0';
00351
00352
00353 printf("Response below: (%d bytes)\n", mgr->buf_read);
00354 printf("------------------------------------------\n");
00355 for (i = 0; i < mgr->buf_read; ++i)
00356 putchar(mgr->buf[i]);
00357 if (mgr->buf[i-1] != '\n')
00358 printf("[no EOL]\n");
00359 printf("------------------------------------------\n");
00360
00361 cleanup:
00362 curl_easy_setopt(mgr->curl, CURLOPT_URL, NULL);
00363 curl_easy_setopt(mgr->curl, CURLOPT_HTTPHEADER, NULL);
00364
00365 if (headers)
00366 curl_slist_free_all(headers);
00367 free(full_uri);
00368 return mgr->error;
00369 }
00370
00371 long libnfsnapi_mgr_result_long(libnfsnapi_mgr_t *mgr)
00372 {
00373 return strtol(mgr->buf, NULL, 10);
00374 }
00375
00376 float libnfsnapi_mgr_result_float(libnfsnapi_mgr_t *mgr)
00377 {
00378 return strtof(mgr->buf, NULL);
00379 }
00380
00381 char *libnfsnapi_mgr_result_string(libnfsnapi_mgr_t *mgr)
00382 {
00383 return strdup(mgr->buf);
00384 }