1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 * vim: ts=4 sw=4 noet ai cindent syntax=c
4 * Conky, a system monitor, based on torsmo
6 * Please see COPYING for details
8 * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
10 * All rights reserved.
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "ccurl_thread.h"
29 #include "text_object.h"
35 #include <curl/curl.h>
36 #include <curl/types.h>
37 #include <curl/easy.h>
40 * The following code is the conky curl thread lib, which can be re-used to
41 * create any curl-based object (see weather and rss). Below is an
42 * implementation of a curl-only object ($curl) which can also be used as an
45 typedef struct _ccurl_memory_t {
50 typedef struct _ccurl_headers_t {
55 /* finds a location based on uri in the list provided */
56 ccurl_location_t *ccurl_find_location(ccurl_location_t **locations_head, char *uri)
58 ccurl_location_t *tail = *locations_head;
59 ccurl_location_t *new = 0;
62 strcmp(tail->uri, uri) == EQUAL) {
67 if (!tail) { /* new location!!!!!!! */
68 DBGP("new curl location: '%s'", uri);
69 new = malloc(sizeof(ccurl_location_t));
70 memset(new, 0, sizeof(ccurl_location_t));
71 new->uri = strndup(uri, text_buffer_size);
72 tail = *locations_head;
73 while (tail && tail->next) {
77 /* omg the first one!!!!!!! */
78 *locations_head = new;
86 /* iterates over the list provided, frees stuff (list item, uri, result) */
87 void ccurl_free_locations(ccurl_location_t **locations_head)
89 ccurl_location_t *tail = *locations_head;
90 ccurl_location_t *last = 0;
93 if (tail->uri) free(tail->uri);
94 if (tail->result) free(tail->result);
95 if (tail->last_modified) free(tail->last_modified);
96 if (tail->etag) free(tail->etag);
104 /* callback used by curl for parsing the header data */
105 size_t ccurl_parse_header_callback(void *ptr, size_t size, size_t nmemb, void *data)
107 size_t realsize = size * nmemb;
108 const char *value = (const char*)ptr;
110 ccurl_headers_t *headers = (ccurl_headers_t*)data;
112 if (strncmp(value, "Last-Modified: ", 15) == EQUAL) {
113 headers->last_modified = strndup(value + 15, realsize - 15);
114 if ((end = strchr(headers->last_modified, '\r')) != NULL) {
117 } else if (strncmp(value,"ETag: ", 6) == EQUAL) {
118 headers->etag = strndup(value + 6, realsize - 6);
119 if ((end = strchr(headers->etag, '\r')) != NULL) {
127 /* callback used by curl for writing the received data */
128 size_t ccurl_write_memory_callback(void *ptr, size_t size, size_t nmemb, void *data)
130 size_t realsize = size * nmemb;
131 ccurl_memory_t *mem = (ccurl_memory_t*)data;
133 mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
135 memcpy(&(mem->memory[mem->size]), ptr, realsize);
136 mem->size += realsize;
137 mem->memory[mem->size] = 0;
143 /* fetch our datums */
144 void ccurl_fetch_data(ccurl_location_t *curloc)
148 struct curl_slist *headers = NULL;
151 ccurl_memory_t chunk;
152 ccurl_headers_t response_headers;
156 memset(&response_headers, 0, sizeof(ccurl_headers_t));
158 curl = curl_easy_init();
160 DBGP("reading curl data from '%s'", curloc->uri);
161 curl_easy_setopt(curl, CURLOPT_URL, curloc->uri);
162 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
163 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ccurl_parse_header_callback);
164 curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &response_headers);
165 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ccurl_write_memory_callback);
166 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
167 curl_easy_setopt(curl, CURLOPT_USERAGENT, "conky-curl/1.1");
168 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
169 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1000);
170 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60);
172 if (curloc->last_modified) {
173 const char *header = "If-Modified-Since: ";
174 int len = strlen(header) + strlen(curloc->last_modified) + 1;
175 char *str = (char*) malloc(len);
176 snprintf(str, len, "%s%s", header, curloc->last_modified);
177 headers = curl_slist_append(headers, str);
181 const char *header = "If-None-Match: ";
182 int len = strlen(header) + strlen(curloc->etag) + 1;
183 char *str = (char*) malloc(len);
184 snprintf(str, len, "%s%s", header, curloc->etag);
185 headers = curl_slist_append(headers, str);
189 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
192 res = curl_easy_perform(curl);
193 if (res == CURLE_OK) {
194 long http_status_code;
196 if (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,
197 &http_status_code) == CURLE_OK) {
198 switch (http_status_code) {
200 timed_thread_lock(curloc->p_timed_thread);
201 if(curloc->last_modified) {
202 free(curloc->last_modified);
203 curloc->last_modified = NULL;
209 if (response_headers.last_modified) {
210 curloc->last_modified =
211 strdup(response_headers.last_modified);
213 if (response_headers.etag) {
214 curloc->etag = strdup(response_headers.etag);
216 (*curloc->process_function)(curloc->result, chunk.memory);
217 timed_thread_unlock(curloc->p_timed_thread);
222 NORM_ERR("curl: no data from server, got HTTP status %ld",
227 NORM_ERR("curl: no HTTP status from server");
231 NORM_ERR("curl: could not retrieve data from server");
234 if(response_headers.last_modified) {
235 free(response_headers.last_modified);
237 if(response_headers.etag) {
238 free(response_headers.etag);
240 curl_slist_free_all(headers);
241 curl_easy_cleanup(curl);
245 void *ccurl_thread(void *) __attribute__((noreturn));
247 void ccurl_init_thread(ccurl_location_t *curloc, int interval)
250 assert(curloc->result);
252 curloc->p_timed_thread =
253 timed_thread_create(&ccurl_thread,
254 (void *)curloc, interval * 1000000);
256 if (!curloc->p_timed_thread) {
257 NORM_ERR("curl thread: error creating timed thread");
259 timed_thread_register(curloc->p_timed_thread,
260 &curloc->p_timed_thread);
261 if (timed_thread_run(curloc->p_timed_thread)) {
262 NORM_ERR("curl thread: error running timed thread");
266 void *ccurl_thread(void *arg)
268 ccurl_location_t *curloc = (ccurl_location_t*)arg;
271 ccurl_fetch_data(curloc);
272 if (timed_thread_test(curloc->p_timed_thread, 0)) {
273 timed_thread_exit(curloc->p_timed_thread);
281 * This is where the $curl section begins.
289 /* internal location pointer for use by $curl, no touchy */
290 static ccurl_location_t *ccurl_locations_head = 0;
292 /* used to free data used by $curl */
293 void ccurl_free_info(void)
295 ccurl_free_locations(&ccurl_locations_head);
298 /* straight copy, used by $curl */
299 static void ccurl_parse_data(void *result, const char *data)
301 strncpy(result, data, max_user_text);
304 /* prints result data to text buffer, used by $curl */
305 void ccurl_process_info(char *p, int p_max_size, char *uri, int interval)
307 ccurl_location_t *curloc = ccurl_find_location(&ccurl_locations_head, uri);
308 if (!curloc->p_timed_thread) {
309 curloc->result = malloc(max_user_text);
310 memset(curloc->result, 0, max_user_text);
311 curloc->process_function = &ccurl_parse_data;
312 ccurl_init_thread(curloc, interval);
313 if (!curloc->p_timed_thread) {
314 NORM_ERR("error setting up curl thread");
318 timed_thread_lock(curloc->p_timed_thread);
319 strncpy(p, curloc->result, p_max_size);
320 timed_thread_unlock(curloc->p_timed_thread);
323 void curl_parse_arg(struct text_object *obj, const char *arg)
326 struct curl_data *cd;
329 cd = malloc(sizeof(struct curl_data));
330 memset(cd, 0, sizeof(struct curl_data));
332 argc = sscanf(arg, "%127s %f", cd->uri, &interval);
335 NORM_ERR("wrong number of arguments for $curl");
338 cd->interval = interval > 0 ? interval * 60 : 15*60;
339 obj->data.opaque = cd;
342 void curl_print(struct text_object *obj, char *p, int p_max_size)
344 struct curl_data *cd = obj->data.opaque;
346 if (!cd || !cd->uri) {
347 NORM_ERR("error processing Curl data");
350 ccurl_process_info(p, p_max_size, cd->uri, cd->interval);
353 void curl_obj_free(struct text_object *obj)
355 if (obj->data.opaque) {
356 free(obj->data.opaque);
357 obj->data.opaque = NULL;