Skip to content

Commit

Permalink
Cross origin resource sharing (CORS) config parameter
Browse files Browse the repository at this point in the history
* Add configuration parameter for a cross origin resource sharing on the Motion stream
  • Loading branch information
jackxbritton authored and Mr-Dave committed Feb 11, 2018
1 parent 113ff69 commit 11e6339
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 21 deletions.
42 changes: 41 additions & 1 deletion conf.c
Expand Up @@ -85,6 +85,7 @@ struct config conf_template = {
.stream_localhost = 1,
.stream_limit = 0,
.stream_auth_method = 0,
.stream_cors_header = NULL,
.stream_authentication = NULL,
.stream_preview_scale = 25,
.stream_preview_newline = 0,
Expand Down Expand Up @@ -1271,6 +1272,16 @@ config_param config_params[] = {
WEBUI_LEVEL_RESTRICTED
},
{
"stream_cors_header",
"# Set the cross-origin resource sharing (CORS) header\n"
"# Default: not defined (Disabled)",
0,
CONF_OFFSET(stream_cors_header),
copy_uri,
print_string,
WEBUI_LEVEL_RESTRICTED
},
{
"stream_authentication",
"# Authentication for the stream. Syntax username:password\n"
"# Default: not defined (Disabled)",
Expand Down Expand Up @@ -2263,6 +2274,7 @@ void conf_output_parms(struct context **cnt)
if (!strncmp(name, "netcam_url", 10) ||
!strncmp(name, "netcam_userpass", 15) ||
!strncmp(name, "netcam_highres", 14) ||
!strncmp(name, "stream_cors_header", 18) ||
!strncmp(name, "stream_authentication", 21) ||
!strncmp(name, "webcontrol_authentication", 25) ||
!strncmp(name, "database_user", 13) ||
Expand Down Expand Up @@ -2300,7 +2312,8 @@ void malloc_strings(struct context *cnt)
unsigned int i = 0;
char **val;
while (config_params[i].param_name != NULL) {
if (config_params[i].copy == copy_string) { /* if member is a string */
if (config_params[i].copy == copy_string ||
config_params[i].copy == copy_uri) { /* if member is a string */
/* val is made to point to a pointer to the current string. */
val = (char **)((char *)cnt+config_params[i].conf_value);

Expand Down Expand Up @@ -2505,6 +2518,31 @@ struct context **copy_vid_ctrl(struct context **cnt, const char *config_val, int
return cnt;
}

struct context **copy_uri(struct context **cnt, const char *str, int val) {

// Here's a complicated regex I found here: https://stackoverflow.com/questions/38608116/how-to-check-a-specified-string-is-a-valid-url-or-not-using-c-code
// Use it for validating URIs.
const char *regex_str = "^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$";

regex_t regex;
if (regcomp(&regex, regex_str, REG_EXTENDED) != 0) {
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, "Error compiling regex in copy_uri");
return cnt;
}

// A single asterisk is also valid, so check for that.
if (strcmp(str, "*") != 0 && regexec(&regex, str, 0, NULL, 0) == REG_NOMATCH) {
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, "Invalid origin for cors_header in copy_uri");
regfree(&regex);
return cnt;
}

regfree(&regex);
cnt = copy_string(cnt, str, val);
return cnt;

}

/**
* mystrcpy
* Is used to assign string type fields (e.g. config options)
Expand Down Expand Up @@ -2586,6 +2624,8 @@ const char *config_type(config_param *configparam)
return "int";
if (configparam->copy == copy_bool)
return "bool";
if (configparam->copy == copy_uri)
return "uri";

return "unknown";
}
Expand Down
2 changes: 2 additions & 0 deletions conf.h
Expand Up @@ -74,6 +74,7 @@ struct config {
int stream_localhost;
int stream_limit;
int stream_auth_method;
const char *stream_cors_header;
const char *stream_authentication;
int stream_preview_scale;
int stream_preview_newline;
Expand Down Expand Up @@ -183,6 +184,7 @@ extern dep_config_param dep_config_params[];

struct context **conf_load(struct context **);
struct context **copy_string(struct context **, const char *, int);
struct context **copy_uri(struct context **, const char *, int);
struct context **conf_cmdparse(struct context **, const char *, const char *);
void conf_output_parms(struct context **cnt);
const char *config_type(config_param *);
Expand Down
16 changes: 16 additions & 0 deletions motion.1
Expand Up @@ -1512,6 +1512,22 @@ The authentication method to use for viewing the stream.
.RE
.RE

.TP
.B stream_cors_header
.RS
.nf
Values: User specified string
Default: Not defined
Description:
.fi
.RS
The Access-Control-Allow-Origin header value to be sent with the stream.
If unspecified, no Access-Control-Allow-Origin header is sent.
The header allows browsers to access the stream via cross-origin resource sharing (CORS).
For example, * allows access from browser client code served from any domain.
.RE
.RE

.TP
.B stream_authentication
.RS
Expand Down
7 changes: 4 additions & 3 deletions motion.c
Expand Up @@ -278,7 +278,8 @@ static void context_destroy(struct context *cnt)

/* Free memory allocated for config parameters */
for (j = 0; config_params[j].param_name != NULL; j++) {
if (config_params[j].copy == copy_string) {
if (config_params[j].copy == copy_string ||
config_params[j].copy == copy_uri) {
void **val;
val = (void *)((char *)cnt+(int)config_params[j].conf_value);
if (*val) {
Expand Down Expand Up @@ -1195,7 +1196,7 @@ static int motion_init(struct context *cnt)
/* Initialize stream server if stream port is specified to not 0 */
if (cnt->conf.stream_port) {
if (stream_init (&(cnt->stream), cnt->conf.stream_port, cnt->conf.stream_localhost,
cnt->conf.ipv6_enabled) == -1) {
cnt->conf.ipv6_enabled, cnt->conf.stream_cors_header) == -1) {
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "Problem enabling motion-stream server in port %d",
cnt->conf.stream_port);
cnt->conf.stream_port = 0;
Expand All @@ -1212,7 +1213,7 @@ static int motion_init(struct context *cnt)
if ((cnt->conf.width / 2) % 8 == 0 && (cnt->conf.height / 2) % 8 == 0
&& cnt->imgs.type == VIDEO_PALETTE_YUV420P){
if (stream_init (&(cnt->substream), cnt->conf.substream_port, cnt->conf.stream_localhost,
cnt->conf.ipv6_enabled) == -1) {
cnt->conf.ipv6_enabled, cnt->conf.stream_cors_header) == -1) {
MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "Problem enabling motion-substream server in port %d",
cnt->conf.substream_port);
cnt->conf.substream_port = 0;
Expand Down
21 changes: 21 additions & 0 deletions motion_config.html
Expand Up @@ -953,6 +953,12 @@ <h2><a name="Configuration_OptionsAlpha"></a> Configuration Options-Listed Alph
<td align="left">stream_auth_method</td>
<td align="left"><a href="#stream_auth_method" >stream_auth_method</a></td>
</tr>
<tr>
<td height="17" align="left"><br /></td>
<td align="left"></td>
<td align="left"></td>
<td align="left"><a href="#stream_cors_header" >stream_cors_header</a></td>
</tr>
<tr>
<td height="17" align="left"><br /></td>
<td align="left">stream_authentication</td>
Expand Down Expand Up @@ -1692,6 +1698,7 @@ <h2><a name="Configuration_OptionsTopic"></a> Configuration Options-Listed by T
</tr>
<tr>
<td bgcolor="#edf4f9" ><a href="#webcontrol_parms" >webcontrol_parms</a> </td>
<td bgcolor="#edf4f9" ><a href="#stream_cors_header" >stream_cors_header</a> </td>
</tr>
</tbody>
</table>
Expand Down Expand Up @@ -4605,6 +4612,20 @@ <h3><a name="stream_authentication"></a> stream_authentication </h3>
The syntax is username:password
<p></p>

<h3><a name="stream_cors_header"></a> stream_cors_header </h3>
<p></p>
<ul>
<li> Type: String</li>
<li> Range / Valid values: * or a valid URI</li>
<li> Default: Not Defined</li>
</ul>
<p></p>
The Access-Control-Allow-Origin header value to be sent with the stream.
If unspecified, no Access-Control-Allow-Origin header is sent.
The header allows browsers to access the stream via cross-origin resource sharing (CORS).
For example, * allows access from browser client code served from any origin.
<p></p>

<h3><a name="stream_preview_scale"></a> stream_preview_scale </h3>
<p></p>
<ul>
Expand Down
79 changes: 63 additions & 16 deletions stream.c
Expand Up @@ -908,6 +908,7 @@ static void stream_flush(struct stream *list, int *stream_count, int lim)
if (--client->tmpbuffer->ref <= 0) {
free(client->tmpbuffer->ptr);
free(client->tmpbuffer);
if (client->cors_header != NULL) free(client->cors_header);
}

/* Mark this client's buffer as empty. */
Expand Down Expand Up @@ -969,6 +970,16 @@ static struct stream_buffer *stream_tmpbuffer(int size)
return tmpbuffer;
}

const char *base_header = "HTTP/1.0 200 OK\r\n"
"Server: Motion/"VERSION"\r\n"
"Connection: close\r\n"
"Max-Age: 0\r\n"
"Expires: 0\r\n"
"Cache-Control: no-cache, private\r\n"
"Pragma: no-cache\r\n"
"Content-Type: multipart/x-mixed-replace; "
"boundary=BoundaryString\r\n\r\n";
#define BASE_HEADER_LEN strlen(base_header)
/**
* stream_add_client
*
Expand All @@ -977,24 +988,41 @@ static struct stream_buffer *stream_tmpbuffer(int size)
static void stream_add_client(struct stream *list, int sc)
{
struct stream *new = mymalloc(sizeof(struct stream));
static const char header[] = "HTTP/1.0 200 OK\r\n"
"Server: Motion/"VERSION"\r\n"
"Connection: close\r\n"
"Max-Age: 0\r\n"
"Expires: 0\r\n"
"Cache-Control: no-cache, private\r\n"
"Pragma: no-cache\r\n"
"Content-Type: multipart/x-mixed-replace; "
"boundary=BoundaryString\r\n\r\n";

memset(new, 0, sizeof(struct stream));
new->socket = sc;

if ((new->tmpbuffer = stream_tmpbuffer(sizeof(header))) == NULL) {
MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "Error creating tmpbuffer in stream_add_client");
// Copy the HTTP headers into tmpbuffer.

if (list->cors_header == NULL) {

new->tmpbuffer = stream_tmpbuffer(BASE_HEADER_LEN);
if (new->tmpbuffer == NULL) {
MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "Error creating tmpbuffer in stream_add_client");
} else {
memcpy(new->tmpbuffer->ptr, base_header, BASE_HEADER_LEN);
new->tmpbuffer->size = BASE_HEADER_LEN;
}

} else {
memcpy(new->tmpbuffer->ptr, header, sizeof(header)-1);
new->tmpbuffer->size = sizeof(header)-1;

const char *cors_header_key = "Access-Control-Allow-Origin: ";
size_t cors_header_key_len = strlen(cors_header_key);
size_t cors_header_len = strlen(list->cors_header);
size_t size = BASE_HEADER_LEN-2 + cors_header_key_len + cors_header_len + 4;

new->tmpbuffer = stream_tmpbuffer(size);
if (new->tmpbuffer == NULL) {
MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "Error creating tmpbuffer in stream_add_client");
} else {
// Basically copy over the base headers (without the second \r\n),
// and then the CORS header key, value, and \r\n\r\n.
memcpy(new->tmpbuffer->ptr, base_header, BASE_HEADER_LEN-2);
memcpy(&new->tmpbuffer->ptr[BASE_HEADER_LEN-2], cors_header_key, cors_header_key_len);
memcpy(&new->tmpbuffer->ptr[BASE_HEADER_LEN-2 + cors_header_key_len], list->cors_header, cors_header_len);
memcpy(&new->tmpbuffer->ptr[BASE_HEADER_LEN-2 + cors_header_key_len + cors_header_len], "\r\n\r\n", 4);
new->tmpbuffer->size = size;
}

}

new->prev = list;
Expand Down Expand Up @@ -1066,11 +1094,28 @@ static int stream_check_write(struct stream *list)
*
* Returns: stream socket descriptor.
*/
int stream_init(struct stream *stm, int stream_port, int stream_localhost, int ipv6_enabled)
int stream_init(struct stream *stm,
int port,
int localhost,
int ipv6_enabled,
const char *cors_header)
{
stm->socket = http_bindsock(stream_port, stream_localhost, ipv6_enabled);
stm->socket = http_bindsock(port, localhost, ipv6_enabled);
stm->next = NULL;
stm->prev = NULL;
stm->cors_header = NULL;

if (cors_header != NULL) {

size_t size = strlen(cors_header) + 1;
stm->cors_header = mymalloc(size);
if (stm->cors_header == NULL) {
MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "Error allocated cors_header in stream_init");
return stm->socket;
}
memcpy(stm->cors_header, cors_header, size);

}

return stm->socket;
}
Expand All @@ -1091,6 +1136,7 @@ void stream_stop(struct stream *stm)

close(stm->socket);
stm->socket = -1;
free(stm->cors_header);

while (next) {
list = next;
Expand All @@ -1099,6 +1145,7 @@ void stream_stop(struct stream *stm)
if (list->tmpbuffer) {
free(list->tmpbuffer->ptr);
free(list->tmpbuffer);
if (list->cors_header != NULL) free(list->cors_header);
}

close(list->socket);
Expand Down
7 changes: 6 additions & 1 deletion stream.h
Expand Up @@ -31,14 +31,19 @@ struct stream {
int socket;
FILE *fwrite;
struct stream_buffer *tmpbuffer;
char *cors_header;
long filepos;
int nr;
unsigned long int last;
struct stream *prev;
struct stream *next;
};

int stream_init(struct stream *, int, int, int);
int stream_init(struct stream *stm,
int port,
int localhost,
int ipv6_enabled,
const char *cors_header);
void stream_put(struct context *, struct stream *, int *, unsigned char *, int);
void stream_stop(struct stream *);

Expand Down

0 comments on commit 11e6339

Please sign in to comment.