Commit af1aff7f authored by Sergey Lyubka's avatar Sergey Lyubka

mongoose.h 6.17

parents dce60c6d 517ef216
...@@ -16,9 +16,6 @@ signature: | ...@@ -16,9 +16,6 @@ signature: |
struct mbuf send_mbuf; /* Data scheduled for sending */ struct mbuf send_mbuf; /* Data scheduled for sending */
time_t last_io_time; /* Timestamp of the last socket IO */ time_t last_io_time; /* Timestamp of the last socket IO */
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
#if MG_ENABLE_SSL
void *ssl_if_data; /* SSL library data. */
#endif
mg_event_handler_t proto_handler; /* Protocol-specific event handler */ mg_event_handler_t proto_handler; /* Protocol-specific event handler */
void *proto_data; /* Protocol-specific data */ void *proto_data; /* Protocol-specific data */
void (*proto_data_destructor)(void *proto_data); void (*proto_data_destructor)(void *proto_data);
...@@ -51,16 +48,25 @@ signature: | ...@@ -51,16 +48,25 @@ signature: |
/* Flags that are settable by user */ /* Flags that are settable by user */
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ #define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ #define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ /* Flags for protocol handlers */
#define MG_F_PROTO_1 (1 << 12)
#define MG_F_PROTO_2 (1 << 13)
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ #define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
#define MG_F_USER_1 (1 << 20) /* Flags left for application */ /* Flags left for application */
#define MG_F_USER_1 (1 << 20)
#define MG_F_USER_2 (1 << 21) #define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22) #define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23) #define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24) #define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25) #define MG_F_USER_6 (1 << 25)
#if MG_ENABLE_SSL
void *ssl_if_data; /* SSL library data. */
#else
void *unused_ssl_if_data; /* To keep the size of the structure the same. */
#endif
}; };
--- ---
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2004-2013 Sergey Lyubka
* Copyright (c) 2013-2015 Cesanta Software Limited
* All rights reserved
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in <https://www.cesanta.com/license>.
*/
#ifndef CS_MONGOOSE_SRC_COMMON_H_ #ifndef CS_MONGOOSE_SRC_COMMON_H_
#define CS_MONGOOSE_SRC_COMMON_H_ #define CS_MONGOOSE_SRC_COMMON_H_
#define MG_VERSION "6.15" #define MG_VERSION "6.17"
/* Local tweaks, applied before any of Mongoose's own headers. */ /* Local tweaks, applied before any of Mongoose's own headers. */
#ifdef MG_LOCALS #ifdef MG_LOCALS
......
...@@ -85,35 +85,39 @@ int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) { ...@@ -85,35 +85,39 @@ int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) {
return mbuf_append(io, begin, end - begin); return mbuf_append(io, begin, end - begin);
} }
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) { int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name) {
const char *s; const char *s;
unsigned char n; unsigned char n;
size_t pos = io->len; size_t pos = io->len;
do { do {
if ((s = strchr(name, '.')) == NULL) { if ((s = mg_strchr(name, '.')) == NULL) {
s = name + len; s = name.p + name.len;
} }
if (s - name > 127) { if (s - name.p > 127) {
return -1; /* TODO(mkm) cover */ return -1; /* TODO(mkm) cover */
} }
n = s - name; /* chunk length */ n = s - name.p; /* chunk length */
mbuf_append(io, &n, 1); /* send length */ mbuf_append(io, &n, 1); /* send length */
mbuf_append(io, name, n); mbuf_append(io, name.p, n);
if (*s == '.') { if (n < name.len && *s == '.') {
n++; n++;
} }
name += n; name.p += n;
len -= n; name.len -= n;
} while (*s != '\0'); } while (name.len > 0);
mbuf_append(io, "\0", 1); /* Mark end of host name */ mbuf_append(io, "\0", 1); /* Mark end of host name */
return io->len - pos; return io->len - pos;
} }
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
return mg_dns_encode_name_s(io, mg_mk_str_n(name, len));
}
int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
const char *name, size_t nlen, const void *rdata, const char *name, size_t nlen, const void *rdata,
size_t rlen) { size_t rlen) {
......
...@@ -124,6 +124,7 @@ int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr, ...@@ -124,6 +124,7 @@ int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
* Encodes a DNS name. * Encodes a DNS name.
*/ */
int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len); int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len);
int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name);
/* Low-level: parses a DNS response. */ /* Low-level: parses a DNS response. */
int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg); int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg);
......
...@@ -328,37 +328,53 @@ static const struct { ...@@ -328,37 +328,53 @@ static const struct {
MIME_ENTRY("asf", "video/x-ms-asf"), MIME_ENTRY("asf", "video/x-ms-asf"),
MIME_ENTRY("avi", "video/x-msvideo"), MIME_ENTRY("avi", "video/x-msvideo"),
MIME_ENTRY("bmp", "image/bmp"), MIME_ENTRY("bmp", "image/bmp"),
{NULL, 0, NULL}}; {NULL, 0, NULL},
};
static struct mg_str mg_get_mime_type(const char *path, const char *dflt, static struct mg_str mg_get_mime_types_entry(struct mg_str path) {
const struct mg_serve_http_opts *opts) { size_t i;
const char *ext, *overrides; for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
size_t i, path_len; if (path.len < mg_static_builtin_mime_types[i].ext_len + 1) continue;
struct mg_str r, k, v; struct mg_str ext = MG_MK_STR_N(mg_static_builtin_mime_types[i].extension,
mg_static_builtin_mime_types[i].ext_len);
struct mg_str pext = MG_MK_STR_N(path.p + (path.len - ext.len), ext.len);
if (pext.p[-1] == '.' && mg_strcasecmp(ext, pext) == 0) {
return mg_mk_str(mg_static_builtin_mime_types[i].mime_type);
}
}
return mg_mk_str(NULL);
}
path_len = strlen(path); MG_INTERNAL int mg_get_mime_type_encoding(
struct mg_str path, struct mg_str *type, struct mg_str *encoding,
const struct mg_serve_http_opts *opts) {
const char *ext, *overrides;
struct mg_str k, v;
overrides = opts->custom_mime_types; overrides = opts->custom_mime_types;
while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) { while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) {
ext = path + (path_len - k.len); ext = path.p + (path.len - k.len);
if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) { if (path.len > k.len && mg_vcasecmp(&k, ext) == 0) {
return v; *type = v;
return 1;
} }
} }
for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) { *type = mg_get_mime_types_entry(path);
ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len);
if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' && /* Check for .html.gz, .js.gz, etc. */
mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) { if (mg_vcmp(type, "application/x-gunzip") == 0) {
r.p = mg_static_builtin_mime_types[i].mime_type; struct mg_str path2 = mg_mk_str_n(path.p, path.len - 3);
r.len = strlen(r.p); struct mg_str type2 = mg_get_mime_types_entry(path2);
return r; LOG(LL_ERROR, ("'%.*s' '%.*s' '%.*s'", (int) path.len, path.p,
(int) path2.len, path2.p, (int) type2.len, type2.p));
if (type2.len > 0) {
*type = type2;
*encoding = mg_mk_str("gzip");
} }
} }
r.p = dflt; return (type->len > 0);
r.len = strlen(r.p);
return r;
} }
#endif #endif
...@@ -1458,12 +1474,15 @@ static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a, ...@@ -1458,12 +1474,15 @@ static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a,
return result; return result;
} }
void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, void mg_http_serve_file_internal(struct mg_connection *nc,
const char *path, const struct mg_str mime_type, struct http_message *hm, const char *path,
const struct mg_str extra_headers) { struct mg_str mime_type,
struct mg_str encoding,
struct mg_str extra_headers) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
cs_stat_t st; cs_stat_t st;
LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p)); LOG(LL_DEBUG, ("%p [%s] %.*s %.*s", nc, path, (int) mime_type.len,
mime_type.p, (int) encoding.len, encoding.p));
if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) { if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) {
int code, err = mg_get_errno(); int code, err = mg_get_errno();
switch (err) { switch (err) {
...@@ -1502,8 +1521,9 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, ...@@ -1502,8 +1521,9 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
} else { } else {
status_code = 206; status_code = 206;
cl = r2 - r1 + 1; cl = r2 - r1 + 1;
snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT snprintf(range, sizeof(range),
"-%" INT64_FMT "/%" INT64_FMT "\r\n", "Content-Range: bytes %" INT64_FMT "-%" INT64_FMT
"/%" INT64_FMT "\r\n",
r1, r1 + cl - 1, (int64_t) st.st_size); r1, r1 + cl - 1, (int64_t) st.st_size);
#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ #if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \
_XOPEN_SOURCE >= 600 _XOPEN_SOURCE >= 600
...@@ -1528,13 +1548,6 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, ...@@ -1528,13 +1548,6 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
mg_http_construct_etag(etag, sizeof(etag), &st); mg_http_construct_etag(etag, sizeof(etag), &st);
mg_gmt_time_string(current_time, sizeof(current_time), &t); mg_gmt_time_string(current_time, sizeof(current_time), &t);
mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime); mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime);
/*
* Content length casted to size_t because:
* 1) that's the maximum buffer size anyway
* 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last
* position
* TODO(mkm): fix ESP8266 RTOS SDK
*/
mg_send_response_line_s(nc, status_code, extra_headers); mg_send_response_line_s(nc, status_code, extra_headers);
mg_printf(nc, mg_printf(nc,
"Date: %s\r\n" "Date: %s\r\n"
...@@ -1544,17 +1557,29 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm, ...@@ -1544,17 +1557,29 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
"Connection: %s\r\n" "Connection: %s\r\n"
"Content-Length: %" SIZE_T_FMT "Content-Length: %" SIZE_T_FMT
"\r\n" "\r\n"
"%sEtag: %s\r\n\r\n", "%s"
"Etag: %s\r\n",
current_time, last_modified, (int) mime_type.len, mime_type.p, current_time, last_modified, (int) mime_type.len, mime_type.p,
(pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range, (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range,
etag); etag);
if (encoding.len > 0) {
mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
encoding.p);
}
mg_send(nc, "\r\n", 2);
pd->file.cl = cl; pd->file.cl = cl;
pd->file.type = DATA_FILE; pd->file.type = DATA_FILE;
mg_http_transfer_file_data(nc); mg_http_transfer_file_data(nc);
} }
} }
void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
const char *path, const struct mg_str mime_type,
const struct mg_str extra_headers) {
mg_http_serve_file_internal(nc, hm, path, mime_type, mg_mk_str(NULL),
extra_headers);
}
static void mg_http_serve_file2(struct mg_connection *nc, const char *path, static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
struct http_message *hm, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
...@@ -1564,8 +1589,12 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path, ...@@ -1564,8 +1589,12 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
return; return;
} }
#endif #endif
mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts), struct mg_str type = MG_NULL_STR, encoding = MG_NULL_STR;
mg_mk_str(opts->extra_headers)); if (!mg_get_mime_type_encoding(mg_mk_str(path), &type, &encoding, opts)) {
type = mg_mk_str("text/plain");
}
mg_http_serve_file_internal(nc, hm, path, type, encoding,
mg_mk_str(opts->extra_headers));
} }
#endif #endif
...@@ -1841,7 +1870,7 @@ void cs_md5(char buf[33], ...) { ...@@ -1841,7 +1870,7 @@ void cs_md5(char buf[33], ...) {
va_list ap; va_list ap;
va_start(ap, buf); va_start(ap, buf);
while ((p = va_arg(ap, const unsigned char *) ) != NULL) { while ((p = va_arg(ap, const unsigned char *)) != NULL) {
msgs[num_msgs] = p; msgs[num_msgs] = p;
msg_lens[num_msgs] = va_arg(ap, size_t); msg_lens[num_msgs] = va_arg(ap, size_t);
num_msgs++; num_msgs++;
...@@ -2472,13 +2501,13 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm, ...@@ -2472,13 +2501,13 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
*p++ = DIRSEP; *p++ = DIRSEP;
/* No NULs and DIRSEPs in the component (percent-encoded). */ /* No NULs and DIRSEPs in the component (percent-encoded). */
for (i = 0; i < component.len; i++, p++) { for (i = 0; i < component.len; i++, p++) {
if (*p == '\0' || *p == DIRSEP if (*p == '\0' ||
*p == DIRSEP
#ifdef _WIN32 #ifdef _WIN32
/* On Windows, "/" is also accepted, so check for that too. */ /* On Windows, "/" is also accepted, so check for that too. */
|| || *p == '/'
*p == '/'
#endif #endif
) { ) {
ok = 0; ok = 0;
break; break;
} }
...@@ -2691,7 +2720,9 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -2691,7 +2720,9 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
mg_http_send_error(nc, 501, NULL); mg_http_send_error(nc, 501, NULL);
#endif #endif
} else if (mg_is_not_modified(hm, &st)) { } else if (mg_is_not_modified(hm, &st)) {
mg_http_send_error(nc, 304, "Not Modified"); /* Note: not using mg_http_send_error in order to keep connection alive */
/* Note: passing extra headers allow users to control session cookies */
mg_send_head(nc, 304, 0, opts->extra_headers);
} else { } else {
mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts); mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts);
} }
...@@ -2824,7 +2855,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, ...@@ -2824,7 +2855,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
#ifdef SPIFFS_ERR_FULL #ifdef SPIFFS_ERR_FULL
|| mg_get_errno() == SPIFFS_ERR_FULL || mg_get_errno() == SPIFFS_ERR_FULL
#endif #endif
) { ) {
mg_printf(nc, mg_printf(nc,
"HTTP/1.1 413 Payload Too Large\r\n" "HTTP/1.1 413 Payload Too Large\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
...@@ -2983,8 +3014,9 @@ struct mg_connection *mg_connect_http_opt( ...@@ -2983,8 +3014,9 @@ struct mg_connection *mg_connect_http_opt(
if (path.len == 0) path = mg_mk_str("/"); if (path.len == 0) path = mg_mk_str("/");
if (host.len == 0) host = mg_mk_str(""); if (host.len == 0) host = mg_mk_str("");
mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT mg_printf(nc,
"\r\n%.*s%s\r\n%s", "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
"\r\n%.*s%s\r\n%s",
(post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p, (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p,
(int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len, (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len,
(auth.buf == NULL ? "" : auth.buf), extra_headers, post_data); (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);
...@@ -3093,7 +3125,7 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, ...@@ -3093,7 +3125,7 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
#if MG_ENABLE_HTTP_STREAMING_MULTIPART #if MG_ENABLE_HTTP_STREAMING_MULTIPART
|| ev == MG_EV_HTTP_MULTIPART_REQUEST || ev == MG_EV_HTTP_MULTIPART_REQUEST
#endif #endif
) { ) {
struct mg_http_endpoint *ep = struct mg_http_endpoint *ep =
mg_http_get_endpoint_handler(nc->listener, &hm->uri); mg_http_get_endpoint_handler(nc->listener, &hm->uri);
if (ep != NULL) { if (ep != NULL) {
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
#if MG_ENABLE_HTTP #if MG_ENABLE_HTTP
#include "mg_net.h"
#include "common/mg_str.h" #include "common/mg_str.h"
#include "mg_net.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -120,6 +120,9 @@ struct mg_ssi_call_ctx { ...@@ -120,6 +120,9 @@ struct mg_ssi_call_ctx {
#define MG_EV_HTTP_MULTIPART_REQUEST_END 125 #define MG_EV_HTTP_MULTIPART_REQUEST_END 125
#endif #endif
#define MG_F_WEBSOCKET_NO_DEFRAG MG_F_PROTO_1
#define MG_F_DELETE_CHUNK MG_F_PROTO_2
/* /*
* Attaches a built-in HTTP event handler to the given connection. * Attaches a built-in HTTP event handler to the given connection.
* The user-defined event handler will receive following extra events: * The user-defined event handler will receive following extra events:
......
...@@ -170,7 +170,7 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, ...@@ -170,7 +170,7 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
const char *path, const char *path,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
FILE *fp; FILE *fp;
struct mg_str mime_type; struct mg_str mime_type = MG_NULL_STR, encoding = MG_NULL_STR;
DBG(("%p %s", nc, path)); DBG(("%p %s", nc, path));
if ((fp = mg_fopen(path, "rb")) == NULL) { if ((fp = mg_fopen(path, "rb")) == NULL) {
...@@ -178,12 +178,20 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc, ...@@ -178,12 +178,20 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
} else { } else {
mg_set_close_on_exec((sock_t) fileno(fp)); mg_set_close_on_exec((sock_t) fileno(fp));
mime_type = mg_get_mime_type(path, "text/plain", opts); if (!mg_get_mime_type_encoding(mg_mk_str(path), &mime_type, &encoding,
opts)) {
mime_type = mg_mk_str("text/plain");
}
mg_send_response_line(nc, 200, opts->extra_headers); mg_send_response_line(nc, 200, opts->extra_headers);
mg_printf(nc, mg_printf(nc,
"Content-Type: %.*s\r\n" "Content-Type: %.*s\r\n"
"Connection: close\r\n\r\n", "Connection: close\r\n",
(int) mime_type.len, mime_type.p); (int) mime_type.len, mime_type.p);
if (encoding.len > 0) {
mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
encoding.p);
}
mg_send(nc, "\r\n", 2);
mg_send_ssi_file(nc, hm, path, fp, 0, opts); mg_send_ssi_file(nc, hm, path, fp, 0, opts);
fclose(fp); fclose(fp);
nc->flags |= MG_F_SEND_AND_CLOSE; nc->flags |= MG_F_SEND_AND_CLOSE;
......
/*
* Copyright (c) 2004-2013 Sergey Lyubka
* Copyright (c) 2013-2020 Cesanta Software Limited
* All rights reserved
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in <https://www.cesanta.com/license>.
*/
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "mg_internal.h" #include "mg_internal.h"
#include "mg_mqtt.h" #include "mg_mqtt.h"
#define MG_F_MQTT_PING_PENDING MG_F_PROTO_1
static uint16_t getu16(const char *p) { static uint16_t getu16(const char *p) {
const uint8_t *up = (const uint8_t *) p; const uint8_t *up = (const uint8_t *) p;
return (up[0] << 8) + up[1]; return (up[0] << 8) + up[1];
...@@ -23,7 +25,7 @@ static const char *scanto(const char *p, struct mg_str *s) { ...@@ -23,7 +25,7 @@ static const char *scanto(const char *p, struct mg_str *s) {
MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
uint8_t header; uint8_t header;
size_t len = 0, len_len = 0; uint32_t len, len_len; /* must be 32-bit, see #1055 */
const char *p, *end, *eop = &io->buf[io->len]; const char *p, *end, *eop = &io->buf[io->len];
unsigned char lc = 0; unsigned char lc = 0;
int cmd; int cmd;
...@@ -40,7 +42,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) { ...@@ -40,7 +42,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
len += (lc & 0x7f) << 7 * len_len; len += (lc & 0x7f) << 7 * len_len;
len_len++; len_len++;
if (!(lc & 0x80)) break; if (!(lc & 0x80)) break;
if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG; if (len_len > sizeof(len)) return MG_MQTT_ERROR_MALFORMED_MSG;
} }
end = p + len; end = p + len;
...@@ -172,6 +174,10 @@ static void mqtt_handler(struct mg_connection *nc, int ev, ...@@ -172,6 +174,10 @@ static void mqtt_handler(struct mg_connection *nc, int ev,
} }
break; break;
} }
if (mm.cmd == MG_MQTT_CMD_PINGRESP) {
LOG(LL_DEBUG, ("Recv PINGRESP"));
nc->flags &= ~MG_F_MQTT_PING_PENDING;
}
nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data)); nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
mbuf_remove(io, len); mbuf_remove(io, len);
...@@ -182,10 +188,26 @@ static void mqtt_handler(struct mg_connection *nc, int ev, ...@@ -182,10 +188,26 @@ static void mqtt_handler(struct mg_connection *nc, int ev,
struct mg_mqtt_proto_data *pd = struct mg_mqtt_proto_data *pd =
(struct mg_mqtt_proto_data *) nc->proto_data; (struct mg_mqtt_proto_data *) nc->proto_data;
double now = mg_time(); double now = mg_time();
if (pd->keep_alive > 0 && pd->last_control_time > 0 && if (pd->keep_alive > 0 && pd->last_control_time > 0) {
(now - pd->last_control_time) > pd->keep_alive) { double diff = (now - pd->last_control_time);
LOG(LL_DEBUG, ("Send PINGREQ")); if (diff > pd->keep_alive) {
mg_mqtt_ping(nc); if (diff < 1500000000) {
if (!(nc->flags & MG_F_MQTT_PING_PENDING)) {
LOG(LL_DEBUG, ("Send PINGREQ"));
nc->flags |= MG_F_MQTT_PING_PENDING;
mg_mqtt_ping(nc);
} else {
LOG(LL_DEBUG, ("Ping timeout"));
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
} else {
/* Wall time has just been set. Avoid immediate ping,
* more likely than not it is not needed. The standard allows for
* 1.5X interval for ping requests, so even if were just about to
* send one, we should be ok waiting 0.4X more. */
pd->last_control_time = now - pd->keep_alive * 0.6;
}
}
} }
break; break;
} }
......
...@@ -101,10 +101,10 @@ struct mg_mqtt_proto_data { ...@@ -101,10 +101,10 @@ struct mg_mqtt_proto_data {
/* Message flags */ /* Message flags */
#define MG_MQTT_RETAIN 0x1 #define MG_MQTT_RETAIN 0x1
#define MG_MQTT_DUP 0x4
#define MG_MQTT_QOS(qos) ((qos) << 1) #define MG_MQTT_QOS(qos) ((qos) << 1)
#define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1) #define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
#define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1) #define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
#define MG_MQTT_DUP 0x8
/* Connection flags */ /* Connection flags */
#define MG_MQTT_CLEAN_SESSION 0x02 #define MG_MQTT_CLEAN_SESSION 0x02
......
...@@ -540,7 +540,7 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { ...@@ -540,7 +540,7 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
nc->iface = lc->iface; nc->iface = lc->iface;
if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
mg_add_conn(nc->mgr, nc); mg_add_conn(nc->mgr, nc);
LOG(LL_DEBUG, ("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
return nc; return nc;
} }
......
...@@ -110,9 +110,6 @@ struct mg_connection { ...@@ -110,9 +110,6 @@ struct mg_connection {
struct mbuf send_mbuf; /* Data scheduled for sending */ struct mbuf send_mbuf; /* Data scheduled for sending */
time_t last_io_time; /* Timestamp of the last socket IO */ time_t last_io_time; /* Timestamp of the last socket IO */
double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */
#if MG_ENABLE_SSL
void *ssl_if_data; /* SSL library data. */
#endif
mg_event_handler_t proto_handler; /* Protocol-specific event handler */ mg_event_handler_t proto_handler; /* Protocol-specific event handler */
void *proto_data; /* Protocol-specific data */ void *proto_data; /* Protocol-specific data */
void (*proto_data_destructor)(void *proto_data); void (*proto_data_destructor)(void *proto_data);
...@@ -145,16 +142,25 @@ struct mg_connection { ...@@ -145,16 +142,25 @@ struct mg_connection {
/* Flags that are settable by user */ /* Flags that are settable by user */
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ #define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ #define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ /* Flags for protocol handlers */
#define MG_F_PROTO_1 (1 << 12)
#define MG_F_PROTO_2 (1 << 13)
#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ #define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
#define MG_F_USER_1 (1 << 20) /* Flags left for application */ /* Flags left for application */
#define MG_F_USER_1 (1 << 20)
#define MG_F_USER_2 (1 << 21) #define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22) #define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23) #define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24) #define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25) #define MG_F_USER_6 (1 << 25)
#if MG_ENABLE_SSL
void *ssl_if_data; /* SSL library data. */
#else
void *unused_ssl_if_data; /* To keep the size of the structure the same. */
#endif
}; };
/* /*
......
...@@ -47,8 +47,8 @@ void mg_socket_if_connect_tcp(struct mg_connection *nc, ...@@ -47,8 +47,8 @@ void mg_socket_if_connect_tcp(struct mg_connection *nc,
#endif #endif
rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); rc = connect(nc->sock, &sa->sa, sizeof(sa->sin));
nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0; nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0;
DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(), DBG(("%p sock %d rc %d errno %d err %d", nc, (int) nc->sock, rc,
nc->err)); mg_get_errno(), nc->err));
} }
void mg_socket_if_connect_udp(struct mg_connection *nc) { void mg_socket_if_connect_udp(struct mg_connection *nc) {
...@@ -218,8 +218,8 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { ...@@ -218,8 +218,8 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
int worth_logging = int worth_logging =
fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE));
if (worth_logging) { if (worth_logging) {
DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc,
fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len,
(int) nc->send_mbuf.len)); (int) nc->send_mbuf.len));
} }
...@@ -271,7 +271,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { ...@@ -271,7 +271,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc); if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc);
if (worth_logging) { if (worth_logging) {
DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, (int) nc->sock,
nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
} }
} }
...@@ -279,8 +279,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { ...@@ -279,8 +279,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
#if MG_ENABLE_BROADCAST #if MG_ENABLE_BROADCAST
static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) { static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
struct ctl_msg ctl_msg; struct ctl_msg ctl_msg;
int len = int len = (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
(int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0); size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0);
DBG(("read %d from ctl socket", len)); DBG(("read %d from ctl socket", len));
(void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */ (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */
...@@ -299,7 +298,7 @@ void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) { ...@@ -299,7 +298,7 @@ void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) {
mg_set_non_blocking_mode(sock); mg_set_non_blocking_mode(sock);
mg_set_close_on_exec(sock); mg_set_close_on_exec(sock);
nc->sock = sock; nc->sock = sock;
DBG(("%p %d", nc, sock)); DBG(("%p %d", nc, (int) sock));
} }
void mg_socket_if_init(struct mg_iface *iface) { void mg_socket_if_init(struct mg_iface *iface) {
......
...@@ -15,7 +15,7 @@ extern "C" { ...@@ -15,7 +15,7 @@ extern "C" {
struct mg_ssl_if_ctx; struct mg_ssl_if_ctx;
struct mg_connection; struct mg_connection;
void mg_ssl_if_init(); void mg_ssl_if_init(void);
enum mg_ssl_if_result { enum mg_ssl_if_result {
MG_SSL_OK = 0, MG_SSL_OK = 0,
......
...@@ -44,7 +44,11 @@ struct mg_ssl_if_ctx { ...@@ -44,7 +44,11 @@ struct mg_ssl_if_ctx {
mbedtls_ssl_context *ssl; mbedtls_ssl_context *ssl;
mbedtls_x509_crt *cert; mbedtls_x509_crt *cert;
mbedtls_pk_context *key; mbedtls_pk_context *key;
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
char *ca_chain_file;
#else
mbedtls_x509_crt *ca_cert; mbedtls_x509_crt *ca_cert;
#endif
struct mbuf cipher_suites; struct mbuf cipher_suites;
size_t saved_len; size_t saved_len;
}; };
...@@ -52,7 +56,7 @@ struct mg_ssl_if_ctx { ...@@ -52,7 +56,7 @@ struct mg_ssl_if_ctx {
/* Must be provided by the platform. ctx is struct mg_connection. */ /* Must be provided by the platform. ctx is struct mg_connection. */
extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len);
void mg_ssl_if_init() { void mg_ssl_if_init(void) {
LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL)); LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL));
} }
...@@ -220,18 +224,17 @@ static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { ...@@ -220,18 +224,17 @@ static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) {
MG_FREE(ctx->key); MG_FREE(ctx->key);
ctx->key = NULL; ctx->key = NULL;
} }
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
MG_FREE(ctx->ca_chain_file);
ctx->ca_chain_file = NULL;
#else
if (ctx->ca_cert != NULL) { if (ctx->ca_cert != NULL) {
mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL); mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL);
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
if (ctx->conf->ca_chain_file != NULL) {
MG_FREE((void *) ctx->conf->ca_chain_file);
ctx->conf->ca_chain_file = NULL;
}
#endif
mbedtls_x509_crt_free(ctx->ca_cert); mbedtls_x509_crt_free(ctx->ca_cert);
MG_FREE(ctx->ca_cert); MG_FREE(ctx->ca_cert);
ctx->ca_cert = NULL; ctx->ca_cert = NULL;
} }
#endif
} }
enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) {
...@@ -312,11 +315,11 @@ void mg_ssl_if_conn_free(struct mg_connection *nc) { ...@@ -312,11 +315,11 @@ void mg_ssl_if_conn_free(struct mg_connection *nc) {
mbedtls_ssl_free(ctx->ssl); mbedtls_ssl_free(ctx->ssl);
MG_FREE(ctx->ssl); MG_FREE(ctx->ssl);
} }
mg_ssl_if_mbed_free_certs_and_keys(ctx);
if (ctx->conf != NULL) { if (ctx->conf != NULL) {
mbedtls_ssl_config_free(ctx->conf); mbedtls_ssl_config_free(ctx->conf);
MG_FREE(ctx->conf); MG_FREE(ctx->conf);
} }
mg_ssl_if_mbed_free_certs_and_keys(ctx);
mbuf_free(&ctx->cipher_suites); mbuf_free(&ctx->cipher_suites);
memset(ctx, 0, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
MG_FREE(ctx); MG_FREE(ctx);
...@@ -328,12 +331,15 @@ static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx, ...@@ -328,12 +331,15 @@ static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx,
mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
return MG_SSL_OK; return MG_SSL_OK;
} }
ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
mbedtls_x509_crt_init(ctx->ca_cert);
#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK #ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
ca_cert = strdup(ca_cert); ctx->ca_chain_file = strdup(ca_cert);
mbedtls_ssl_conf_ca_chain_file(ctx->conf, ca_cert, NULL); if (ctx->ca_chain_file == NULL) return MG_SSL_ERROR;
if (mbedtls_ssl_conf_ca_chain_file(ctx->conf, ctx->ca_chain_file, NULL) != 0) {
return MG_SSL_ERROR;
}
#else #else
ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
mbedtls_x509_crt_init(ctx->ca_cert);
if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) { if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) {
return MG_SSL_ERROR; return MG_SSL_ERROR;
} }
......
...@@ -21,7 +21,7 @@ struct mg_ssl_if_ctx { ...@@ -21,7 +21,7 @@ struct mg_ssl_if_ctx {
size_t identity_len; size_t identity_len;
}; };
void mg_ssl_if_init() { void mg_ssl_if_init(void) {
SSL_library_init(); SSL_library_init();
} }
......
...@@ -4590,6 +4590,15 @@ static const char *test_dns_encode(void) { ...@@ -4590,6 +4590,15 @@ static const char *test_dns_encode(void) {
return NULL; return NULL;
} }
static const char *test_dns_encode_name(void) {
struct mbuf mb;
mbuf_init(&mb, 0);
ASSERT_EQ(mg_dns_encode_name(&mb, "www.cesanta.com.net.org", 15), 17);
ASSERT_STREQ_NZ(mb.buf, "\x03" "www" "\x07" "cesanta" "\x03" "com");
mbuf_free(&mb);
return NULL;
}
static const char *test_dns_uncompress(void) { static const char *test_dns_uncompress(void) {
/* /*
* Order or string constants is important. Names being uncompressed * Order or string constants is important. Names being uncompressed
...@@ -5781,6 +5790,7 @@ const char *tests_run(const char *filter) { ...@@ -5781,6 +5790,7 @@ const char *tests_run(const char *filter) {
RUN_TEST(test_mqtt_broker); RUN_TEST(test_mqtt_broker);
#endif #endif
RUN_TEST(test_dns_encode); RUN_TEST(test_dns_encode);
RUN_TEST(test_dns_encode_name);
RUN_TEST(test_dns_uncompress); RUN_TEST(test_dns_uncompress);
RUN_TEST(test_dns_decode); RUN_TEST(test_dns_decode);
RUN_TEST(test_dns_decode_truncated); RUN_TEST(test_dns_decode_truncated);
......
...@@ -59,6 +59,7 @@ parser.add_argument('--exportable-headers', dest="export", action='store_true', ...@@ -59,6 +59,7 @@ parser.add_argument('--exportable-headers', dest="export", action='store_true',
help='allow exporting internal headers') help='allow exporting internal headers')
parser.add_argument('-I', default=['.'], dest='include_path', help='include path', action='append') parser.add_argument('-I', default=['.'], dest='include_path', help='include path', action='append')
parser.add_argument('sources', nargs='*', help='sources') parser.add_argument('sources', nargs='*', help='sources')
parser.add_argument('--license', dest="license", help='License file')
class File(object): class File(object):
def __init__(self, name, parent_name): def __init__(self, name, parent_name):
...@@ -121,7 +122,22 @@ def emit_body(out, name, parent_name): ...@@ -121,7 +122,22 @@ def emit_body(out, name, parent_name):
return return
with open(resolved_name) as f: with open(resolved_name) as f:
in_comment = False
comment = ''
for l in f: for l in f:
if in_comment:
comment += l
if re.match('\s*\*/$', l):
in_comment = False
if not re.match('.*Copyright.*Cesanta', comment, re.M | re.S):
print >>out, comment,
continue
if re.match('/\*$', l):
in_comment = True
comment = l
continue
match = re.match('( *#include "(.*)")', l) match = re.match('( *#include "(.*)")', l)
if match: if match:
all, path_to_include = match.groups() all, path_to_include = match.groups()
...@@ -155,6 +171,11 @@ if sys.platform == "win32": ...@@ -155,6 +171,11 @@ if sys.platform == "win32":
import os, msvcrt import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
if args.license:
with open(args.license) as f:
print f.read()
if args.public: if args.public:
print '#include "%s"' % (args.public) print '#include "%s"' % (args.public)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment