Merged revisions 140169 via svnmerge from
[asterisk-bristuff.git] / res / res_http_post.c
blob2e4a20a1d44358afc05957d6c4ae85cdff7862ee
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*!
20 * \file
21 * \brief HTTP POST upload support for Asterisk HTTP server
23 * \author Terry Wilson <twilson@digium.com
25 * \ref AstHTTP - AMI over the http protocol
28 /*** MODULEINFO
29 <depend>gmime</depend>
30 ***/
33 #include "asterisk.h"
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 111213 $")
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <gmime/gmime.h>
41 #include "asterisk/linkedlists.h"
42 #include "asterisk/http.h"
43 #include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
44 #include "asterisk/tcptls.h"
45 #include "asterisk/manager.h"
46 #include "asterisk/cli.h"
47 #include "asterisk/module.h"
48 #include "asterisk/ast_version.h"
50 #define MAX_PREFIX 80
52 /* just a little structure to hold callback info for gmime */
53 struct mime_cbinfo {
54 int count;
55 const char *post_dir;
58 /* all valid URIs must be prepended by the string in prefix. */
59 static char prefix[MAX_PREFIX];
61 static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
63 char filename[PATH_MAX];
64 GMimeDataWrapper *content;
65 GMimeStream *stream;
66 int fd;
68 snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
70 ast_debug(1, "Posting raw data to %s\n", filename);
72 if ((fd = open(filename, O_CREAT | O_WRONLY, 0666)) == -1) {
73 ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
75 return;
78 stream = g_mime_stream_fs_new(fd);
80 content = g_mime_part_get_content_object(part);
81 g_mime_data_wrapper_write_to_stream(content, stream);
82 g_mime_stream_flush(stream);
84 g_object_unref(content);
85 g_object_unref(stream);
88 static GMimeMessage *parse_message(FILE *f)
90 GMimeMessage *message;
91 GMimeParser *parser;
92 GMimeStream *stream;
94 stream = g_mime_stream_file_new(f);
96 parser = g_mime_parser_new_with_stream(stream);
97 g_mime_parser_set_respect_content_length(parser, 1);
99 g_object_unref(stream);
101 message = g_mime_parser_construct_message(parser);
103 g_object_unref(parser);
105 return message;
108 static void process_message_callback(GMimeObject *part, gpointer user_data)
110 struct mime_cbinfo *cbinfo = user_data;
112 cbinfo->count++;
114 /* We strip off the headers before we get here, so should only see GMIME_IS_PART */
115 if (GMIME_IS_MESSAGE_PART(part)) {
116 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PART\n");
117 return;
118 } else if (GMIME_IS_MESSAGE_PARTIAL(part)) {
119 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MESSAGE_PARTIAL\n");
120 return;
121 } else if (GMIME_IS_MULTIPART(part)) {
122 GList *l;
124 ast_log(LOG_WARNING, "Got unexpected GMIME_IS_MULTIPART, trying to process subparts\n");
125 l = GMIME_MULTIPART(part)->subparts;
126 while (l) {
127 process_message_callback(l->data, cbinfo);
128 l = l->next;
130 } else if (GMIME_IS_PART(part)) {
131 const char *filename;
133 if (ast_strlen_zero(filename = g_mime_part_get_filename(GMIME_PART(part)))) {
134 ast_debug(1, "Skipping part with no filename\n");
135 return;
138 post_raw(GMIME_PART(part), cbinfo->post_dir, filename);
139 } else {
140 ast_log(LOG_ERROR, "Encountered unknown MIME part. This should never happen!\n");
144 static int process_message(GMimeMessage *message, const char *post_dir)
146 struct mime_cbinfo cbinfo = {
147 .count = 0,
148 .post_dir = post_dir,
151 g_mime_message_foreach_part(message, process_message_callback, &cbinfo);
153 return cbinfo.count;
156 static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
158 struct ast_variable *var;
159 unsigned long ident = 0;
160 char buf[4096];
161 FILE *f;
162 size_t res;
163 int content_len = 0;
164 struct ast_str *post_dir;
165 GMimeMessage *message;
166 int message_count = 0;
168 if (!urih) {
169 return ast_http_error((*status = 400),
170 (*title = ast_strdup("Missing URI handle")),
171 NULL, "There was an error parsing the request");
174 for (var = vars; var; var = var->next) {
175 if (strcasecmp(var->name, "mansession_id")) {
176 continue;
179 if (sscanf(var->value, "%lx", &ident) != 1) {
180 return ast_http_error((*status = 400),
181 (*title = ast_strdup("Bad Request")),
182 NULL, "The was an error parsing the request.");
185 if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
186 return ast_http_error((*status = 401),
187 (*title = ast_strdup("Unauthorized")),
188 NULL, "You are not authorized to make this request.");
191 break;
194 if (!var) {
195 return ast_http_error((*status = 401),
196 (*title = ast_strdup("Unauthorized")),
197 NULL, "You are not authorized to make this request.");
200 if (!(f = tmpfile())) {
201 ast_log(LOG_ERROR, "Could not create temp file.\n");
202 return NULL;
205 for (var = headers; var; var = var->next) {
206 fprintf(f, "%s: %s\r\n", var->name, var->value);
208 if (!strcasecmp(var->name, "Content-Length")) {
209 if ((sscanf(var->value, "%u", &content_len)) != 1) {
210 ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
211 fclose(f);
213 return NULL;
215 ast_debug(1, "Got a Content-Length of %d\n", content_len);
219 fprintf(f, "\r\n");
221 for (res = sizeof(buf); content_len; content_len -= res) {
222 if (content_len < res) {
223 res = content_len;
225 fread(buf, 1, res, ser->f);
226 fwrite(buf, 1, res, f);
229 if (fseek(f, SEEK_SET, 0)) {
230 ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
231 fclose(f);
233 return NULL;
236 post_dir = urih->data;
238 message = parse_message(f); /* Takes ownership and will close f */
240 if (!message) {
241 ast_log(LOG_ERROR, "Error parsing MIME data\n");
243 return ast_http_error((*status = 400),
244 (*title = ast_strdup("Bad Request")),
245 NULL, "The was an error parsing the request.");
248 if (!(message_count = process_message(message, post_dir->str))) {
249 ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
250 g_object_unref(message);
251 return ast_http_error((*status = 400),
252 (*title = ast_strdup("Bad Request")),
253 NULL, "The was an error parsing the request.");
256 g_object_unref(message);
258 return ast_http_error((*status = 200),
259 (*title = ast_strdup("OK")),
260 NULL, "File successfully uploaded.");
263 static int __ast_http_post_load(int reload)
265 struct ast_config *cfg;
266 struct ast_variable *v;
267 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
269 if ((cfg = ast_config_load2("http.conf", "http", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
270 return 0;
273 if (reload) {
274 ast_http_uri_unlink_all_with_key(__FILE__);
277 if (cfg) {
278 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
279 if (!strcasecmp(v->name, "prefix")) {
280 ast_copy_string(prefix, v->value, sizeof(prefix));
281 if (prefix[strlen(prefix)] == '/') {
282 prefix[strlen(prefix)] = '\0';
287 for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
288 struct ast_http_uri *urih;
289 struct ast_str *ds;
291 if (!(urih = ast_calloc(sizeof(*urih), 1))) {
292 ast_config_destroy(cfg);
293 return -1;
296 if (!(ds = ast_str_create(32))) {
297 ast_free(urih);
298 ast_config_destroy(cfg);
299 return -1;
302 urih->description = ast_strdup("HTTP POST mapping");
303 urih->uri = ast_strdup(v->name);
304 ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
305 urih->data = ds;
306 urih->has_subtree = 0;
307 urih->supports_get = 0;
308 urih->supports_post = 1;
309 urih->callback = http_post_callback;
310 urih->key = __FILE__;
311 urih->mallocd = urih->dmallocd = 1;
313 ast_http_uri_link(urih);
316 ast_config_destroy(cfg);
318 return 0;
321 static int unload_module(void)
323 ast_http_uri_unlink_all_with_key(__FILE__);
325 return 0;
328 static int reload(void)
330 __ast_http_post_load(1);
332 return AST_MODULE_LOAD_SUCCESS;
335 static int load_module(void)
337 g_mime_init(0);
339 __ast_http_post_load(0);
341 return AST_MODULE_LOAD_SUCCESS;
344 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP POST support",
345 .load = load_module,
346 .unload = unload_module,
347 .reload = reload,