Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongoose
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
esp
mongoose
Commits
3b69f5d5
Commit
3b69f5d5
authored
8 years ago
by
Deomid Ryabkov
Committed by
Cesanta Bot
8 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MG_DISABLE_DAV -> MG_ENABLE_HTTP_WEBDAV
PUBLISHED_FROM=62267ea0a8e10d8ba7bad590d1a56b179bcffce9
parent
b298d46a
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
305 additions
and
276 deletions
+305
-276
enabling-flags.md
docs/overview/build-options/enabling-flags.md
+2
-1
mongoose.c
mongoose.c
+299
-271
mongoose.h
mongoose.h
+4
-4
No files found.
docs/overview/build-options/enabling-flags.md
View file @
3b69f5d5
...
@@ -5,9 +5,10 @@ title: Enabling flags
...
@@ -5,9 +5,10 @@ title: Enabling flags
-
`MG_ENABLE_CGI`
Enable CGI support
-
`MG_ENABLE_CGI`
Enable CGI support
-
`MG_ENABLE_SSL`
Enable SSL/TLS support (OpenSSL API)
-
`MG_ENABLE_SSL`
Enable SSL/TLS support (OpenSSL API)
-
`MG_ENABLE_IPV6`
Enable IPV6 support
-
`MG_ENABLE_IPV6`
Enable IPV6 support
-
`MG_ENABLE_THREADS`
enable
`mg_start_thread()`
API
-
`MG_ENABLE_MQTT`
enable MQTT client
-
`MG_ENABLE_MQTT`
enable MQTT client
-
`MG_ENABLE_MQTT_BROKER`
enable MQTT broker
-
`MG_ENABLE_MQTT_BROKER`
enable MQTT broker
-
`MG_ENABLE_DNS_SERVER`
enable DNS server
-
`MG_ENABLE_DNS_SERVER`
enable DNS server
-
`MG_ENABLE_COAP`
enable CoAP protocol
-
`MG_ENABLE_COAP`
enable CoAP protocol
-
`MG_ENABLE_HTTP_WEBDAV`
enable WebDAV extensions to HTTP
-
`MG_ENABLE_GETADDRINFO`
enable
`getaddrinfo()`
in
`mg_resolve2()`
-
`MG_ENABLE_GETADDRINFO`
enable
`getaddrinfo()`
in
`mg_resolve2()`
-
`MG_ENABLE_THREADS`
enable
`mg_start_thread()`
API
This diff is collapsed.
Click to expand it.
mongoose.c
View file @
3b69f5d5
...
@@ -130,6 +130,23 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog,
...
@@ -130,6 +130,23 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog,
struct
mg_http_proto_data_cgi
;
struct
mg_http_proto_data_cgi
;
MG_INTERNAL
void
mg_http_free_proto_data_cgi
(
struct
mg_http_proto_data_cgi
*
d
);
MG_INTERNAL
void
mg_http_free_proto_data_cgi
(
struct
mg_http_proto_data_cgi
*
d
);
#endif
#endif
#if !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
MG_INTERNAL
int
mg_is_dav_request
(
const
struct
mg_str
*
s
);
MG_INTERNAL
void
mg_handle_propfind
(
struct
mg_connection
*
nc
,
const
char
*
path
,
cs_stat_t
*
stp
,
struct
http_message
*
hm
,
struct
mg_serve_http_opts
*
opts
);
MG_INTERNAL
void
mg_handle_lock
(
struct
mg_connection
*
nc
,
const
char
*
path
);
MG_INTERNAL
void
mg_handle_mkcol
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
);
MG_INTERNAL
void
mg_handle_move
(
struct
mg_connection
*
c
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
,
struct
http_message
*
hm
);
MG_INTERNAL
void
mg_handle_delete
(
struct
mg_connection
*
nc
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
);
MG_INTERNAL
void
mg_handle_put
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
);
#endif
#endif
/* CS_MONGOOSE_SRC_INTERNAL_H_ */
#endif
/* CS_MONGOOSE_SRC_INTERNAL_H_ */
#ifdef MG_MODULE_LINES
#ifdef MG_MODULE_LINES
...
@@ -3999,17 +4016,6 @@ static const struct {
...
@@ -3999,17 +4016,6 @@ static const struct {
MIME_ENTRY
(
"bmp"
,
"image/bmp"
),
MIME_ENTRY
(
"bmp"
,
"image/bmp"
),
{
NULL
,
0
,
NULL
}};
{
NULL
,
0
,
NULL
}};
#if !MG_DISABLE_DAV
static
int
mg_mkdir
(
const
char
*
path
,
uint32_t
mode
)
{
#ifndef _WIN32
return
mkdir
(
path
,
mode
);
#else
(
void
)
mode
;
return
_mkdir
(
path
);
#endif
}
#endif
static
struct
mg_str
mg_get_mime_type
(
const
char
*
path
,
const
char
*
dflt
,
static
struct
mg_str
mg_get_mime_type
(
const
char
*
path
,
const
char
*
dflt
,
const
struct
mg_serve_http_opts
*
opts
)
{
const
struct
mg_serve_http_opts
*
opts
)
{
const
char
*
ext
,
*
overrides
;
const
char
*
ext
,
*
overrides
;
...
@@ -6196,367 +6202,114 @@ static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
...
@@ -6196,367 +6202,114 @@ static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
}
}
#endif
/* MG_DISABLE_DIRECTORY_LISTING */
#endif
/* MG_DISABLE_DIRECTORY_LISTING */
#if !MG_DISABLE_DAV
/*
static
void
mg_print_props
(
struct
mg_connection
*
nc
,
const
char
*
name
,
* Given a directory path, find one of the files specified in the
cs_stat_t
*
stp
)
{
* comma-separated list of index files `list`.
char
mtime
[
64
],
buf
[
MAX_PATH_SIZE
*
3
];
* First found index file wins. If an index file is found, then gets
time_t
t
=
stp
->
st_mtime
;
/* store in local variable for NDK compile */
* appended to the `path`, stat-ed, and result of `stat()` passed to `stp`.
mg_gmt_time_string
(
mtime
,
sizeof
(
mtime
),
&
t
);
* If index file is not found, then `path` and `stp` remain unchanged.
mg_url_encode
(
name
,
strlen
(
name
),
buf
,
sizeof
(
buf
));
*/
mg_printf
(
nc
,
MG_INTERNAL
void
mg_find_index_file
(
const
char
*
path
,
const
char
*
list
,
"<d:response>"
char
**
index_file
,
cs_stat_t
*
stp
)
{
"<d:href>%s</d:href>"
struct
mg_str
vec
;
"<d:propstat>"
size_t
path_len
=
strlen
(
path
);
"<d:prop>"
int
found
=
0
;
"<d:resourcetype>%s</d:resourcetype>"
*
index_file
=
NULL
;
"<d:getcontentlength>%"
INT64_FMT
"</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>"
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>
\n
"
,
buf
,
S_ISDIR
(
stp
->
st_mode
)
?
"<d:collection/>"
:
""
,
(
int64_t
)
stp
->
st_size
,
mtime
);
}
static
void
mg_handle_propfind
(
struct
mg_connection
*
nc
,
const
char
*
path
,
/* Traverse index files list. For each entry, append it to the given */
cs_stat_t
*
stp
,
struct
http_message
*
hm
,
/* path and see if the file exists. If it exists, break the loop */
struct
mg_serve_http_opts
*
opts
)
{
while
((
list
=
mg_next_comma_list_entry
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
static
const
char
header
[]
=
cs_stat_t
st
;
"HTTP/1.1 207 Multi-Status
\r\n
"
size_t
len
=
path_len
+
1
+
vec
.
len
+
1
;
"Connection: close
\r\n
"
*
index_file
=
(
char
*
)
MG_REALLOC
(
*
index_file
,
len
);
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
if
(
*
index_file
==
NULL
)
break
;
"<?xml version=
\"
1.0
\"
encoding=
\"
utf-8
\"
?>"
snprintf
(
*
index_file
,
len
,
"%s%c%.*s"
,
path
,
DIRSEP
,
(
int
)
vec
.
len
,
vec
.
p
);
"<d:multistatus xmlns:d='DAV:'>
\n
"
;
static
const
char
footer
[]
=
"</d:multistatus>
\n
"
;
const
struct
mg_str
*
depth
=
mg_get_http_header
(
hm
,
"Depth"
);
/* Print properties for the requested resource itself */
/* Does it exist? Is it a file? */
if
(
S_ISDIR
(
stp
->
st_mode
)
&&
if
(
mg_stat
(
*
index_file
,
&
st
)
==
0
&&
S_ISREG
(
st
.
st_mode
))
{
strcmp
(
opts
->
enable_directory_listing
,
"yes"
)
!=
0
)
{
/* Yes it does, break the loop */
mg_printf
(
nc
,
"%s"
,
"HTTP/1.1 403 Directory Listing Denied
\r\n\r\n
"
);
*
stp
=
st
;
}
else
{
found
=
1
;
char
uri
[
MAX_PATH_SIZE
];
break
;
mg_send
(
nc
,
header
,
sizeof
(
header
)
-
1
);
snprintf
(
uri
,
sizeof
(
uri
),
"%.*s"
,
(
int
)
hm
->
uri
.
len
,
hm
->
uri
.
p
);
mg_print_props
(
nc
,
uri
,
stp
);
if
(
S_ISDIR
(
stp
->
st_mode
)
&&
(
depth
==
NULL
||
mg_vcmp
(
depth
,
"0"
)
!=
0
))
{
mg_scan_directory
(
nc
,
path
,
opts
,
mg_print_props
);
}
}
mg_send
(
nc
,
footer
,
sizeof
(
footer
)
-
1
);
nc
->
flags
|=
MG_F_SEND_AND_CLOSE
;
}
}
}
if
(
!
found
)
{
MG_FREE
(
*
index_file
);
#if MG_ENABLE_FAKE_DAVLOCK
*
index_file
=
NULL
;
/*
* Windows explorer (probably there are another WebDav clients like it)
* requires LOCK support in webdav. W/out this, it still works, but fails
* to save file: shows error message and offers "Save As".
* "Save as" works, but this message is very annoying.
* This is fake lock, which doesn't lock something, just returns LOCK token,
* UNLOCK always answers "OK".
* With this fake LOCK Windows Explorer looks happy and saves file.
* NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
* Windows native DAV client. This is why FAKE LOCK is not enabed by default
*/
static
void
mg_handle_lock
(
struct
mg_connection
*
nc
,
const
char
*
path
)
{
static
const
char
*
reply
=
"HTTP/1.1 207 Multi-Status
\r\n
"
"Connection: close
\r\n
"
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
"<?xml version=
\"
1.0
\"
encoding=
\"
utf-8
\"
?>"
"<d:multistatus xmlns:d='DAV:'>
\n
"
"<D:lockdiscovery>
\n
"
"<D:activelock>
\n
"
"<D:locktoken>
\n
"
"<D:href>
\n
"
"opaquelocktoken:%s%u"
"</D:href>"
"</D:locktoken>"
"</D:activelock>
\n
"
"</D:lockdiscovery>"
"</d:multistatus>
\n
"
;
mg_printf
(
nc
,
reply
,
path
,
(
unsigned
int
)
time
(
NULL
));
nc
->
flags
|=
MG_F_SEND_AND_CLOSE
;
}
#endif
static
void
mg_handle_mkcol
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
)
{
int
status_code
=
500
;
if
(
hm
->
body
.
len
!=
(
size_t
)
~
0
&&
hm
->
body
.
len
>
0
)
{
status_code
=
415
;
}
else
if
(
!
mg_mkdir
(
path
,
0755
))
{
status_code
=
201
;
}
else
if
(
errno
==
EEXIST
)
{
status_code
=
405
;
}
else
if
(
errno
==
EACCES
)
{
status_code
=
403
;
}
else
if
(
errno
==
ENOENT
)
{
status_code
=
409
;
}
else
{
status_code
=
500
;
}
}
mg_http_send_error
(
nc
,
status_code
,
NULL
);
DBG
((
"[%s] [%s]"
,
path
,
(
*
index_file
?
*
index_file
:
""
))
);
}
}
static
int
mg_
remove_directory
(
const
struct
mg_serve_http_opts
*
opts
,
static
int
mg_
http_send_port_based_redirect
(
const
char
*
dir
)
{
struct
mg_connection
*
c
,
struct
http_message
*
hm
,
char
path
[
MAX_PATH_SIZE
];
const
struct
mg_serve_http_opts
*
opts
)
{
struct
dirent
*
dp
;
const
char
*
rewrites
=
opts
->
url_rewrites
;
cs_stat_t
st
;
struct
mg_str
a
,
b
;
DIR
*
dirp
;
char
local_port
[
20
]
=
{
'%'
}
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
return
0
;
mg_conn_addr_to_str
(
c
,
local_port
+
1
,
sizeof
(
local_port
)
-
1
,
MG_SOCK_STRINGIFY_PORT
);
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
while
((
rewrites
=
mg_next_comma_list_entry
(
rewrites
,
&
a
,
&
b
))
!=
NULL
)
{
if
(
mg_is_file_hidden
((
const
char
*
)
dp
->
d_name
,
opts
,
1
))
{
if
(
mg_vcmp
(
&
a
,
local_port
)
==
0
)
{
continue
;
mg_send_response_line
(
c
,
301
,
NULL
);
}
mg_printf
(
c
,
"Content-Length: 0
\r\n
Location: %.*s%.*s
\r\n\r\n
"
,
snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
(
int
)
b
.
len
,
b
.
p
,
(
int
)
(
hm
->
proto
.
p
-
hm
->
uri
.
p
-
1
),
mg_stat
(
path
,
&
st
);
hm
->
uri
.
p
);
if
(
S_ISDIR
(
st
.
st_mode
))
{
return
1
;
mg_remove_directory
(
opts
,
path
);
}
else
{
remove
(
path
);
}
}
}
}
closedir
(
dirp
);
rmdir
(
dir
);
return
1
;
return
0
;
}
}
static
void
mg_handle_move
(
struct
mg_connection
*
c
,
MG_INTERNAL
int
mg_uri_to_local_path
(
struct
http_message
*
hm
,
const
struct
mg_serve_http_opts
*
opts
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
,
struct
http_message
*
hm
)
{
char
**
local_path
,
const
struct
mg_str
*
dest
=
mg_get_http_header
(
hm
,
"Destination"
);
struct
mg_str
*
remainder
)
{
if
(
dest
==
NULL
)
{
int
ok
=
1
;
mg_http_send_error
(
c
,
411
,
NULL
);
const
char
*
cp
=
hm
->
uri
.
p
,
*
cp_end
=
hm
->
uri
.
p
+
hm
->
uri
.
len
;
struct
mg_str
root
=
{
NULL
,
0
};
const
char
*
file_uri_start
=
cp
;
*
local_path
=
NULL
;
remainder
->
p
=
NULL
;
remainder
->
len
=
0
;
{
/* 1. Determine which root to use. */
const
char
*
rewrites
=
opts
->
url_rewrites
;
struct
mg_str
*
hh
=
mg_get_http_header
(
hm
,
"Host"
);
struct
mg_str
a
,
b
;
/* Check rewrites first. */
while
((
rewrites
=
mg_next_comma_list_entry
(
rewrites
,
&
a
,
&
b
))
!=
NULL
)
{
if
(
a
.
len
>
1
&&
a
.
p
[
0
]
==
'@'
)
{
/* Host rewrite. */
if
(
hh
!=
NULL
&&
hh
->
len
==
a
.
len
-
1
&&
mg_ncasecmp
(
a
.
p
+
1
,
hh
->
p
,
a
.
len
-
1
)
==
0
)
{
root
=
b
;
break
;
}
}
else
{
}
else
{
const
char
*
p
=
(
char
*
)
memchr
(
dest
->
p
,
'/'
,
dest
->
len
);
/* Regular rewrite, URI=directory */
if
(
p
!=
NULL
&&
p
[
1
]
==
'/'
&&
int
match_len
=
mg_match_prefix_n
(
a
,
hm
->
uri
);
(
p
=
(
char
*
)
memchr
(
p
+
2
,
'/'
,
dest
->
p
+
dest
->
len
-
p
))
!=
NULL
)
{
if
(
match_len
>
0
)
{
char
buf
[
MAX_PATH_SIZE
];
file_uri_start
=
hm
->
uri
.
p
+
match_len
;
snprintf
(
buf
,
sizeof
(
buf
),
"%s%.*s"
,
opts
->
dav_document_root
,
if
(
*
file_uri_start
==
'/'
||
file_uri_start
==
cp_end
)
{
(
int
)
(
dest
->
p
+
dest
->
len
-
p
),
p
);
/* Match ended at component boundary, ok. */
if
(
rename
(
path
,
buf
)
==
0
)
{
}
else
if
(
*
(
file_uri_start
-
1
)
==
'/'
)
{
mg_http_send_error
(
c
,
200
,
NULL
);
/* Pattern ends with '/', backtrack. */
file_uri_start
--
;
}
else
{
}
else
{
mg_http_send_error
(
c
,
418
,
NULL
);
/* No match: must fall on the component boundary. */
continue
;
}
}
}
else
{
root
=
b
;
mg_http_send_error
(
c
,
500
,
NULL
);
break
;
}
}
}
static
void
mg_handle_delete
(
struct
mg_connection
*
nc
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
)
{
cs_stat_t
st
;
if
(
mg_stat
(
path
,
&
st
)
!=
0
)
{
mg_http_send_error
(
nc
,
404
,
NULL
);
}
else
if
(
S_ISDIR
(
st
.
st_mode
))
{
mg_remove_directory
(
opts
,
path
);
mg_http_send_error
(
nc
,
204
,
NULL
);
}
else
if
(
remove
(
path
)
==
0
)
{
mg_http_send_error
(
nc
,
204
,
NULL
);
}
else
{
mg_http_send_error
(
nc
,
423
,
NULL
);
}
}
/* Return -1 on error, 1 on success. */
static
int
mg_create_itermediate_directories
(
const
char
*
path
)
{
const
char
*
s
;
/* Create intermediate directories if they do not exist */
for
(
s
=
path
+
1
;
*
s
!=
'\0'
;
s
++
)
{
if
(
*
s
==
'/'
)
{
char
buf
[
MAX_PATH_SIZE
];
cs_stat_t
st
;
snprintf
(
buf
,
sizeof
(
buf
),
"%.*s"
,
(
int
)
(
s
-
path
),
path
);
buf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
if
(
mg_stat
(
buf
,
&
st
)
!=
0
&&
mg_mkdir
(
buf
,
0755
)
!=
0
)
{
return
-
1
;
}
}
}
return
1
;
}
static
void
mg_handle_put
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
)
{
struct
mg_http_proto_data
*
pd
=
mg_http_get_proto_data
(
nc
);
cs_stat_t
st
;
const
struct
mg_str
*
cl_hdr
=
mg_get_http_header
(
hm
,
"Content-Length"
);
int
rc
,
status_code
=
mg_stat
(
path
,
&
st
)
==
0
?
200
:
201
;
mg_http_free_proto_data_file
(
&
pd
->
file
);
if
((
rc
=
mg_create_itermediate_directories
(
path
))
==
0
)
{
mg_printf
(
nc
,
"HTTP/1.1 %d OK
\r\n
Content-Length: 0
\r\n\r\n
"
,
status_code
);
}
else
if
(
rc
==
-
1
)
{
mg_http_send_error
(
nc
,
500
,
NULL
);
}
else
if
(
cl_hdr
==
NULL
)
{
mg_http_send_error
(
nc
,
411
,
NULL
);
}
else
if
((
pd
->
file
.
fp
=
fopen
(
path
,
"w+b"
))
==
NULL
)
{
mg_http_send_error
(
nc
,
500
,
NULL
);
}
else
{
const
struct
mg_str
*
range_hdr
=
mg_get_http_header
(
hm
,
"Content-Range"
);
int64_t
r1
=
0
,
r2
=
0
;
pd
->
file
.
type
=
DATA_PUT
;
mg_set_close_on_exec
(
fileno
(
pd
->
file
.
fp
));
pd
->
file
.
cl
=
to64
(
cl_hdr
->
p
);
if
(
range_hdr
!=
NULL
&&
mg_http_parse_range_header
(
range_hdr
,
&
r1
,
&
r2
)
>
0
)
{
status_code
=
206
;
fseeko
(
pd
->
file
.
fp
,
r1
,
SEEK_SET
);
pd
->
file
.
cl
=
r2
>
r1
?
r2
-
r1
+
1
:
pd
->
file
.
cl
-
r1
;
}
mg_printf
(
nc
,
"HTTP/1.1 %d OK
\r\n
Content-Length: 0
\r\n\r\n
"
,
status_code
);
/* Remove HTTP request from the mbuf, leave only payload */
mbuf_remove
(
&
nc
->
recv_mbuf
,
hm
->
message
.
len
-
hm
->
body
.
len
);
mg_http_transfer_file_data
(
nc
);
}
}
#endif
/* MG_DISABLE_DAV */
static
int
mg_is_dav_request
(
const
struct
mg_str
*
s
)
{
static
const
char
*
methods
[]
=
{
"PUT"
,
"DELETE"
,
"MKCOL"
,
"PROPFIND"
,
"MOVE"
#if MG_ENABLE_FAKE_DAVLOCK
,
"LOCK"
,
"UNLOCK"
#endif
};
size_t
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
methods
);
i
++
)
{
if
(
mg_vcmp
(
s
,
methods
[
i
])
==
0
)
{
return
1
;
}
}
return
0
;
}
/*
* Given a directory path, find one of the files specified in the
* comma-separated list of index files `list`.
* First found index file wins. If an index file is found, then gets
* appended to the `path`, stat-ed, and result of `stat()` passed to `stp`.
* If index file is not found, then `path` and `stp` remain unchanged.
*/
MG_INTERNAL
void
mg_find_index_file
(
const
char
*
path
,
const
char
*
list
,
char
**
index_file
,
cs_stat_t
*
stp
)
{
struct
mg_str
vec
;
size_t
path_len
=
strlen
(
path
);
int
found
=
0
;
*
index_file
=
NULL
;
/* Traverse index files list. For each entry, append it to the given */
/* path and see if the file exists. If it exists, break the loop */
while
((
list
=
mg_next_comma_list_entry
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
cs_stat_t
st
;
size_t
len
=
path_len
+
1
+
vec
.
len
+
1
;
*
index_file
=
(
char
*
)
MG_REALLOC
(
*
index_file
,
len
);
if
(
*
index_file
==
NULL
)
break
;
snprintf
(
*
index_file
,
len
,
"%s%c%.*s"
,
path
,
DIRSEP
,
(
int
)
vec
.
len
,
vec
.
p
);
/* Does it exist? Is it a file? */
if
(
mg_stat
(
*
index_file
,
&
st
)
==
0
&&
S_ISREG
(
st
.
st_mode
))
{
/* Yes it does, break the loop */
*
stp
=
st
;
found
=
1
;
break
;
}
}
if
(
!
found
)
{
MG_FREE
(
*
index_file
);
*
index_file
=
NULL
;
}
DBG
((
"[%s] [%s]"
,
path
,
(
*
index_file
?
*
index_file
:
""
)));
}
static
int
mg_http_send_port_based_redirect
(
struct
mg_connection
*
c
,
struct
http_message
*
hm
,
const
struct
mg_serve_http_opts
*
opts
)
{
const
char
*
rewrites
=
opts
->
url_rewrites
;
struct
mg_str
a
,
b
;
char
local_port
[
20
]
=
{
'%'
};
mg_conn_addr_to_str
(
c
,
local_port
+
1
,
sizeof
(
local_port
)
-
1
,
MG_SOCK_STRINGIFY_PORT
);
while
((
rewrites
=
mg_next_comma_list_entry
(
rewrites
,
&
a
,
&
b
))
!=
NULL
)
{
if
(
mg_vcmp
(
&
a
,
local_port
)
==
0
)
{
mg_send_response_line
(
c
,
301
,
NULL
);
mg_printf
(
c
,
"Content-Length: 0
\r\n
Location: %.*s%.*s
\r\n\r\n
"
,
(
int
)
b
.
len
,
b
.
p
,
(
int
)
(
hm
->
proto
.
p
-
hm
->
uri
.
p
-
1
),
hm
->
uri
.
p
);
return
1
;
}
}
return
0
;
}
MG_INTERNAL
int
mg_uri_to_local_path
(
struct
http_message
*
hm
,
const
struct
mg_serve_http_opts
*
opts
,
char
**
local_path
,
struct
mg_str
*
remainder
)
{
int
ok
=
1
;
const
char
*
cp
=
hm
->
uri
.
p
,
*
cp_end
=
hm
->
uri
.
p
+
hm
->
uri
.
len
;
struct
mg_str
root
=
{
NULL
,
0
};
const
char
*
file_uri_start
=
cp
;
*
local_path
=
NULL
;
remainder
->
p
=
NULL
;
remainder
->
len
=
0
;
{
/* 1. Determine which root to use. */
const
char
*
rewrites
=
opts
->
url_rewrites
;
struct
mg_str
*
hh
=
mg_get_http_header
(
hm
,
"Host"
);
struct
mg_str
a
,
b
;
/* Check rewrites first. */
while
((
rewrites
=
mg_next_comma_list_entry
(
rewrites
,
&
a
,
&
b
))
!=
NULL
)
{
if
(
a
.
len
>
1
&&
a
.
p
[
0
]
==
'@'
)
{
/* Host rewrite. */
if
(
hh
!=
NULL
&&
hh
->
len
==
a
.
len
-
1
&&
mg_ncasecmp
(
a
.
p
+
1
,
hh
->
p
,
a
.
len
-
1
)
==
0
)
{
root
=
b
;
break
;
}
}
else
{
/* Regular rewrite, URI=directory */
int
match_len
=
mg_match_prefix_n
(
a
,
hm
->
uri
);
if
(
match_len
>
0
)
{
file_uri_start
=
hm
->
uri
.
p
+
match_len
;
if
(
*
file_uri_start
==
'/'
||
file_uri_start
==
cp_end
)
{
/* Match ended at component boundary, ok. */
}
else
if
(
*
(
file_uri_start
-
1
)
==
'/'
)
{
/* Pattern ends with '/', backtrack. */
file_uri_start
--
;
}
else
{
/* No match: must fall on the component boundary. */
continue
;
}
root
=
b
;
break
;
}
}
}
}
}
}
/* If no rewrite rules matched, use DAV or regular document root. */
/* If no rewrite rules matched, use DAV or regular document root. */
if
(
root
.
p
==
NULL
)
{
if
(
root
.
p
==
NULL
)
{
#if
!MG_DISABLE_
DAV
#if
MG_ENABLE_HTTP_WEB
DAV
if
(
opts
->
dav_document_root
!=
NULL
&&
mg_is_dav_request
(
&
hm
->
method
))
{
if
(
opts
->
dav_document_root
!=
NULL
&&
mg_is_dav_request
(
&
hm
->
method
))
{
root
.
p
=
opts
->
dav_document_root
;
root
.
p
=
opts
->
dav_document_root
;
root
.
len
=
strlen
(
opts
->
dav_document_root
);
root
.
len
=
strlen
(
opts
->
dav_document_root
);
...
@@ -6729,7 +6482,7 @@ static void mg_http_send_digest_auth_request(struct mg_connection *c,
...
@@ -6729,7 +6482,7 @@ static void mg_http_send_digest_auth_request(struct mg_connection *c,
static
void
mg_http_send_options
(
struct
mg_connection
*
nc
)
{
static
void
mg_http_send_options
(
struct
mg_connection
*
nc
)
{
mg_printf
(
nc
,
"%s"
,
mg_printf
(
nc
,
"%s"
,
"HTTP/1.1 200 OK
\r\n
Allow: GET, POST, HEAD, CONNECT, OPTIONS"
"HTTP/1.1 200 OK
\r\n
Allow: GET, POST, HEAD, CONNECT, OPTIONS"
#if
!MG_DISABLE_
DAV
#if
MG_ENABLE_HTTP_WEB
DAV
", MKCOL, PUT, DELETE, PROPFIND, MOVE
\r\n
DAV: 1,2"
", MKCOL, PUT, DELETE, PROPFIND, MOVE
\r\n
DAV: 1,2"
#endif
#endif
"
\r\n\r\n
"
);
"
\r\n\r\n
"
);
...
@@ -6744,8 +6497,12 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
...
@@ -6744,8 +6497,12 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
const
struct
mg_str
*
path_info
,
const
struct
mg_str
*
path_info
,
struct
http_message
*
hm
,
struct
http_message
*
hm
,
struct
mg_serve_http_opts
*
opts
)
{
struct
mg_serve_http_opts
*
opts
)
{
int
exists
,
is_directory
,
is_dav
=
mg_is_dav_request
(
&
hm
->
method
);
int
exists
,
is_directory
,
is_cgi
;
int
is_cgi
;
#if MG_ENABLE_HTTP_WEBDAV
int
is_dav
=
mg_is_dav_request
(
&
hm
->
method
);
#else
int
is_dav
=
0
;
#endif
char
*
index_file
=
NULL
;
char
*
index_file
=
NULL
;
cs_stat_t
st
;
cs_stat_t
st
;
...
@@ -6796,7 +6553,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
...
@@ -6796,7 +6553,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
mg_is_file_hidden
(
path
,
opts
,
0
/* specials are ok */
))
&&
mg_is_file_hidden
(
path
,
opts
,
0
/* specials are ok */
))
&&
!
mg_is_creation_request
(
hm
))
{
!
mg_is_creation_request
(
hm
))
{
mg_http_send_error
(
nc
,
404
,
NULL
);
mg_http_send_error
(
nc
,
404
,
NULL
);
#if
!MG_DISABLE_
DAV
#if
MG_ENABLE_HTTP_WEB
DAV
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"PROPFIND"
))
{
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"PROPFIND"
))
{
mg_handle_propfind
(
nc
,
path
,
&
st
,
hm
,
opts
);
mg_handle_propfind
(
nc
,
path
,
&
st
,
hm
,
opts
);
#if !MG_DISABLE_DAV_AUTH
#if !MG_DISABLE_DAV_AUTH
...
@@ -6819,7 +6576,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
...
@@ -6819,7 +6576,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"LOCK"
))
{
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"LOCK"
))
{
mg_handle_lock
(
nc
,
path
);
mg_handle_lock
(
nc
,
path
);
#endif
#endif
#endif
#endif
/* MG_ENABLE_HTTP_WEBDAV */
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"OPTIONS"
))
{
}
else
if
(
!
mg_vcmp
(
&
hm
->
method
,
"OPTIONS"
))
{
mg_http_send_options
(
nc
);
mg_http_send_options
(
nc
);
}
else
if
(
is_directory
&&
index_file
==
NULL
)
{
}
else
if
(
is_directory
&&
index_file
==
NULL
)
{
...
@@ -7603,6 +7360,277 @@ MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
...
@@ -7603,6 +7360,277 @@ MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
#endif
/* MG_ENABLE_HTTP && MG_ENABLE_CGI */
#endif
/* MG_ENABLE_HTTP && MG_ENABLE_CGI */
#ifdef MG_MODULE_LINES
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/http_webdav.c"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#if !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
MG_INTERNAL
int
mg_is_dav_request
(
const
struct
mg_str
*
s
)
{
static
const
char
*
methods
[]
=
{
"PUT"
,
"DELETE"
,
"MKCOL"
,
"PROPFIND"
,
"MOVE"
#if MG_ENABLE_FAKE_DAVLOCK
,
"LOCK"
,
"UNLOCK"
#endif
};
size_t
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
methods
);
i
++
)
{
if
(
mg_vcmp
(
s
,
methods
[
i
])
==
0
)
{
return
1
;
}
}
return
0
;
}
static
int
mg_mkdir
(
const
char
*
path
,
uint32_t
mode
)
{
#ifndef _WIN32
return
mkdir
(
path
,
mode
);
#else
(
void
)
mode
;
return
_mkdir
(
path
);
#endif
}
static
void
mg_print_props
(
struct
mg_connection
*
nc
,
const
char
*
name
,
cs_stat_t
*
stp
)
{
char
mtime
[
64
],
buf
[
MAX_PATH_SIZE
*
3
];
time_t
t
=
stp
->
st_mtime
;
/* store in local variable for NDK compile */
mg_gmt_time_string
(
mtime
,
sizeof
(
mtime
),
&
t
);
mg_url_encode
(
name
,
strlen
(
name
),
buf
,
sizeof
(
buf
));
mg_printf
(
nc
,
"<d:response>"
"<d:href>%s</d:href>"
"<d:propstat>"
"<d:prop>"
"<d:resourcetype>%s</d:resourcetype>"
"<d:getcontentlength>%"
INT64_FMT
"</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>"
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>
\n
"
,
buf
,
S_ISDIR
(
stp
->
st_mode
)
?
"<d:collection/>"
:
""
,
(
int64_t
)
stp
->
st_size
,
mtime
);
}
MG_INTERNAL
void
mg_handle_propfind
(
struct
mg_connection
*
nc
,
const
char
*
path
,
cs_stat_t
*
stp
,
struct
http_message
*
hm
,
struct
mg_serve_http_opts
*
opts
)
{
static
const
char
header
[]
=
"HTTP/1.1 207 Multi-Status
\r\n
"
"Connection: close
\r\n
"
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
"<?xml version=
\"
1.0
\"
encoding=
\"
utf-8
\"
?>"
"<d:multistatus xmlns:d='DAV:'>
\n
"
;
static
const
char
footer
[]
=
"</d:multistatus>
\n
"
;
const
struct
mg_str
*
depth
=
mg_get_http_header
(
hm
,
"Depth"
);
/* Print properties for the requested resource itself */
if
(
S_ISDIR
(
stp
->
st_mode
)
&&
strcmp
(
opts
->
enable_directory_listing
,
"yes"
)
!=
0
)
{
mg_printf
(
nc
,
"%s"
,
"HTTP/1.1 403 Directory Listing Denied
\r\n\r\n
"
);
}
else
{
char
uri
[
MAX_PATH_SIZE
];
mg_send
(
nc
,
header
,
sizeof
(
header
)
-
1
);
snprintf
(
uri
,
sizeof
(
uri
),
"%.*s"
,
(
int
)
hm
->
uri
.
len
,
hm
->
uri
.
p
);
mg_print_props
(
nc
,
uri
,
stp
);
if
(
S_ISDIR
(
stp
->
st_mode
)
&&
(
depth
==
NULL
||
mg_vcmp
(
depth
,
"0"
)
!=
0
))
{
mg_scan_directory
(
nc
,
path
,
opts
,
mg_print_props
);
}
mg_send
(
nc
,
footer
,
sizeof
(
footer
)
-
1
);
nc
->
flags
|=
MG_F_SEND_AND_CLOSE
;
}
}
#if MG_ENABLE_FAKE_DAVLOCK
/*
* Windows explorer (probably there are another WebDav clients like it)
* requires LOCK support in webdav. W/out this, it still works, but fails
* to save file: shows error message and offers "Save As".
* "Save as" works, but this message is very annoying.
* This is fake lock, which doesn't lock something, just returns LOCK token,
* UNLOCK always answers "OK".
* With this fake LOCK Windows Explorer looks happy and saves file.
* NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
* Windows native DAV client. This is why FAKE LOCK is not enabed by default
*/
MG_INTERNAL
void
mg_handle_lock
(
struct
mg_connection
*
nc
,
const
char
*
path
)
{
static
const
char
*
reply
=
"HTTP/1.1 207 Multi-Status
\r\n
"
"Connection: close
\r\n
"
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
"<?xml version=
\"
1.0
\"
encoding=
\"
utf-8
\"
?>"
"<d:multistatus xmlns:d='DAV:'>
\n
"
"<D:lockdiscovery>
\n
"
"<D:activelock>
\n
"
"<D:locktoken>
\n
"
"<D:href>
\n
"
"opaquelocktoken:%s%u"
"</D:href>"
"</D:locktoken>"
"</D:activelock>
\n
"
"</D:lockdiscovery>"
"</d:multistatus>
\n
"
;
mg_printf
(
nc
,
reply
,
path
,
(
unsigned
int
)
time
(
NULL
));
nc
->
flags
|=
MG_F_SEND_AND_CLOSE
;
}
#endif
MG_INTERNAL
void
mg_handle_mkcol
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
)
{
int
status_code
=
500
;
if
(
hm
->
body
.
len
!=
(
size_t
)
~
0
&&
hm
->
body
.
len
>
0
)
{
status_code
=
415
;
}
else
if
(
!
mg_mkdir
(
path
,
0755
))
{
status_code
=
201
;
}
else
if
(
errno
==
EEXIST
)
{
status_code
=
405
;
}
else
if
(
errno
==
EACCES
)
{
status_code
=
403
;
}
else
if
(
errno
==
ENOENT
)
{
status_code
=
409
;
}
else
{
status_code
=
500
;
}
mg_http_send_error
(
nc
,
status_code
,
NULL
);
}
static
int
mg_remove_directory
(
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
dir
)
{
char
path
[
MAX_PATH_SIZE
];
struct
dirent
*
dp
;
cs_stat_t
st
;
DIR
*
dirp
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
return
0
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
if
(
mg_is_file_hidden
((
const
char
*
)
dp
->
d_name
,
opts
,
1
))
{
continue
;
}
snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
mg_stat
(
path
,
&
st
);
if
(
S_ISDIR
(
st
.
st_mode
))
{
mg_remove_directory
(
opts
,
path
);
}
else
{
remove
(
path
);
}
}
closedir
(
dirp
);
rmdir
(
dir
);
return
1
;
}
MG_INTERNAL
void
mg_handle_move
(
struct
mg_connection
*
c
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
,
struct
http_message
*
hm
)
{
const
struct
mg_str
*
dest
=
mg_get_http_header
(
hm
,
"Destination"
);
if
(
dest
==
NULL
)
{
mg_http_send_error
(
c
,
411
,
NULL
);
}
else
{
const
char
*
p
=
(
char
*
)
memchr
(
dest
->
p
,
'/'
,
dest
->
len
);
if
(
p
!=
NULL
&&
p
[
1
]
==
'/'
&&
(
p
=
(
char
*
)
memchr
(
p
+
2
,
'/'
,
dest
->
p
+
dest
->
len
-
p
))
!=
NULL
)
{
char
buf
[
MAX_PATH_SIZE
];
snprintf
(
buf
,
sizeof
(
buf
),
"%s%.*s"
,
opts
->
dav_document_root
,
(
int
)
(
dest
->
p
+
dest
->
len
-
p
),
p
);
if
(
rename
(
path
,
buf
)
==
0
)
{
mg_http_send_error
(
c
,
200
,
NULL
);
}
else
{
mg_http_send_error
(
c
,
418
,
NULL
);
}
}
else
{
mg_http_send_error
(
c
,
500
,
NULL
);
}
}
}
MG_INTERNAL
void
mg_handle_delete
(
struct
mg_connection
*
nc
,
const
struct
mg_serve_http_opts
*
opts
,
const
char
*
path
)
{
cs_stat_t
st
;
if
(
mg_stat
(
path
,
&
st
)
!=
0
)
{
mg_http_send_error
(
nc
,
404
,
NULL
);
}
else
if
(
S_ISDIR
(
st
.
st_mode
))
{
mg_remove_directory
(
opts
,
path
);
mg_http_send_error
(
nc
,
204
,
NULL
);
}
else
if
(
remove
(
path
)
==
0
)
{
mg_http_send_error
(
nc
,
204
,
NULL
);
}
else
{
mg_http_send_error
(
nc
,
423
,
NULL
);
}
}
/* Return -1 on error, 1 on success. */
static
int
mg_create_itermediate_directories
(
const
char
*
path
)
{
const
char
*
s
;
/* Create intermediate directories if they do not exist */
for
(
s
=
path
+
1
;
*
s
!=
'\0'
;
s
++
)
{
if
(
*
s
==
'/'
)
{
char
buf
[
MAX_PATH_SIZE
];
cs_stat_t
st
;
snprintf
(
buf
,
sizeof
(
buf
),
"%.*s"
,
(
int
)
(
s
-
path
),
path
);
buf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
if
(
mg_stat
(
buf
,
&
st
)
!=
0
&&
mg_mkdir
(
buf
,
0755
)
!=
0
)
{
return
-
1
;
}
}
}
return
1
;
}
MG_INTERNAL
void
mg_handle_put
(
struct
mg_connection
*
nc
,
const
char
*
path
,
struct
http_message
*
hm
)
{
struct
mg_http_proto_data
*
pd
=
mg_http_get_proto_data
(
nc
);
cs_stat_t
st
;
const
struct
mg_str
*
cl_hdr
=
mg_get_http_header
(
hm
,
"Content-Length"
);
int
rc
,
status_code
=
mg_stat
(
path
,
&
st
)
==
0
?
200
:
201
;
mg_http_free_proto_data_file
(
&
pd
->
file
);
if
((
rc
=
mg_create_itermediate_directories
(
path
))
==
0
)
{
mg_printf
(
nc
,
"HTTP/1.1 %d OK
\r\n
Content-Length: 0
\r\n\r\n
"
,
status_code
);
}
else
if
(
rc
==
-
1
)
{
mg_http_send_error
(
nc
,
500
,
NULL
);
}
else
if
(
cl_hdr
==
NULL
)
{
mg_http_send_error
(
nc
,
411
,
NULL
);
}
else
if
((
pd
->
file
.
fp
=
fopen
(
path
,
"w+b"
))
==
NULL
)
{
mg_http_send_error
(
nc
,
500
,
NULL
);
}
else
{
const
struct
mg_str
*
range_hdr
=
mg_get_http_header
(
hm
,
"Content-Range"
);
int64_t
r1
=
0
,
r2
=
0
;
pd
->
file
.
type
=
DATA_PUT
;
mg_set_close_on_exec
(
fileno
(
pd
->
file
.
fp
));
pd
->
file
.
cl
=
to64
(
cl_hdr
->
p
);
if
(
range_hdr
!=
NULL
&&
mg_http_parse_range_header
(
range_hdr
,
&
r1
,
&
r2
)
>
0
)
{
status_code
=
206
;
fseeko
(
pd
->
file
.
fp
,
r1
,
SEEK_SET
);
pd
->
file
.
cl
=
r2
>
r1
?
r2
-
r1
+
1
:
pd
->
file
.
cl
-
r1
;
}
mg_printf
(
nc
,
"HTTP/1.1 %d OK
\r\n
Content-Length: 0
\r\n\r\n
"
,
status_code
);
/* Remove HTTP request from the mbuf, leave only payload */
mbuf_remove
(
&
nc
->
recv_mbuf
,
hm
->
message
.
len
-
hm
->
body
.
len
);
mg_http_transfer_file_data
(
nc
);
}
}
#endif
/* !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/util.c"
#line 1 "mongoose/src/util.c"
#endif
#endif
/*
/*
...
@@ -9772,7 +9800,7 @@ int mg_set_protocol_coap(struct mg_connection *nc) {
...
@@ -9772,7 +9800,7 @@ int mg_set_protocol_coap(struct mg_connection *nc) {
return
0
;
return
0
;
}
}
#endif
/* MG_
DIS
ABLE_COAP */
#endif
/* MG_
EN
ABLE_COAP */
#ifdef MG_MODULE_LINES
#ifdef MG_MODULE_LINES
#line 1 "common/platforms/cc3200/cc3200_libc.c"
#line 1 "common/platforms/cc3200/cc3200_libc.c"
#endif
#endif
...
...
This diff is collapsed.
Click to expand it.
mongoose.h
View file @
3b69f5d5
...
@@ -1215,10 +1215,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
...
@@ -1215,10 +1215,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
#define MG_DISABLE_HTTP_WEBSOCKET 0
#define MG_DISABLE_HTTP_WEBSOCKET 0
#endif
#endif
#ifndef MG_DISABLE_DAV
#define MG_DISABLE_DAV 0
#endif
#ifndef MG_DISABLE_PFS
#ifndef MG_DISABLE_PFS
#define MG_DISABLE_PFS 0
#define MG_DISABLE_PFS 0
#endif
#endif
...
@@ -1275,6 +1271,10 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
...
@@ -1275,6 +1271,10 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0
#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0
#endif
#endif
#ifndef MG_ENABLE_HTTP_WEBDAV
#define MG_ENABLE_HTTP_WEBDAV 0
#endif
#ifndef MG_ENABLE_IPV6
#ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0
#define MG_ENABLE_IPV6 0
#endif
#endif
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment