5cf4b167df1b66526c1aec46a0cc9033cad8bfb8
[asterisk/asterisk.git] / apps / app_curl.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C)  2004 - 2005, Tilghman Lesher
5  *
6  * Tilghman Lesher <curl-20050919@the-tilghman.com>
7  * and Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
8  *
9  * app_curl.c is distributed with no restrictions on usage or
10  * redistribution.
11  *
12  * See http://www.asterisk.org for more information about
13  * the Asterisk project. Please do not directly contact
14  * any of the maintainers of this project for assistance;
15  * the project provides a web site, mailing lists and IRC
16  * channels for your use.
17  *
18  */
19
20 /*! \file
21  * \brief Curl - App to load a URL
22  * 
23  * \ingroup applications
24  */
25  
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <curl/curl.h>
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/lock.h"
36 #include "asterisk/file.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/cli.h"
41 #include "asterisk/options.h"
42 #include "asterisk/module.h"
43
44 static char *tdesc = "Load external URL";
45
46 static char *app = "Curl";
47
48 static char *synopsis = "Load an external URL";
49
50 static char *descrip = 
51 "  Curl(URL[|postdata]): This application will request the specified URL.\n"
52 "It is mainly used for signalling external applications of an event.\n"
53 "Parameters:\n"
54 "  URL      - This is the external URL to request.\n"
55 "  postdata - This information will be treated as POST data.\n"
56 "This application will set the following variable:\n"
57 "  CURL - This variable will contain the resulting page.\n"
58 "This application has been deprecated in favor of the CURL function.\n";
59
60 STANDARD_LOCAL_USER;
61
62 LOCAL_USER_DECL;
63
64 struct MemoryStruct {
65         char *memory;
66         size_t size;
67 };
68
69 static void *myrealloc(void *ptr, size_t size)
70 {
71         /* There might be a realloc() out there that doesn't like reallocing
72            NULL pointers, so we take care of it here */
73         if (ptr)
74                 return realloc(ptr, size);
75         else
76                 return malloc(size);
77 }
78
79 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
80 {
81         register int realsize = size * nmemb;
82         struct MemoryStruct *mem = (struct MemoryStruct *)data;
83
84         mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
85         if (mem->memory) {
86                 memcpy(&(mem->memory[mem->size]), ptr, realsize);
87                 mem->size += realsize;
88                 mem->memory[mem->size] = 0;
89         }
90         return realsize;
91 }
92
93 static int curl_internal(struct MemoryStruct *chunk, char *url, char *post)
94 {
95         CURL *curl;
96
97         curl_global_init(CURL_GLOBAL_ALL);
98         curl = curl_easy_init();
99
100         if (!curl) {
101                 return -1;
102         }
103
104         curl_easy_setopt(curl, CURLOPT_URL, url);
105         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
106         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
107         curl_easy_setopt(curl, CURLOPT_USERAGENT, "asterisk-libcurl-agent/1.0");
108
109         if (post) {
110                 curl_easy_setopt(curl, CURLOPT_POST, 1);
111                 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
112         }
113
114         curl_easy_perform(curl);
115         curl_easy_cleanup(curl);
116         return 0;
117 }
118
119 static int curl_exec(struct ast_channel *chan, void *data)
120 {
121         int res = 0;
122         struct localuser *u;
123         char *info, *post_data=NULL, *url;
124         struct MemoryStruct chunk = { NULL, 0 };
125         static int dep_warning = 0;
126         
127         if (!dep_warning) {
128                 ast_log(LOG_WARNING, "The application Curl is deprecated.  Please use the CURL() function instead.\n");
129                 dep_warning = 1;
130         }
131
132         if (ast_strlen_zero(data)) {
133                 ast_log(LOG_WARNING, "Curl requires an argument (URL)\n");
134                 return -1;
135         }
136         
137         LOCAL_USER_ADD(u);
138         
139         if ((info = ast_strdupa(data))) {
140                 url = strsep(&info, "|");
141                 post_data = info;
142         } else {
143                 ast_log(LOG_ERROR, "Out of memory\n");
144                 LOCAL_USER_REMOVE(u);
145                 return -1;
146         }
147
148         if (! curl_internal(&chunk, url, post_data)) {
149                 if (chunk.memory) {
150                         chunk.memory[chunk.size] = '\0';
151                         if (chunk.memory[chunk.size - 1] == 10)
152                                 chunk.memory[chunk.size - 1] = '\0';
153
154                         pbx_builtin_setvar_helper(chan, "CURL", chunk.memory);
155
156                         free(chunk.memory);
157                 }
158         } else {
159                 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
160                 res = -1;
161         }
162
163         LOCAL_USER_REMOVE(u);
164         return res;
165 }
166
167 static char *acf_curl_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
168 {
169         struct localuser *u;
170         char *info, *post_data=NULL, *url;
171         struct MemoryStruct chunk = { NULL, 0 };
172
173         *buf = '\0';
174         
175         if (ast_strlen_zero(data)) {
176                 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
177                 return buf;
178         }
179
180         LOCAL_USER_ACF_ADD(u);
181
182         info = ast_strdupa(data);
183         if (!info) {
184                 ast_log(LOG_ERROR, "Out of memory\n");
185                 LOCAL_USER_REMOVE(u);
186                 return buf;
187         }
188         
189         url = strsep(&info, "|");
190         post_data = info;
191         
192         if (! curl_internal(&chunk, url, post_data)) {
193                 if (chunk.memory) {
194                         chunk.memory[chunk.size] = '\0';
195                         if (chunk.memory[chunk.size - 1] == 10)
196                                 chunk.memory[chunk.size - 1] = '\0';
197
198                         ast_copy_string(buf, chunk.memory, len);
199                         free(chunk.memory);
200                 }
201         } else {
202                 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
203         }
204
205         LOCAL_USER_REMOVE(u);
206         return buf;
207 }
208
209 struct ast_custom_function acf_curl = {
210         .name = "CURL",
211         .synopsis = "Retrieves the contents of a URL",
212         .syntax = "CURL(url[|post-data])",
213         .desc =
214         "  url       - URL to retrieve\n"
215         "  post-data - Optional data to send as a POST (GET is default action)\n",
216         .read = acf_curl_exec,
217 };
218
219 int unload_module(void)
220 {
221         int res;
222
223         res = ast_custom_function_unregister(&acf_curl);
224         res |= ast_unregister_application(app);
225
226         STANDARD_HANGUP_LOCALUSERS;
227         
228         return res;
229 }
230
231 int load_module(void)
232 {
233         int res;
234
235         res = ast_custom_function_register(&acf_curl);
236         res |= ast_register_application(app, curl_exec, synopsis, descrip);
237
238         return res;
239 }
240
241 char *description(void)
242 {
243         return tdesc;
244 }
245
246 int usecount(void)
247 {
248         int res;
249         STANDARD_USECOUNT(res);
250         return res;
251 }
252
253 char *key()
254 {
255         return ASTERISK_GPL_KEY;
256 }