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
d3577b78
Commit
d3577b78
authored
Oct 01, 2013
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved auth code to separate file
parent
199e0a33
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1865 additions
and
1866 deletions
+1865
-1866
Makefile
build/Makefile
+1
-1
auth.c
build/src/auth.c
+301
-0
crypto.c
build/src/crypto.c
+196
-0
internal.h
build/src/internal.h
+2
-0
mongoose.c
build/src/mongoose.c
+0
-499
mongoose.c
mongoose.c
+1365
-1366
No files found.
build/Makefile
View file @
d3577b78
...
@@ -27,7 +27,7 @@ VERSION = $(shell perl -lne \
...
@@ -27,7 +27,7 @@ VERSION = $(shell perl -lne \
# The order in which files are listed is important
# The order in which files are listed is important
SOURCES
=
src/internal.h src/string.c src/parse_date.c src/options.c
\
SOURCES
=
src/internal.h src/string.c src/parse_date.c src/options.c
\
src/mongoose.c
src/
crypto.c src/auth.c src/
mongoose.c
TINY_SOURCES
=
../mongoose.c main.c
TINY_SOURCES
=
../mongoose.c main.c
LUA_SOURCES
=
$(TINY_SOURCES)
sqlite3.c lsqlite3.c lua_5.2.1.c
LUA_SOURCES
=
$(TINY_SOURCES)
sqlite3.c lsqlite3.c lua_5.2.1.c
...
...
build/src/auth.c
0 → 100644
View file @
d3577b78
#include "internal.h"
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static
void
bin2str
(
char
*
to
,
const
unsigned
char
*
p
,
size_t
len
)
{
static
const
char
*
hex
=
"0123456789abcdef"
;
for
(;
len
--
;
p
++
)
{
*
to
++
=
hex
[
p
[
0
]
>>
4
];
*
to
++
=
hex
[
p
[
0
]
&
0x0f
];
}
*
to
=
'\0'
;
}
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char
*
mg_md5
(
char
buf
[
33
],
...)
{
unsigned
char
hash
[
16
];
const
char
*
p
;
va_list
ap
;
MD5_CTX
ctx
;
MD5Init
(
&
ctx
);
va_start
(
ap
,
buf
);
while
((
p
=
va_arg
(
ap
,
const
char
*
))
!=
NULL
)
{
MD5Update
(
&
ctx
,
(
const
unsigned
char
*
)
p
,
(
unsigned
)
strlen
(
p
));
}
va_end
(
ap
);
MD5Final
(
hash
,
&
ctx
);
bin2str
(
buf
,
hash
,
sizeof
(
hash
));
return
buf
;
}
// Check the user's password, return 1 if OK
static
int
check_password
(
const
char
*
method
,
const
char
*
ha1
,
const
char
*
uri
,
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
cnonce
,
const
char
*
qop
,
const
char
*
response
)
{
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
// Some of the parameters may be NULL
if
(
method
==
NULL
||
nonce
==
NULL
||
nc
==
NULL
||
cnonce
==
NULL
||
qop
==
NULL
||
response
==
NULL
)
{
return
0
;
}
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// TODO(lsm): check for authentication timeout
if
(
// strcmp(dig->uri, c->ouri) != 0 ||
strlen
(
response
)
!=
32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
)
{
return
0
;
}
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
mg_md5
(
expected_response
,
ha1
,
":"
,
nonce
,
":"
,
nc
,
":"
,
cnonce
,
":"
,
qop
,
":"
,
ha2
,
NULL
);
return
mg_strcasecmp
(
response
,
expected_response
)
==
0
;
}
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static
FILE
*
open_auth_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
name
[
PATH_MAX
];
const
char
*
p
,
*
e
,
*
gpass
=
conn
->
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
];
struct
file
file
=
STRUCT_FILE_INITIALIZER
;
FILE
*
fp
=
NULL
;
if
(
gpass
!=
NULL
)
{
// Use global passwords file
fp
=
mg_fopen
(
gpass
,
"r"
);
// Important: using local struct file to test path for is_directory flag.
// If filep is used, mg_stat() makes it appear as if auth file was opened.
}
else
if
(
mg_stat
(
path
,
&
file
)
&&
file
.
is_directory
)
{
mg_snprintf
(
name
,
sizeof
(
name
),
"%s%c%s"
,
path
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
else
{
// Try to find .htpasswd in requested directory.
for
(
p
=
path
,
e
=
p
+
strlen
(
p
)
-
1
;
e
>
p
;
e
--
)
if
(
e
[
0
]
==
'/'
)
break
;
mg_snprintf
(
name
,
sizeof
(
name
),
"%.*s%c%s"
,
(
int
)
(
e
-
p
),
p
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
return
fp
;
}
// Parsed Authorization header
struct
ah
{
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
};
// Return 1 on success. Always initializes the ah structure.
static
int
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
size_t
buf_size
,
struct
ah
*
ah
)
{
char
*
name
,
*
value
,
*
s
;
const
char
*
auth_header
;
(
void
)
memset
(
ah
,
0
,
sizeof
(
*
ah
));
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
return
0
;
}
// Make modifiable copy of the auth header
(
void
)
mg_strlcpy
(
buf
,
auth_header
+
7
,
buf_size
);
s
=
buf
;
// Parse authorization header
for
(;;)
{
// Gobble initial spaces
while
(
isspace
(
*
(
unsigned
char
*
)
s
))
{
s
++
;
}
name
=
skip_quoted
(
&
s
,
"="
,
" "
,
0
);
// Value is either quote-delimited, or ends at first comma or space.
if
(
s
[
0
]
==
'\"'
)
{
s
++
;
value
=
skip_quoted
(
&
s
,
"
\"
"
,
" "
,
'\\'
);
if
(
s
[
0
]
==
','
)
{
s
++
;
}
}
else
{
value
=
skip_quoted
(
&
s
,
", "
,
" "
,
0
);
// IE uses commas, FF uses spaces
}
if
(
*
name
==
'\0'
)
{
break
;
}
if
(
!
strcmp
(
name
,
"username"
))
{
ah
->
user
=
value
;
}
else
if
(
!
strcmp
(
name
,
"cnonce"
))
{
ah
->
cnonce
=
value
;
}
else
if
(
!
strcmp
(
name
,
"response"
))
{
ah
->
response
=
value
;
}
else
if
(
!
strcmp
(
name
,
"uri"
))
{
ah
->
uri
=
value
;
}
else
if
(
!
strcmp
(
name
,
"qop"
))
{
ah
->
qop
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nc"
))
{
ah
->
nc
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nonce"
))
{
ah
->
nonce
=
value
;
}
}
// CGI needs it as REMOTE_USER
if
(
ah
->
user
!=
NULL
)
{
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
}
else
{
return
0
;
}
return
1
;
}
// Authorize against the opened passwords file. Return 1 if authorized.
static
int
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
struct
ah
ah
;
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MG_BUF_LEN
];
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
return
0
;
}
// Loop over passwords file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%s"
,
f_user
,
f_domain
,
ha1
)
!=
3
)
{
continue
;
}
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
!
strcmp
(
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
f_domain
))
return
check_password
(
conn
->
request_info
.
request_method
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ah
.
response
);
}
return
0
;
}
// Return 1 if request is authorised, 0 otherwise.
static
int
check_authorization
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
fname
[
PATH_MAX
];
struct
vec
uri_vec
,
filename_vec
;
const
char
*
list
;
FILE
*
fp
=
NULL
;
int
authorized
=
1
;
list
=
conn
->
ctx
->
config
[
PROTECT_URI
];
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
mg_snprintf
(
fname
,
sizeof
(
fname
),
"%.*s"
,
(
int
)
filename_vec
.
len
,
filename_vec
.
ptr
);
fp
=
mg_fopen
(
fname
,
"r"
);
break
;
}
}
if
(
fp
==
NULL
)
{
fp
=
open_auth_file
(
conn
,
path
);
}
if
(
fp
!=
NULL
)
{
authorized
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
authorized
;
}
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
conn
->
status_code
=
401
;
mg_printf
(
conn
,
"HTTP/1.1 401 Unauthorized
\r\n
"
"Content-Length: 0
\r\n
"
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
(
unsigned
long
)
time
(
NULL
));
}
static
int
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
const
char
*
passfile
=
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
];
FILE
*
fp
;
int
ret
=
0
;
if
(
passfile
!=
NULL
&&
(
fp
=
mg_fopen
(
passfile
,
"r"
))
!=
NULL
)
{
ret
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
ret
;
}
int
mg_modify_passwords_file
(
const
char
*
fname
,
const
char
*
domain
,
const
char
*
user
,
const
char
*
pass
)
{
int
found
;
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
FILE
*
fp
,
*
fp2
;
found
=
0
;
fp
=
fp2
=
NULL
;
// Regard empty password as no password - remove user record.
if
(
pass
!=
NULL
&&
pass
[
0
]
==
'\0'
)
{
pass
=
NULL
;
}
(
void
)
snprintf
(
tmp
,
sizeof
(
tmp
),
"%s.tmp"
,
fname
);
// Create the file if does not exist
if
((
fp
=
fopen
(
fname
,
"a+"
))
!=
NULL
)
{
fclose
(
fp
);
}
// Open the given file and temporary file
if
((
fp
=
fopen
(
fname
,
"r"
))
==
NULL
)
{
return
0
;
}
else
if
((
fp2
=
fopen
(
tmp
,
"w+"
))
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
// Copy the stuff to temporary file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%*s"
,
u
,
d
)
!=
2
)
{
continue
;
}
if
(
!
strcmp
(
u
,
user
)
&&
!
strcmp
(
d
,
domain
))
{
found
++
;
if
(
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
}
else
{
fprintf
(
fp2
,
"%s"
,
line
);
}
}
// If new user, just add it
if
(
!
found
&&
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
// Close files
fclose
(
fp
);
fclose
(
fp2
);
// Put the temp file in place of real file
remove
(
fname
);
rename
(
tmp
,
fname
);
return
1
;
}
build/src/crypto.c
0 → 100644
View file @
d3577b78
static
int
is_big_endian
(
void
)
{
static
const
int
n
=
1
;
return
((
char
*
)
&
n
)[
0
]
==
0
;
}
#ifndef HAVE_MD5
typedef
struct
MD5Context
{
uint32_t
buf
[
4
];
uint32_t
bits
[
2
];
unsigned
char
in
[
64
];
}
MD5_CTX
;
static
void
byteReverse
(
unsigned
char
*
buf
,
unsigned
longs
)
{
uint32_t
t
;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if
(
is_big_endian
())
{
do
{
t
=
(
uint32_t
)
((
unsigned
)
buf
[
3
]
<<
8
|
buf
[
2
])
<<
16
|
((
unsigned
)
buf
[
1
]
<<
8
|
buf
[
0
]);
*
(
uint32_t
*
)
buf
=
t
;
buf
+=
4
;
}
while
(
--
longs
);
}
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
// initialization constants.
static
void
MD5Init
(
MD5_CTX
*
ctx
)
{
ctx
->
buf
[
0
]
=
0x67452301
;
ctx
->
buf
[
1
]
=
0xefcdab89
;
ctx
->
buf
[
2
]
=
0x98badcfe
;
ctx
->
buf
[
3
]
=
0x10325476
;
ctx
->
bits
[
0
]
=
0
;
ctx
->
bits
[
1
]
=
0
;
}
static
void
MD5Transform
(
uint32_t
buf
[
4
],
uint32_t
const
in
[
16
])
{
register
uint32_t
a
,
b
,
c
,
d
;
a
=
buf
[
0
];
b
=
buf
[
1
];
c
=
buf
[
2
];
d
=
buf
[
3
];
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xd76aa478
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
1
]
+
0xe8c7b756
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x242070db
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
3
]
+
0xc1bdceee
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf57c0faf
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
5
]
+
0x4787c62a
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa8304613
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
7
]
+
0xfd469501
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x698098d8
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
9
]
+
0x8b44f7af
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffff5bb1
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
11
]
+
0x895cd7be
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x6b901122
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
13
]
+
0xfd987193
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xa679438e
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
15
]
+
0x49b40821
,
22
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xf61e2562
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
6
]
+
0xc040b340
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x265e5a51
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
0
]
+
0xe9b6c7aa
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xd62f105d
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
10
]
+
0x02441453
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0xd8a1e681
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
4
]
+
0xe7d3fbc8
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0x21e1cde6
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
14
]
+
0xc33707d6
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xf4d50d87
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
8
]
+
0x455a14ed
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0xa9e3e905
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
2
]
+
0xfcefa3f8
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0x676f02d9
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
12
]
+
0x8d2a4c8a
,
20
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xfffa3942
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
8
]
+
0x8771f681
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x6d9d6122
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
14
]
+
0xfde5380c
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xa4beea44
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
4
]
+
0x4bdecfa9
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0xf6bb4b60
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
10
]
+
0xbebfbc70
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0x289b7ec6
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
0
]
+
0xeaa127fa
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xd4ef3085
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
6
]
+
0x04881d05
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0xd9d4d039
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
12
]
+
0xe6db99e5
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0x1fa27cf8
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
2
]
+
0xc4ac5665
,
23
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xf4292244
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
7
]
+
0x432aff97
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xab9423a7
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
5
]
+
0xfc93a039
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x655b59c3
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
3
]
+
0x8f0ccc92
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffeff47d
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
1
]
+
0x85845dd1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x6fa87e4f
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
15
]
+
0xfe2ce6e0
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa3014314
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
13
]
+
0x4e0811a1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf7537e82
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
11
]
+
0xbd3af235
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x2ad7d2bb
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
9
]
+
0xeb86d391
,
21
);
buf
[
0
]
+=
a
;
buf
[
1
]
+=
b
;
buf
[
2
]
+=
c
;
buf
[
3
]
+=
d
;
}
static
void
MD5Update
(
MD5_CTX
*
ctx
,
unsigned
char
const
*
buf
,
unsigned
len
)
{
uint32_t
t
;
t
=
ctx
->
bits
[
0
];
if
((
ctx
->
bits
[
0
]
=
t
+
((
uint32_t
)
len
<<
3
))
<
t
)
ctx
->
bits
[
1
]
++
;
ctx
->
bits
[
1
]
+=
len
>>
29
;
t
=
(
t
>>
3
)
&
0x3f
;
if
(
t
)
{
unsigned
char
*
p
=
(
unsigned
char
*
)
ctx
->
in
+
t
;
t
=
64
-
t
;
if
(
len
<
t
)
{
memcpy
(
p
,
buf
,
len
);
return
;
}
memcpy
(
p
,
buf
,
t
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
t
;
len
-=
t
;
}
while
(
len
>=
64
)
{
memcpy
(
ctx
->
in
,
buf
,
64
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
64
;
len
-=
64
;
}
memcpy
(
ctx
->
in
,
buf
,
len
);
}
static
void
MD5Final
(
unsigned
char
digest
[
16
],
MD5_CTX
*
ctx
)
{
unsigned
count
;
unsigned
char
*
p
;
uint32_t
*
a
;
count
=
(
ctx
->
bits
[
0
]
>>
3
)
&
0x3F
;
p
=
ctx
->
in
+
count
;
*
p
++
=
0x80
;
count
=
64
-
1
-
count
;
if
(
count
<
8
)
{
memset
(
p
,
0
,
count
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
memset
(
ctx
->
in
,
0
,
56
);
}
else
{
memset
(
p
,
0
,
count
-
8
);
}
byteReverse
(
ctx
->
in
,
14
);
a
=
(
uint32_t
*
)
ctx
->
in
;
a
[
14
]
=
ctx
->
bits
[
0
];
a
[
15
]
=
ctx
->
bits
[
1
];
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
byteReverse
((
unsigned
char
*
)
ctx
->
buf
,
4
);
memcpy
(
digest
,
ctx
->
buf
,
16
);
memset
((
char
*
)
ctx
,
0
,
sizeof
(
*
ctx
));
}
#endif // !HAVE_MD5
build/src/internal.h
View file @
d3577b78
...
@@ -458,3 +458,5 @@ struct de {
...
@@ -458,3 +458,5 @@ struct de {
struct
file
file
;
struct
file
file
;
};
};
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
);
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
);
build/src/mongoose.c
View file @
d3577b78
...
@@ -1205,505 +1205,6 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
...
@@ -1205,505 +1205,6 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
vec
->
len
=
strlen
(
vec
->
ptr
);
vec
->
len
=
strlen
(
vec
->
ptr
);
}
}
static
int
is_big_endian
(
void
)
{
static
const
int
n
=
1
;
return
((
char
*
)
&
n
)[
0
]
==
0
;
}
#ifndef HAVE_MD5
typedef
struct
MD5Context
{
uint32_t
buf
[
4
];
uint32_t
bits
[
2
];
unsigned
char
in
[
64
];
}
MD5_CTX
;
static
void
byteReverse
(
unsigned
char
*
buf
,
unsigned
longs
)
{
uint32_t
t
;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if
(
is_big_endian
())
{
do
{
t
=
(
uint32_t
)
((
unsigned
)
buf
[
3
]
<<
8
|
buf
[
2
])
<<
16
|
((
unsigned
)
buf
[
1
]
<<
8
|
buf
[
0
]);
*
(
uint32_t
*
)
buf
=
t
;
buf
+=
4
;
}
while
(
--
longs
);
}
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
// initialization constants.
static
void
MD5Init
(
MD5_CTX
*
ctx
)
{
ctx
->
buf
[
0
]
=
0x67452301
;
ctx
->
buf
[
1
]
=
0xefcdab89
;
ctx
->
buf
[
2
]
=
0x98badcfe
;
ctx
->
buf
[
3
]
=
0x10325476
;
ctx
->
bits
[
0
]
=
0
;
ctx
->
bits
[
1
]
=
0
;
}
static
void
MD5Transform
(
uint32_t
buf
[
4
],
uint32_t
const
in
[
16
])
{
register
uint32_t
a
,
b
,
c
,
d
;
a
=
buf
[
0
];
b
=
buf
[
1
];
c
=
buf
[
2
];
d
=
buf
[
3
];
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xd76aa478
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
1
]
+
0xe8c7b756
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x242070db
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
3
]
+
0xc1bdceee
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf57c0faf
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
5
]
+
0x4787c62a
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa8304613
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
7
]
+
0xfd469501
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x698098d8
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
9
]
+
0x8b44f7af
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffff5bb1
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
11
]
+
0x895cd7be
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x6b901122
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
13
]
+
0xfd987193
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xa679438e
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
15
]
+
0x49b40821
,
22
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xf61e2562
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
6
]
+
0xc040b340
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x265e5a51
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
0
]
+
0xe9b6c7aa
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xd62f105d
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
10
]
+
0x02441453
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0xd8a1e681
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
4
]
+
0xe7d3fbc8
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0x21e1cde6
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
14
]
+
0xc33707d6
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xf4d50d87
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
8
]
+
0x455a14ed
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0xa9e3e905
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
2
]
+
0xfcefa3f8
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0x676f02d9
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
12
]
+
0x8d2a4c8a
,
20
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xfffa3942
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
8
]
+
0x8771f681
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x6d9d6122
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
14
]
+
0xfde5380c
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xa4beea44
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
4
]
+
0x4bdecfa9
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0xf6bb4b60
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
10
]
+
0xbebfbc70
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0x289b7ec6
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
0
]
+
0xeaa127fa
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xd4ef3085
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
6
]
+
0x04881d05
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0xd9d4d039
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
12
]
+
0xe6db99e5
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0x1fa27cf8
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
2
]
+
0xc4ac5665
,
23
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xf4292244
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
7
]
+
0x432aff97
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xab9423a7
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
5
]
+
0xfc93a039
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x655b59c3
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
3
]
+
0x8f0ccc92
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffeff47d
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
1
]
+
0x85845dd1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x6fa87e4f
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
15
]
+
0xfe2ce6e0
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa3014314
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
13
]
+
0x4e0811a1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf7537e82
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
11
]
+
0xbd3af235
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x2ad7d2bb
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
9
]
+
0xeb86d391
,
21
);
buf
[
0
]
+=
a
;
buf
[
1
]
+=
b
;
buf
[
2
]
+=
c
;
buf
[
3
]
+=
d
;
}
static
void
MD5Update
(
MD5_CTX
*
ctx
,
unsigned
char
const
*
buf
,
unsigned
len
)
{
uint32_t
t
;
t
=
ctx
->
bits
[
0
];
if
((
ctx
->
bits
[
0
]
=
t
+
((
uint32_t
)
len
<<
3
))
<
t
)
ctx
->
bits
[
1
]
++
;
ctx
->
bits
[
1
]
+=
len
>>
29
;
t
=
(
t
>>
3
)
&
0x3f
;
if
(
t
)
{
unsigned
char
*
p
=
(
unsigned
char
*
)
ctx
->
in
+
t
;
t
=
64
-
t
;
if
(
len
<
t
)
{
memcpy
(
p
,
buf
,
len
);
return
;
}
memcpy
(
p
,
buf
,
t
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
t
;
len
-=
t
;
}
while
(
len
>=
64
)
{
memcpy
(
ctx
->
in
,
buf
,
64
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
64
;
len
-=
64
;
}
memcpy
(
ctx
->
in
,
buf
,
len
);
}
static
void
MD5Final
(
unsigned
char
digest
[
16
],
MD5_CTX
*
ctx
)
{
unsigned
count
;
unsigned
char
*
p
;
uint32_t
*
a
;
count
=
(
ctx
->
bits
[
0
]
>>
3
)
&
0x3F
;
p
=
ctx
->
in
+
count
;
*
p
++
=
0x80
;
count
=
64
-
1
-
count
;
if
(
count
<
8
)
{
memset
(
p
,
0
,
count
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
memset
(
ctx
->
in
,
0
,
56
);
}
else
{
memset
(
p
,
0
,
count
-
8
);
}
byteReverse
(
ctx
->
in
,
14
);
a
=
(
uint32_t
*
)
ctx
->
in
;
a
[
14
]
=
ctx
->
bits
[
0
];
a
[
15
]
=
ctx
->
bits
[
1
];
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
byteReverse
((
unsigned
char
*
)
ctx
->
buf
,
4
);
memcpy
(
digest
,
ctx
->
buf
,
16
);
memset
((
char
*
)
ctx
,
0
,
sizeof
(
*
ctx
));
}
#endif // !HAVE_MD5
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static
void
bin2str
(
char
*
to
,
const
unsigned
char
*
p
,
size_t
len
)
{
static
const
char
*
hex
=
"0123456789abcdef"
;
for
(;
len
--
;
p
++
)
{
*
to
++
=
hex
[
p
[
0
]
>>
4
];
*
to
++
=
hex
[
p
[
0
]
&
0x0f
];
}
*
to
=
'\0'
;
}
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char
*
mg_md5
(
char
buf
[
33
],
...)
{
unsigned
char
hash
[
16
];
const
char
*
p
;
va_list
ap
;
MD5_CTX
ctx
;
MD5Init
(
&
ctx
);
va_start
(
ap
,
buf
);
while
((
p
=
va_arg
(
ap
,
const
char
*
))
!=
NULL
)
{
MD5Update
(
&
ctx
,
(
const
unsigned
char
*
)
p
,
(
unsigned
)
strlen
(
p
));
}
va_end
(
ap
);
MD5Final
(
hash
,
&
ctx
);
bin2str
(
buf
,
hash
,
sizeof
(
hash
));
return
buf
;
}
// Check the user's password, return 1 if OK
static
int
check_password
(
const
char
*
method
,
const
char
*
ha1
,
const
char
*
uri
,
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
cnonce
,
const
char
*
qop
,
const
char
*
response
)
{
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
// Some of the parameters may be NULL
if
(
method
==
NULL
||
nonce
==
NULL
||
nc
==
NULL
||
cnonce
==
NULL
||
qop
==
NULL
||
response
==
NULL
)
{
return
0
;
}
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// TODO(lsm): check for authentication timeout
if
(
// strcmp(dig->uri, c->ouri) != 0 ||
strlen
(
response
)
!=
32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
)
{
return
0
;
}
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
mg_md5
(
expected_response
,
ha1
,
":"
,
nonce
,
":"
,
nc
,
":"
,
cnonce
,
":"
,
qop
,
":"
,
ha2
,
NULL
);
return
mg_strcasecmp
(
response
,
expected_response
)
==
0
;
}
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static
FILE
*
open_auth_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
name
[
PATH_MAX
];
const
char
*
p
,
*
e
,
*
gpass
=
conn
->
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
];
struct
file
file
=
STRUCT_FILE_INITIALIZER
;
FILE
*
fp
=
NULL
;
if
(
gpass
!=
NULL
)
{
// Use global passwords file
if
((
fp
=
mg_fopen
(
gpass
,
"r"
))
==
NULL
)
{
cry
(
conn
,
"fopen(%s): %s"
,
gpass
,
strerror
(
ERRNO
));
}
// Important: using local struct file to test path for is_directory flag.
// If filep is used, mg_stat() makes it appear as if auth file was opened.
}
else
if
(
mg_stat
(
path
,
&
file
)
&&
file
.
is_directory
)
{
mg_snprintf
(
name
,
sizeof
(
name
),
"%s%c%s"
,
path
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
else
{
// Try to find .htpasswd in requested directory.
for
(
p
=
path
,
e
=
p
+
strlen
(
p
)
-
1
;
e
>
p
;
e
--
)
if
(
e
[
0
]
==
'/'
)
break
;
mg_snprintf
(
name
,
sizeof
(
name
),
"%.*s%c%s"
,
(
int
)
(
e
-
p
),
p
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
return
fp
;
}
// Parsed Authorization header
struct
ah
{
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
};
// Return 1 on success. Always initializes the ah structure.
static
int
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
size_t
buf_size
,
struct
ah
*
ah
)
{
char
*
name
,
*
value
,
*
s
;
const
char
*
auth_header
;
(
void
)
memset
(
ah
,
0
,
sizeof
(
*
ah
));
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
return
0
;
}
// Make modifiable copy of the auth header
(
void
)
mg_strlcpy
(
buf
,
auth_header
+
7
,
buf_size
);
s
=
buf
;
// Parse authorization header
for
(;;)
{
// Gobble initial spaces
while
(
isspace
(
*
(
unsigned
char
*
)
s
))
{
s
++
;
}
name
=
skip_quoted
(
&
s
,
"="
,
" "
,
0
);
// Value is either quote-delimited, or ends at first comma or space.
if
(
s
[
0
]
==
'\"'
)
{
s
++
;
value
=
skip_quoted
(
&
s
,
"
\"
"
,
" "
,
'\\'
);
if
(
s
[
0
]
==
','
)
{
s
++
;
}
}
else
{
value
=
skip_quoted
(
&
s
,
", "
,
" "
,
0
);
// IE uses commas, FF uses spaces
}
if
(
*
name
==
'\0'
)
{
break
;
}
if
(
!
strcmp
(
name
,
"username"
))
{
ah
->
user
=
value
;
}
else
if
(
!
strcmp
(
name
,
"cnonce"
))
{
ah
->
cnonce
=
value
;
}
else
if
(
!
strcmp
(
name
,
"response"
))
{
ah
->
response
=
value
;
}
else
if
(
!
strcmp
(
name
,
"uri"
))
{
ah
->
uri
=
value
;
}
else
if
(
!
strcmp
(
name
,
"qop"
))
{
ah
->
qop
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nc"
))
{
ah
->
nc
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nonce"
))
{
ah
->
nonce
=
value
;
}
}
// CGI needs it as REMOTE_USER
if
(
ah
->
user
!=
NULL
)
{
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
}
else
{
return
0
;
}
return
1
;
}
// Authorize against the opened passwords file. Return 1 if authorized.
static
int
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
struct
ah
ah
;
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MG_BUF_LEN
];
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
return
0
;
}
// Loop over passwords file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%s"
,
f_user
,
f_domain
,
ha1
)
!=
3
)
{
continue
;
}
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
!
strcmp
(
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
f_domain
))
return
check_password
(
conn
->
request_info
.
request_method
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ah
.
response
);
}
return
0
;
}
// Return 1 if request is authorised, 0 otherwise.
static
int
check_authorization
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
fname
[
PATH_MAX
];
struct
vec
uri_vec
,
filename_vec
;
const
char
*
list
;
FILE
*
fp
=
NULL
;
int
authorized
=
1
;
list
=
conn
->
ctx
->
config
[
PROTECT_URI
];
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
mg_snprintf
(
fname
,
sizeof
(
fname
),
"%.*s"
,
(
int
)
filename_vec
.
len
,
filename_vec
.
ptr
);
if
((
fp
=
mg_fopen
(
fname
,
"r"
))
==
NULL
)
{
cry
(
conn
,
"%s: cannot open %s: %s"
,
__func__
,
fname
,
strerror
(
errno
));
}
break
;
}
}
if
(
fp
==
NULL
)
{
fp
=
open_auth_file
(
conn
,
path
);
}
if
(
fp
!=
NULL
)
{
authorized
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
authorized
;
}
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
conn
->
status_code
=
401
;
mg_printf
(
conn
,
"HTTP/1.1 401 Unauthorized
\r\n
"
"Content-Length: 0
\r\n
"
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
(
unsigned
long
)
time
(
NULL
));
}
static
int
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
const
char
*
passfile
=
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
];
FILE
*
fp
;
int
ret
=
0
;
if
(
passfile
!=
NULL
&&
(
fp
=
mg_fopen
(
passfile
,
"r"
))
!=
NULL
)
{
ret
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
ret
;
}
int
mg_modify_passwords_file
(
const
char
*
fname
,
const
char
*
domain
,
const
char
*
user
,
const
char
*
pass
)
{
int
found
;
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
FILE
*
fp
,
*
fp2
;
found
=
0
;
fp
=
fp2
=
NULL
;
// Regard empty password as no password - remove user record.
if
(
pass
!=
NULL
&&
pass
[
0
]
==
'\0'
)
{
pass
=
NULL
;
}
(
void
)
snprintf
(
tmp
,
sizeof
(
tmp
),
"%s.tmp"
,
fname
);
// Create the file if does not exist
if
((
fp
=
fopen
(
fname
,
"a+"
))
!=
NULL
)
{
fclose
(
fp
);
}
// Open the given file and temporary file
if
((
fp
=
fopen
(
fname
,
"r"
))
==
NULL
)
{
return
0
;
}
else
if
((
fp2
=
fopen
(
tmp
,
"w+"
))
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
// Copy the stuff to temporary file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%*s"
,
u
,
d
)
!=
2
)
{
continue
;
}
if
(
!
strcmp
(
u
,
user
)
&&
!
strcmp
(
d
,
domain
))
{
found
++
;
if
(
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
}
else
{
fprintf
(
fp2
,
"%s"
,
line
);
}
}
// If new user, just add it
if
(
!
found
&&
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
// Close files
fclose
(
fp
);
fclose
(
fp2
);
// Put the temp file in place of real file
remove
(
fname
);
rename
(
tmp
,
fname
);
return
1
;
}
static
SOCKET
conn2
(
const
char
*
host
,
int
port
,
int
use_ssl
,
static
SOCKET
conn2
(
const
char
*
host
,
int
port
,
int
use_ssl
,
char
*
ebuf
,
size_t
ebuf_len
)
{
char
*
ebuf
,
size_t
ebuf_len
)
{
struct
sockaddr_in
sin
;
struct
sockaddr_in
sin
;
...
...
mongoose.c
View file @
d3577b78
...
@@ -458,6 +458,8 @@ struct de {
...
@@ -458,6 +458,8 @@ struct de {
struct
file
file
;
struct
file
file
;
};
};
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
);
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
);
static
void
mg_strlcpy
(
register
char
*
dst
,
register
const
char
*
src
,
size_t
n
)
{
static
void
mg_strlcpy
(
register
char
*
dst
,
register
const
char
*
src
,
size_t
n
)
{
for
(;
*
src
!=
'\0'
&&
n
>
1
;
n
--
)
{
for
(;
*
src
!=
'\0'
&&
n
>
1
;
n
--
)
{
...
@@ -806,6 +808,502 @@ const char *mg_get_option(const struct mg_context *ctx, const char *name) {
...
@@ -806,6 +808,502 @@ const char *mg_get_option(const struct mg_context *ctx, const char *name) {
return
ctx
->
config
[
i
];
return
ctx
->
config
[
i
];
}
}
}
}
static
int
is_big_endian
(
void
)
{
static
const
int
n
=
1
;
return
((
char
*
)
&
n
)[
0
]
==
0
;
}
#ifndef HAVE_MD5
typedef
struct
MD5Context
{
uint32_t
buf
[
4
];
uint32_t
bits
[
2
];
unsigned
char
in
[
64
];
}
MD5_CTX
;
static
void
byteReverse
(
unsigned
char
*
buf
,
unsigned
longs
)
{
uint32_t
t
;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if
(
is_big_endian
())
{
do
{
t
=
(
uint32_t
)
((
unsigned
)
buf
[
3
]
<<
8
|
buf
[
2
])
<<
16
|
((
unsigned
)
buf
[
1
]
<<
8
|
buf
[
0
]);
*
(
uint32_t
*
)
buf
=
t
;
buf
+=
4
;
}
while
(
--
longs
);
}
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
// initialization constants.
static
void
MD5Init
(
MD5_CTX
*
ctx
)
{
ctx
->
buf
[
0
]
=
0x67452301
;
ctx
->
buf
[
1
]
=
0xefcdab89
;
ctx
->
buf
[
2
]
=
0x98badcfe
;
ctx
->
buf
[
3
]
=
0x10325476
;
ctx
->
bits
[
0
]
=
0
;
ctx
->
bits
[
1
]
=
0
;
}
static
void
MD5Transform
(
uint32_t
buf
[
4
],
uint32_t
const
in
[
16
])
{
register
uint32_t
a
,
b
,
c
,
d
;
a
=
buf
[
0
];
b
=
buf
[
1
];
c
=
buf
[
2
];
d
=
buf
[
3
];
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xd76aa478
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
1
]
+
0xe8c7b756
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x242070db
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
3
]
+
0xc1bdceee
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf57c0faf
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
5
]
+
0x4787c62a
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa8304613
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
7
]
+
0xfd469501
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x698098d8
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
9
]
+
0x8b44f7af
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffff5bb1
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
11
]
+
0x895cd7be
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x6b901122
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
13
]
+
0xfd987193
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xa679438e
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
15
]
+
0x49b40821
,
22
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xf61e2562
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
6
]
+
0xc040b340
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x265e5a51
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
0
]
+
0xe9b6c7aa
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xd62f105d
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
10
]
+
0x02441453
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0xd8a1e681
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
4
]
+
0xe7d3fbc8
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0x21e1cde6
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
14
]
+
0xc33707d6
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xf4d50d87
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
8
]
+
0x455a14ed
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0xa9e3e905
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
2
]
+
0xfcefa3f8
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0x676f02d9
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
12
]
+
0x8d2a4c8a
,
20
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xfffa3942
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
8
]
+
0x8771f681
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x6d9d6122
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
14
]
+
0xfde5380c
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xa4beea44
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
4
]
+
0x4bdecfa9
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0xf6bb4b60
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
10
]
+
0xbebfbc70
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0x289b7ec6
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
0
]
+
0xeaa127fa
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xd4ef3085
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
6
]
+
0x04881d05
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0xd9d4d039
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
12
]
+
0xe6db99e5
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0x1fa27cf8
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
2
]
+
0xc4ac5665
,
23
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xf4292244
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
7
]
+
0x432aff97
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xab9423a7
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
5
]
+
0xfc93a039
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x655b59c3
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
3
]
+
0x8f0ccc92
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffeff47d
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
1
]
+
0x85845dd1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x6fa87e4f
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
15
]
+
0xfe2ce6e0
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa3014314
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
13
]
+
0x4e0811a1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf7537e82
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
11
]
+
0xbd3af235
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x2ad7d2bb
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
9
]
+
0xeb86d391
,
21
);
buf
[
0
]
+=
a
;
buf
[
1
]
+=
b
;
buf
[
2
]
+=
c
;
buf
[
3
]
+=
d
;
}
static
void
MD5Update
(
MD5_CTX
*
ctx
,
unsigned
char
const
*
buf
,
unsigned
len
)
{
uint32_t
t
;
t
=
ctx
->
bits
[
0
];
if
((
ctx
->
bits
[
0
]
=
t
+
((
uint32_t
)
len
<<
3
))
<
t
)
ctx
->
bits
[
1
]
++
;
ctx
->
bits
[
1
]
+=
len
>>
29
;
t
=
(
t
>>
3
)
&
0x3f
;
if
(
t
)
{
unsigned
char
*
p
=
(
unsigned
char
*
)
ctx
->
in
+
t
;
t
=
64
-
t
;
if
(
len
<
t
)
{
memcpy
(
p
,
buf
,
len
);
return
;
}
memcpy
(
p
,
buf
,
t
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
t
;
len
-=
t
;
}
while
(
len
>=
64
)
{
memcpy
(
ctx
->
in
,
buf
,
64
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
64
;
len
-=
64
;
}
memcpy
(
ctx
->
in
,
buf
,
len
);
}
static
void
MD5Final
(
unsigned
char
digest
[
16
],
MD5_CTX
*
ctx
)
{
unsigned
count
;
unsigned
char
*
p
;
uint32_t
*
a
;
count
=
(
ctx
->
bits
[
0
]
>>
3
)
&
0x3F
;
p
=
ctx
->
in
+
count
;
*
p
++
=
0x80
;
count
=
64
-
1
-
count
;
if
(
count
<
8
)
{
memset
(
p
,
0
,
count
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
memset
(
ctx
->
in
,
0
,
56
);
}
else
{
memset
(
p
,
0
,
count
-
8
);
}
byteReverse
(
ctx
->
in
,
14
);
a
=
(
uint32_t
*
)
ctx
->
in
;
a
[
14
]
=
ctx
->
bits
[
0
];
a
[
15
]
=
ctx
->
bits
[
1
];
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
byteReverse
((
unsigned
char
*
)
ctx
->
buf
,
4
);
memcpy
(
digest
,
ctx
->
buf
,
16
);
memset
((
char
*
)
ctx
,
0
,
sizeof
(
*
ctx
));
}
#endif // !HAVE_MD5
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static
void
bin2str
(
char
*
to
,
const
unsigned
char
*
p
,
size_t
len
)
{
static
const
char
*
hex
=
"0123456789abcdef"
;
for
(;
len
--
;
p
++
)
{
*
to
++
=
hex
[
p
[
0
]
>>
4
];
*
to
++
=
hex
[
p
[
0
]
&
0x0f
];
}
*
to
=
'\0'
;
}
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char
*
mg_md5
(
char
buf
[
33
],
...)
{
unsigned
char
hash
[
16
];
const
char
*
p
;
va_list
ap
;
MD5_CTX
ctx
;
MD5Init
(
&
ctx
);
va_start
(
ap
,
buf
);
while
((
p
=
va_arg
(
ap
,
const
char
*
))
!=
NULL
)
{
MD5Update
(
&
ctx
,
(
const
unsigned
char
*
)
p
,
(
unsigned
)
strlen
(
p
));
}
va_end
(
ap
);
MD5Final
(
hash
,
&
ctx
);
bin2str
(
buf
,
hash
,
sizeof
(
hash
));
return
buf
;
}
// Check the user's password, return 1 if OK
static
int
check_password
(
const
char
*
method
,
const
char
*
ha1
,
const
char
*
uri
,
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
cnonce
,
const
char
*
qop
,
const
char
*
response
)
{
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
// Some of the parameters may be NULL
if
(
method
==
NULL
||
nonce
==
NULL
||
nc
==
NULL
||
cnonce
==
NULL
||
qop
==
NULL
||
response
==
NULL
)
{
return
0
;
}
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// TODO(lsm): check for authentication timeout
if
(
// strcmp(dig->uri, c->ouri) != 0 ||
strlen
(
response
)
!=
32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
)
{
return
0
;
}
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
mg_md5
(
expected_response
,
ha1
,
":"
,
nonce
,
":"
,
nc
,
":"
,
cnonce
,
":"
,
qop
,
":"
,
ha2
,
NULL
);
return
mg_strcasecmp
(
response
,
expected_response
)
==
0
;
}
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static
FILE
*
open_auth_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
name
[
PATH_MAX
];
const
char
*
p
,
*
e
,
*
gpass
=
conn
->
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
];
struct
file
file
=
STRUCT_FILE_INITIALIZER
;
FILE
*
fp
=
NULL
;
if
(
gpass
!=
NULL
)
{
// Use global passwords file
fp
=
mg_fopen
(
gpass
,
"r"
);
// Important: using local struct file to test path for is_directory flag.
// If filep is used, mg_stat() makes it appear as if auth file was opened.
}
else
if
(
mg_stat
(
path
,
&
file
)
&&
file
.
is_directory
)
{
mg_snprintf
(
name
,
sizeof
(
name
),
"%s%c%s"
,
path
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
else
{
// Try to find .htpasswd in requested directory.
for
(
p
=
path
,
e
=
p
+
strlen
(
p
)
-
1
;
e
>
p
;
e
--
)
if
(
e
[
0
]
==
'/'
)
break
;
mg_snprintf
(
name
,
sizeof
(
name
),
"%.*s%c%s"
,
(
int
)
(
e
-
p
),
p
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
return
fp
;
}
// Parsed Authorization header
struct
ah
{
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
};
// Return 1 on success. Always initializes the ah structure.
static
int
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
size_t
buf_size
,
struct
ah
*
ah
)
{
char
*
name
,
*
value
,
*
s
;
const
char
*
auth_header
;
(
void
)
memset
(
ah
,
0
,
sizeof
(
*
ah
));
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
return
0
;
}
// Make modifiable copy of the auth header
(
void
)
mg_strlcpy
(
buf
,
auth_header
+
7
,
buf_size
);
s
=
buf
;
// Parse authorization header
for
(;;)
{
// Gobble initial spaces
while
(
isspace
(
*
(
unsigned
char
*
)
s
))
{
s
++
;
}
name
=
skip_quoted
(
&
s
,
"="
,
" "
,
0
);
// Value is either quote-delimited, or ends at first comma or space.
if
(
s
[
0
]
==
'\"'
)
{
s
++
;
value
=
skip_quoted
(
&
s
,
"
\"
"
,
" "
,
'\\'
);
if
(
s
[
0
]
==
','
)
{
s
++
;
}
}
else
{
value
=
skip_quoted
(
&
s
,
", "
,
" "
,
0
);
// IE uses commas, FF uses spaces
}
if
(
*
name
==
'\0'
)
{
break
;
}
if
(
!
strcmp
(
name
,
"username"
))
{
ah
->
user
=
value
;
}
else
if
(
!
strcmp
(
name
,
"cnonce"
))
{
ah
->
cnonce
=
value
;
}
else
if
(
!
strcmp
(
name
,
"response"
))
{
ah
->
response
=
value
;
}
else
if
(
!
strcmp
(
name
,
"uri"
))
{
ah
->
uri
=
value
;
}
else
if
(
!
strcmp
(
name
,
"qop"
))
{
ah
->
qop
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nc"
))
{
ah
->
nc
=
value
;
}
else
if
(
!
strcmp
(
name
,
"nonce"
))
{
ah
->
nonce
=
value
;
}
}
// CGI needs it as REMOTE_USER
if
(
ah
->
user
!=
NULL
)
{
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
}
else
{
return
0
;
}
return
1
;
}
// Authorize against the opened passwords file. Return 1 if authorized.
static
int
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
struct
ah
ah
;
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MG_BUF_LEN
];
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
return
0
;
}
// Loop over passwords file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%s"
,
f_user
,
f_domain
,
ha1
)
!=
3
)
{
continue
;
}
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
!
strcmp
(
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
f_domain
))
return
check_password
(
conn
->
request_info
.
request_method
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ah
.
response
);
}
return
0
;
}
// Return 1 if request is authorised, 0 otherwise.
static
int
check_authorization
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
char
fname
[
PATH_MAX
];
struct
vec
uri_vec
,
filename_vec
;
const
char
*
list
;
FILE
*
fp
=
NULL
;
int
authorized
=
1
;
list
=
conn
->
ctx
->
config
[
PROTECT_URI
];
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
mg_snprintf
(
fname
,
sizeof
(
fname
),
"%.*s"
,
(
int
)
filename_vec
.
len
,
filename_vec
.
ptr
);
fp
=
mg_fopen
(
fname
,
"r"
);
break
;
}
}
if
(
fp
==
NULL
)
{
fp
=
open_auth_file
(
conn
,
path
);
}
if
(
fp
!=
NULL
)
{
authorized
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
authorized
;
}
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
conn
->
status_code
=
401
;
mg_printf
(
conn
,
"HTTP/1.1 401 Unauthorized
\r\n
"
"Content-Length: 0
\r\n
"
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
(
unsigned
long
)
time
(
NULL
));
}
static
int
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
const
char
*
passfile
=
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
];
FILE
*
fp
;
int
ret
=
0
;
if
(
passfile
!=
NULL
&&
(
fp
=
mg_fopen
(
passfile
,
"r"
))
!=
NULL
)
{
ret
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
return
ret
;
}
int
mg_modify_passwords_file
(
const
char
*
fname
,
const
char
*
domain
,
const
char
*
user
,
const
char
*
pass
)
{
int
found
;
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
FILE
*
fp
,
*
fp2
;
found
=
0
;
fp
=
fp2
=
NULL
;
// Regard empty password as no password - remove user record.
if
(
pass
!=
NULL
&&
pass
[
0
]
==
'\0'
)
{
pass
=
NULL
;
}
(
void
)
snprintf
(
tmp
,
sizeof
(
tmp
),
"%s.tmp"
,
fname
);
// Create the file if does not exist
if
((
fp
=
fopen
(
fname
,
"a+"
))
!=
NULL
)
{
fclose
(
fp
);
}
// Open the given file and temporary file
if
((
fp
=
fopen
(
fname
,
"r"
))
==
NULL
)
{
return
0
;
}
else
if
((
fp2
=
fopen
(
tmp
,
"w+"
))
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
// Copy the stuff to temporary file
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%*s"
,
u
,
d
)
!=
2
)
{
continue
;
}
if
(
!
strcmp
(
u
,
user
)
&&
!
strcmp
(
d
,
domain
))
{
found
++
;
if
(
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
}
else
{
fprintf
(
fp2
,
"%s"
,
line
);
}
}
// If new user, just add it
if
(
!
found
&&
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
// Close files
fclose
(
fp
);
fclose
(
fp2
);
// Put the temp file in place of real file
remove
(
fname
);
rename
(
tmp
,
fname
);
return
1
;
}
// Return number of bytes left to read for this connection
// Return number of bytes left to read for this connection
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
...
@@ -995,1520 +1493,1021 @@ static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
...
@@ -995,1520 +1493,1021 @@ static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
}
}
static
int
pthread_cond_signal
(
pthread_cond_t
*
cv
)
{
static
int
pthread_cond_signal
(
pthread_cond_t
*
cv
)
{
return
SetEvent
(
cv
->
signal
)
==
0
?
-
1
:
0
;
return
SetEvent
(
cv
->
signal
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_broadcast
(
pthread_cond_t
*
cv
)
{
// Implementation with PulseEvent() has race condition, see
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
return
PulseEvent
(
cv
->
broadcast
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_destroy
(
pthread_cond_t
*
cv
)
{
return
CloseHandle
(
cv
->
signal
)
&&
CloseHandle
(
cv
->
broadcast
)
?
0
:
-
1
;
}
// For Windows, change all slashes to backslashes in path names.
static
void
change_slashes_to_backslashes
(
char
*
path
)
{
int
i
;
for
(
i
=
0
;
path
[
i
]
!=
'\0'
;
i
++
)
{
if
(
path
[
i
]
==
'/'
)
path
[
i
]
=
'\\'
;
// i > 0 check is to preserve UNC paths, like \\server\file.txt
if
(
path
[
i
]
==
'\\'
&&
i
>
0
)
while
(
path
[
i
+
1
]
==
'\\'
||
path
[
i
+
1
]
==
'/'
)
(
void
)
memmove
(
path
+
i
+
1
,
path
+
i
+
2
,
strlen
(
path
+
i
+
1
));
}
}
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
static
void
to_unicode
(
const
char
*
path
,
wchar_t
*
wbuf
,
size_t
wbuf_len
)
{
char
buf
[
PATH_MAX
*
2
],
buf2
[
PATH_MAX
*
2
];
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
// Convert to Unicode and back. If doubly-converted string does not
// match the original, something is fishy, reject.
memset
(
wbuf
,
0
,
wbuf_len
*
sizeof
(
wchar_t
));
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
(
int
)
wbuf_len
);
WideCharToMultiByte
(
CP_UTF8
,
0
,
wbuf
,
(
int
)
wbuf_len
,
buf2
,
sizeof
(
buf2
),
NULL
,
NULL
);
if
(
strcmp
(
buf
,
buf2
)
!=
0
)
{
wbuf
[
0
]
=
L'\0'
;
}
}
#if defined(_WIN32_WCE)
static
time_t
time
(
time_t
*
ptime
)
{
time_t
t
;
SYSTEMTIME
st
;
FILETIME
ft
;
GetSystemTime
(
&
st
);
SystemTimeToFileTime
(
&
st
,
&
ft
);
t
=
SYS2UNIX_TIME
(
ft
.
dwLowDateTime
,
ft
.
dwHighDateTime
);
if
(
ptime
!=
NULL
)
{
*
ptime
=
t
;
}
return
t
;
}
static
struct
tm
*
localtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
int64_t
t
=
((
int64_t
)
*
ptime
)
*
RATE_DIFF
+
EPOCH_DIFF
;
FILETIME
ft
,
lft
;
SYSTEMTIME
st
;
TIME_ZONE_INFORMATION
tzinfo
;
if
(
ptm
==
NULL
)
{
return
NULL
;
}
*
(
int64_t
*
)
&
ft
=
t
;
FileTimeToLocalFileTime
(
&
ft
,
&
lft
);
FileTimeToSystemTime
(
&
lft
,
&
st
);
ptm
->
tm_year
=
st
.
wYear
-
1900
;
ptm
->
tm_mon
=
st
.
wMonth
-
1
;
ptm
->
tm_wday
=
st
.
wDayOfWeek
;
ptm
->
tm_mday
=
st
.
wDay
;
ptm
->
tm_hour
=
st
.
wHour
;
ptm
->
tm_min
=
st
.
wMinute
;
ptm
->
tm_sec
=
st
.
wSecond
;
ptm
->
tm_yday
=
0
;
// hope nobody uses this
ptm
->
tm_isdst
=
GetTimeZoneInformation
(
&
tzinfo
)
==
TIME_ZONE_ID_DAYLIGHT
?
1
:
0
;
return
ptm
;
}
static
struct
tm
*
gmtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
// FIXME(lsm): fix this.
return
localtime
(
ptime
,
ptm
);
}
static
size_t
strftime
(
char
*
dst
,
size_t
dst_size
,
const
char
*
fmt
,
const
struct
tm
*
tm
)
{
(
void
)
snprintf
(
dst
,
dst_size
,
"implement strftime() for WinCE"
);
return
0
;
}
#endif
// Windows happily opens files with some garbage at the end of file name.
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
// This function returns non-0 if path ends with some garbage.
static
int
path_cannot_disclose_cgi
(
const
char
*
path
)
{
static
const
char
*
allowed_last_characters
=
"_-"
;
int
last
=
path
[
strlen
(
path
)
-
1
];
return
isalnum
(
last
)
||
strchr
(
allowed_last_characters
,
last
)
!=
NULL
;
}
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
wchar_t
wbuf
[
PATH_MAX
]
=
L"
\\\\
?
\\
"
;
WIN32_FILE_ATTRIBUTE_DATA
info
;
filep
->
modification_time
=
0
;
to_unicode
(
path
,
wbuf
+
4
,
ARRAY_SIZE
(
wbuf
)
-
4
);
if
(
GetFileAttributesExW
(
wbuf
,
GetFileExInfoStandard
,
&
info
)
!=
0
)
{
filep
->
size
=
MAKEUQUAD
(
info
.
nFileSizeLow
,
info
.
nFileSizeHigh
);
filep
->
modification_time
=
SYS2UNIX_TIME
(
info
.
ftLastWriteTime
.
dwLowDateTime
,
info
.
ftLastWriteTime
.
dwHighDateTime
);
filep
->
is_directory
=
info
.
dwFileAttributes
&
FILE_ATTRIBUTE_DIRECTORY
;
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if
(
!
filep
->
is_directory
&&
!
path_cannot_disclose_cgi
(
path
))
{
memset
(
filep
,
0
,
sizeof
(
*
filep
));
}
}
return
filep
->
modification_time
!=
0
;
}
static
int
mg_remove
(
const
char
*
path
)
{
wchar_t
wbuf
[
PATH_MAX
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
DeleteFileW
(
wbuf
)
?
0
:
-
1
;
}
static
int
mg_mkdir
(
const
char
*
path
,
int
mode
)
{
char
buf
[
PATH_MAX
];
wchar_t
wbuf
[
PATH_MAX
];
(
void
)
mode
;
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
(
void
)
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
CreateDirectoryW
(
wbuf
,
NULL
)
?
0
:
-
1
;
}
// Implementation of POSIX opendir/closedir/readdir for Windows.
static
DIR
*
opendir
(
const
char
*
name
)
{
DIR
*
dir
=
NULL
;
wchar_t
wpath
[
PATH_MAX
];
DWORD
attrs
;
if
(
name
==
NULL
)
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
else
if
((
dir
=
(
DIR
*
)
malloc
(
sizeof
(
*
dir
)))
==
NULL
)
{
SetLastError
(
ERROR_NOT_ENOUGH_MEMORY
);
}
else
{
to_unicode
(
name
,
wpath
,
ARRAY_SIZE
(
wpath
));
attrs
=
GetFileAttributesW
(
wpath
);
if
(
attrs
!=
0xFFFFFFFF
&&
((
attrs
&
FILE_ATTRIBUTE_DIRECTORY
)
==
FILE_ATTRIBUTE_DIRECTORY
))
{
(
void
)
wcscat
(
wpath
,
L"
\\
*"
);
dir
->
handle
=
FindFirstFileW
(
wpath
,
&
dir
->
info
);
dir
->
result
.
d_name
[
0
]
=
'\0'
;
}
else
{
free
(
dir
);
dir
=
NULL
;
}
}
return
dir
;
}
static
int
closedir
(
DIR
*
dir
)
{
int
result
=
0
;
if
(
dir
!=
NULL
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
result
=
FindClose
(
dir
->
handle
)
?
0
:
-
1
;
free
(
dir
);
}
else
{
result
=
-
1
;
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
static
struct
dirent
*
readdir
(
DIR
*
dir
)
{
struct
dirent
*
result
=
0
;
if
(
dir
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
{
result
=
&
dir
->
result
;
(
void
)
WideCharToMultiByte
(
CP_UTF8
,
0
,
dir
->
info
.
cFileName
,
-
1
,
result
->
d_name
,
sizeof
(
result
->
d_name
),
NULL
,
NULL
);
if
(
!
FindNextFileW
(
dir
->
handle
,
&
dir
->
info
))
{
(
void
)
FindClose
(
dir
->
handle
);
dir
->
handle
=
INVALID_HANDLE_VALUE
;
}
}
else
{
SetLastError
(
ERROR_FILE_NOT_FOUND
);
}
}
else
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
#ifndef HAVE_POLL
static
int
poll
(
struct
pollfd
*
pfd
,
int
n
,
int
milliseconds
)
{
struct
timeval
tv
;
fd_set
set
;
int
i
,
result
;
SOCKET
maxfd
=
0
;
tv
.
tv_sec
=
milliseconds
/
1000
;
tv
.
tv_usec
=
(
milliseconds
%
1000
)
*
1000
;
FD_ZERO
(
&
set
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
FD_SET
((
SOCKET
)
pfd
[
i
].
fd
,
&
set
);
pfd
[
i
].
revents
=
0
;
if
(
pfd
[
i
].
fd
>
maxfd
)
{
maxfd
=
pfd
[
i
].
fd
;
}
}
if
((
result
=
select
(
maxfd
+
1
,
&
set
,
NULL
,
NULL
,
&
tv
))
>
0
)
{
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
FD_ISSET
(
pfd
[
i
].
fd
,
&
set
))
{
pfd
[
i
].
revents
=
POLLIN
;
}
}
}
return
result
;
}
#endif // HAVE_POLL
static
void
set_close_on_exec
(
SOCKET
sock
)
{
(
void
)
SetHandleInformation
((
HANDLE
)
sock
,
HANDLE_FLAG_INHERIT
,
0
);
}
}
int
mg_start_thread
(
mg_thread_func_t
f
,
void
*
p
)
{
static
int
pthread_cond_broadcast
(
pthread_cond_t
*
cv
)
{
return
(
long
)
_beginthread
((
void
(
__cdecl
*
)(
void
*
))
f
,
0
,
p
)
==
-
1L
?
-
1
:
0
;
// Implementation with PulseEvent() has race condition, see
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
return
PulseEvent
(
cv
->
broadcast
)
==
0
?
-
1
:
0
;
}
}
static
HANDLE
dlopen
(
const
char
*
dll_name
,
int
flags
)
{
static
int
pthread_cond_destroy
(
pthread_cond_t
*
cv
)
{
wchar_t
wbuf
[
PATH_MAX
];
return
CloseHandle
(
cv
->
signal
)
&&
CloseHandle
(
cv
->
broadcast
)
?
0
:
-
1
;
(
void
)
flags
;
to_unicode
(
dll_name
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
LoadLibraryW
(
wbuf
);
}
}
#if !defined(NO_CGI)
// For Windows, change all slashes to backslashes in path names.
#define SIGKILL 0
static
void
change_slashes_to_backslashes
(
char
*
path
)
{
static
int
kill
(
pid_t
pid
,
int
sig_num
)
{
int
i
;
(
void
)
TerminateProcess
(
pid
,
sig_num
);
(
void
)
CloseHandle
(
pid
);
return
0
;
}
static
void
trim_trailing_whitespaces
(
char
*
s
)
{
for
(
i
=
0
;
path
[
i
]
!=
'\0'
;
i
++
)
{
char
*
e
=
s
+
strlen
(
s
)
-
1
;
if
(
path
[
i
]
==
'/'
)
while
(
e
>
s
&&
isspace
(
*
(
unsigned
char
*
)
e
))
{
path
[
i
]
=
'\\'
;
*
e
--
=
'\0'
;
// i > 0 check is to preserve UNC paths, like \\server\file.txt
if
(
path
[
i
]
==
'\\'
&&
i
>
0
)
while
(
path
[
i
+
1
]
==
'\\'
||
path
[
i
+
1
]
==
'/'
)
(
void
)
memmove
(
path
+
i
+
1
,
path
+
i
+
2
,
strlen
(
path
+
i
+
1
));
}
}
}
}
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
char
*
envblk
,
char
*
envp
[],
int
fdin
,
// wbuf and wbuf_len is a target buffer and its length.
int
fdout
,
const
char
*
dir
)
{
static
void
to_unicode
(
const
char
*
path
,
wchar_t
*
wbuf
,
size_t
wbuf_len
)
{
HANDLE
me
;
char
buf
[
PATH_MAX
*
2
],
buf2
[
PATH_MAX
*
2
];
char
*
interp
,
full_interp
[
PATH_MAX
],
full_dir
[
PATH_MAX
],
cmdline
[
PATH_MAX
],
buf
[
PATH_MAX
];
FILE
*
fp
;
STARTUPINFOA
si
;
PROCESS_INFORMATION
pi
=
{
0
};
(
void
)
envp
;
memset
(
&
si
,
0
,
sizeof
(
si
));
si
.
cb
=
sizeof
(
si
);
// TODO(lsm): redirect CGI errors to the error log file
si
.
dwFlags
=
STARTF_USESTDHANDLES
|
STARTF_USESHOWWINDOW
;
si
.
wShowWindow
=
SW_HIDE
;
me
=
GetCurrentProcess
();
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdin
),
me
,
&
si
.
hStdInput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdout
),
me
,
&
si
.
hStdOutput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
// If CGI file is a script, try to read the interpreter line
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
interp
==
NULL
)
{
buf
[
0
]
=
buf
[
1
]
=
'\0'
;
// Read the first line of the script into the buffer
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%c%s"
,
dir
,
'/'
,
prog
);
change_slashes_to_backslashes
(
buf
);
if
((
fp
=
mg_fopen
(
cmdline
,
"r"
))
!=
NULL
)
{
fgets
(
buf
,
sizeof
(
buf
),
fp
);
fclose
(
fp
);
buf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
}
if
(
buf
[
0
]
==
'#'
&&
buf
[
1
]
==
'!'
)
{
// Convert to Unicode and back. If doubly-converted string does not
trim_trailing_whitespaces
(
buf
+
2
);
// match the original, something is fishy, reject.
}
else
{
memset
(
wbuf
,
0
,
wbuf_len
*
sizeof
(
wchar_t
));
buf
[
2
]
=
'\0'
;
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
(
int
)
wbuf_len
);
}
WideCharToMultiByte
(
CP_UTF8
,
0
,
wbuf
,
(
int
)
wbuf_len
,
buf2
,
sizeof
(
buf2
),
interp
=
buf
+
2
;
NULL
,
NULL
);
if
(
strcmp
(
buf
,
buf2
)
!=
0
)
{
wbuf
[
0
]
=
L'\0'
;
}
}
}
if
(
interp
[
0
]
!=
'\0'
)
{
#if defined(_WIN32_WCE)
GetFullPathNameA
(
interp
,
sizeof
(
full_interp
),
full_interp
,
NULL
);
static
time_t
time
(
time_t
*
ptime
)
{
interp
=
full_interp
;
time_t
t
;
}
SYSTEMTIME
st
;
GetFullPathNameA
(
dir
,
sizeof
(
full_dir
),
full_dir
,
NULL
)
;
FILETIME
ft
;
mg_snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%s
\"
%s
\\
%s
\"
"
,
GetSystemTime
(
&
st
);
interp
,
interp
[
0
]
==
'\0'
?
""
:
" "
,
full_dir
,
prog
);
SystemTimeToFileTime
(
&
st
,
&
ft
);
t
=
SYS2UNIX_TIME
(
ft
.
dwLowDateTime
,
ft
.
dwHighDateTime
);
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
if
(
ptime
!=
NULL
)
{
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
TRUE
,
*
ptime
=
t
;
CREATE_NEW_PROCESS_GROUP
,
envblk
,
NULL
,
&
si
,
&
pi
)
==
0
)
{
cry
(
conn
,
"%s: CreateProcess(%s): %ld"
,
__func__
,
cmdline
,
ERRNO
);
pi
.
hProcess
=
(
pid_t
)
-
1
;
}
}
(
void
)
CloseHandle
(
si
.
hStdOutput
);
return
t
;
(
void
)
CloseHandle
(
si
.
hStdInput
);
(
void
)
CloseHandle
(
pi
.
hThread
);
return
(
pid_t
)
pi
.
hProcess
;
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
unsigned
long
on
=
1
;
return
ioctlsocket
(
sock
,
FIONBIO
,
&
on
);
}
}
#else
static
struct
tm
*
localtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
int64_t
t
=
((
int64_t
)
*
ptime
)
*
RATE_DIFF
+
EPOCH_DIFF
;
struct
stat
st
;
FILETIME
ft
,
lft
;
SYSTEMTIME
st
;
filep
->
modification_time
=
(
time_t
)
0
;
TIME_ZONE_INFORMATION
tzinfo
;
if
(
stat
(
path
,
&
st
)
==
0
)
{
filep
->
size
=
st
.
st_size
;
filep
->
modification_time
=
st
.
st_mtime
;
filep
->
is_directory
=
S_ISDIR
(
st
.
st_mode
);
// See https://github.com/cesanta/mongoose/issues/109
if
(
ptm
==
NULL
)
{
// Some filesystems report modification time as 0. Artificially
return
NULL
;
// bump it up to mark mg_stat() success.
if
(
filep
->
modification_time
==
(
time_t
)
0
)
{
filep
->
modification_time
=
(
time_t
)
1
;
}
}
}
return
filep
->
modification_time
!=
(
time_t
)
0
;
*
(
int64_t
*
)
&
ft
=
t
;
}
FileTimeToLocalFileTime
(
&
ft
,
&
lft
);
FileTimeToSystemTime
(
&
lft
,
&
st
);
ptm
->
tm_year
=
st
.
wYear
-
1900
;
ptm
->
tm_mon
=
st
.
wMonth
-
1
;
ptm
->
tm_wday
=
st
.
wDayOfWeek
;
ptm
->
tm_mday
=
st
.
wDay
;
ptm
->
tm_hour
=
st
.
wHour
;
ptm
->
tm_min
=
st
.
wMinute
;
ptm
->
tm_sec
=
st
.
wSecond
;
ptm
->
tm_yday
=
0
;
// hope nobody uses this
ptm
->
tm_isdst
=
GetTimeZoneInformation
(
&
tzinfo
)
==
TIME_ZONE_ID_DAYLIGHT
?
1
:
0
;
static
void
set_close_on_exec
(
int
fd
)
{
return
ptm
;
fcntl
(
fd
,
F_SETFD
,
FD_CLOEXEC
);
}
}
int
mg_start_thread
(
mg_thread_func_t
func
,
void
*
param
)
{
static
struct
tm
*
gmtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
pthread_t
thread_id
;
// FIXME(lsm): fix this.
pthread_attr_t
attr
;
return
localtime
(
ptime
,
ptm
);
int
result
;
}
(
void
)
pthread_attr_init
(
&
attr
);
(
void
)
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
);
#if USE_STACK_SIZE > 1
static
size_t
strftime
(
char
*
dst
,
size_t
dst_size
,
const
char
*
fmt
,
// Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
const
struct
tm
*
tm
)
{
(
void
)
pthread_attr_setstacksize
(
&
attr
,
USE_STACK_SIZE
);
(
void
)
snprintf
(
dst
,
dst_size
,
"implement strftime() for WinCE"
);
return
0
;
}
#endif
#endif
result
=
pthread_create
(
&
thread_id
,
&
attr
,
func
,
param
);
// Windows happily opens files with some garbage at the end of file name.
pthread_attr_destroy
(
&
attr
);
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
return
result
;
// This function returns non-0 if path ends with some garbage.
static
int
path_cannot_disclose_cgi
(
const
char
*
path
)
{
static
const
char
*
allowed_last_characters
=
"_-"
;
int
last
=
path
[
strlen
(
path
)
-
1
];
return
isalnum
(
last
)
||
strchr
(
allowed_last_characters
,
last
)
!=
NULL
;
}
}
#ifndef NO_CGI
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
wchar_t
wbuf
[
PATH_MAX
]
=
L"
\\\\
?
\\
"
;
char
*
envblk
,
char
*
envp
[],
int
fdin
,
WIN32_FILE_ATTRIBUTE_DATA
info
;
int
fdout
,
const
char
*
dir
)
{
pid_t
pid
;
const
char
*
interp
;
(
void
)
envblk
;
if
((
pid
=
fork
())
==
-
1
)
{
// Parent
send_http_error
(
conn
,
500
,
http_500_error
,
"fork(): %s"
,
strerror
(
ERRNO
));
}
else
if
(
pid
==
0
)
{
// Child
if
(
chdir
(
dir
)
!=
0
)
{
cry
(
conn
,
"%s: chdir(%s): %s"
,
__func__
,
dir
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdin
,
0
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 0): %s"
,
__func__
,
fdin
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdout
,
1
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fdout
,
strerror
(
ERRNO
));
}
else
{
// Not redirecting stderr to stdout, to avoid output being littered
// with the error messages.
(
void
)
close
(
fdin
);
(
void
)
close
(
fdout
);
// After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
// implementation, SIGCHLD's handler will leave unchanged after exec
// if it was set to be ignored. Restore it to default action.
signal
(
SIGCHLD
,
SIG_DFL
);
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
filep
->
modification_time
=
0
;
if
(
interp
==
NULL
)
{
to_unicode
(
path
,
wbuf
+
4
,
ARRAY_SIZE
(
wbuf
)
-
4
);
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
if
(
GetFileAttributesExW
(
wbuf
,
GetFileExInfoStandard
,
&
info
)
!=
0
)
{
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
filep
->
size
=
MAKEUQUAD
(
info
.
nFileSizeLow
,
info
.
nFileSizeHigh
);
}
else
{
filep
->
modification_time
=
SYS2UNIX_TIME
(
(
void
)
execle
(
interp
,
interp
,
prog
,
NULL
,
envp
);
info
.
ftLastWriteTime
.
dwLowDateTime
,
cry
(
conn
,
"%s: execle(%s %s): %s"
,
__func__
,
interp
,
prog
,
info
.
ftLastWriteTime
.
dwHighDateTime
);
strerror
(
ERRNO
));
filep
->
is_directory
=
info
.
dwFileAttributes
&
FILE_ATTRIBUTE_DIRECTORY
;
}
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if
(
!
filep
->
is_directory
&&
!
path_cannot_disclose_cgi
(
path
))
{
memset
(
filep
,
0
,
sizeof
(
*
filep
));
}
}
exit
(
EXIT_FAILURE
);
}
}
return
pid
;
return
filep
->
modification_time
!=
0
;
}
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
int
flags
;
flags
=
fcntl
(
sock
,
F_GETFL
,
0
);
(
void
)
fcntl
(
sock
,
F_SETFL
,
flags
|
O_NONBLOCK
);
return
0
;
static
int
mg_remove
(
const
char
*
path
)
{
wchar_t
wbuf
[
PATH_MAX
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
DeleteFileW
(
wbuf
)
?
0
:
-
1
;
}
}
#endif // _WIN32
// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static
int64_t
push
(
FILE
*
fp
,
SOCKET
sock
,
SSL
*
ssl
,
const
char
*
buf
,
int64_t
len
)
{
int64_t
sent
;
int
n
,
k
;
(
void
)
ssl
;
// Get rid of warning
sent
=
0
;
while
(
sent
<
len
)
{
// How many bytes we send in this iteration
k
=
len
-
sent
>
INT_MAX
?
INT_MAX
:
(
int
)
(
len
-
sent
);
if
(
ssl
!=
NULL
)
{
static
int
mg_mkdir
(
const
char
*
path
,
int
mode
)
{
n
=
SSL_write
(
ssl
,
buf
+
sent
,
k
);
char
buf
[
PATH_MAX
];
}
else
if
(
fp
!=
NULL
)
{
wchar_t
wbuf
[
PATH_MAX
];
n
=
(
int
)
fwrite
(
buf
+
sent
,
1
,
(
size_t
)
k
,
fp
);
if
(
ferror
(
fp
))
n
=
-
1
;
}
else
{
n
=
send
(
sock
,
buf
+
sent
,
(
size_t
)
k
,
MSG_NOSIGNAL
);
}
if
(
n
<=
0
)
(
void
)
mode
;
break
;
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
sent
+=
n
;
(
void
)
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
}
return
sent
;
return
CreateDirectoryW
(
wbuf
,
NULL
)
?
0
:
-
1
;
}
}
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
// Implementation of POSIX opendir/closedir/readdir for Windows.
// Return negative value on error, or number of bytes read on success.
static
DIR
*
opendir
(
const
char
*
name
)
{
static
int
pull
(
FILE
*
fp
,
struct
mg_connection
*
conn
,
char
*
buf
,
int
len
)
{
DIR
*
dir
=
NULL
;
int
nread
;
wchar_t
wpath
[
PATH_MAX
];
DWORD
attrs
;
if
(
len
<=
0
)
return
0
;
if
(
name
==
NULL
)
{
if
(
fp
!=
NULL
)
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
// Use read() instead of fread(), because if we're reading from the CGI
}
else
if
((
dir
=
(
DIR
*
)
malloc
(
sizeof
(
*
dir
)))
==
NULL
)
{
// pipe, fread() may block until IO buffer is filled up. We cannot afford
SetLastError
(
ERROR_NOT_ENOUGH_MEMORY
);
// to block and must pass all read bytes immediately to the client.
nread
=
read
(
fileno
(
fp
),
buf
,
(
size_t
)
len
);
#ifndef NO_SSL
}
else
if
(
conn
->
ssl
!=
NULL
)
{
nread
=
SSL_read
(
conn
->
ssl
,
buf
,
len
);
#endif
}
else
{
}
else
{
nread
=
recv
(
conn
->
client
.
sock
,
buf
,
(
size_t
)
len
,
0
);
to_unicode
(
name
,
wpath
,
ARRAY_SIZE
(
wpath
));
attrs
=
GetFileAttributesW
(
wpath
);
if
(
attrs
!=
0xFFFFFFFF
&&
((
attrs
&
FILE_ATTRIBUTE_DIRECTORY
)
==
FILE_ATTRIBUTE_DIRECTORY
))
{
(
void
)
wcscat
(
wpath
,
L"
\\
*"
);
dir
->
handle
=
FindFirstFileW
(
wpath
,
&
dir
->
info
);
dir
->
result
.
d_name
[
0
]
=
'\0'
;
}
else
{
free
(
dir
);
dir
=
NULL
;
}
}
if
(
nread
>
0
)
{
conn
->
num_bytes_read
+=
nread
;
}
}
return
conn
->
ctx
->
stop_flag
?
-
1
:
nread
;
return
dir
;
}
}
static
int
pull_all
(
FILE
*
fp
,
struct
mg_connection
*
conn
,
char
*
buf
,
int
len
)
{
static
int
closedir
(
DIR
*
dir
)
{
int
n
,
nread
=
0
;
int
result
=
0
;
while
(
len
>
0
&&
conn
->
ctx
->
stop_flag
==
0
)
{
if
(
dir
!=
NULL
)
{
n
=
pull
(
fp
,
conn
,
buf
+
nread
,
len
);
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
if
(
n
<
0
)
{
result
=
FindClose
(
dir
->
handle
)
?
0
:
-
1
;
nread
=
n
;
// Propagate the error
break
;
free
(
dir
);
}
else
if
(
n
==
0
)
{
break
;
// No more data to read
}
else
{
}
else
{
nread
+=
n
;
result
=
-
1
;
len
-=
n
;
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
}
}
return
nread
;
return
result
;
}
}
int
mg_read
(
struct
mg_connection
*
conn
,
void
*
buf
,
int
len
)
{
static
struct
dirent
*
readdir
(
DIR
*
dir
)
{
int
n
,
buffered_len
,
nread
=
0
;
struct
dirent
*
result
=
0
;
int64_t
left
;
// If Content-Length is not set, read until socket is closed
if
(
conn
->
content_len
<=
0
)
{
conn
->
content_len
=
INT64_MAX
;
conn
->
must_close
=
1
;
}
// conn->buf body
// |=================|==========|===============|
// |<--request_len-->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
// First, check for data buffered in conn->buf by read_request().
if
(
dir
)
{
if
(
len
>
0
&&
(
buffered_len
=
conn
->
data_len
-
conn
->
request_len
)
>
0
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
{
char
*
body
=
conn
->
buf
+
conn
->
request_len
;
result
=
&
dir
->
result
;
if
(
buffered_len
>
len
)
buffered_len
=
len
;
(
void
)
WideCharToMultiByte
(
CP_UTF8
,
0
,
if
(
buffered_len
>
conn
->
content_len
)
buffered_len
=
(
int
)
conn
->
content_len
;
dir
->
info
.
cFileName
,
-
1
,
result
->
d_name
,
sizeof
(
result
->
d_name
),
NULL
,
NULL
);
memcpy
(
buf
,
body
,
(
size_t
)
buffered_len
);
if
(
!
FindNextFileW
(
dir
->
handle
,
&
dir
->
info
))
{
memmove
(
body
,
body
+
buffered_len
,
(
void
)
FindClose
(
dir
->
handle
);
&
conn
->
buf
[
conn
->
data_len
]
-
&
body
[
buffered_len
]);
dir
->
handle
=
INVALID_HANDLE_VALUE
;
len
-=
buffered_len
;
conn
->
data_len
-=
buffered_len
;
nread
+=
buffered_len
;
}
}
// Read data from the socket.
}
else
{
if
(
len
>
0
&&
(
left
=
left_to_read
(
conn
))
>
0
)
{
SetLastError
(
ERROR_FILE_NOT_FOUND
);
if
(
left
<
len
)
{
len
=
(
int
)
left
;
}
}
n
=
pull_all
(
NULL
,
conn
,
(
char
*
)
buf
+
nread
,
(
int
)
len
);
}
else
{
nread
=
n
>=
0
?
nread
+
n
:
n
;
SetLastError
(
ERROR_BAD_ARGUMENTS
)
;
}
}
return
nread
;
return
result
;
}
}
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
#ifndef HAVE_POLL
time_t
now
;
static
int
poll
(
struct
pollfd
*
pfd
,
int
n
,
int
milliseconds
)
{
int64_t
n
,
total
,
allowed
;
struct
timeval
tv
;
fd_set
set
;
int
i
,
result
;
SOCKET
maxfd
=
0
;
if
(
conn
->
throttle
>
0
)
{
tv
.
tv_sec
=
milliseconds
/
1000
;
if
((
now
=
time
(
NULL
))
!=
conn
->
last_throttle_time
)
{
tv
.
tv_usec
=
(
milliseconds
%
1000
)
*
1000
;
conn
->
last_throttle_time
=
now
;
FD_ZERO
(
&
set
);
conn
->
last_throttle_bytes
=
0
;
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
allowed
=
conn
->
throttle
-
conn
->
last_throttle_bytes
;
FD_SET
((
SOCKET
)
pfd
[
i
].
fd
,
&
set
);
if
(
allowed
>
(
int64_t
)
len
)
{
pfd
[
i
].
revents
=
0
;
allowed
=
len
;
if
(
pfd
[
i
].
fd
>
maxfd
)
{
maxfd
=
pfd
[
i
].
fd
;
}
}
if
((
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
==
allowed
)
{
buf
=
(
char
*
)
buf
+
total
;
conn
->
last_throttle_bytes
+=
total
;
while
(
total
<
(
int64_t
)
len
&&
conn
->
ctx
->
stop_flag
==
0
)
{
allowed
=
conn
->
throttle
>
(
int64_t
)
len
-
total
?
(
int64_t
)
len
-
total
:
conn
->
throttle
;
if
((
n
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
!=
allowed
)
{
break
;
}
}
sleep
(
1
);
conn
->
last_throttle_bytes
=
allowed
;
if
((
result
=
select
(
maxfd
+
1
,
&
set
,
NULL
,
NULL
,
&
tv
))
>
0
)
{
conn
->
last_throttle_time
=
time
(
NULL
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
buf
=
(
char
*
)
buf
+
n
;
if
(
FD_ISSET
(
pfd
[
i
].
fd
,
&
set
))
{
total
+=
n
;
pfd
[
i
].
revents
=
POLLIN
;
}
}
}
}
}
else
{
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
len
);
}
}
return
(
int
)
total
;
return
result
;
}
}
#endif // HAVE_POLL
// Print message to buffer. If buffer is large enough to hold the message,
static
void
set_close_on_exec
(
SOCKET
sock
)
{
// return buffer. If buffer is to small, allocate large enough buffer on heap,
(
void
)
SetHandleInformation
((
HANDLE
)
sock
,
HANDLE_FLAG_INHERIT
,
0
);
// and return allocated buffer.
}
static
int
alloc_vprintf
(
char
**
buf
,
size_t
size
,
const
char
*
fmt
,
va_list
ap
)
{
va_list
ap_copy
;
int
len
;
// Windows is not standard-compliant, and vsnprintf() returns -1 if
int
mg_start_thread
(
mg_thread_func_t
f
,
void
*
p
)
{
// buffer is too small. Also, older versions of msvcrt.dll do not have
return
(
long
)
_beginthread
((
void
(
__cdecl
*
)(
void
*
))
f
,
0
,
p
)
==
-
1L
?
-
1
:
0
;
// _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
}
// Therefore, we make two passes: on first pass, get required message length.
// On second pass, actually print the message.
va_copy
(
ap_copy
,
ap
);
len
=
vsnprintf
(
NULL
,
0
,
fmt
,
ap_copy
);
if
(
len
>
(
int
)
size
&&
static
HANDLE
dlopen
(
const
char
*
dll_name
,
int
flags
)
{
(
size
=
len
+
1
)
>
0
&&
wchar_t
wbuf
[
PATH_MAX
];
(
*
buf
=
(
char
*
)
malloc
(
size
))
==
NULL
)
{
(
void
)
flags
;
len
=
-
1
;
// Allocation failed, mark failure
to_unicode
(
dll_name
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
}
else
{
return
LoadLibraryW
(
wbuf
);
va_copy
(
ap_copy
,
ap
);
}
vsnprintf
(
*
buf
,
size
,
fmt
,
ap_copy
);
}
return
len
;
#if !defined(NO_CGI)
#define SIGKILL 0
static
int
kill
(
pid_t
pid
,
int
sig_num
)
{
(
void
)
TerminateProcess
(
pid
,
sig_num
);
(
void
)
CloseHandle
(
pid
);
return
0
;
}
}
int
mg_vprintf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
va_list
ap
)
{
static
void
trim_trailing_whitespaces
(
char
*
s
)
{
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
char
*
e
=
s
+
strlen
(
s
)
-
1
;
int
len
;
while
(
e
>
s
&&
isspace
(
*
(
unsigned
char
*
)
e
))
{
*
e
--
=
'\0'
;
if
((
len
=
alloc_vprintf
(
&
buf
,
sizeof
(
mem
),
fmt
,
ap
))
>
0
)
{
len
=
mg_write
(
conn
,
buf
,
(
size_t
)
len
);
}
if
(
buf
!=
mem
&&
buf
!=
NULL
)
{
free
(
buf
);
}
}
return
len
;
}
}
int
mg_printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
va_list
ap
;
char
*
envblk
,
char
*
envp
[],
int
fdin
,
va_start
(
ap
,
fmt
);
int
fdout
,
const
char
*
dir
)
{
return
mg_vprintf
(
conn
,
fmt
,
ap
);
HANDLE
me
;
}
char
*
interp
,
full_interp
[
PATH_MAX
],
full_dir
[
PATH_MAX
],
cmdline
[
PATH_MAX
],
buf
[
PATH_MAX
];
FILE
*
fp
;
STARTUPINFOA
si
;
PROCESS_INFORMATION
pi
=
{
0
};
static
int
mg_chunked_printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
(
void
)
envp
;
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
int
len
;
va_list
ap
;
memset
(
&
si
,
0
,
sizeof
(
si
));
va_start
(
ap
,
fmt
);
si
.
cb
=
sizeof
(
si
);
if
((
len
=
alloc_vprintf
(
&
buf
,
sizeof
(
mem
),
fmt
,
ap
))
>
0
)
{
len
=
mg_printf
(
conn
,
"%X
\r\n
%s
\r\n
"
,
len
,
buf
);
}
if
(
buf
!=
mem
&&
buf
!=
NULL
)
{
// TODO(lsm): redirect CGI errors to the error log file
free
(
buf
)
;
si
.
dwFlags
=
STARTF_USESTDHANDLES
|
STARTF_USESHOWWINDOW
;
}
si
.
wShowWindow
=
SW_HIDE
;
return
len
;
me
=
GetCurrentProcess
();
}
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdin
),
me
,
&
si
.
hStdInput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdout
),
me
,
&
si
.
hStdOutput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
// If CGI file is a script, try to read the interpreter line
int
dst_len
,
int
is_form_url_encoded
)
{
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
i
nt
i
,
j
,
a
,
b
;
i
f
(
interp
==
NULL
)
{
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
buf
[
0
]
=
buf
[
1
]
=
'\0'
;
for
(
i
=
j
=
0
;
i
<
src_len
&&
j
<
dst_len
-
1
;
i
++
,
j
++
)
{
// Read the first line of the script into the buffer
if
(
src
[
i
]
==
'%'
&&
i
<
src_len
-
2
&&
snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%c%s"
,
dir
,
'/'
,
prog
);
isxdigit
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
1
))
&&
if
((
fp
=
mg_fopen
(
cmdline
,
"r"
))
!=
NULL
)
{
isxdigit
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
2
)))
{
fgets
(
buf
,
sizeof
(
buf
),
fp
);
a
=
tolower
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
1
)
);
fclose
(
fp
);
b
=
tolower
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
2
))
;
b
uf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
dst
[
j
]
=
(
char
)
((
HEXTOI
(
a
)
<<
4
)
|
HEXTOI
(
b
));
}
i
+=
2
;
}
else
if
(
is_form_url_encoded
&&
src
[
i
]
==
'+
'
)
{
if
(
buf
[
0
]
==
'#'
&&
buf
[
1
]
==
'!
'
)
{
dst
[
j
]
=
' '
;
trim_trailing_whitespaces
(
buf
+
2
)
;
}
else
{
}
else
{
dst
[
j
]
=
src
[
i
]
;
buf
[
2
]
=
'\0'
;
}
}
interp
=
buf
+
2
;
}
}
dst
[
j
]
=
'\0'
;
// Null-terminate the destination
if
(
interp
[
0
]
!=
'\0'
)
{
GetFullPathNameA
(
interp
,
sizeof
(
full_interp
),
full_interp
,
NULL
);
interp
=
full_interp
;
}
GetFullPathNameA
(
dir
,
sizeof
(
full_dir
),
full_dir
,
NULL
);
return
i
>=
src_len
?
j
:
-
1
;
mg_snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%s
\"
%s
\\
%s
\"
"
,
}
interp
,
interp
[
0
]
==
'\0'
?
""
:
" "
,
full_dir
,
prog
);
int
mg_get_var
(
const
char
*
data
,
size_t
data_len
,
const
char
*
name
,
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
char
*
dst
,
size_t
dst_len
)
{
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
TRUE
,
const
char
*
p
,
*
e
,
*
s
;
CREATE_NEW_PROCESS_GROUP
,
envblk
,
NULL
,
&
si
,
&
pi
)
==
0
)
{
size_t
name_len
;
cry
(
conn
,
"%s: CreateProcess(%s): %ld"
,
int
len
;
__func__
,
cmdline
,
ERRNO
);
pi
.
hProcess
=
(
pid_t
)
-
1
;
}
if
(
dst
==
NULL
||
dst_len
==
0
)
{
(
void
)
CloseHandle
(
si
.
hStdOutput
);
len
=
-
2
;
(
void
)
CloseHandle
(
si
.
hStdInput
);
}
else
if
(
data
==
NULL
||
name
==
NULL
||
data_len
==
0
)
{
(
void
)
CloseHandle
(
pi
.
hThread
);
len
=
-
1
;
dst
[
0
]
=
'\0'
;
}
else
{
name_len
=
strlen
(
name
);
e
=
data
+
data_len
;
len
=
-
1
;
dst
[
0
]
=
'\0'
;
// data is "var1=val1&var2=val2...". Find variable first
return
(
pid_t
)
pi
.
hProcess
;
for
(
p
=
data
;
p
+
name_len
<
e
;
p
++
)
{
}
if
((
p
==
data
||
p
[
-
1
]
==
'&'
)
&&
p
[
name_len
]
==
'='
&&
#endif // !NO_CGI
!
mg_strncasecmp
(
name
,
p
,
name_len
))
{
// Point p to variable value
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
p
+=
name_len
+
1
;
unsigned
long
on
=
1
;
return
ioctlsocket
(
sock
,
FIONBIO
,
&
on
);
}
// Point s to the end of the value
#else
s
=
(
const
char
*
)
memchr
(
p
,
'&'
,
(
size_t
)(
e
-
p
));
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
if
(
s
==
NULL
)
{
struct
stat
st
;
s
=
e
;
}
assert
(
s
>=
p
);
// Decode variable into destination buffer
filep
->
modification_time
=
(
time_t
)
0
;
len
=
mg_url_decode
(
p
,
(
size_t
)(
s
-
p
),
dst
,
dst_len
,
1
);
if
(
stat
(
path
,
&
st
)
==
0
)
{
filep
->
size
=
st
.
st_size
;
filep
->
modification_time
=
st
.
st_mtime
;
filep
->
is_directory
=
S_ISDIR
(
st
.
st_mode
);
// Redirect error code from -1 to -2 (destination buffer too small).
// See https://github.com/cesanta/mongoose/issues/109
if
(
len
==
-
1
)
{
// Some filesystems report modification time as 0. Artificially
len
=
-
2
;
// bump it up to mark mg_stat() success.
}
if
(
filep
->
modification_time
==
(
time_t
)
0
)
{
break
;
filep
->
modification_time
=
(
time_t
)
1
;
}
}
}
}
}
return
len
;
return
filep
->
modification_time
!=
(
time_t
)
0
;
}
}
int
mg_get_cookie
(
const
char
*
cookie_header
,
const
char
*
var_name
,
static
void
set_close_on_exec
(
int
fd
)
{
char
*
dst
,
size_t
dst_size
)
{
fcntl
(
fd
,
F_SETFD
,
FD_CLOEXEC
);
const
char
*
s
,
*
p
,
*
end
;
}
int
name_len
,
len
=
-
1
;
if
(
dst
==
NULL
||
dst_size
==
0
)
{
int
mg_start_thread
(
mg_thread_func_t
func
,
void
*
param
)
{
len
=
-
2
;
pthread_t
thread_id
;
}
else
if
(
var_name
==
NULL
||
(
s
=
cookie_header
)
==
NULL
)
{
pthread_attr_t
attr
;
len
=
-
1
;
int
result
;
dst
[
0
]
=
'\0'
;
(
void
)
pthread_attr_init
(
&
attr
);
(
void
)
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
);
#if USE_STACK_SIZE > 1
// Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
(
void
)
pthread_attr_setstacksize
(
&
attr
,
USE_STACK_SIZE
);
#endif
result
=
pthread_create
(
&
thread_id
,
&
attr
,
func
,
param
);
pthread_attr_destroy
(
&
attr
);
return
result
;
}
#ifndef NO_CGI
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
char
*
envblk
,
char
*
envp
[],
int
fdin
,
int
fdout
,
const
char
*
dir
)
{
pid_t
pid
;
const
char
*
interp
;
(
void
)
envblk
;
if
((
pid
=
fork
())
==
-
1
)
{
// Parent
send_http_error
(
conn
,
500
,
http_500_error
,
"fork(): %s"
,
strerror
(
ERRNO
));
}
else
if
(
pid
==
0
)
{
// Child
if
(
chdir
(
dir
)
!=
0
)
{
cry
(
conn
,
"%s: chdir(%s): %s"
,
__func__
,
dir
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdin
,
0
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 0): %s"
,
__func__
,
fdin
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdout
,
1
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fdout
,
strerror
(
ERRNO
));
}
else
{
}
else
{
name_len
=
(
int
)
strlen
(
var_name
);
// Not redirecting stderr to stdout, to avoid output being littered
end
=
s
+
strlen
(
s
);
// with the error messages.
dst
[
0
]
=
'\0'
;
(
void
)
close
(
fdin
);
(
void
)
close
(
fdout
);
for
(;
(
s
=
mg_strcasestr
(
s
,
var_name
))
!=
NULL
;
s
+=
name_len
)
{
// After exec, all signal handlers are restored to their default values,
if
(
s
[
name_len
]
==
'='
)
{
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
s
+=
name_len
+
1
;
// implementation, SIGCHLD's handler will leave unchanged after exec
if
((
p
=
strchr
(
s
,
' '
))
==
NULL
)
// if it was set to be ignored. Restore it to default action.
p
=
end
;
signal
(
SIGCHLD
,
SIG_DFL
);
if
(
p
[
-
1
]
==
';'
)
p
--
;
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
*
s
==
'"'
&&
p
[
-
1
]
==
'"'
&&
p
>
s
+
1
)
{
if
(
interp
==
NULL
)
{
s
++
;
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
p
--
;
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
}
if
((
size_t
)
(
p
-
s
)
<
dst_size
)
{
len
=
p
-
s
;
mg_strlcpy
(
dst
,
s
,
(
size_t
)
len
+
1
);
}
else
{
}
else
{
len
=
-
3
;
(
void
)
execle
(
interp
,
interp
,
prog
,
NULL
,
envp
)
;
}
cry
(
conn
,
"%s: execle(%s %s): %s"
,
__func__
,
interp
,
prog
,
break
;
strerror
(
ERRNO
))
;
}
}
}
}
exit
(
EXIT_FAILURE
);
}
}
return
len
;
return
pid
;
}
}
#endif // !NO_CGI
// Return 1 if real file has been found, 0 otherwise
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
static
int
convert_uri_to_file_name
(
struct
mg_connection
*
conn
,
char
*
buf
,
int
flags
;
size_t
buf_len
,
struct
file
*
filep
)
{
struct
vec
a
,
b
;
const
char
*
rewrite
,
*
uri
=
conn
->
request_info
.
uri
,
*
root
=
conn
->
ctx
->
config
[
DOCUMENT_ROOT
];
char
*
p
;
int
match_len
;
char
gz_path
[
PATH_MAX
];
char
const
*
accept_encoding
;
// No filesystem access
flags
=
fcntl
(
sock
,
F_GETFL
,
0
);
if
(
root
==
NULL
)
{
(
void
)
fcntl
(
sock
,
F_SETFL
,
flags
|
O_NONBLOCK
);
return
0
;
}
// Using buf_len - 1 because memmove() for PATH_INFO may shift part
return
0
;
// of the path one byte on the right.
}
// If document_root is NULL, leave the file empty.
#endif // _WIN32
mg_snprintf
(
buf
,
buf_len
-
1
,
"%s%s"
,
root
,
uri
);
rewrite
=
conn
->
ctx
->
config
[
REWRITE
];
// Write data to the IO channel - opened file descriptor, socket or SSL
while
((
rewrite
=
next_option
(
rewrite
,
&
a
,
&
b
))
!=
NULL
)
{
// descriptor. Return number of bytes written.
if
((
match_len
=
match_prefix
(
a
.
ptr
,
a
.
len
,
uri
))
>
0
)
{
static
int64_t
push
(
FILE
*
fp
,
SOCKET
sock
,
SSL
*
ssl
,
const
char
*
buf
,
mg_snprintf
(
buf
,
buf_len
-
1
,
"%.*s%s"
,
(
int
)
b
.
len
,
b
.
ptr
,
int64_t
len
)
{
uri
+
match_len
);
int64_t
sent
;
break
;
int
n
,
k
;
}
}
if
(
mg_stat
(
buf
,
filep
))
{
(
void
)
ssl
;
// Get rid of warning
return
1
;
sent
=
0
;
}
while
(
sent
<
len
)
{
// if we can't find the actual file, look for the file
// How many bytes we send in this iteration
// with the same name but a .gz extension. If we find it,
k
=
len
-
sent
>
INT_MAX
?
INT_MAX
:
(
int
)
(
len
-
sent
);
// use that and set the gzipped flag in the file struct
// to indicate that the response need to have the content-
// encoding: gzip header
// we can only do this if the browser declares support
if
((
accept_encoding
=
mg_get_header
(
conn
,
"Accept-Encoding"
))
!=
NULL
)
{
if
(
strstr
(
accept_encoding
,
"gzip"
)
!=
NULL
)
{
snprintf
(
gz_path
,
sizeof
(
gz_path
),
"%s.gz"
,
buf
);
if
(
mg_stat
(
gz_path
,
filep
))
{
filep
->
gzipped
=
1
;
return
1
;
}
}
}
// Support PATH_INFO for CGI scripts.
if
(
ssl
!=
NULL
)
{
for
(
p
=
buf
+
strlen
(
root
==
NULL
?
""
:
root
);
*
p
!=
'\0'
;
p
++
)
{
n
=
SSL_write
(
ssl
,
buf
+
sent
,
k
);
if
(
*
p
==
'/'
)
{
}
else
if
(
fp
!=
NULL
)
{
*
p
=
'\0'
;
n
=
(
int
)
fwrite
(
buf
+
sent
,
1
,
(
size_t
)
k
,
fp
);
if
(
match_prefix
(
conn
->
ctx
->
config
[
CGI_EXTENSIONS
],
if
(
ferror
(
fp
))
strlen
(
conn
->
ctx
->
config
[
CGI_EXTENSIONS
]),
buf
)
>
0
&&
n
=
-
1
;
mg_stat
(
buf
,
filep
))
{
// Shift PATH_INFO block one character right, e.g.
// "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
// conn->path_info is pointing to the local variable "path" declared
// in handle_request(), so PATH_INFO is not valid after
// handle_request returns.
conn
->
path_info
=
p
+
1
;
memmove
(
p
+
2
,
p
+
1
,
strlen
(
p
+
1
)
+
1
);
// +1 is for trailing \0
p
[
1
]
=
'/'
;
return
1
;
}
else
{
}
else
{
*
p
=
'/'
;
n
=
send
(
sock
,
buf
+
sent
,
(
size_t
)
k
,
MSG_NOSIGNAL
);
}
}
}
if
(
n
<=
0
)
break
;
sent
+=
n
;
}
}
return
0
;
return
sent
;
}
}
// Check whether full request is buffered. Return:
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
// -1 if request is malformed
// Return negative value on error, or number of bytes read on success.
// 0 if request is not yet fully buffered
static
int
pull
(
FILE
*
fp
,
struct
mg_connection
*
conn
,
char
*
buf
,
int
len
)
{
// >0 actual request length, including last \r\n\r\n
int
nread
;
static
int
get_request_len
(
const
char
*
buf
,
int
buf_len
)
{
int
i
;
for
(
i
=
0
;
i
<
buf_len
;
i
++
)
{
if
(
len
<=
0
)
return
0
;
// Control characters are not allowed but >=128 is.
if
(
fp
!=
NULL
)
{
//
Abort scan as soon as one malformed character is found;
//
Use read() instead of fread(), because if we're reading from the CGI
//
don't let subsequent \r\n\r\n win us over anyhow
//
pipe, fread() may block until IO buffer is filled up. We cannot afford
if
(
!
isprint
(
*
(
const
unsigned
char
*
)
&
buf
[
i
])
&&
buf
[
i
]
!=
'\r'
&&
// to block and must pass all read bytes immediately to the client.
buf
[
i
]
!=
'\n'
&&
*
(
const
unsigned
char
*
)
&
buf
[
i
]
<
128
)
{
nread
=
read
(
fileno
(
fp
),
buf
,
(
size_t
)
len
);
return
-
1
;
#ifndef NO_SSL
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
1
<
buf_len
&&
buf
[
i
+
1
]
==
'\n'
)
{
}
else
if
(
conn
->
ssl
!=
NULL
)
{
return
i
+
2
;
nread
=
SSL_read
(
conn
->
ssl
,
buf
,
len
)
;
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
2
<
buf_len
&&
buf
[
i
+
1
]
==
'\r'
&&
#endif
buf
[
i
+
2
]
==
'\n'
)
{
}
else
{
return
i
+
3
;
nread
=
recv
(
conn
->
client
.
sock
,
buf
,
(
size_t
)
len
,
0
)
;
}
}
if
(
nread
>
0
)
{
conn
->
num_bytes_read
+=
nread
;
}
}
return
0
;
return
conn
->
ctx
->
stop_flag
?
-
1
:
nread
;
}
}
// Protect against directory disclosure attack by removing '..',
static
int
pull_all
(
FILE
*
fp
,
struct
mg_connection
*
conn
,
char
*
buf
,
int
len
)
{
// excessive '/' and '\' characters
int
n
,
nread
=
0
;
static
void
remove_double_dots_and_double_slashes
(
char
*
s
)
{
char
*
p
=
s
;
while
(
*
s
!=
'\0'
)
{
while
(
len
>
0
&&
conn
->
ctx
->
stop_flag
==
0
)
{
*
p
++
=
*
s
++
;
n
=
pull
(
fp
,
conn
,
buf
+
nread
,
len
);
if
(
s
[
-
1
]
==
'/'
||
s
[
-
1
]
==
'\\'
)
{
if
(
n
<
0
)
{
// Skip all following slashes, backslashes and double-dots
nread
=
n
;
// Propagate the error
while
(
s
[
0
]
!=
'\0'
)
{
if
(
s
[
0
]
==
'/'
||
s
[
0
]
==
'\\'
)
{
s
++
;
}
else
if
(
s
[
0
]
==
'.'
&&
s
[
1
]
==
'.'
)
{
s
+=
2
;
}
else
{
break
;
break
;
}
else
if
(
n
==
0
)
{
break
;
// No more data to read
}
else
{
nread
+=
n
;
len
-=
n
;
}
}
}
}
}
}
*
p
=
'\0'
;
}
static
const
struct
{
const
char
*
extension
;
size_t
ext_len
;
const
char
*
mime_type
;
}
builtin_mime_types
[]
=
{
{
".html"
,
5
,
"text/html"
},
{
".htm"
,
4
,
"text/html"
},
{
".shtm"
,
5
,
"text/html"
},
{
".shtml"
,
6
,
"text/html"
},
{
".css"
,
4
,
"text/css"
},
{
".js"
,
3
,
"application/x-javascript"
},
{
".ico"
,
4
,
"image/x-icon"
},
{
".gif"
,
4
,
"image/gif"
},
{
".jpg"
,
4
,
"image/jpeg"
},
{
".jpeg"
,
5
,
"image/jpeg"
},
{
".png"
,
4
,
"image/png"
},
{
".svg"
,
4
,
"image/svg+xml"
},
{
".txt"
,
4
,
"text/plain"
},
{
".torrent"
,
8
,
"application/x-bittorrent"
},
{
".wav"
,
4
,
"audio/x-wav"
},
{
".mp3"
,
4
,
"audio/x-mp3"
},
{
".mid"
,
4
,
"audio/mid"
},
{
".m3u"
,
4
,
"audio/x-mpegurl"
},
{
".ogg"
,
4
,
"application/ogg"
},
{
".ram"
,
4
,
"audio/x-pn-realaudio"
},
{
".xml"
,
4
,
"text/xml"
},
{
".json"
,
5
,
"text/json"
},
{
".xslt"
,
5
,
"application/xml"
},
{
".xsl"
,
4
,
"application/xml"
},
{
".ra"
,
3
,
"audio/x-pn-realaudio"
},
{
".doc"
,
4
,
"application/msword"
},
{
".exe"
,
4
,
"application/octet-stream"
},
{
".zip"
,
4
,
"application/x-zip-compressed"
},
{
".xls"
,
4
,
"application/excel"
},
{
".tgz"
,
4
,
"application/x-tar-gz"
},
{
".tar"
,
4
,
"application/x-tar"
},
{
".gz"
,
3
,
"application/x-gunzip"
},
{
".arj"
,
4
,
"application/x-arj-compressed"
},
{
".rar"
,
4
,
"application/x-arj-compressed"
},
{
".rtf"
,
4
,
"application/rtf"
},
{
".pdf"
,
4
,
"application/pdf"
},
{
".swf"
,
4
,
"application/x-shockwave-flash"
},
{
".mpg"
,
4
,
"video/mpeg"
},
{
".webm"
,
5
,
"video/webm"
},
{
".mpeg"
,
5
,
"video/mpeg"
},
{
".mov"
,
4
,
"video/quicktime"
},
{
".mp4"
,
4
,
"video/mp4"
},
{
".m4v"
,
4
,
"video/x-m4v"
},
{
".asf"
,
4
,
"video/x-ms-asf"
},
{
".avi"
,
4
,
"video/x-msvideo"
},
{
".bmp"
,
4
,
"image/bmp"
},
{
".ttf"
,
4
,
"application/x-font-ttf"
},
{
NULL
,
0
,
NULL
}
};
const
char
*
mg_get_builtin_mime_type
(
const
char
*
path
)
{
return
nread
;
const
char
*
ext
;
}
size_t
i
,
path_len
;
path_len
=
strlen
(
path
);
int
mg_read
(
struct
mg_connection
*
conn
,
void
*
buf
,
int
len
)
{
int
n
,
buffered_len
,
nread
=
0
;
int64_t
left
;
for
(
i
=
0
;
builtin_mime_types
[
i
].
extension
!=
NULL
;
i
++
)
{
// If Content-Length is not set, read until socket is closed
ext
=
path
+
(
path_len
-
builtin_mime_types
[
i
].
ext_len
);
if
(
conn
->
content_len
<=
0
)
{
if
(
path_len
>
builtin_mime_types
[
i
].
ext_len
&&
conn
->
content_len
=
INT64_MAX
;
mg_strcasecmp
(
ext
,
builtin_mime_types
[
i
].
extension
)
==
0
)
{
conn
->
must_close
=
1
;
return
builtin_mime_types
[
i
].
mime_type
;
}
}
}
return
"text/plain"
;
// conn->buf body
}
// |=================|==========|===============|
// |<--request_len-->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
// Look at the "path" extension and figure what mime type it has.
// First, check for data buffered in conn->buf by read_request().
// Store mime type in the vector.
if
(
len
>
0
&&
(
buffered_len
=
conn
->
data_len
-
conn
->
request_len
)
>
0
)
{
static
void
get_mime_type
(
struct
mg_context
*
ctx
,
const
char
*
path
,
char
*
body
=
conn
->
buf
+
conn
->
request_len
;
struct
vec
*
vec
)
{
if
(
buffered_len
>
len
)
buffered_len
=
len
;
struct
vec
ext_vec
,
mime_vec
;
if
(
buffered_len
>
conn
->
content_len
)
buffered_len
=
(
int
)
conn
->
content_len
;
const
char
*
list
,
*
ext
;
size_t
path_len
;
path_len
=
strlen
(
path
);
memcpy
(
buf
,
body
,
(
size_t
)
buffered_len
);
memmove
(
body
,
body
+
buffered_len
,
&
conn
->
buf
[
conn
->
data_len
]
-
&
body
[
buffered_len
]);
len
-=
buffered_len
;
conn
->
data_len
-=
buffered_len
;
nread
+=
buffered_len
;
}
// Scan user-defined mime types first, in case user wants to
// Read data from the socket.
// override default mime types.
if
(
len
>
0
&&
(
left
=
left_to_read
(
conn
))
>
0
)
{
list
=
ctx
->
config
[
EXTRA_MIME_TYPES
];
if
(
left
<
len
)
{
while
((
list
=
next_option
(
list
,
&
ext_vec
,
&
mime_vec
))
!=
NULL
)
{
len
=
(
int
)
left
;
// ext now points to the path suffix
ext
=
path
+
path_len
-
ext_vec
.
len
;
if
(
mg_strncasecmp
(
ext
,
ext_vec
.
ptr
,
ext_vec
.
len
)
==
0
)
{
*
vec
=
mime_vec
;
return
;
}
}
n
=
pull_all
(
NULL
,
conn
,
(
char
*
)
buf
+
nread
,
(
int
)
len
);
nread
=
n
>=
0
?
nread
+
n
:
n
;
}
}
vec
->
ptr
=
mg_get_builtin_mime_type
(
path
);
return
nread
;
vec
->
len
=
strlen
(
vec
->
ptr
);
}
static
int
is_big_endian
(
void
)
{
static
const
int
n
=
1
;
return
((
char
*
)
&
n
)[
0
]
==
0
;
}
}
#ifndef HAVE_MD5
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
typedef
struct
MD5Context
{
time_t
now
;
uint32_t
buf
[
4
];
int64_t
n
,
total
,
allowed
;
uint32_t
bits
[
2
];
unsigned
char
in
[
64
];
}
MD5_CTX
;
static
void
byteReverse
(
unsigned
char
*
buf
,
unsigned
longs
)
{
uint32_t
t
;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if
(
conn
->
throttle
>
0
)
{
if
(
is_big_endian
())
{
if
((
now
=
time
(
NULL
))
!=
conn
->
last_throttle_time
)
{
do
{
conn
->
last_throttle_time
=
now
;
t
=
(
uint32_t
)
((
unsigned
)
buf
[
3
]
<<
8
|
buf
[
2
])
<<
16
|
conn
->
last_throttle_bytes
=
0
;
((
unsigned
)
buf
[
1
]
<<
8
|
buf
[
0
]);
}
*
(
uint32_t
*
)
buf
=
t
;
allowed
=
conn
->
throttle
-
conn
->
last_throttle_bytes
;
buf
+=
4
;
if
(
allowed
>
(
int64_t
)
len
)
{
}
while
(
--
longs
);
allowed
=
len
;
}
if
((
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
==
allowed
)
{
buf
=
(
char
*
)
buf
+
total
;
conn
->
last_throttle_bytes
+=
total
;
while
(
total
<
(
int64_t
)
len
&&
conn
->
ctx
->
stop_flag
==
0
)
{
allowed
=
conn
->
throttle
>
(
int64_t
)
len
-
total
?
(
int64_t
)
len
-
total
:
conn
->
throttle
;
if
((
n
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
!=
allowed
)
{
break
;
}
sleep
(
1
);
conn
->
last_throttle_bytes
=
allowed
;
conn
->
last_throttle_time
=
time
(
NULL
);
buf
=
(
char
*
)
buf
+
n
;
total
+=
n
;
}
}
}
else
{
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
len
);
}
}
return
(
int
)
total
;
}
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
// Print message to buffer. If buffer is large enough to hold the message,
#define F2(x, y, z) F1(z, x, y)
// return buffer. If buffer is to small, allocate large enough buffer on heap,
#define F3(x, y, z) (x ^ y ^ z)
// and return allocated buffer.
#define F4(x, y, z) (y ^ (x | ~z))
static
int
alloc_vprintf
(
char
**
buf
,
size_t
size
,
const
char
*
fmt
,
va_list
ap
)
{
va_list
ap_copy
;
int
len
;
#define MD5STEP(f, w, x, y, z, data, s) \
// Windows is not standard-compliant, and vsnprintf() returns -1 if
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// buffer is too small. Also, older versions of msvcrt.dll do not have
// _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
// Therefore, we make two passes: on first pass, get required message length.
// On second pass, actually print the message.
va_copy
(
ap_copy
,
ap
);
len
=
vsnprintf
(
NULL
,
0
,
fmt
,
ap_copy
);
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
if
(
len
>
(
int
)
size
&&
// initialization constants.
(
size
=
len
+
1
)
>
0
&&
static
void
MD5Init
(
MD5_CTX
*
ctx
)
{
(
*
buf
=
(
char
*
)
malloc
(
size
))
==
NULL
)
{
ctx
->
buf
[
0
]
=
0x67452301
;
len
=
-
1
;
// Allocation failed, mark failure
ctx
->
buf
[
1
]
=
0xefcdab89
;
}
else
{
ctx
->
buf
[
2
]
=
0x98badcfe
;
va_copy
(
ap_copy
,
ap
);
ctx
->
buf
[
3
]
=
0x10325476
;
vsnprintf
(
*
buf
,
size
,
fmt
,
ap_copy
);
}
ctx
->
bits
[
0
]
=
0
;
return
len
;
ctx
->
bits
[
1
]
=
0
;
}
}
static
void
MD5Transform
(
uint32_t
buf
[
4
],
uint32_t
const
in
[
16
])
{
int
mg_vprintf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
va_list
ap
)
{
register
uint32_t
a
,
b
,
c
,
d
;
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
int
len
;
a
=
buf
[
0
];
b
=
buf
[
1
];
c
=
buf
[
2
];
d
=
buf
[
3
];
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xd76aa478
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
1
]
+
0xe8c7b756
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x242070db
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
3
]
+
0xc1bdceee
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf57c0faf
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
5
]
+
0x4787c62a
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa8304613
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
7
]
+
0xfd469501
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x698098d8
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
9
]
+
0x8b44f7af
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffff5bb1
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
11
]
+
0x895cd7be
,
22
);
MD5STEP
(
F1
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x6b901122
,
7
);
MD5STEP
(
F1
,
d
,
a
,
b
,
c
,
in
[
13
]
+
0xfd987193
,
12
);
MD5STEP
(
F1
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xa679438e
,
17
);
MD5STEP
(
F1
,
b
,
c
,
d
,
a
,
in
[
15
]
+
0x49b40821
,
22
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xf61e2562
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
6
]
+
0xc040b340
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x265e5a51
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
0
]
+
0xe9b6c7aa
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xd62f105d
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
10
]
+
0x02441453
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0xd8a1e681
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
4
]
+
0xe7d3fbc8
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0x21e1cde6
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
14
]
+
0xc33707d6
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xf4d50d87
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
8
]
+
0x455a14ed
,
20
);
MD5STEP
(
F2
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0xa9e3e905
,
5
);
MD5STEP
(
F2
,
d
,
a
,
b
,
c
,
in
[
2
]
+
0xfcefa3f8
,
9
);
MD5STEP
(
F2
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0x676f02d9
,
14
);
MD5STEP
(
F2
,
b
,
c
,
d
,
a
,
in
[
12
]
+
0x8d2a4c8a
,
20
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
5
]
+
0xfffa3942
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
8
]
+
0x8771f681
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
11
]
+
0x6d9d6122
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
14
]
+
0xfde5380c
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
1
]
+
0xa4beea44
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
4
]
+
0x4bdecfa9
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
7
]
+
0xf6bb4b60
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
10
]
+
0xbebfbc70
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
13
]
+
0x289b7ec6
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
0
]
+
0xeaa127fa
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
3
]
+
0xd4ef3085
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
6
]
+
0x04881d05
,
23
);
MD5STEP
(
F3
,
a
,
b
,
c
,
d
,
in
[
9
]
+
0xd9d4d039
,
4
);
MD5STEP
(
F3
,
d
,
a
,
b
,
c
,
in
[
12
]
+
0xe6db99e5
,
11
);
MD5STEP
(
F3
,
c
,
d
,
a
,
b
,
in
[
15
]
+
0x1fa27cf8
,
16
);
MD5STEP
(
F3
,
b
,
c
,
d
,
a
,
in
[
2
]
+
0xc4ac5665
,
23
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
0
]
+
0xf4292244
,
6
);
if
((
len
=
alloc_vprintf
(
&
buf
,
sizeof
(
mem
),
fmt
,
ap
))
>
0
)
{
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
7
]
+
0x432aff97
,
10
);
len
=
mg_write
(
conn
,
buf
,
(
size_t
)
len
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
14
]
+
0xab9423a7
,
15
);
}
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
5
]
+
0xfc93a039
,
21
);
if
(
buf
!=
mem
&&
buf
!=
NULL
)
{
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
12
]
+
0x655b59c3
,
6
);
free
(
buf
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
3
]
+
0x8f0ccc92
,
10
);
}
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
10
]
+
0xffeff47d
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
1
]
+
0x85845dd1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
8
]
+
0x6fa87e4f
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
15
]
+
0xfe2ce6e0
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
6
]
+
0xa3014314
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
13
]
+
0x4e0811a1
,
21
);
MD5STEP
(
F4
,
a
,
b
,
c
,
d
,
in
[
4
]
+
0xf7537e82
,
6
);
MD5STEP
(
F4
,
d
,
a
,
b
,
c
,
in
[
11
]
+
0xbd3af235
,
10
);
MD5STEP
(
F4
,
c
,
d
,
a
,
b
,
in
[
2
]
+
0x2ad7d2bb
,
15
);
MD5STEP
(
F4
,
b
,
c
,
d
,
a
,
in
[
9
]
+
0xeb86d391
,
21
);
buf
[
0
]
+=
a
;
return
len
;
buf
[
1
]
+=
b
;
buf
[
2
]
+=
c
;
buf
[
3
]
+=
d
;
}
}
static
void
MD5Update
(
MD5_CTX
*
ctx
,
unsigned
char
const
*
buf
,
unsigned
len
)
{
int
mg_printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
uint32_t
t
;
va_list
ap
;
va_start
(
ap
,
fmt
);
t
=
ctx
->
bits
[
0
];
return
mg_vprintf
(
conn
,
fmt
,
ap
);
if
((
ctx
->
bits
[
0
]
=
t
+
((
uint32_t
)
len
<<
3
))
<
t
)
}
ctx
->
bits
[
1
]
++
;
ctx
->
bits
[
1
]
+=
len
>>
29
;
t
=
(
t
>>
3
)
&
0x3f
;
if
(
t
)
{
static
int
mg_chunked_printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
unsigned
char
*
p
=
(
unsigned
char
*
)
ctx
->
in
+
t
;
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
int
len
;
t
=
64
-
t
;
va_list
ap
;
if
(
len
<
t
)
{
va_start
(
ap
,
fmt
);
memcpy
(
p
,
buf
,
len
);
if
((
len
=
alloc_vprintf
(
&
buf
,
sizeof
(
mem
),
fmt
,
ap
))
>
0
)
{
return
;
len
=
mg_printf
(
conn
,
"%X
\r\n
%s
\r\n
"
,
len
,
buf
);
}
memcpy
(
p
,
buf
,
t
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
t
;
len
-=
t
;
}
}
while
(
len
>=
64
)
{
if
(
buf
!=
mem
&&
buf
!=
NULL
)
{
memcpy
(
ctx
->
in
,
buf
,
64
);
free
(
buf
);
byteReverse
(
ctx
->
in
,
16
);
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
buf
+=
64
;
len
-=
64
;
}
}
memcpy
(
ctx
->
in
,
buf
,
len
)
;
return
len
;
}
}
static
void
MD5Final
(
unsigned
char
digest
[
16
],
MD5_CTX
*
ctx
)
{
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
unsigned
count
;
int
dst_len
,
int
is_form_url_encoded
)
{
unsigned
char
*
p
;
int
i
,
j
,
a
,
b
;
uint32_t
*
a
;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
count
=
(
ctx
->
bits
[
0
]
>>
3
)
&
0x3F
;
p
=
ctx
->
in
+
count
;
for
(
i
=
j
=
0
;
i
<
src_len
&&
j
<
dst_len
-
1
;
i
++
,
j
++
)
{
*
p
++
=
0x80
;
if
(
src
[
i
]
==
'%'
&&
i
<
src_len
-
2
&&
count
=
64
-
1
-
count
;
isxdigit
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
1
))
&&
if
(
count
<
8
)
{
isxdigit
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
2
)))
{
memset
(
p
,
0
,
count
);
a
=
tolower
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
1
));
byteReverse
(
ctx
->
in
,
16
);
b
=
tolower
(
*
(
const
unsigned
char
*
)
(
src
+
i
+
2
));
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
dst
[
j
]
=
(
char
)
((
HEXTOI
(
a
)
<<
4
)
|
HEXTOI
(
b
));
memset
(
ctx
->
in
,
0
,
56
);
i
+=
2
;
}
else
if
(
is_form_url_encoded
&&
src
[
i
]
==
'+'
)
{
dst
[
j
]
=
' '
;
}
else
{
}
else
{
memset
(
p
,
0
,
count
-
8
);
dst
[
j
]
=
src
[
i
];
}
}
}
byteReverse
(
ctx
->
in
,
14
);
a
=
(
uint32_t
*
)
ctx
->
in
;
dst
[
j
]
=
'\0'
;
// Null-terminate the destination
a
[
14
]
=
ctx
->
bits
[
0
];
a
[
15
]
=
ctx
->
bits
[
1
];
MD5Transform
(
ctx
->
buf
,
(
uint32_t
*
)
ctx
->
in
);
return
i
>=
src_len
?
j
:
-
1
;
byteReverse
((
unsigned
char
*
)
ctx
->
buf
,
4
);
memcpy
(
digest
,
ctx
->
buf
,
16
);
memset
((
char
*
)
ctx
,
0
,
sizeof
(
*
ctx
));
}
}
#endif // !HAVE_MD5
// Stringify binary data. Output buffer must be twice as big as input,
int
mg_get_var
(
const
char
*
data
,
size_t
data_len
,
const
char
*
name
,
// because each byte takes 2 bytes in string representation
char
*
dst
,
size_t
dst_len
)
{
static
void
bin2str
(
char
*
to
,
const
unsigned
char
*
p
,
size_t
len
)
{
const
char
*
p
,
*
e
,
*
s
;
static
const
char
*
hex
=
"0123456789abcdef"
;
size_t
name_len
;
int
len
;
for
(;
len
--
;
p
++
)
{
if
(
dst
==
NULL
||
dst_len
==
0
)
{
*
to
++
=
hex
[
p
[
0
]
>>
4
];
len
=
-
2
;
*
to
++
=
hex
[
p
[
0
]
&
0x0f
];
}
else
if
(
data
==
NULL
||
name
==
NULL
||
data_len
==
0
)
{
}
len
=
-
1
;
*
to
=
'\0'
;
dst
[
0
]
=
'\0'
;
}
}
else
{
name_len
=
strlen
(
name
);
e
=
data
+
data_len
;
len
=
-
1
;
dst
[
0
]
=
'\0'
;
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
// data is "var1=val1&var2=val2...". Find variable first
char
*
mg_md5
(
char
buf
[
33
],
...)
{
for
(
p
=
data
;
p
+
name_len
<
e
;
p
++
)
{
unsigned
char
hash
[
16
];
if
((
p
==
data
||
p
[
-
1
]
==
'&'
)
&&
p
[
name_len
]
==
'='
&&
const
char
*
p
;
!
mg_strncasecmp
(
name
,
p
,
name_len
))
{
va_list
ap
;
MD5_CTX
ctx
;
MD5Init
(
&
ctx
);
// Point p to variable value
p
+=
name_len
+
1
;
va_start
(
ap
,
buf
);
// Point s to the end of the value
while
((
p
=
va_arg
(
ap
,
const
char
*
))
!=
NULL
)
{
s
=
(
const
char
*
)
memchr
(
p
,
'&'
,
(
size_t
)(
e
-
p
));
MD5Update
(
&
ctx
,
(
const
unsigned
char
*
)
p
,
(
unsigned
)
strlen
(
p
));
if
(
s
==
NULL
)
{
s
=
e
;
}
}
va_end
(
ap
);
assert
(
s
>=
p
);
MD5Final
(
hash
,
&
ctx
);
bin2str
(
buf
,
hash
,
sizeof
(
hash
));
return
buf
;
}
// Check the user's password, return 1 if OK
// Decode variable into destination buffer
static
int
check_password
(
const
char
*
method
,
const
char
*
ha1
,
const
char
*
uri
,
len
=
mg_url_decode
(
p
,
(
size_t
)(
s
-
p
),
dst
,
dst_len
,
1
);
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
cnonce
,
const
char
*
qop
,
const
char
*
response
)
{
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
// Some of the parameters may be NULL
// Redirect error code from -1 to -2 (destination buffer too small).
if
(
method
==
NULL
||
nonce
==
NULL
||
nc
==
NULL
||
cnonce
==
NULL
||
if
(
len
==
-
1
)
{
qop
==
NULL
||
response
==
NULL
)
{
len
=
-
2
;
return
0
;
}
break
;
}
}
}
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// TODO(lsm): check for authentication timeout
if
(
// strcmp(dig->uri, c->ouri) != 0 ||
strlen
(
response
)
!=
32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
)
{
return
0
;
}
}
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
return
len
;
mg_md5
(
expected_response
,
ha1
,
":"
,
nonce
,
":"
,
nc
,
":"
,
cnonce
,
":"
,
qop
,
":"
,
ha2
,
NULL
);
return
mg_strcasecmp
(
response
,
expected_response
)
==
0
;
}
}
// Use the global passwords file, if specified by auth_gpass option,
int
mg_get_cookie
(
const
char
*
cookie_header
,
const
char
*
var_name
,
// or search for .htpasswd in the requested directory.
char
*
dst
,
size_t
dst_size
)
{
static
FILE
*
open_auth_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
s
,
*
p
,
*
end
;
char
name
[
PATH_MAX
];
int
name_len
,
len
=
-
1
;
const
char
*
p
,
*
e
,
*
gpass
=
conn
->
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
];
struct
file
file
=
STRUCT_FILE_INITIALIZER
;
FILE
*
fp
=
NULL
;
if
(
gpass
!=
NULL
)
{
if
(
dst
==
NULL
||
dst_size
==
0
)
{
// Use global passwords file
len
=
-
2
;
if
((
fp
=
mg_fopen
(
gpass
,
"r"
))
==
NULL
)
{
}
else
if
(
var_name
==
NULL
||
(
s
=
cookie_header
)
==
NULL
)
{
cry
(
conn
,
"fopen(%s): %s"
,
gpass
,
strerror
(
ERRNO
));
len
=
-
1
;
dst
[
0
]
=
'\0'
;
}
else
{
name_len
=
(
int
)
strlen
(
var_name
);
end
=
s
+
strlen
(
s
);
dst
[
0
]
=
'\0'
;
for
(;
(
s
=
mg_strcasestr
(
s
,
var_name
))
!=
NULL
;
s
+=
name_len
)
{
if
(
s
[
name_len
]
==
'='
)
{
s
+=
name_len
+
1
;
if
((
p
=
strchr
(
s
,
' '
))
==
NULL
)
p
=
end
;
if
(
p
[
-
1
]
==
';'
)
p
--
;
if
(
*
s
==
'"'
&&
p
[
-
1
]
==
'"'
&&
p
>
s
+
1
)
{
s
++
;
p
--
;
}
}
// Important: using local struct file to test path for is_directory flag.
if
((
size_t
)
(
p
-
s
)
<
dst_size
)
{
// If filep is used, mg_stat() makes it appear as if auth file was opened.
len
=
p
-
s
;
}
else
if
(
mg_stat
(
path
,
&
file
)
&&
file
.
is_directory
)
{
mg_strlcpy
(
dst
,
s
,
(
size_t
)
len
+
1
);
mg_snprintf
(
name
,
sizeof
(
name
),
"%s%c%s"
,
path
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
else
{
}
else
{
// Try to find .htpasswd in requested directory.
len
=
-
3
;
for
(
p
=
path
,
e
=
p
+
strlen
(
p
)
-
1
;
e
>
p
;
e
--
)
}
if
(
e
[
0
]
==
'/'
)
break
;
break
;
mg_snprintf
(
name
,
sizeof
(
name
),
"%.*s%c%s"
,
(
int
)
(
e
-
p
),
p
,
'/'
,
PASSWORDS_FILE_NAME
);
fp
=
mg_fopen
(
name
,
"r"
);
}
}
}
return
fp
;
}
return
len
;
}
}
//
Parsed Authorization header
//
Return 1 if real file has been found, 0 otherwise
st
ruct
ah
{
st
atic
int
convert_uri_to_file_name
(
struct
mg_connection
*
conn
,
char
*
buf
,
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
size_t
buf_len
,
struct
file
*
filep
)
{
}
;
struct
vec
a
,
b
;
const
char
*
rewrite
,
*
uri
=
conn
->
request_info
.
uri
,
// Return 1 on success. Always initializes the ah structure.
*
root
=
conn
->
ctx
->
config
[
DOCUMENT_ROOT
];
static
int
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
char
*
p
;
size_t
buf_size
,
struct
ah
*
ah
)
{
int
match_len
;
char
*
name
,
*
value
,
*
s
;
char
gz_path
[
PATH_MAX
]
;
c
onst
char
*
auth_header
;
c
har
const
*
accept_encoding
;
(
void
)
memset
(
ah
,
0
,
sizeof
(
*
ah
));
// No filesystem access
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
if
(
root
==
NULL
)
{
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
return
0
;
return
0
;
}
}
// Make modifiable copy of the auth header
// Using buf_len - 1 because memmove() for PATH_INFO may shift part
(
void
)
mg_strlcpy
(
buf
,
auth_header
+
7
,
buf_size
);
// of the path one byte on the right.
s
=
buf
;
// If document_root is NULL, leave the file empty.
mg_snprintf
(
buf
,
buf_len
-
1
,
"%s%s"
,
root
,
uri
);
// Parse authorization header
rewrite
=
conn
->
ctx
->
config
[
REWRITE
];
for
(;;)
{
while
((
rewrite
=
next_option
(
rewrite
,
&
a
,
&
b
))
!=
NULL
)
{
// Gobble initial spaces
if
((
match_len
=
match_prefix
(
a
.
ptr
,
a
.
len
,
uri
))
>
0
)
{
while
(
isspace
(
*
(
unsigned
char
*
)
s
))
{
mg_snprintf
(
buf
,
buf_len
-
1
,
"%.*s%s"
,
(
int
)
b
.
len
,
b
.
ptr
,
s
++
;
uri
+
match_len
);
}
break
;
name
=
skip_quoted
(
&
s
,
"="
,
" "
,
0
);
// Value is either quote-delimited, or ends at first comma or space.
if
(
s
[
0
]
==
'\"'
)
{
s
++
;
value
=
skip_quoted
(
&
s
,
"
\"
"
,
" "
,
'\\'
);
if
(
s
[
0
]
==
','
)
{
s
++
;
}
}
}
else
{
value
=
skip_quoted
(
&
s
,
", "
,
" "
,
0
);
// IE uses commas, FF uses spaces
}
}
if
(
*
name
==
'\0'
)
{
break
;
if
(
mg_stat
(
buf
,
filep
))
{
return
1
;
}
}
if
(
!
strcmp
(
name
,
"username"
))
{
// if we can't find the actual file, look for the file
ah
->
user
=
value
;
// with the same name but a .gz extension. If we find it,
}
else
if
(
!
strcmp
(
name
,
"cnonce"
))
{
// use that and set the gzipped flag in the file struct
ah
->
cnonce
=
value
;
// to indicate that the response need to have the content-
}
else
if
(
!
strcmp
(
name
,
"response"
))
{
// encoding: gzip header
ah
->
response
=
value
;
// we can only do this if the browser declares support
}
else
if
(
!
strcmp
(
name
,
"uri"
))
{
if
((
accept_encoding
=
mg_get_header
(
conn
,
"Accept-Encoding"
))
!=
NULL
)
{
ah
->
uri
=
value
;
if
(
strstr
(
accept_encoding
,
"gzip"
)
!=
NULL
)
{
}
else
if
(
!
strcmp
(
name
,
"qop"
))
{
snprintf
(
gz_path
,
sizeof
(
gz_path
),
"%s.gz"
,
buf
);
ah
->
qop
=
value
;
if
(
mg_stat
(
gz_path
,
filep
))
{
}
else
if
(
!
strcmp
(
name
,
"nc"
))
{
filep
->
gzipped
=
1
;
ah
->
nc
=
value
;
return
1
;
}
else
if
(
!
strcmp
(
name
,
"nonce"
))
{
}
ah
->
nonce
=
value
;
}
}
}
}
// CGI needs it as REMOTE_USER
// Support PATH_INFO for CGI scripts.
if
(
ah
->
user
!=
NULL
)
{
for
(
p
=
buf
+
strlen
(
root
==
NULL
?
""
:
root
);
*
p
!=
'\0'
;
p
++
)
{
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
if
(
*
p
==
'/'
)
{
*
p
=
'\0'
;
if
(
match_prefix
(
conn
->
ctx
->
config
[
CGI_EXTENSIONS
],
strlen
(
conn
->
ctx
->
config
[
CGI_EXTENSIONS
]),
buf
)
>
0
&&
mg_stat
(
buf
,
filep
))
{
// Shift PATH_INFO block one character right, e.g.
// "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
// conn->path_info is pointing to the local variable "path" declared
// in handle_request(), so PATH_INFO is not valid after
// handle_request returns.
conn
->
path_info
=
p
+
1
;
memmove
(
p
+
2
,
p
+
1
,
strlen
(
p
+
1
)
+
1
);
// +1 is for trailing \0
p
[
1
]
=
'/'
;
return
1
;
}
else
{
}
else
{
return
0
;
*
p
=
'/'
;
}
}
}
}
return
1
;
return
0
;
}
}
// Authorize against the opened passwords file. Return 1 if authorized.
// Check whether full request is buffered. Return:
static
int
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
// -1 if request is malformed
struct
ah
ah
;
// 0 if request is not yet fully buffered
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MG_BUF_LEN
];
// >0 actual request length, including last \r\n\r\n
static
int
get_request_len
(
const
char
*
buf
,
int
buf_len
)
{
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
int
i
;
return
0
;
}
// Loop over passwords file
for
(
i
=
0
;
i
<
buf_len
;
i
++
)
{
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
// Control characters are not allowed but >=128 is.
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%s"
,
f_user
,
f_domain
,
ha1
)
!=
3
)
{
// Abort scan as soon as one malformed character is found;
continue
;
// don't let subsequent \r\n\r\n win us over anyhow
if
(
!
isprint
(
*
(
const
unsigned
char
*
)
&
buf
[
i
])
&&
buf
[
i
]
!=
'\r'
&&
buf
[
i
]
!=
'\n'
&&
*
(
const
unsigned
char
*
)
&
buf
[
i
]
<
128
)
{
return
-
1
;
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
1
<
buf_len
&&
buf
[
i
+
1
]
==
'\n'
)
{
return
i
+
2
;
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
2
<
buf_len
&&
buf
[
i
+
1
]
==
'\r'
&&
buf
[
i
+
2
]
==
'\n'
)
{
return
i
+
3
;
}
}
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
!
strcmp
(
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
f_domain
))
return
check_password
(
conn
->
request_info
.
request_method
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ah
.
response
);
}
}
return
0
;
return
0
;
}
}
// Return 1 if request is authorised, 0 otherwise.
// Protect against directory disclosure attack by removing '..',
static
int
check_authorization
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
// excessive '/' and '\' characters
char
fname
[
PATH_MAX
];
static
void
remove_double_dots_and_double_slashes
(
char
*
s
)
{
struct
vec
uri_vec
,
filename_vec
;
char
*
p
=
s
;
const
char
*
list
;
FILE
*
fp
=
NULL
;
int
authorized
=
1
;
list
=
conn
->
ctx
->
config
[
PROTECT_URI
];
while
(
*
s
!=
'\0'
)
{
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
*
p
++
=
*
s
++
;
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
if
(
s
[
-
1
]
==
'/'
||
s
[
-
1
]
==
'\\'
)
{
mg_snprintf
(
fname
,
sizeof
(
fname
),
"%.*s"
,
// Skip all following slashes, backslashes and double-dots
(
int
)
filename_vec
.
len
,
filename_vec
.
ptr
);
while
(
s
[
0
]
!=
'\0'
)
{
if
((
fp
=
mg_fopen
(
fname
,
"r"
))
==
NULL
)
{
if
(
s
[
0
]
==
'/'
||
s
[
0
]
==
'\\'
)
{
cry
(
conn
,
"%s: cannot open %s: %s"
,
__func__
,
fname
,
strerror
(
errno
));
s
++
;
}
}
else
if
(
s
[
0
]
==
'.'
&&
s
[
1
]
==
'.'
)
{
s
+=
2
;
}
else
{
break
;
break
;
}
}
}
}
if
(
fp
==
NULL
)
{
fp
=
open_auth_file
(
conn
,
path
);
}
}
if
(
fp
!=
NULL
)
{
authorized
=
authorize
(
conn
,
fp
);
fclose
(
fp
);
}
}
*
p
=
'\0'
;
return
authorized
;
}
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
conn
->
status_code
=
401
;
mg_printf
(
conn
,
"HTTP/1.1 401 Unauthorized
\r\n
"
"Content-Length: 0
\r\n
"
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
],
(
unsigned
long
)
time
(
NULL
));
}
}
static
int
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
static
const
struct
{
const
char
*
passfile
=
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
];
const
char
*
extension
;
FILE
*
fp
;
size_t
ext_len
;
int
ret
=
0
;
const
char
*
mime_type
;
}
builtin_mime_types
[]
=
{
if
(
passfile
!=
NULL
&&
(
fp
=
mg_fopen
(
passfile
,
"r"
))
!=
NULL
)
{
{
".html"
,
5
,
"text/html"
},
ret
=
authorize
(
conn
,
fp
);
{
".htm"
,
4
,
"text/html"
},
fclose
(
fp
);
{
".shtm"
,
5
,
"text/html"
},
}
{
".shtml"
,
6
,
"text/html"
},
{
".css"
,
4
,
"text/css"
},
return
ret
;
{
".js"
,
3
,
"application/x-javascript"
},
}
{
".ico"
,
4
,
"image/x-icon"
},
{
".gif"
,
4
,
"image/gif"
},
{
".jpg"
,
4
,
"image/jpeg"
},
{
".jpeg"
,
5
,
"image/jpeg"
},
{
".png"
,
4
,
"image/png"
},
{
".svg"
,
4
,
"image/svg+xml"
},
{
".txt"
,
4
,
"text/plain"
},
{
".torrent"
,
8
,
"application/x-bittorrent"
},
{
".wav"
,
4
,
"audio/x-wav"
},
{
".mp3"
,
4
,
"audio/x-mp3"
},
{
".mid"
,
4
,
"audio/mid"
},
{
".m3u"
,
4
,
"audio/x-mpegurl"
},
{
".ogg"
,
4
,
"application/ogg"
},
{
".ram"
,
4
,
"audio/x-pn-realaudio"
},
{
".xml"
,
4
,
"text/xml"
},
{
".json"
,
5
,
"text/json"
},
{
".xslt"
,
5
,
"application/xml"
},
{
".xsl"
,
4
,
"application/xml"
},
{
".ra"
,
3
,
"audio/x-pn-realaudio"
},
{
".doc"
,
4
,
"application/msword"
},
{
".exe"
,
4
,
"application/octet-stream"
},
{
".zip"
,
4
,
"application/x-zip-compressed"
},
{
".xls"
,
4
,
"application/excel"
},
{
".tgz"
,
4
,
"application/x-tar-gz"
},
{
".tar"
,
4
,
"application/x-tar"
},
{
".gz"
,
3
,
"application/x-gunzip"
},
{
".arj"
,
4
,
"application/x-arj-compressed"
},
{
".rar"
,
4
,
"application/x-arj-compressed"
},
{
".rtf"
,
4
,
"application/rtf"
},
{
".pdf"
,
4
,
"application/pdf"
},
{
".swf"
,
4
,
"application/x-shockwave-flash"
},
{
".mpg"
,
4
,
"video/mpeg"
},
{
".webm"
,
5
,
"video/webm"
},
{
".mpeg"
,
5
,
"video/mpeg"
},
{
".mov"
,
4
,
"video/quicktime"
},
{
".mp4"
,
4
,
"video/mp4"
},
{
".m4v"
,
4
,
"video/x-m4v"
},
{
".asf"
,
4
,
"video/x-ms-asf"
},
{
".avi"
,
4
,
"video/x-msvideo"
},
{
".bmp"
,
4
,
"image/bmp"
},
{
".ttf"
,
4
,
"application/x-font-ttf"
},
{
NULL
,
0
,
NULL
}
};
int
mg_modify_passwords_file
(
const
char
*
fname
,
const
char
*
domain
,
const
char
*
mg_get_builtin_mime_type
(
const
char
*
path
)
{
const
char
*
user
,
const
char
*
pass
)
{
const
char
*
ext
;
int
found
;
size_t
i
,
path_len
;
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
FILE
*
fp
,
*
fp2
;
found
=
0
;
path_len
=
strlen
(
path
);
fp
=
fp2
=
NULL
;
// Regard empty password as no password - remove user record.
for
(
i
=
0
;
builtin_mime_types
[
i
].
extension
!=
NULL
;
i
++
)
{
if
(
pass
!=
NULL
&&
pass
[
0
]
==
'\0'
)
{
ext
=
path
+
(
path_len
-
builtin_mime_types
[
i
].
ext_len
);
pass
=
NULL
;
if
(
path_len
>
builtin_mime_types
[
i
].
ext_len
&&
mg_strcasecmp
(
ext
,
builtin_mime_types
[
i
].
extension
)
==
0
)
{
return
builtin_mime_types
[
i
].
mime_type
;
}
}
(
void
)
snprintf
(
tmp
,
sizeof
(
tmp
),
"%s.tmp"
,
fname
);
// Create the file if does not exist
if
((
fp
=
fopen
(
fname
,
"a+"
))
!=
NULL
)
{
fclose
(
fp
);
}
}
// Open the given file and temporary file
return
"text/plain"
;
if
((
fp
=
fopen
(
fname
,
"r"
))
==
NULL
)
{
}
return
0
;
}
else
if
((
fp2
=
fopen
(
tmp
,
"w+"
))
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
// Copy the stuff to temporary file
// Look at the "path" extension and figure what mime type it has.
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
// Store mime type in the vector.
if
(
sscanf
(
line
,
"%[^:]:%[^:]:%*s"
,
u
,
d
)
!=
2
)
{
static
void
get_mime_type
(
struct
mg_context
*
ctx
,
const
char
*
path
,
continue
;
struct
vec
*
vec
)
{
}
struct
vec
ext_vec
,
mime_vec
;
const
char
*
list
,
*
ext
;
size_t
path_len
;
if
(
!
strcmp
(
u
,
user
)
&&
!
strcmp
(
d
,
domain
))
{
path_len
=
strlen
(
path
);
found
++
;
if
(
pass
!=
NULL
)
{
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
}
}
else
{
fprintf
(
fp2
,
"%s"
,
line
);
}
}
// If new user, just add it
// Scan user-defined mime types first, in case user wants to
if
(
!
found
&&
pass
!=
NULL
)
{
// override default mime types.
mg_md5
(
ha1
,
user
,
":"
,
domain
,
":"
,
pass
,
NULL
);
list
=
ctx
->
config
[
EXTRA_MIME_TYPES
];
fprintf
(
fp2
,
"%s:%s:%s
\n
"
,
user
,
domain
,
ha1
);
while
((
list
=
next_option
(
list
,
&
ext_vec
,
&
mime_vec
))
!=
NULL
)
{
// ext now points to the path suffix
ext
=
path
+
path_len
-
ext_vec
.
len
;
if
(
mg_strncasecmp
(
ext
,
ext_vec
.
ptr
,
ext_vec
.
len
)
==
0
)
{
*
vec
=
mime_vec
;
return
;
}
}
}
// Close files
vec
->
ptr
=
mg_get_builtin_mime_type
(
path
);
fclose
(
fp
);
vec
->
len
=
strlen
(
vec
->
ptr
);
fclose
(
fp2
);
// Put the temp file in place of real file
remove
(
fname
);
rename
(
tmp
,
fname
);
return
1
;
}
}
static
SOCKET
conn2
(
const
char
*
host
,
int
port
,
int
use_ssl
,
static
SOCKET
conn2
(
const
char
*
host
,
int
port
,
int
use_ssl
,
...
...
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