Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 360 lines (327 sloc) 11.488 kb
579e6de @residuum Made functions static where applicable.
authored
1 /*
2 * [rest-json] makes RESTful calls to webservices that work with JSON data.
3 * */
4
5a9caf9 @eighthave update Makefile and arrange files to match the Library Template:
eighthave authored
5 #include "purest_json.h"
6
7 static t_class *rest_class;
8
31bd302 @residuum Fixed threading issues, refactoring of methods
authored
9 static size_t write_memory_callback(void *ptr, size_t size, size_t nmemb, void *data) {
10 size_t realsize = size * nmemb;
11 t_memory_struct *mem = (t_memory_struct *)data;
12
b7c0014 @residuum Cleaning up.
authored
13 mem->memory = (char *) resizebytes(mem->memory, mem->size, mem->size + realsize + sizeof(char));
31bd302 @residuum Fixed threading issues, refactoring of methods
authored
14 if (mem->memory == NULL) {
15 /* out of memory! */
5854491 @residuum rest-json is now working again.
authored
16 error("not enough memory");
31bd302 @residuum Fixed threading issues, refactoring of methods
authored
17 }
18 memcpy(&mem->memory[mem->size], ptr, realsize);
19 if (mem->size + realsize - mem->size != realsize) {
20 error("Integer overflow or similar. Bad Things can happen.");
21 }
22 mem->size += realsize;
23 mem->memory[mem->size] = '\0';
24
25 return realsize;
26 }
27
28 static size_t read_memory_callback(void *ptr, size_t size, size_t nmemb, void *data) {
29 size_t realsize = size * nmemb;
30 t_memory_struct *mem = (t_memory_struct *)data;
31 size_t to_copy = (mem->size < realsize) ? mem->size : realsize;
32
33 memcpy(ptr, mem->memory, to_copy);
34 mem->size -= to_copy;
35 mem->memory += to_copy;
36 return to_copy;
37 }
38
579e6de @residuum Made functions static where applicable.
authored
39 static void *execute_rest_thread(void *thread_args) {
40 t_rest *x = (t_rest *)thread_args;
41 CURL *curl_handle;
42 CURLcode result;
43 t_memory_struct in_memory;
44 t_memory_struct out_memory;
e4c4ee1 @residuum Status outlet for [rest-json].
authored
45 long http_code;
46 t_atom http_status_data[3];
579e6de @residuum Made functions static where applicable.
authored
47
48 curl_global_init(CURL_GLOBAL_ALL);
49 curl_handle = curl_easy_init();
50 if (curl_handle) {
51 curl_easy_setopt(curl_handle, CURLOPT_URL, x->complete_url);
52 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
f2ad3b5 @residuum Cookie authentication.
authored
53 if (strlen(x->auth_token) != 0) {
54 curl_easy_setopt(curl_handle, CURLOPT_COOKIE, x->auth_token);
55 }
579e6de @residuum Made functions static where applicable.
authored
56 if (strcmp(x->request_type, "PUT") == 0) {
57 curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, TRUE);
58 curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_memory_callback);
59 /* Prepare data for reading */
60 in_memory.memory = getbytes(strlen(x->parameters) + 1);
61 if (in_memory.memory == NULL) {
62 error("not enough memory");
63 }
64 in_memory.size = strlen(x->parameters);
65 memcpy(in_memory.memory, x->parameters, strlen(x->parameters));
66 curl_easy_setopt(curl_handle, CURLOPT_READDATA, (void *)&in_memory);
67 } else if (strcmp(x->request_type, "POST") == 0) {
68 curl_easy_setopt(curl_handle, CURLOPT_POST, TRUE);
69 } else if (strcmp(x->request_type, "DELETE") == 0) {
70 curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
71 }
72 out_memory.memory = getbytes(1);
73 out_memory.size = 0;
74 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_memory_callback);
75 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&out_memory);
76 result = curl_easy_perform(curl_handle);
77
e4c4ee1 @residuum Status outlet for [rest-json].
authored
78 /* output status */
79 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
80 SETSYMBOL(&http_status_data[0], gensym(x->request_type));
81 if (http_code == 200 && result == CURLE_OK) {
82 SETSYMBOL(&http_status_data[1], gensym("bang"));
83 outlet_list(x->status_info_outlet, &s_list, 2, &http_status_data[0]);
84 if (result == CURLE_OK) {
85 x->is_data_locked = 0;
86 output_json_string(out_memory.memory, x->x_ob.ob_outlet, x->done_outlet);
87 /* Free memory */
88 if (out_memory.memory) {
89 freebytes(out_memory.memory, (out_memory.size + 1) * sizeof(char));
90 }
91 free((void *)result);
92 } else {
93 x->is_data_locked = 0;
94 error("Error while performing request: %s", curl_easy_strerror(result));
579e6de @residuum Made functions static where applicable.
authored
95 }
96 } else {
e4c4ee1 @residuum Status outlet for [rest-json].
authored
97 SETFLOAT(&http_status_data[1], (float)http_code);
98 SETFLOAT(&http_status_data[2], (float)result);
99 outlet_list(x->status_info_outlet, &s_list, 3, &http_status_data[0]);
579e6de @residuum Made functions static where applicable.
authored
100 }
101 curl_easy_cleanup(curl_handle);
102 } else {
103 error("Cannot init curl.");
e533743 @residuum Unlock object before outputting data.
authored
104 x->is_data_locked = 0;
579e6de @residuum Made functions static where applicable.
authored
105 }
2a60bf9 @residuum No more warnings.
authored
106 return NULL;
579e6de @residuum Made functions static where applicable.
authored
107 }
108
109 static void execute_rest(t_rest *x) {
110 int rc;
111 pthread_t thread;
112 pthread_attr_t thread_attributes;
113
114 pthread_attr_init(&thread_attributes);
115 pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
116 rc = pthread_create(&thread, &thread_attributes, execute_rest_thread, (void *)x);
117 pthread_attr_destroy(&thread_attributes);
118 if (rc) {
119 error("Could not create thread with code %d", rc);
120 x->is_data_locked = 0;
121 }
122 }
123
f2ad3b5 @residuum Cookie authentication.
authored
124 static void *get_auth_token_thread(void *thread_args) {
125 t_rest *x = (t_rest *)thread_args;
126 CURL *curl_handle;
127 CURLcode result;
128 t_memory_struct out_content;
129 t_memory_struct out_header;
130 char *post_data;
131 size_t post_data_length;
132 char *header_line;
133 char *cookie_params;
e4c4ee1 @residuum Status outlet for [rest-json].
authored
134 long http_code;
135 t_atom auth_status_data[3];
f2ad3b5 @residuum Cookie authentication.
authored
136
137 curl_global_init(CURL_GLOBAL_ALL);
138 curl_handle = curl_easy_init();
139 if (curl_handle) {
140 memset(x->complete_url, 0x00, MAXPDSTRING);
141 post_data_length = strlen(x->username) + strlen(x->password) + 17;/*name=&password=*/
142 post_data = (char *)getbytes(sizeof(char) * post_data_length);
143 if (post_data == NULL) {
144 error("not enough memory");
145 } else {
146 strcpy(post_data, "name=");
147 strcat(post_data, x->username);
148 strcat(post_data, "&password=");
149 strcat(post_data, x->password);
150 }
151 strcpy(x->complete_url, x->base_url);
152 strcat(x->complete_url, x->login_path);
153 curl_easy_setopt(curl_handle, CURLOPT_URL, x->complete_url);
154 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
155 curl_easy_setopt(curl_handle, CURLOPT_POST, TRUE);
156 curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, post_data);
157 out_content.memory = getbytes(1);
158 out_content.size = 0;
159 out_header.memory = getbytes(1);
160 out_header.size = 0;
161 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_memory_callback);
162 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&out_content);
163 curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, write_memory_callback);
164 curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void *)&out_header);
165 result = curl_easy_perform(curl_handle);
166
e4c4ee1 @residuum Status outlet for [rest-json].
authored
167 /* output the status code at third outlet */
168 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
169 SETSYMBOL(&auth_status_data[0], gensym("cookie"));
170 if (http_code == 200 && result == CURLE_OK) {
171 SETSYMBOL(&auth_status_data[1], gensym("bang"));
172 outlet_list(x->status_info_outlet, &s_list, 2, &auth_status_data[0]);
173 } else {
174 SETFLOAT(&auth_status_data[1], (float)http_code);
175 SETFLOAT(&auth_status_data[2], (float)result);
176 outlet_list(x->status_info_outlet, &s_list, 3, &auth_status_data[0]);
177 }
178
f2ad3b5 @residuum Cookie authentication.
authored
179 if (result == CURLE_OK) {
180 if (out_header.memory) {
181 header_line = strtok(out_header.memory, "\n");
182 while (header_line != NULL) {
183 if (strncmp(header_line, "Set-Cookie:", 11) == 0) {
184 cookie_params = strtok(header_line, ": ");
185 /*remove "Set-Cookie:" */
186 cookie_params = strtok(NULL, "; ");
187 while (cookie_params != NULL) {
188 if (strlen(cookie_params) != 0) {
189 strcpy(x->auth_token, cookie_params);
190 break;
191 }
192 cookie_params = strtok(NULL, "; ");
193 }
194 break;
195 }
196 header_line = strtok(NULL, "\n");
197 }
198
e533743 @residuum Unlock object before outputting data.
authored
199 /* Free memory */
f2ad3b5 @residuum Cookie authentication.
authored
200 freebytes(out_header.memory, (out_header.size + 1) * sizeof(char));
201 }
202 if (out_content.memory) {
203 freebytes(out_content.memory, (out_content.size + 1) * sizeof(char));
204 }
205 }
206 freebytes(post_data, post_data_length * sizeof(char));
207 }
208 x->is_data_locked = 0;
2a60bf9 @residuum No more warnings.
authored
209 return NULL;
f2ad3b5 @residuum Cookie authentication.
authored
210 }
211
212 static void get_auth_token(t_rest *x) {
213 int rc;
214 pthread_t thread;
215 pthread_attr_t thread_attributes;
e533743 @residuum Unlock object before outputting data.
authored
216
f2ad3b5 @residuum Cookie authentication.
authored
217 pthread_attr_init(&thread_attributes);
218 pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
219 rc = pthread_create(&thread, &thread_attributes, get_auth_token_thread, (void *)x);
220 pthread_attr_destroy(&thread_attributes);
221 if (rc) {
222 error("Could not create thread with code %d", rc);
223 x->is_data_locked = 0;
224 }
225 }
226
227 static void set_url_parameters(t_rest *x, int argcount, t_atom *argvec) {
228 switch (argcount) {
229 case 1:
230 if (argvec[0].a_type != A_SYMBOL) {
231 error("Base URL cannot be set.");
232 } else {
233 atom_string(argvec, x->base_url, MAXPDSTRING);
234 }
235 memset(x->login_path, 0x00, MAXPDSTRING);
236 memset(x->username, 0x00, MAXPDSTRING);
237 memset(x->password, 0x00, MAXPDSTRING);
238 memset(x->auth_token, 0x00, MAXPDSTRING);
239 break;
240 case 4:
241 x->is_data_locked = 1;
242 if (argvec[0].a_type != A_SYMBOL) {
243 error("Base URL cannot be set.");
244 } else {
245 atom_string(argvec, x->base_url, MAXPDSTRING);
246 }
247 if (argvec[1].a_type != A_SYMBOL) {
248 error("Username cannot be set.");
249 } else {
250 atom_string(argvec + 1, x->login_path, MAXPDSTRING);
251 }
252 if (argvec[2].a_type != A_SYMBOL) {
253 error("Username cannot be set.");
254 } else {
255 atom_string(argvec + 2, x->username, MAXPDSTRING);
256 }
257 if (argvec[3].a_type != A_SYMBOL) {
258 error("Password cannot be set.");
259 } else {
260 atom_string(argvec + 3, x->password, MAXPDSTRING);
261 }
262 get_auth_token(x);
263 break;
264 case 0:
265 memset(x->base_url, 0x00, MAXPDSTRING);
266 memset(x->login_path, 0x00, MAXPDSTRING);
267 memset(x->username, 0x00, MAXPDSTRING);
268 memset(x->password, 0x00, MAXPDSTRING);
269 memset(x->auth_token, 0x00, MAXPDSTRING);
270 break;
271 default:
272 error("Wrong number of parameters.");
273 break;
274 }
275 }
276
5a9caf9 @eighthave update Makefile and arrange files to match the Library Template:
eighthave authored
277 void setup_rest0x2djson(void) {
4596bdf @residuum Renamed library to purest_json;
authored
278 rest_class = class_new(gensym("rest-json"), (t_newmethod)rest_new,
f2ad3b5 @residuum Cookie authentication.
authored
279 0, sizeof(t_rest), 0, A_GIMME, 0);
4596bdf @residuum Renamed library to purest_json;
authored
280 class_addmethod(rest_class, (t_method)rest_oauth, gensym("oauth"), A_GIMME, 0);
281 class_addmethod(rest_class, (t_method)rest_url, gensym("url"), A_GIMME, 0);
282 class_addmethod(rest_class, (t_method)rest_command, gensym("PUT"), A_GIMME, 0);
283 class_addmethod(rest_class, (t_method)rest_command, gensym("GET"), A_GIMME, 0);
284 class_addmethod(rest_class, (t_method)rest_command, gensym("DELETE"), A_GIMME, 0);
285 class_addmethod(rest_class, (t_method)rest_command, gensym("POST"), A_GIMME, 0);
631941d @residuum Setup methods in their respective files.
authored
286 }
287
4596bdf @residuum Renamed library to purest_json;
authored
288 void rest_command(t_rest *x, t_symbol *selector, int argcount, t_atom *argvec) {
84f05ed @residuum Removed unnecessary selector from CouchDB message.
authored
289 char *request_type;
5854491 @residuum rest-json is now working again.
authored
290 char path[MAXPDSTRING];
291 char parameters[MAXPDSTRING];
292 char *cleaned_parameters;
293 size_t memsize = 0;
8c29f73 @residuum Locking rest-json
authored
294 if(x->is_data_locked) {
295 post("rest-json object is performing request and locked");
296 } else {
5854491 @residuum rest-json is now working again.
authored
297 memset(x->request_type, 0x00, 7);
298 memset(x->parameters, 0x00, MAXPDSTRING);
299 memset(x->complete_url, 0x00, MAXPDSTRING);
8c29f73 @residuum Locking rest-json
authored
300 switch (argcount) {
301 case 0:
302 break;
303 default:
304 request_type = selector->s_name;
5854491 @residuum rest-json is now working again.
authored
305 atom_string(argvec, path, MAXPDSTRING);
8c29f73 @residuum Locking rest-json
authored
306 if (argcount > 1) {
5854491 @residuum rest-json is now working again.
authored
307 atom_string(argvec + 1, parameters, MAXPDSTRING);
8c29f73 @residuum Locking rest-json
authored
308 }
309 x->is_data_locked = 1;
5854491 @residuum rest-json is now working again.
authored
310 if (x->base_url != NULL) {
311 strcpy(x->complete_url, x->base_url);
312 }
8c29f73 @residuum Locking rest-json
authored
313 strcat(x->complete_url, path);
314 strcpy(x->request_type, request_type);
5854491 @residuum rest-json is now working again.
authored
315 if (parameters != NULL) {
316 cleaned_parameters = remove_backslashes(parameters, memsize);
317 strcpy(x->parameters, cleaned_parameters);
318 freebytes(cleaned_parameters, memsize);
319 }
8c29f73 @residuum Locking rest-json
authored
320 execute_rest(x);
321 break;
322 }
cd8c974 @residuum First commit.
authored
323 }
324 }
325
4596bdf @residuum Renamed library to purest_json;
authored
326 void rest_oauth(t_rest *x, t_symbol *selector, int argcount, t_atom *argvec) {
cd8c974 @residuum First commit.
authored
327 error("OAUTH not implemented yet.");
68efd0b @residuum Do not show unused parameter warning.
authored
328
329 (void) x;
330 (void) selector;
331 (void) argcount;
332 (void) argvec;
cd8c974 @residuum First commit.
authored
333 }
334
4596bdf @residuum Renamed library to purest_json;
authored
335 void rest_url(t_rest *x, t_symbol *selector, int argcount, t_atom *argvec) {
68efd0b @residuum Do not show unused parameter warning.
authored
336
337 (void) selector;
338
8c29f73 @residuum Locking rest-json
authored
339 if(x->is_data_locked) {
340 post("rest-json object is performing request and locked");
341 } else {
f2ad3b5 @residuum Cookie authentication.
authored
342 set_url_parameters(x, argcount, argvec);
cd8c974 @residuum First commit.
authored
343 }
344 }
345
4596bdf @residuum Renamed library to purest_json;
authored
346 void *rest_new(t_symbol *selector, int argcount, t_atom *argvec) {
347 t_rest *x = (t_rest *)pd_new(rest_class);
31bd302 @residuum Fixed threading issues, refactoring of methods
authored
348
68efd0b @residuum Do not show unused parameter warning.
authored
349 (void) selector;
350
f2ad3b5 @residuum Cookie authentication.
authored
351 set_url_parameters(x, argcount, argvec);
352
76dfd69 @residuum JSON decoding working.
authored
353 outlet_new(&x->x_ob, NULL);
7cfd381 @residuum Output JSON data as several lists, followed by a bang.
authored
354 x->done_outlet = outlet_new(&x->x_ob, &s_bang);
e4c4ee1 @residuum Status outlet for [rest-json].
authored
355 x->status_info_outlet = outlet_new(&x->x_ob, NULL);
91985ed @residuum Improved threading:
authored
356 x->is_data_locked = 0;
5854491 @residuum rest-json is now working again.
authored
357
cd8c974 @residuum First commit.
authored
358 return (void *)x;
359 }
Something went wrong with that request. Please try again.