/* * webhttpd.c * * HTTP Control interface for motion. * * Specs : http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpAPI * * Copyright 2004-2005 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU Public License Version 2 * See also the file 'COPYING'. * */ #include "webhttpd.h" /* already includes motion.h */ #include #include #include #include #include /* Timeout in seconds, used for read and write */ const int NONBLOCK_TIMEOUT = 1; pthread_mutex_t httpd_mutex; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions int warningkill; static const char *ini_template = "\n" "\n" "Motion "VERSION"\n" "\n"; static const char *set_template = "\n" "\n" "\n" "Motion "VERSION"\n" "\n" "\n" "\n"; static const char *end_template = "\n" "\n"; static const char *ok_response = "HTTP/1.1 200 OK\r\n" "Server: Motion-httpd/"VERSION"\r\n" "Connection: close\r\n" "Max-Age: 0\r\n" "Expires: 0\r\n" "Cache-Control: no-cache\r\n" "Cache-Control: private\r\n" "Pragma: no-cache\r\n" "Content-type: text/html\r\n\r\n"; static const char *ok_response_raw = "HTTP/1.1 200 OK\r\n" "Server: Motion-httpd/"VERSION"\r\n" "Connection: close\r\n" "Max-Age: 0\r\n" "Expires: 0\r\n" "Cache-Control: no-cache\r\n" "Cache-Control: private\r\n" "Pragma: no-cache\r\n" "Content-type: text/plain\r\n\r\n"; static const char *bad_request_response = "HTTP/1.0 400 Bad Request\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Bad Request

\n" "

The server did not understand your request.

\n" "\n" "\n"; static const char *bad_request_response_raw = "HTTP/1.0 400 Bad Request\r\n" "Content-type: text/plain\r\n\r\n" "Bad Request"; static const char *not_found_response_template = "HTTP/1.0 404 Not Found\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Not Found

\n" "

The requested URL was not found on the server.

\n" "\n" "\n"; static const char *not_found_response_template_raw = "HTTP/1.0 404 Not Found\r\n" "Content-type: text/plain\r\n\r\n" "Not Found"; static const char *not_found_response_valid = "HTTP/1.0 404 Not Valid\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Not Valid

\n" "

The requested URL is not valid.

\n" "\n" "\n"; static const char *not_found_response_valid_raw = "HTTP/1.0 404 Not Valid\r\n" "Content-type: text/plain\r\n\r\n" "The requested URL is not valid."; static const char *not_valid_syntax = "HTTP/1.0 404 Not Valid Syntax\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Not Valid Syntax

\n" "\n" "\n"; static const char *not_valid_syntax_raw = "HTTP/1.0 404 Not Valid Syntax\r\n" "Content-type: text/plain\r\n\r\n" "Not Valid Syntax\n"; static const char *not_track = "HTTP/1.0 200 OK\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Tracking Not Enabled

\n"; static const char *not_track_raw = "HTTP/1.0 200 OK\r\n" "Content-type: text/plain\r\n\r\n" "Tracking Not Enabled"; static const char *track_error = "HTTP/1.0 200 OK\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Track Error

\n"; static const char *track_error_raw = "HTTP/1.0 200 OK\r\n" "Content-type: text/plain\r\n\r\n" "Track Error"; static const char *error_value = "HTTP/1.0 200 OK\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Value Error

\n"; static const char *error_value_raw = "HTTP/1.0 200 OK\r\n" "Content-type: text/plain\r\n\r\n" "Value Error"; static const char *not_found_response_valid_command = "HTTP/1.0 404 Not Valid Command\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Not Valid Command

\n" "

The requested URL is not valid Command.

\n" "\n" "\n"; static const char *not_found_response_valid_command_raw = "HTTP/1.0 404 Not Valid Command\r\n" "Content-type: text/plain\n\n" "Not Valid Command\n"; static const char *bad_method_response_template = "HTTP/1.0 501 Method Not Implemented\r\n" "Content-type: text/html\r\n\r\n" "\n" "\n" "\n" "

Method Not Implemented

\n" "

The method is not implemented by this server.

\n" "\n" "\n"; static const char *bad_method_response_template_raw = "HTTP/1.0 501 Method Not Implemented\r\n" "Content-type: text/plain\r\n\r\n" "Method Not Implemented\n"; static const char *request_auth_response_template= "HTTP/1.0 401 Authorization Required\r\n" "WWW-Authenticate: Basic realm=\"Motion Security Access\"\r\n"; /** * write_nonblock */ static ssize_t write_nonblock(int fd, const void *buf, size_t size) { ssize_t nwrite = -1; struct timeval tm; fd_set fds; tm.tv_sec = NONBLOCK_TIMEOUT; tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, NULL, &fds, NULL, &tm) > 0) { if (FD_ISSET(fd, &fds)) { if ((nwrite = write(fd , buf, size)) < 0) { if (errno != EWOULDBLOCK) return -1; } } } return nwrite; } /** * read_nonblock */ static ssize_t read_nonblock(int fd ,void *buf, ssize_t size) { ssize_t nread = -1; struct timeval tm; fd_set fds; tm.tv_sec = NONBLOCK_TIMEOUT; /* Timeout in seconds */ tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, &fds, NULL, NULL, &tm) > 0) { if (FD_ISSET(fd, &fds)) { if ((nread = read(fd , buf, size)) < 0) { if (errno != EWOULDBLOCK) return -1; } } } return nread; } /** * send_template_ini_client */ static void send_template_ini_client(int client_socket, const char *template) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, ok_response, strlen(ok_response)); nwrite += write_nonblock(client_socket, template, strlen(template)); if (nwrite != (ssize_t)(strlen(ok_response) + strlen(template))) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template_ini_client_raw */ static void send_template_ini_client_raw(int client_socket) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, ok_response_raw, strlen(ok_response_raw)); if (nwrite != (ssize_t)strlen(ok_response_raw)) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template */ static void send_template(int client_socket, char *res) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, res, strlen(res)); if (nwrite != (ssize_t)strlen(res)) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template_raw */ static void send_template_raw(int client_socket, char *res) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, res, strlen(res)); if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * send_template_end_client */ static void send_template_end_client(int client_socket) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, end_template, strlen(end_template)); if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * response_client */ static void response_client(int client_socket, const char *template, char *back) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, template, strlen(template)); if (back != NULL) { send_template(client_socket, back); send_template_end_client(client_socket); } if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * replace */ static char *replace(const char *str, const char *old, const char *new) { char *ret, *r; const char *p, *q; size_t oldlen = strlen(old); size_t count, retlen, newlen = strlen(new); if (oldlen != newlen) { for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) count++; /* this is undefined if p - str > PTRDIFF_MAX */ retlen = p - str + strlen(p) + count * (newlen - oldlen); } else retlen = strlen(str); ret = mymalloc(retlen + 1); for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) { /* this is undefined if q - p > PTRDIFF_MAX */ ptrdiff_t l = q - p; memcpy(r, p, l); r += l; memcpy(r, new, newlen); r += newlen; } strcpy(r, p); return ret; } /** * url_decode * This function decode the values from GET request following the http RFC. * * Returns nothing. */ static void url_decode(char *urlencoded, size_t length) { char *data = urlencoded; char *urldecoded = urlencoded; while (length > 0) { if (*data == '%') { char c[3]; int i; data++; length--; c[0] = *data++; length--; c[1] = *data; c[2] = 0; warningkill = sscanf(c, "%x", &i); if (i < 128) { *urldecoded++ = (char)i; } else { *urldecoded++ = '%'; *urldecoded++ = c[0]; *urldecoded++ = c[1]; } } else if (*data == '<' || *data == '+' || *data == '>') { *urldecoded++ = ' '; } else { *urldecoded++ = *data; } data++; length--; } *urldecoded = '\0'; } /** * config * Manages/parses the config action for motion ( set , get , write , list ). * * Returns 1 to exit from function. */ static unsigned int config(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char question='\0'; char command[256] = {'\0'}; unsigned int i; struct context **cnt = userdata; warningkill = sscanf(pointer, "%255[a-z]%c", command , &question); if (!strcmp(command, "list")) { pointer = pointer + 4; length_uri = length_uri - 4; if (length_uri == 0) { const char *value = NULL; char *retval = NULL; /*call list*/ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s\n
    ", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); for (i=0; config_params[i].param_name != NULL; i++) { if ((thread != 0) && (config_params[i].main_thread)) continue; value = config_params[i].print(cnt, NULL, i, thread); if (value == NULL) { retval = NULL; /* Only get the thread value for main thread */ if (thread == 0) config_params[i].print(cnt, &retval, i, thread); /* thread value*/ if (retval) { if (!strcmp(retval,"")) { free(retval); retval = mystrdup("No threads"); } else { char *temp = retval; size_t retval_miss = 0; size_t retval_len = strlen(retval); unsigned int ind = 0; char thread_strings[1024] = {'\0'}; while (retval_miss != retval_len) { while (*temp != '\n') { thread_strings[ind++] = *temp; retval_miss++; temp++; } temp++; thread_strings[ind++] = '<'; thread_strings[ind++] = 'b'; thread_strings[ind++] = 'r'; thread_strings[ind++] = '>'; retval_miss++; } free(retval); retval = NULL; retval = mystrdup(thread_strings); } sprintf(res, "
  • %s = %s
  • \n", thread, config_params[i].param_name, config_params[i].param_name, retval); free(retval); } else if (thread != 0) { /* get the value from main thread for the rest of threads */ value = config_params[i].print(cnt, NULL, i, 0); sprintf(res, "
  • %s = %s
  • \n", thread, config_params[i].param_name, config_params[i].param_name, value ? value : "(not defined)"); } else { sprintf(res, "
  • %s = %s
  • \n", thread, config_params[i].param_name, config_params[i].param_name, "(not defined)"); } } else { sprintf(res, "
  • %s = %s
  • \n", thread, config_params[i].param_name, config_params[i].param_name, value); } send_template(client_socket, res); } sprintf(res, "
<– back", thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); for (i=0; config_params[i].param_name != NULL; i++) { value = config_params[i].print(cnt, NULL, i, thread); if (value == NULL) value = config_params[i].print(cnt, NULL, i, 0); sprintf(res, "%s = %s\n", config_params[i].param_name, value); send_template_raw(client_socket, res); } } } else { /*error*/ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "set")) { /* set?param_name=value */ pointer = pointer + 3; length_uri = length_uri - 3; if ((length_uri != 0) && (question == '?')) { pointer++; length_uri--; warningkill = sscanf(pointer,"%255[-0-9a-z_]%c", command, &question); /*check command , question == '=' length_uri too*/ if ((question == '=') && (command[0] != '\0')) { length_uri = length_uri - strlen(command) - 1; pointer = pointer + strlen(command) + 1; /* check if command exists and type of command and not end of URI */ i=0; while (config_params[i].param_name != NULL) { if ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } if (!strcasecmp(command, config_params[i].param_name)) break; i++; } if (config_params[i].param_name) { if (length_uri > 0) { char Value[1024] = {'\0'}; warningkill = sscanf(pointer,"%1023s", Value); length_uri = length_uri - strlen(Value); if ((length_uri == 0) && (strlen(Value) > 0)) { /* FIXME need to assure that is a valid value */ url_decode(Value, strlen(Value)); conf_cmdparse(cnt + thread, config_params[i].param_name, Value); if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back" "

\nCamera %d%s%s\n" "
  • %s = %s" "
Done", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, config_params[i].param_name, config_params[i].param_name, Value); send_template_ini_client(client_socket, ini_template); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, Value); send_template_raw(client_socket, res); } } else { /*error*/ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); } } else { char *type = NULL; type = mystrdup(config_type(&config_params[i])); if (!strcmp(type, "string")) { char *value = NULL; conf_cmdparse(cnt+thread, config_params[i].param_name, value); free(type); type = mystrdup("(null)"); } else if (!strcmp(type, "int")) { free(type); type = mystrdup("0"); conf_cmdparse(cnt+thread, config_params[i].param_name, type); } else if (!strcmp(type, "bool")) { free(type); type = mystrdup("off"); conf_cmdparse(cnt+thread, config_params[i].param_name, type); } else { free(type); type = mystrdup("unknown"); } if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

\n" "Camera %d%s%s\n
Done", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, config_params[i].param_name, config_params[i].param_name, type); send_template_ini_client(client_socket, ini_template); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, type); send_template_raw(client_socket, res); } free(type); } } else { /*error*/ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { /* Show param_name dialogue only for html output */ if ((cnt[0]->conf.webcontrol_html_output) && (command[0] != '\0') && (((length_uri = length_uri - strlen(command)) == 0))) { i=0; while (config_params[i].param_name != NULL) { if ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } if (!strcasecmp(command, config_params[i].param_name)) break; i++; } /* param_name exists */ if (config_params[i].param_name) { const char *value = NULL; char *text_help = NULL; char *sharp = NULL; value = config_params[i].print(cnt, NULL, i, thread); sharp = strstr(config_params[i].param_help, "#\n\n#"); if (sharp == NULL) sharp = strstr(config_params[i].param_help, "#"); sharp++; text_help = replace(sharp, "\n#", "
"); send_template_ini_client(client_socket, ini_template); if (!strcmp ("bool", config_type(&config_params[i]))) { char option[80] = {'\0'}; if ((value == NULL) && (thread != 0)) value = config_params[i].print(cnt, NULL, i, 0); if (!strcmp ("on", value)) sprintf(option, "\n" "\n"); else sprintf(option, "\n" "\n"); sprintf(res, "<– back

\n" "Camera %d%s%s\n" "
\n" "%s \n" "    " "[help]" "
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, config_params[i].param_name, option, TWIKI_URL, config_params[i].param_name, text_help); } else { if (value == NULL) { if (thread != 0) /* get the value from main thread for the rest of threads */ value = config_params[i].print(cnt, NULL, i, 0); if (value == NULL) value = ""; } sprintf(res, "<– back

\n" "Camera %d%s%s\n
\n" "%s \n" "\n" "    " "[help]" "
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, config_params[i].param_name, value, TWIKI_URL, config_params[i].param_name, text_help); } send_template(client_socket, res); send_template_end_client(client_socket); free(text_help); } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } } else if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, set_template); sprintf(res, "<– back

\nCamera %d%s%s\n" "
\n\n
\n" "
\n" "\n" "\n" "
\n"); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "set needs param_name=value\n"); send_template_raw(client_socket, res); } } else { /*error*/ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "get")) { /* get?query=param_name */ pointer = pointer + 3; length_uri = length_uri - 3; if ((length_uri > 7) && (question == '?')) { /* 8 -> query=param_name FIXME minimum length param_name */ pointer++; length_uri--; warningkill = sscanf(pointer,"%255[-0-9a-z]%c", command, &question); if ((question == '=') && (!strcmp(command, "query"))) { pointer = pointer + 6; length_uri = length_uri - 6; warningkill = sscanf(pointer, "%255[-0-9a-z_]", command); /*check if command exist, length_uri too*/ length_uri = length_uri - strlen(command); if (length_uri == 0) { const char *value = NULL; i = 0; while (config_params[i].param_name != NULL) { if ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } if (!strcasecmp(command, config_params[i].param_name)) break; i++; } /* * FIXME bool values or commented values maybe that should be * solved with config_type */ if (config_params[i].param_name) { const char *type = NULL; type = config_type(&config_params[i]); if (!strcmp(type, "unknown")) { /* error doesn't exists this param_name */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); return 1; } else { char *text_help = NULL; char *sharp = NULL; value = config_params[i].print(cnt, NULL, i, thread); sharp = strstr(config_params[i].param_help, "#\n\n#"); if (sharp == NULL) sharp = strstr(config_params[i].param_help, "#"); sharp++; text_help = replace(sharp, "\n#", "
"); if (value == NULL) value = config_params[i].print(cnt, NULL, i, 0); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, value, TWIKI_URL, config_params[i].param_name, text_help); send_template(client_socket, res); send_template_end_client(client_socket); free(text_help); } else { send_template_ini_client_raw(client_socket); sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, value); send_template_raw(client_socket, res); } } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } } else if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s
\n" "
\n" "\n" "\n" "
\n"); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "get needs param_name\n"); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); } } else if (!strcmp(command, "write")) { pointer = pointer + 5; length_uri = length_uri - 5; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Are you sure? Yes\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { conf_print(cnt); send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d write\nDone\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "writeyes")) { pointer = pointer + 8; length_uri = length_uri - 8; if (length_uri == 0) { conf_print(cnt); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s write done !\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d write\nDone\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) { response_client(client_socket, not_found_response_valid_command, NULL); } else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } return 1; } /** * action * manages/parses the actions for motion ( makemovie , snapshot , restart , quit ). * * Returns * 0 for restart & quit * 1 for makemovie & snaphost */ static unsigned int action(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { /* parse action commands */ char command[256] = {'\0'}; struct context **cnt = userdata; unsigned int i = 0; warningkill = sscanf(pointer, "%255[a-z]" , command); if (!strcmp(command, "makemovie")) { pointer = pointer + 9; length_uri = length_uri - 9; if (length_uri == 0) { /* call makemovie */ if (thread == 0) { while (cnt[++i]) cnt[i]->makemovie = 1; } else { cnt[thread]->makemovie = 1; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "makemovie for thread %u done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "makemovie for thread %u\nDone\n", thread); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "snapshot")) { pointer = pointer + 8; length_uri = length_uri - 8; if (length_uri == 0) { /* call snapshot */ if (thread == 0) { while (cnt[++i]) cnt[i]->snapshot = 1; } else { cnt[thread]->snapshot = 1; } cnt[thread]->snapshot = 1; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "snapshot for thread %u done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "snapshot for thread %u\nDone\n", thread); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "restart")) { pointer = pointer + 7; length_uri = length_uri - 7; if (length_uri == 0) { /* call restart */ if (thread == 0) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd is going to restart"); kill(getpid(),SIGHUP); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "restart in progress ... bye
\nHome"); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "restart in progress ...\nDone\n"); send_template_raw(client_socket, res); } return 0; // to restart } else { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd is going to restart thread %d", thread); if (cnt[thread]->running) { cnt[thread]->makemovie = 1; cnt[thread]->finish = 1; } cnt[thread]->restart = 1; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "restart for thread %u done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "restart for thread %u\nDone\n", thread); send_template_raw(client_socket, res); } } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "quit")) { pointer = pointer + 4; length_uri = length_uri - 4; if (length_uri == 0) { /* call quit */ if (thread == 0) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd quits"); kill(getpid(),SIGQUIT); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "quit in progress ... bye"); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res,"quit in progress ... bye\nDone\n"); send_template_raw(client_socket, res); } return 0; // to quit } else { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd quits thread %d", thread); cnt[thread]->restart = 0; cnt[thread]->makemovie = 1; cnt[thread]->finish = 1; cnt[thread]->watchdog = WATCHDOG_OFF; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "quit for thread %u done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "quit for thread %u\nDone\n", thread); send_template_raw(client_socket, res); } } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } return 1; } /** * detection * manages/parses the detection actions for motion ( status , start , pause ). * * Returns * 1 to exit from function. */ static unsigned int detection(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char command[256] = {'\0'}; struct context **cnt = userdata; unsigned int i = 0; warningkill = sscanf(pointer, "%255[a-z]" , command); if (!strcmp(command, "status")) { pointer = pointer + 6; length_uri = length_uri - 6; if (length_uri == 0) { /* call status */ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

Camera %d%s%s" " Detection status %s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d Detection status %s\n", cnt[thread]->conf.camera_id, (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "start")) { pointer = pointer + 5; length_uri = length_uri - 5; if (length_uri == 0) { /* call start */ if (thread == 0) { do { cnt[i]->pause = 0; } while (cnt[++i]); } else { cnt[thread]->pause = 0; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" " Detection resumed\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d%s%s Detection resumed\nDone\n", cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "pause")) { pointer = pointer + 5; length_uri = length_uri - 5; if (length_uri == 0) { /* call pause */ if (thread == 0) { do { cnt[i]->pause = 1; } while (cnt[++i]); } else { cnt[thread]->pause = 1; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" " Detection paused\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d%s%s Detection paused\nDone\n", cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "connection")) { pointer = pointer + 10; length_uri = length_uri - 10; if (length_uri == 0) { /* call connection */ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n", thread); send_template(client_socket, res); if (thread == 0) { do{ sprintf(res, "Camera %d%s%s %s
\n", cnt[i]->conf.camera_id, cnt[i]->conf.camera_name ? " -- " : "", cnt[i]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK); send_template(client_socket, res); } while (cnt[++i]); } else { sprintf(res, "Camera %d%s%s %s\n", cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template(client_socket, res); } send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); if (thread == 0) { do { sprintf(res, "Camera %d %s\n", cnt[i]->conf.camera_id, (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template_raw(client_socket, res); } while (cnt[++i]); } else { sprintf(res, "Camera %d %s\n", cnt[thread]->conf.camera_id, (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template_raw(client_socket, res); } } } else { /* error */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } return 1; } /** * track * manages/parses the track action for motion ( set , pan , tilt , auto ). * * Returns * 1 to exit from function. */ static unsigned int track(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char question='\0'; char command[256] = {'\0'}; struct context **cnt = userdata; warningkill = sscanf(pointer, "%255[a-z]%c", command, &question); if (!strcmp(command, "set")) { pointer = pointer+3; length_uri = length_uri-3; /* * FIXME need to check each value * Relative movement set?pan=0&tilt=0 | set?pan=0 | set?tilt=0 * Absolute movement set?x=0&y=0 | set?x=0 | set?y=0 */ if ((question == '?') && (length_uri > 2)) { char panvalue[12] = {'\0'}, tiltvalue[12] = {'\0'}; char x_value[12] = {'\0'}, y_value[12] = {'\0'}; struct context *setcnt; int pan = 0, tilt = 0, X = 0 , Y = 0; pointer++; length_uri--; /* * set?pan=value&tilt=value * set?x=value&y=value * pan= or x= | tilt= or y= */ warningkill = sscanf(pointer, "%255[a-z]%c" , command, &question); if ((question != '=') || (command[0] == '\0')) { /* no valid syntax */ MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 1"); if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } pointer++; length_uri--; /* Check first parameter */ if (!strcmp(command, "pan")) { pointer = pointer + 3; length_uri = length_uri - 3; pan = 1; if ((warningkill = sscanf(pointer, "%10[-0-9]", panvalue))) { pointer = pointer + strlen(panvalue); length_uri = length_uri - strlen(panvalue); } } else if (!strcmp(command, "tilt")) { pointer = pointer + 4; length_uri = length_uri - 4; tilt = 1; if ((warningkill = sscanf(pointer, "%10[-0-9]", tiltvalue))) { pointer = pointer + strlen(tiltvalue); length_uri = length_uri - strlen(tiltvalue); } } else if (!strcmp(command, "x")) { pointer++; length_uri--; X = 1; if ((warningkill = sscanf(pointer, "%10[-0-9]", x_value))) { pointer = pointer + strlen(x_value); length_uri = length_uri - strlen(x_value); } } else if (!strcmp(command, "y")) { pointer++; length_uri--; Y = 1; if ((warningkill = sscanf(pointer, "%10[-0-9]" , y_value))) { pointer = pointer + strlen(y_value); length_uri = length_uri - strlen(y_value); } } else { /* no valid syntax */ MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 2"); if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } /* first value check for error */ if (!warningkill) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 3"); /* error value */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, error_value, NULL); else response_client(client_socket, error_value_raw, NULL); return 1; } /* Only one parameter (pan= ,tilt= ,x= ,y= ) */ if (length_uri == 0) { if (pan) { struct coord cent; struct context *pancnt; /* move pan */ pancnt = cnt[thread]; cent.width = pancnt->imgs.width; cent.height = pancnt->imgs.height; cent.y = 0; cent.x = atoi(panvalue); // Add the number of frame to skip for motion detection cnt[thread]->moved = track_move(pancnt, pancnt->video_dev, ¢, &pancnt->imgs, 1); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set relative pan=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", panvalue); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track set relative pan=%s\nDone\n", panvalue); send_template_raw(client_socket, res); } } else { /* error in track action */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } else if (tilt) { struct coord cent; struct context *tiltcnt; /* move tilt */ tiltcnt = cnt[thread]; cent.width = tiltcnt->imgs.width; cent.height = tiltcnt->imgs.height; cent.x = 0; cent.y = atoi(tiltvalue); // Add the number of frame to skip for motion detection cnt[thread]->moved = track_move(tiltcnt, tiltcnt->video_dev, ¢, &tiltcnt->imgs, 1); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set relative tilt=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", tiltvalue); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track set relative tilt=%s\nDone\n", tiltvalue); send_template_raw(client_socket, res); } } else { /* error in track action */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } else if (X) { /* X */ setcnt = cnt[thread]; // 1000 is out of range for pwc cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), 1000); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set absolute x=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", x_value); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track set absolute x=%s\nDone\n", x_value); send_template_raw(client_socket, res); } } else { /* error in track action */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } else { /* Y */ setcnt = cnt[thread]; // 1000 is out of range for pwc cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 1000, atoi(y_value)); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set absolute y=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", y_value); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track set absolute y=%s\nDone\n", y_value); send_template_raw(client_socket, res); } } else { /* error in track action */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } return 1; } /* Check Second parameter */ warningkill = sscanf(pointer, "%c%255[a-z]" , &question, command); if ((question != '&') || (command[0] == '\0')) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 4"); if (strstr(pointer, "&")) { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, error_value, NULL); else response_client(client_socket, error_value_raw, NULL); /* no valid syntax */ } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); } return 1; } pointer++; length_uri--; if (!strcmp(command, "pan")) { pointer = pointer + 3; length_uri = length_uri - 3; if (pan || !tilt || X || Y) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 5"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } pan=2; warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, panvalue); } else if (!strcmp(command, "tilt")) { pointer = pointer + 4; length_uri = length_uri - 4; if (tilt || !pan || X || Y) { /* no valid syntax */ MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 6"); if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } tilt = 2; warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, tiltvalue); } else if (!strcmp(command, "x")) { pointer++; length_uri--; if (X || !Y || pan || tilt) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 7"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } X = 2; warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, x_value); } else if (!strcmp(command, "y")) { pointer++; length_uri--; if (Y || !X || pan || tilt) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 8"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } Y = 2; warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, y_value); } else { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 9"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } /* Second value check */ if ((warningkill < 2) && (question != '=')) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 10"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_valid_syntax, NULL); else response_client(client_socket, not_valid_syntax_raw, NULL); return 1; } else if ((question == '=') && (warningkill == 1)) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 11"); if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, error_value, NULL); else response_client(client_socket, error_value_raw, NULL); return 1; } if (pan == 2) { pointer = pointer + strlen(panvalue) + 1; length_uri = length_uri - strlen(panvalue) - 1; } else if (tilt == 2) { pointer = pointer + strlen(tiltvalue) + 1; length_uri = length_uri - strlen(tiltvalue) - 1; } else if (X == 2) { pointer = pointer + strlen(x_value) + 1; length_uri = length_uri - strlen(x_value) - 1; } else { pointer = pointer + strlen(y_value) + 1; length_uri = length_uri - strlen(y_value) - 1; } if (length_uri != 0) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 12"); if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, error_value, NULL); else response_client(client_socket, error_value_raw, NULL); return 1; } /* track set absolute ( x , y )*/ if (X && Y) { setcnt = cnt[thread]; cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), atoi(y_value)); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track absolute set x=%s y=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", x_value, y_value); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track absolute set x=%s y=%s\nDone\n", x_value, y_value); send_template_raw(client_socket, res); } } else { /* error in track action */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } /* track set relative ( pan , tilt )*/ } else { struct coord cent; struct context *relativecnt; /* move pan */ relativecnt = cnt[thread]; cent.width = relativecnt->imgs.width; cent.height = relativecnt->imgs.height; cent.y = 0; cent.x = atoi(panvalue); // Add the number of frame to skip for motion detection cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); if (cnt[thread]->moved) { /* move tilt */ relativecnt = cnt[thread]; cent.width = relativecnt->imgs.width; cent.height = relativecnt->imgs.height; cent.x = 0; cent.y = atoi(tiltvalue); // SLEEP(1,0); cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); if (cnt[thread]->moved) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track relative pan=%s tilt=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", panvalue, tiltvalue); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track relative pan=%s tilt=%s\nDone\n", panvalue, tiltvalue); send_template_raw(client_socket, res); } return 1; } else { /* error in track tilt */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } /* error in track pan */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } else if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s
\n" "
\n" "Pan\n" "Tilt\n" "\n" "
\n" "
\n" "X\n" "Y\n" "\n" "
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "set needs a pan/tilt or x/y values\n"); send_template_raw(client_socket, res); } } else { /* error not valid command */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "center")) { pointer = pointer+6; length_uri = length_uri-6; if (length_uri==0) { struct context *setcnt; setcnt = cnt[thread]; // 1000 is out of range for pwc cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 0, 0); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" "
track set center", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track set center\nDone\n", cnt[thread]->conf.camera_id); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error not valid command */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "status")) { pointer = pointer+6; length_uri = length_uri-6; if (length_uri==0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" "
track auto %s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active)? "enabled":"disabled"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track auto %s\nDone\n", cnt[thread]->conf.camera_id, (cnt[thread]->track.active)? "enabled":"disabled"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error not valid command */ if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "auto")) { pointer = pointer + 4; length_uri = length_uri - 4; if ((question == '?') && (length_uri > 0)) { char query[256] = {'\0'}; pointer++; length_uri--; /* value= */ warningkill = sscanf(pointer, "%255[a-z]%c", query, &question); if ((question == '=') && (!strcmp(query, "value"))) { pointer = pointer + 6; length_uri = length_uri - 6; warningkill = sscanf(pointer, "%255[-0-9a-z]" , command); if (strlen(command) > 0) { struct context *autocnt; /* auto value=0|1|status */ if (!strcmp(command, "status")) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
" "track auto %s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active)? "enabled":"disabled"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track auto %s\nDone\n", cnt[thread]->conf.camera_id, (cnt[thread]->track.active)? "enabled":"disabled"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { int active; active = atoi(command); /* CHECK */ if (active > -1 && active < 2) { autocnt = cnt[thread]; autocnt->track.active = active; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s" "
track auto %s
", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", active ? "enabled":"disabled"); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track auto %s\nDone\n", active ? "enabled":"disabled"); send_template_raw(client_socket, res); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s\n" "
\n" "
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active) ? "selected":"", (cnt[thread]->track.active) ? "selected":""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "auto accepts only 0,1 or status as valid value\n"); send_template_raw(client_socket, res); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } return 1; } /** * handle_get * parses the action requested for motion ( config , action , detection , track ) * and call to action function if needed. * * Returns * 0 on action restart or quit * 1 on success */ static unsigned int handle_get(int client_socket, const char *url, void *userdata, const char *httphostname) { struct context **cnt = userdata; if (*url == '/') { int i = 0; char *res=NULL; res = mymalloc(2048); /* get the number of threads */ while (cnt[++i]); /* ROOT_URI -> GET / */ if (!strcmp(url, "/")) { int y; int counter; char hostname[1024]; //Send the webcontrol section if applicable if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "Motion "VERSION" Running [%d] Cameras
\n" "All\n", i); send_template(client_socket, res); counter = 0; for (y = 1; y < i; y++) { counter++; if (counter == 6){ sprintf(res, "
"); send_template(client_socket, res); counter = 0; } sprintf(res, "Camera %d%s%s\n", y, cnt[y]->conf.camera_id, cnt[y]->conf.camera_name ? " -- " : "", cnt[y]->conf.camera_name ? cnt[y]->conf.camera_name : ""); send_template(client_socket, res); } sprintf(res, "
"); send_template(client_socket, res); if (!httphostname) { hostname[1023] = '\0'; gethostname(hostname, 1023); httphostname = hostname; } //Send the preview section for (y = 0; y < i; y++) { if (cnt[y]->conf.stream_port) { if (cnt[y]->conf.stream_preview_newline) { sprintf(res, "
"); send_template(client_socket, res); } sprintf(res, " " "\n" ,httphostname,cnt[y]->conf.stream_port ,httphostname,cnt[y]->conf.stream_port ,cnt[y]->conf.stream_preview_scale); send_template(client_socket, res); } } send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Motion "VERSION" Running [%d] Cameras\n0\n", i); send_template_raw(client_socket, res); for (y = 1; y < i; y++) { sprintf(res, "%d\n", y); send_template_raw(client_socket, res); } } } else { char command[256] = {'\0'}; char slash; int thread = -1; size_t length_uri = 0; char *pointer = (char *)url; length_uri = strlen(url); /* Check for Thread number first -> GET /2 */ pointer++; length_uri--; warningkill = sscanf(pointer, "%d%c", &thread, &slash); if ((thread != -1) && (thread < i)) { /* thread_number found */ if (thread > 9) { pointer = pointer + 2; length_uri = length_uri - 2; } else { pointer++; length_uri--; } if (slash == '/') { /* slash found /2/ */ pointer++; length_uri--; } if (length_uri != 0) { warningkill = sscanf(pointer, "%255[a-z]%c" , command , &slash); /* config */ if (!strcmp(command, "config")) { pointer = pointer + 6; length_uri = length_uri - 6; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "list
\n" "write
\n" "set
\n" "get
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, thread, thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d\nlist\nwrite\nset\nget\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else if ((slash == '/') && (length_uri >= 4)) { /* call config() */ pointer++; length_uri--; config(pointer, res, length_uri, thread, client_socket, cnt); } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command, "action")) { /* action */ pointer = pointer + 6; length_uri = length_uri - 6; /* call action() */ if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "makemovie
\n" "snapshot
\n" "restart
\n" "quit
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, thread, thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d\nmakemovie\nsnapshot\nrestart\nquit\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else if ((slash == '/') && (length_uri > 4)) { unsigned int ret = 1; pointer++; length_uri--; ret = action(pointer, res, length_uri, thread, client_socket, cnt); free(res); return ret; } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command,NULL); else response_client(client_socket, not_found_response_valid_command_raw,NULL); } } else if (!strcmp(command, "detection")) { /* detection */ pointer = pointer + 9; length_uri = length_uri - 9; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "status
\n" "start
\n" "pause
\n" "connection
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, thread, thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d\nstatus\nstart\npause\nconnection\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else if ((slash == '/') && (length_uri > 5)) { pointer++; length_uri--; /* call detection() */ detection(pointer, res, length_uri, thread, client_socket, cnt); } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_valid_command, NULL); else response_client(client_socket, not_found_response_valid_command_raw, NULL); } } else if (!strcmp(command,"track")) { /* track */ pointer = pointer + 5; length_uri = length_uri - 5; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "track set pan/tilt
\n" "track center
\n" "track auto
\n" "track status
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, thread, thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d\nset pan/tilt\ncenter\nauto\nstatus\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else if ((slash == '/') && (length_uri >= 4)) { pointer++; length_uri--; /* call track() */ if (cnt[thread]->track.type) { track(pointer, res, length_uri, thread, client_socket, cnt); } else { /* error track not enable */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back\n", thread); response_client(client_socket, not_track, res); } else { response_client(client_socket, not_track_raw, NULL); } } } else { if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back\n", thread); response_client(client_socket, not_found_response_valid_command, res); } else { response_client(client_socket, not_found_response_valid_command_raw, NULL); } } } else { if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back\n", thread); response_client(client_socket, not_found_response_valid_command, res); } else { response_client(client_socket, not_found_response_valid_command_raw, NULL); } } } else { /* /thread_number/ requested */ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d -- Thread %d%s%s
\n" "config
\n" "action
\n" "detection
\n" "track
\n", cnt[thread]->conf.camera_id, thread, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, thread, thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d\nconfig\naction\ndetection\ntrack\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } } else { if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back\n"); response_client(client_socket, not_found_response_valid, res); } else { response_client(client_socket, not_found_response_valid_raw, NULL); } } } free(res); } else { if (cnt[0]->conf.webcontrol_html_output) response_client(client_socket, not_found_response_template,NULL); else response_client(client_socket, not_found_response_template_raw,NULL); } return 1; } /** * read_client * As usually web clients uses nonblocking connect/read * read_client should handle nonblocking sockets. * * Returns * 0 to quit or restart * 1 on success */ static unsigned int read_client(int client_socket, void *userdata, char *auth) { unsigned int alive = 1; unsigned int ret = 1; char buffer[1024] = {'\0'}; ssize_t length = 1023; struct context **cnt = userdata; /* lock the mutex */ pthread_mutex_lock(&httpd_mutex); while (alive) { ssize_t nread = 0, readb = -1; nread = read_nonblock(client_socket, buffer, length); if (nread <= 0) { MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: motion-httpd First Read Error"); pthread_mutex_unlock(&httpd_mutex); return 1; } else { char method[10]={'\0'}; char url[512]={'\0'}; char protocol[10]={'\0'}; char *authentication=NULL; char *hostname=NULL; buffer[nread] = '\0'; warningkill = sscanf(buffer, "%9s %511s %9s", method, url, protocol); if (warningkill != 3) { if (cnt[0]->conf.webcontrol_html_output) warningkill = write_nonblock(client_socket, bad_request_response, sizeof (bad_request_response)); else warningkill = write_nonblock(client_socket, bad_request_response_raw, sizeof (bad_request_response_raw)); pthread_mutex_unlock(&httpd_mutex); return 1; } while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < length)) { readb = read_nonblock(client_socket, buffer+nread, sizeof (buffer) - nread); if (readb == -1) { nread = -1; break; } nread += readb; if (nread > length) { MOTION_LOG(WRN, TYPE_STREAM, SHOW_ERRNO, "%s: motion-httpd End buffer" " reached waiting for buffer ending"); break; } buffer[nread] = '\0'; } /* * Make sure the last read didn't fail. If it did, there's a * problem with the connection, so give up. */ if (nread == -1) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: motion-httpd READ give up!"); pthread_mutex_unlock(&httpd_mutex); return 1; } alive = 0; /* Check Protocol */ if (strcmp(protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { /* We don't understand this protocol. Report a bad response. */ if (cnt[0]->conf.webcontrol_html_output) warningkill = write_nonblock(client_socket, bad_request_response, sizeof (bad_request_response)); else warningkill = write_nonblock(client_socket, bad_request_response_raw, sizeof (bad_request_response_raw)); pthread_mutex_unlock(&httpd_mutex); return 1; } if (strcmp (method, "GET")) { /* * This server only implements the GET method. If client * uses other method, report the failure. */ char response[1024]; if (cnt[0]->conf.webcontrol_html_output) snprintf(response, sizeof (response), bad_method_response_template, method); else snprintf(response, sizeof (response), bad_method_response_template_raw, method); warningkill = write_nonblock(client_socket, response, strlen(response)); pthread_mutex_unlock(&httpd_mutex); return 1; } if ((hostname = strstr(buffer,"Host:"))) { /* use the hostname the browser used to connect to us when * constructing links to the stream ports. If available * (which it is in all modern browsers) it is more likely to * work that the result of gethostname(), which is reliant on * the machine we're running on having it's hostname setup * correctly and corresponding DNS in place. */ hostname += strlen("Host:"); char *end_host = strstr(hostname,"\r\n"); if (end_host) { while (hostname < end_host && isspace(hostname[0])) hostname++; while (hostname < end_host && isspace(end_host[-1])) end_host--; /* we have a string that is the hostname followed by * optionally a colon and a port number - strip off any * port number & colon */ char *colon = NULL; if (hostname[0] == '[') { // hostname is a IPv6 address like "[::1]" char *end_bracket = memchr(hostname, ']', end_host-hostname); // look for the colon after the "]" colon = memchr(end_bracket, ':', end_host-end_bracket); } else { colon = memchr(hostname, ':', end_host-hostname); } if (colon) end_host = colon; hostname = strndup(hostname, end_host-hostname); } else { hostname = NULL; } } if (auth != NULL) { if ((authentication = strstr(buffer,"Basic"))) { char *end_auth = NULL; authentication = authentication + 6; if ((end_auth = strstr(authentication,"\r\n"))) { authentication[end_auth - authentication] = '\0'; } else { char response[1024]; snprintf(response, sizeof (response), request_auth_response_template, method); warningkill = write_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } if (strcmp(auth, authentication)) { char response[1024] = {'\0'}; snprintf(response, sizeof (response), request_auth_response_template, method); warningkill = write_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } else { ret = handle_get(client_socket, url, cnt, hostname); /* A valid auth request. Process it. */ } } else { // Request Authorization char response[1024] = {'\0'}; snprintf(response, sizeof (response), request_auth_response_template, method); warningkill = write_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } } else { ret = handle_get(client_socket, url, cnt, hostname); /* A valid request. Process it. */ } free(hostname); } } pthread_mutex_unlock(&httpd_mutex); return ret; } /** * acceptnonblocking * waits timeout seconds for listen socket. * * Returns * -1 if the timeout expires or on accept error. * curfd (client socket) on accept success. */ static int acceptnonblocking(int serverfd, int timeout) { int curfd; struct sockaddr_in6 client; socklen_t client_len = sizeof(client); struct timeval tm; fd_set fds; tm.tv_sec = timeout; /* Timeout in seconds */ tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(serverfd, &fds); if (select(serverfd + 1, &fds, NULL, NULL, &tm) > 0) { if (FD_ISSET(serverfd, &fds)) { if ((curfd = accept(serverfd, (struct sockaddr *)&client, &client_len)) > 0) return curfd; } } return -1; } /** * httpd_run * Creates the listening socket and waits client requests. */ void httpd_run(struct context **cnt) { unsigned int client_sent_quit_message = 1, closehttpd = 0; struct sigaction act; char *authentication = NULL; /* Initialize the mutex */ pthread_mutex_init(&httpd_mutex, NULL); /* set signal handlers TO IGNORE */ memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGCHLD, &act, NULL); int sd = http_bindsock(cnt[0]->conf.webcontrol_port, cnt[0]->conf.webcontrol_localhost, cnt[0]->conf.ipv6_enabled); if (sd < 0) { pthread_mutex_destroy(&httpd_mutex); return; } MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,"%s: Started motion-httpd server on port %d (auth %s)", cnt[0]->conf.webcontrol_port, cnt[0]->conf.webcontrol_authentication ? "Enabled":"Disabled"); if (cnt[0]->conf.webcontrol_authentication != NULL) { char *userpass = NULL; size_t auth_size = strlen(cnt[0]->conf.webcontrol_authentication); authentication = mymalloc(BASE64_LENGTH(auth_size) + 1); userpass = mymalloc(auth_size + 4); /* motion_base64_encode can read 3 bytes after the end of the string, initialize it */ memset(userpass, 0, auth_size + 4); strcpy(userpass, cnt[0]->conf.webcontrol_authentication); motion_base64_encode(userpass, authentication, auth_size); free(userpass); } while ((client_sent_quit_message) && (!closehttpd)) { int client_socket_fd = acceptnonblocking(sd, NONBLOCK_TIMEOUT); if (client_socket_fd < 0) { if ((!cnt[0]) || (cnt[0]->webcontrol_finish)) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd - Finishing"); closehttpd = 1; } } else { /* Get the Client request */ client_sent_quit_message = read_client(client_socket_fd, cnt, authentication); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd - Read from client"); /* Close Connection */ if (client_socket_fd) close(client_socket_fd); } } free(authentication); close(sd); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd Closing"); pthread_mutex_destroy(&httpd_mutex); } /** * motion_web_control * Calls main function httpd_run */ void *motion_web_control(void *arg) { struct context **cnt = arg; MOTION_PTHREAD_SETNAME("web_control"); httpd_run(cnt); /* * Update how many threads we have running. This is done within a * mutex lock to prevent multiple simultaneous updates to * 'threads_running'. */ pthread_mutex_lock(&global_lock); threads_running--; cnt[0]->webcontrol_running = 0; pthread_mutex_unlock(&global_lock); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd thread exit"); pthread_exit(NULL); }