mirror of
https://github.com/lammertb/libhttp.git
synced 2025-08-07 16:02:55 +03:00
Enhanced documentation and code quality
This commit is contained in:
@@ -71,14 +71,23 @@ LibHTTP is often used as HTTP and HTTPS library inside a larger application. A
|
||||
* [`httplib_modify_passwords_file( passwords_file_name, domain, user, password );`](api/httplib_modify_passwords_file.md)
|
||||
* [`httplib_set_auth_handler( ctx, uri, handler, cbdata );`](api/httplib_set_auth_handler.md)
|
||||
|
||||
### String Data Functions
|
||||
### Data Manipulation and Comparison Functions
|
||||
|
||||
* [`httplib_atomic_dec( addr );`](api/httplib_atomic_dec.md)
|
||||
* [`httplib_atomic_inc( addr );`](api/httplib_atomic_inc.md)
|
||||
* [`httplib_base64_encode( src, src_len, dst, dst_len );`](api/httplib_base64_encode.md)
|
||||
* [`httplib_md5( buf, ... );`](api/httplib_md5.md)
|
||||
* [`httplib_strcasecmp( s1, s2 );`](api/httplib_strcasecmp.md)
|
||||
* [`httplib_strlcpy( dst, src, len );`](api/httplib_strlcpy.md)
|
||||
* [`httplib_strncasecmp( s1, s2, len );`](api/httplib_strncasecmp.md)
|
||||
* [`httplib_strndup( str, len );`](api/httplib_strndup.md)
|
||||
* [`httplib_url_decode( src, src_len, dst, dst_len, is_form_url_encoded );`](api/httplib_url_decode.md)
|
||||
* [`httplib_url_encode( src, dst, dst_len );`](api/httplib_url_encode.md)
|
||||
|
||||
### Memory Allocation Functions
|
||||
|
||||
* [`httplib_free( ptr );`](api/httplib_free.md)
|
||||
|
||||
### Process Functions
|
||||
|
||||
* [`httplib_kill( pid, sig );`](httplib_kill.md)
|
||||
@@ -89,9 +98,10 @@ LibHTTP is often used as HTTP and HTTPS library inside a larger application. A
|
||||
* [`httplib_unlock_connection( conn );`](api/httplib_unlock_connection.md)
|
||||
* [`httplib_unlock_context( ctx );`](api/httplib_unlock_context.md)
|
||||
|
||||
### Directory Functions
|
||||
### File and Directory Functions
|
||||
|
||||
* [`httplib_closedir( dir );`](httplib_closedir.md)
|
||||
* [`httplib_mkdir( path, mode );`](httplib_mkdir.md)
|
||||
* [`httplib_opendir( name );`](httplib_opendir.md)
|
||||
* [`httplib_readdir( dir );`](httplib_readdir.md)
|
||||
* [`httplib_remove( path );`](httplib_remove.md)
|
||||
|
23
doc/api/httplib_atomic_dec.md
Normal file
23
doc/api/httplib_atomic_dec.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_atomic_dec( addr );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`addr`**|`volatile int *`|The address of the integer to decrement|
|
||||
|
||||
### Return Value
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`int`|The value of the integer after the decrement|
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_atomic_dec()` performs an atomic decrement if an integer. This function can be used to decrement an integer in a reliable way where multiple processes or threads have simultaneous access to the variable. The function returns the value of the integer after it has been decremented.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_atomic_inc();`](httplib_atomic_inc.md)
|
23
doc/api/httplib_atomic_inc.md
Normal file
23
doc/api/httplib_atomic_inc.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_atomic_inc( addr );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`addr`**|`volatile int *`|The address of the integer to increment|
|
||||
|
||||
### Return Value
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`int`|The value of the integer after the increment|
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_atomic_inc()` performs an atomic increment if an integer. This function can be used to increment an integer in a reliable way where multiple processes or threads have simultaneous access to the variable. The function returns the value of the integer after it has been incremented.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_atomic_dec();`](httplib_atomic_dec.md)
|
27
doc/api/httplib_base64_encode.md
Normal file
27
doc/api/httplib_base64_encode.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_base64_encode( src, src_len, dst, dst_len );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`src`**|`const unsigned char *`|Pointer to binary data to be BASE64 encoded|
|
||||
|**`src_len`**|`int`|The number of bytes of the binary data to encode|
|
||||
|**`dst`**|`char *`|Destination buffer for the encoding string|
|
||||
|**`dst_len`**|`int`|Length of the destination buffer|
|
||||
|
||||
### Return Value
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`int`|The size of the destination string or an error|
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_base64_encode()` encodes a block of binary data to a BASE64 encoded NUL terminated string. The destination buffer should be large enough to contain the whole string and NUL terminating character. If the function succeeds the actual number of used bytes in the destination buffer is returned. An error is indicated with the return value **-1**.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_url_decode();`](httplib_url_decode.md)
|
||||
* [`httplib_url_encode();`](httplib_url_encode.md)
|
@@ -19,7 +19,7 @@
|
||||
||**`FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
|
||||
||**`FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
|
||||
|**`field_get`**|**`int field_get( const char *key, const char *value, size_t valuelen, void *user_data );`**|
|
||||
|**`field_store`**|**`int field_store( const char *path, long long file_size, void *user_data );`**|
|
||||
|**`field_store`**|**`int field_store( const char *path, int64_t file_size, void *user_data );`**|
|
||||
||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_STORE`, LibHTTP will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, LibHTTP will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call:|
|
||||
||**`path`** -|
|
||||
||**`file_size`** - The path on the server where the file was stored|
|
||||
|
@@ -26,3 +26,4 @@ The `httplib_mkdir()` function returns **0** when it was successful and **-1** w
|
||||
* [`httplib_closedir();`](httplib_closedir.md)
|
||||
* [`httplib_opendir();`](httplib_opendir.md)
|
||||
* [`httplib_readdir();`](httplib_readdir.md)
|
||||
* [`httplib_remove();`](httplib_remove.md)
|
||||
|
25
doc/api/httplib_remove.md
Normal file
25
doc/api/httplib_remove.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_remove( path );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`path`**|`const char *`|The path of the file or directory to remove|
|
||||
|
||||
### Return Value
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`int`|An integer which indicates success or failure of the call|
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_remove()` provides a platform independent way to remove an entry from a directory. The function can be used to remove both file and directory entries. Remove will only function on directories, if the contents of the directory to remove is empty. In Posix compliant environments this function is a wrapper around the Posix `remove()` function. On other systems the Posix `remove()` functionality is emulated with own code.
|
||||
|
||||
The function returns **0** when successful and **-1** if an error occurs.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_mkdir();`](httplib_mkdir.md)
|
@@ -14,7 +14,7 @@
|
||||
|**`remote_user`**|`const char *`| The name of the authenticated remote user, or NULL if no authentication was used |
|
||||
|**`remote addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address. |
|
||||
|~~`remote_ip`~~|`long`| *Deprecated. Use* `remote_addr` *instead* |
|
||||
|**`content_length`**|`long long`| The content length of the request body. This value can be -1 if no content length was provided. |
|
||||
|**`content_length`**|`int64_t`| The content length of the request body. This value can be -1 if no content length was provided. |
|
||||
|**`remote_port`**|`int`| The port number at the client's side |
|
||||
|**`is_ssl`**|`int`| 1 if the connection is over SSL, and 0 if it is a plain connection |
|
||||
|**`user_data`**|`void *`| A pointer to the `user_data` information which was provided as a parameter to `httplib_start()`. |
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`long long`|Number of bytes written to the file, or an error code|
|
||||
|`int64_t`|Number of bytes written to the file, or an error code|
|
||||
|
||||
### Description
|
||||
|
||||
|
27
doc/api/httplib_strlcpy.md
Normal file
27
doc/api/httplib_strlcpy.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_strlcpy( dst, src, len );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`dst`**|`const char *`|Pointer to the destination buffer|
|
||||
|**`src`**|`const char *`|Pointer to the source string which must be copied|
|
||||
|**`len`**|`size_t`|The size of the receiving buffer in bytes|
|
||||
|
||||
### Return Value
|
||||
|
||||
*none*
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_strlcpy()` provides a platform independent safe way to copy a string from one memory location to another. The size of the receiving buffer is provided as a parameter and the function ensures that no more the number of the characters fitting in the buffer will be copied. The function also ensures that if the destination buffer is not NULL and the size is at least one byte long that the resulting string is terminated with a NUL character.
|
||||
|
||||
If the source string is longer than will fit in the receiving buffer, the remaining characters will be ignored.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_strcasecmp();`](httplib_strcasecmp.md)
|
||||
* [`httplib_strncasecmp();`](httplib_strncasecmp.md)
|
||||
* [`httplib_strndup()'`](httplib_strndup.md)
|
30
doc/api/httplib_strndup.md
Normal file
30
doc/api/httplib_strndup.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# LibHTTP API Reference
|
||||
|
||||
### `httplib_strndup( str, len );`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
|**`str`**|`const char *`|Pointer to the source string which must be duplicated|
|
||||
|**`len`**|`size_t`|The maximum number of string characters to duplicate|
|
||||
|
||||
### Return Value
|
||||
|
||||
| Type | Description |
|
||||
| :--- | :--- |
|
||||
|`char *`|Pointer to the duplicate, or NULL if an error occured|
|
||||
|
||||
### Description
|
||||
|
||||
The function `httplib_strndup()` duplicates a given number of characters of a string to a new string and terminates the result with a NUL character. If less than the specified maximum amount of characters are available, only the available characters are copied. The duplicate is stored in a newly allocated block of memory. The function is equivalent to the Posix `strndup()` function with the difference that the LibHTTP memory allocation functions are used which allow for tracking of allocation requests and memory leaks through a monitor hook. The size of the allocated memory block is the given maximum string length plus one byte for the terminating NUL character.
|
||||
|
||||
If the duplicate of the string is no longer used, the allocated memory should be returned to the heap with a call to [`httplib_free()`](httplib_free.md).
|
||||
|
||||
If the function fails the value `NULL` is returned, otherwise a pointer to the duplicate.
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_free();`](httplib_free.md)
|
||||
* [`httplib_strcasecmp();`](httplib_strcasecmp.md)
|
||||
* [`httplib_strncasecmp();`](httplib_strncasecmp.md)
|
@@ -24,4 +24,5 @@ The function `httplib_url_decode()` Decodes a in input buffer. Both normal URIs
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_base64_encode()`](httplib_base64_encode.md)
|
||||
* [`httplib_url_encode();`](httplib_url_encode.md)
|
||||
|
@@ -22,4 +22,5 @@ The function `httplib_url_encode()` encodes a in input buffer. Both normal URIs
|
||||
|
||||
### See Also
|
||||
|
||||
* [`httplib_base64_encode();`](htptlib_base64_encode.md)
|
||||
* [`httplib_url_decode();`](httplib_url_decode.md)
|
||||
|
@@ -249,7 +249,7 @@ field_get(const char *key, const char *value, size_t valuelen, void *user_data)
|
||||
|
||||
|
||||
int
|
||||
field_stored(const char *path, long long file_size, void *user_data)
|
||||
field_stored(const char *path, int64_t file_size, void *user_data)
|
||||
{
|
||||
struct httplib_connection *conn = (struct httplib_connection *)user_data;
|
||||
|
||||
@@ -312,7 +312,7 @@ FileUploadForm(struct httplib_connection *conn, void *cbdata)
|
||||
|
||||
struct tfile_checksum {
|
||||
char name[128];
|
||||
unsigned long long length;
|
||||
uint64_t length;
|
||||
md5_state_t chksum;
|
||||
};
|
||||
|
||||
|
@@ -147,9 +147,10 @@ class FooHandler : public CivetHandler
|
||||
{
|
||||
/* Handler may access the request info using httplib_get_request_info */
|
||||
const struct httplib_request_info *req_info = httplib_get_request_info(conn);
|
||||
long long rlen, wlen;
|
||||
long long nlen = 0;
|
||||
long long tlen = req_info->content_length;
|
||||
int64_t rlen;
|
||||
int64_t wlen;
|
||||
int64_t nlen = 0;
|
||||
int64_t tlen = req_info->content_length;
|
||||
char buf[1024];
|
||||
|
||||
httplib_printf(conn,
|
||||
@@ -191,9 +192,10 @@ class FooHandler : public CivetHandler
|
||||
{
|
||||
/* Handler may access the request info using httplib_get_request_info */
|
||||
const struct httplib_request_info *req_info = httplib_get_request_info(conn);
|
||||
long long rlen, wlen;
|
||||
long long nlen = 0;
|
||||
long long tlen = req_info->content_length;
|
||||
int64_t rlen;
|
||||
int64_t wlen;
|
||||
int64_t nlen = 0;
|
||||
int64_t tlen = req_info->content_length;
|
||||
FILE * f;
|
||||
char buf[1024];
|
||||
int fail = 0;
|
||||
|
@@ -139,7 +139,7 @@ struct httplib_request_info {
|
||||
used */
|
||||
char remote_addr[48]; /* Client's IP address as a string. */
|
||||
|
||||
long long content_length; /* Length (in bytes) of the request body,
|
||||
int64_t content_length; /* Length (in bytes) of the request body,
|
||||
can be -1 if no length was given. */
|
||||
int remote_port; /* Client's port */
|
||||
int is_ssl; /* 1 if SSL-ed, 0 if not */
|
||||
@@ -610,7 +610,7 @@ LIBHTTP_API void httplib_send_mime_file(struct httplib_connection *conn, const c
|
||||
LIBHTTP_API void httplib_send_mime_file2(struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers);
|
||||
|
||||
/* Store body data into a file. */
|
||||
LIBHTTP_API long long httplib_store_body(struct httplib_connection *conn, const char *path);
|
||||
LIBHTTP_API int64_t httplib_store_body(struct httplib_connection *conn, const char *path);
|
||||
/* Read entire request body and store it in a file "path".
|
||||
Return:
|
||||
< 0 Error
|
||||
@@ -780,7 +780,7 @@ struct httplib_form_data_handler {
|
||||
* Return value:
|
||||
* TODO: Needs to be defined.
|
||||
*/
|
||||
int (*field_store)(const char *path, long long file_size, void *user_data);
|
||||
int (*field_store)(const char *path, int64_t file_size, void *user_data);
|
||||
|
||||
/* User supplied argument, passed to all callback functions. */
|
||||
void *user_data;
|
||||
@@ -961,12 +961,17 @@ LIBHTTP_API int httplib_get_response(struct httplib_connection *conn, char *ebuf
|
||||
*/
|
||||
LIBHTTP_API unsigned httplib_check_feature(unsigned feature);
|
||||
|
||||
LIBHTTP_API int httplib_atomic_dec( volatile int *addr );
|
||||
LIBHTTP_API int httplib_atomic_inc( volatile int *addr );
|
||||
LIBHTTP_API int httplib_closedir( DIR *dir );
|
||||
LIBHTTP_API int httplib_kill( pid_t pid, int sig_num );
|
||||
LIBHTTP_API int httplib_mkdir( const char *path, int mode );
|
||||
LIBHTTP_API DIR * httplib_opendir( const char *name );
|
||||
LIBHTTP_API int httplib_poll( struct pollfd *pfd, unsigned int n, int timeout );
|
||||
LIBHTTP_API int httplib_poll( struct pollfd *pfd, unsigned int nfds, int timeout );
|
||||
LIBHTTP_API struct dirent * httplib_readdir( DIR *dir );
|
||||
LIBHTTP_API int httplib_remove( const char *path );
|
||||
LIBHTTP_API void httplib_strlcpy( char *dst, const char *src, size_t len );
|
||||
LIBHTTP_API char * httplib_strndup( const char *str, size_t len );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -22,26 +22,40 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
#include "httplib_utils.h"
|
||||
|
||||
int XX_httplib_atomic_dec( volatile int *addr ) {
|
||||
/*
|
||||
* int httplib_atomic_dec( volatile int *addr );
|
||||
*
|
||||
* The function httplib_atomic_dec() performs an atomic decrement of an integer
|
||||
* which can be used process interlocking. The function returns the value of
|
||||
* the integer after decrementing.
|
||||
*/
|
||||
|
||||
LIBHTTP_API int httplib_atomic_dec( volatile int *addr ) {
|
||||
|
||||
int ret;
|
||||
#if defined(_WIN32)
|
||||
/* Depending on the SDK, this function uses either
|
||||
* (volatile unsigned int *) or (volatile LONG *),
|
||||
* so whatever you use, the other SDK is likely to raise a warning. */
|
||||
ret = InterlockedDecrement((volatile long *)addr);
|
||||
#elif defined(__GNUC__) \
|
||||
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
|
||||
ret = __sync_sub_and_fetch(addr, 1);
|
||||
#else
|
||||
ret = (--(*addr));
|
||||
#endif
|
||||
return ret;
|
||||
|
||||
} /* XX_httplib_atomic_dec */
|
||||
/*
|
||||
* Depending on the SDK, this function uses either
|
||||
* (volatile unsigned int *) or (volatile LONG *),
|
||||
* so whatever you use, the other SDK is likely to raise a warning.
|
||||
*/
|
||||
|
||||
return InterlockedDecrement( (volatile long *)addr );
|
||||
|
||||
#elif defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
|
||||
|
||||
return __sync_sub_and_fetch( addr, 1 );
|
||||
|
||||
#else
|
||||
|
||||
return (--(*addr));
|
||||
|
||||
#endif
|
||||
|
||||
} /* httplib_atomic_dec */
|
||||
|
@@ -22,26 +22,40 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
#include "httplib_utils.h"
|
||||
|
||||
int XX_httplib_atomic_inc( volatile int *addr ) {
|
||||
/*
|
||||
* int httplib_atomic_inc( volatile int *addr );
|
||||
*
|
||||
* The function httplib_atomic_inc() performs an atomic increment of an
|
||||
* integer. This function can be used for inter process locking. The function
|
||||
* returns the value of the integer after it has been incremented.
|
||||
*/
|
||||
|
||||
LIBHTTP_API int httplib_atomic_inc( volatile int *addr ) {
|
||||
|
||||
int ret;
|
||||
#if defined(_WIN32)
|
||||
/* Depending on the SDK, this function uses either
|
||||
* (volatile unsigned int *) or (volatile LONG *),
|
||||
* so whatever you use, the other SDK is likely to raise a warning. */
|
||||
ret = InterlockedIncrement((volatile long *)addr);
|
||||
#elif defined(__GNUC__) \
|
||||
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
|
||||
ret = __sync_add_and_fetch(addr, 1);
|
||||
#else
|
||||
ret = (++(*addr));
|
||||
#endif
|
||||
return ret;
|
||||
|
||||
} /* XX_httplib_atomic_inc */
|
||||
/*
|
||||
* Depending on the SDK, this function uses either
|
||||
* (volatile unsigned int *) or (volatile LONG *),
|
||||
* so whatever you use, the other SDK is likely to raise a warning.
|
||||
*/
|
||||
|
||||
return InterlockedIncrement( (volatile long *)addr );
|
||||
|
||||
#elif defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
|
||||
|
||||
return __sync_add_and_fetch( addr, 1 );
|
||||
|
||||
#else
|
||||
|
||||
return (++(*addr));
|
||||
|
||||
#endif
|
||||
|
||||
} /* httplib_atomic_inc */
|
||||
|
@@ -22,13 +22,22 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
#include "httplib_utils.h"
|
||||
|
||||
void XX_httplib_base64_encode( const unsigned char *src, int src_len, char *dst ) {
|
||||
/*
|
||||
* int httplib_base64_encode( const unsigned char *src, int src_len, char *dst, int dst_len );
|
||||
*
|
||||
* The function httplib_base64_encode() converts a binary buffer of given
|
||||
* length to its BASE64 equivalent. If an error occurs or the receive buffer is
|
||||
* too small, -1 is returned. Otherwise the return value is the length of the
|
||||
* data in the receive buffer, including the terminating NUL character.
|
||||
*/
|
||||
|
||||
LIBHTTP_API int httplib_base64_encode( const unsigned char *src, int src_len, char *dst, int dst_len ) {
|
||||
|
||||
static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int i;
|
||||
@@ -37,19 +46,38 @@ void XX_httplib_base64_encode( const unsigned char *src, int src_len, char *dst
|
||||
int b;
|
||||
int c;
|
||||
|
||||
for (i = j = 0; i < src_len; i += 3) {
|
||||
if ( src == NULL || src_len <= 0 || dst == NULL || dst_len < 1 ) -1;
|
||||
|
||||
j = 0;
|
||||
|
||||
for (i=0; i<src_len; i+=3) {
|
||||
|
||||
a = src[i];
|
||||
b = ((i + 1) >= src_len) ? 0 : src[i + 1];
|
||||
c = ((i + 2) >= src_len) ? 0 : src[i + 2];
|
||||
|
||||
b = ((i+1) >= src_len) ? 0 : src[i+1];
|
||||
c = ((i+2) >= src_len) ? 0 : src[i+2];
|
||||
|
||||
dst[j++] = b64[a >> 2];
|
||||
dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
|
||||
if (i + 1 < src_len) {
|
||||
dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
|
||||
}
|
||||
if (i + 2 < src_len) dst[j++] = b64[c & 63];
|
||||
}
|
||||
while (j % 4 != 0) dst[j++] = '=';
|
||||
dst[j++] = '\0';
|
||||
if ( j >= dst_len ) return -1;
|
||||
|
||||
} /* XX_httplib_base64_encode */
|
||||
dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
|
||||
if ( j >= dst_len ) return -1;
|
||||
|
||||
if (i+1 < src_len) dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
|
||||
if ( j >= dst_len ) return -1;
|
||||
|
||||
if (i+2 < src_len) dst[j++] = b64[ c & 63 ];
|
||||
if ( j >= dst_len ) return -1;
|
||||
}
|
||||
|
||||
while ( j % 4 != 0 ) {
|
||||
|
||||
dst[j++] = '=';
|
||||
if ( j >= dst_len ) return -1;
|
||||
}
|
||||
|
||||
dst[j++] = 0;
|
||||
|
||||
return j;
|
||||
|
||||
} /* httplib_base64_encode */
|
||||
|
@@ -73,7 +73,7 @@ void XX_httplib_delete_file( struct httplib_connection *conn, const char *path )
|
||||
}
|
||||
|
||||
/* Try to delete it. */
|
||||
if (XX_httplib_remove(conn, path) == 0) {
|
||||
if (httplib_remove( path ) == 0) {
|
||||
/* Delete was successful: Return 204 without content. */
|
||||
XX_httplib_send_http_error(conn, 204, "%s", "");
|
||||
} else {
|
||||
|
@@ -22,13 +22,25 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
/*
|
||||
* void XX_httplib_fclose( struct file *filep );
|
||||
*
|
||||
* The function XX_httplib_fclose() closed a file associated with a filep
|
||||
* structure. The function doesn't return a success or error code, but the
|
||||
* value of the fp parameter in the filep structure is reset to NULL which
|
||||
* prevents the old file pointer to be reused.
|
||||
*/
|
||||
|
||||
void XX_httplib_fclose( struct file *filep ) {
|
||||
|
||||
if (filep != NULL && filep->fp != NULL) fclose(filep->fp);
|
||||
if ( filep == NULL || filep->fp == NULL ) return;
|
||||
|
||||
fclose( filep->fp );
|
||||
filep->fp = NULL;
|
||||
|
||||
} /* XX_httplib_fclose */
|
||||
|
@@ -22,41 +22,55 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
/* XX_httplib_fopen will open a file either in memory or on the disk.
|
||||
* The input parameter path is a string in UTF-8 encoding.
|
||||
* The input parameter mode is the same as for fopen.
|
||||
* Either fp or membuf will be set in the output struct filep.
|
||||
* The function returns 1 on success, 0 on error. */
|
||||
int XX_httplib_fopen( const struct httplib_connection *conn, const char *path, const char *mode, struct file *filep ) {
|
||||
/*
|
||||
* bool XX_httplib_fopen( const struct httplib_connection *conn, const char *path, const char *mode, struct file *filep );
|
||||
*
|
||||
* The function XX_httplib_fopen() can be used to open a file which is either
|
||||
* in memory or on the disk. The path is in UTF-8 and therefore needs
|
||||
* conversion in Windows based sytems which use UTF-16 by default. The mode
|
||||
* parameter is identical to the mode parameter used in the standard function
|
||||
* fopen().
|
||||
*
|
||||
* The function will return the information of the file in the struct filep. If
|
||||
* open the file was successful the value true is returned. Otherwise false.
|
||||
*
|
||||
* TODO (high): XX_httplib_fopen should only open a file, while XX_httplib_stat
|
||||
* should only get the file status. They should not work on different members
|
||||
* of the same structure (bad cohesion).
|
||||
*/
|
||||
|
||||
bool XX_httplib_fopen( const struct httplib_connection *conn, const char *path, const char *mode, struct file *filep ) {
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (!filep) return 0;
|
||||
if ( filep == NULL ) return false;
|
||||
|
||||
/* TODO (high): XX_httplib_fopen should only open a file, while XX_httplib_stat should
|
||||
* only get the file status. They should not work on different members of
|
||||
* the same structure (bad cohesion). */
|
||||
memset(filep, 0, sizeof(*filep));
|
||||
memset( filep, 0, sizeof(*filep) );
|
||||
|
||||
if (stat(path, &st) == 0) filep->size = (uint64_t)(st.st_size);
|
||||
if ( stat( path, &st ) == 0 ) filep->size = (uint64_t)st.st_size;
|
||||
|
||||
if (!XX_httplib_is_file_in_memory(conn, path, filep)) {
|
||||
if ( ! XX_httplib_is_file_in_memory( conn, path, filep ) ) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wbuf[PATH_MAX], wmode[20];
|
||||
XX_httplib_path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
|
||||
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
|
||||
filep->fp = _wfopen(wbuf, wmode);
|
||||
#else
|
||||
/* Linux et al already use unicode. No need to convert. */
|
||||
filep->fp = fopen(path, mode);
|
||||
#endif
|
||||
wchar_t wbuf[PATH_MAX];
|
||||
wchar_t wmode[20];
|
||||
|
||||
XX_httplib_path_to_unicode( path, wbuf, ARRAY_SIZE(wbuf) );
|
||||
MultiByteToWideChar( CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode) );
|
||||
|
||||
filep->fp = _wfopen( wbuf, wmode );
|
||||
|
||||
#else /* _WIN32 */
|
||||
|
||||
filep->fp = fopen( path, mode );
|
||||
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
return XX_httplib_is_file_opened(filep);
|
||||
return XX_httplib_is_file_opened( filep );
|
||||
|
||||
} /* XX_httplib_fopen */
|
||||
|
@@ -96,7 +96,7 @@ void XX_httplib_free_context( struct httplib_context *ctx ) {
|
||||
if (ctx->workerthreadids != NULL) XX_httplib_free(ctx->workerthreadids);
|
||||
|
||||
/* Deallocate the tls variable */
|
||||
if (XX_httplib_atomic_dec(&XX_httplib_sTlsInit) == 0) {
|
||||
if (httplib_atomic_dec(&XX_httplib_sTlsInit) == 0) {
|
||||
#if defined(_WIN32)
|
||||
DeleteCriticalSection(&global_log_file_lock);
|
||||
#endif /* _WIN32 */
|
||||
|
@@ -22,13 +22,23 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
struct httplib_context * httplib_get_context(const struct httplib_connection *conn) {
|
||||
/*
|
||||
* struct httplib_context *httplib_get_context( const struct httplib_connection *conn );
|
||||
*
|
||||
* The function httplib_get_context() returns a pointer to the context
|
||||
* associated with a connection or NULL if the connection or context could not
|
||||
* be found.
|
||||
*/
|
||||
|
||||
return (conn == NULL) ? (struct httplib_context *)NULL : (conn->ctx);
|
||||
struct httplib_context *httplib_get_context( const struct httplib_connection *conn ) {
|
||||
|
||||
if ( conn == NULL ) return NULL;
|
||||
|
||||
return conn->ctx;
|
||||
|
||||
} /* httplib_get_context */
|
||||
|
@@ -59,7 +59,7 @@ int httplib_get_cookie(const char *cookie_header, const char *var_name, char *ds
|
||||
}
|
||||
if ((size_t)(p - s) < dst_size) {
|
||||
len = (int)(p - s);
|
||||
XX_httplib_strlcpy(dst, s, (size_t)len + 1);
|
||||
httplib_strlcpy( dst, s, (size_t)len+1 );
|
||||
} else len = -3;
|
||||
break;
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ void XX_httplib_gmt_time_string( char *buf, size_t buf_len, time_t *t ) {
|
||||
if (tm != NULL) {
|
||||
strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
|
||||
} else {
|
||||
XX_httplib_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
|
||||
httplib_strlcpy( buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len );
|
||||
buf[buf_len - 1] = '\0';
|
||||
}
|
||||
|
||||
|
@@ -123,7 +123,7 @@ static int unencoded_field_get(const struct httplib_connection *conn,
|
||||
}
|
||||
|
||||
|
||||
static int field_stored(const struct httplib_connection *conn, const char *path, long long file_size, struct httplib_form_data_handler *fdh) {
|
||||
static int field_stored(const struct httplib_connection *conn, const char *path, int64_t file_size, struct httplib_form_data_handler *fdh) {
|
||||
|
||||
/* Equivalent to "upload" callback of "httplib_upload". */
|
||||
|
||||
|
@@ -46,11 +46,11 @@ void XX_httplib_handle_request( struct httplib_connection *conn ) {
|
||||
char path[PATH_MAX];
|
||||
int uri_len;
|
||||
int ssl_index;
|
||||
int is_found = 0;
|
||||
int is_script_resource = 0;
|
||||
int is_websocket_request = 0;
|
||||
int is_put_or_delete_request = 0;
|
||||
int is_callback_resource = 0;
|
||||
bool is_found = false;
|
||||
bool is_script_resource = false;
|
||||
bool is_websocket_request = false;
|
||||
bool is_put_or_delete_request = false;
|
||||
bool is_callback_resource = false;
|
||||
int i;
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
httplib_request_handler callback_handler = NULL;
|
||||
@@ -58,9 +58,9 @@ void XX_httplib_handle_request( struct httplib_connection *conn ) {
|
||||
httplib_websocket_ready_handler ws_ready_handler = NULL;
|
||||
httplib_websocket_data_handler ws_data_handler = NULL;
|
||||
httplib_websocket_close_handler ws_close_handler = NULL;
|
||||
void *callback_data = NULL;
|
||||
void *callback_data = NULL;
|
||||
httplib_authorization_handler auth_handler = NULL;
|
||||
void *auth_callback_data = NULL;
|
||||
void *auth_callback_data = NULL;
|
||||
#if !defined(NO_FILES)
|
||||
time_t curtime = time(NULL);
|
||||
char date[64];
|
||||
@@ -149,15 +149,15 @@ void XX_httplib_handle_request( struct httplib_connection *conn ) {
|
||||
* handled
|
||||
* by a callback have to be considered as requests to a script
|
||||
* resource. */
|
||||
is_callback_resource = 1;
|
||||
is_script_resource = 1;
|
||||
is_callback_resource = true;
|
||||
is_script_resource = true;
|
||||
is_put_or_delete_request = XX_httplib_is_put_or_delete_method(conn);
|
||||
} else {
|
||||
no_callback_resource:
|
||||
/* 5.2.2. No callback is responsible for this request. The URI
|
||||
* addresses a file based resource (static content or Lua/cgi
|
||||
* scripts in the file system). */
|
||||
is_callback_resource = 0;
|
||||
is_callback_resource = false;
|
||||
XX_httplib_interpret_uri(conn,
|
||||
path,
|
||||
sizeof(path),
|
||||
|
@@ -64,7 +64,7 @@ int XX_httplib_initialize_ssl( struct httplib_context *ctx ) {
|
||||
}
|
||||
#endif /* NO_SSL_DL */
|
||||
|
||||
if (XX_httplib_atomic_inc(&XX_httplib_cryptolib_users) > 1) return 1;
|
||||
if (httplib_atomic_inc(&XX_httplib_cryptolib_users) > 1) return 1;
|
||||
|
||||
/* Initialize locking callbacks, needed for thread safety.
|
||||
* http://www.openssl.org/support/faq.html#PROG1
|
||||
|
@@ -34,17 +34,17 @@
|
||||
* The function XX_httplib_interpret_uri() interprets an URI and decides what
|
||||
* type of request is involved. The function takes the following parameters:
|
||||
*
|
||||
* conn: in: The request (must be valid)
|
||||
* filename: out: Filename
|
||||
* filename_buf_len: in: Size of the filename buffer
|
||||
* filep: out: file structure
|
||||
* is_found: out: file is found (directly)
|
||||
* is_script_resource: out: handled by a script?
|
||||
* conn: in: The request (must be valid)
|
||||
* filename: out: Filename
|
||||
* filename_buf_len: in: Size of the filename buffer
|
||||
* filep: out: file structure
|
||||
* is_found: out: file is found (directly)
|
||||
* is_script_resource: out: handled by a script?
|
||||
* is_websocket_request: out: websocket connection?
|
||||
* is_put_or_delete_request: out: put/delete file?
|
||||
*/
|
||||
|
||||
void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, size_t filename_buf_len, struct file *filep, int *is_found, int *is_script_resource, int *is_websocket_request, int *is_put_or_delete_request ) {
|
||||
void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, size_t filename_buf_len, struct file *filep, bool *is_found, bool *is_script_resource, bool *is_websocket_request, bool *is_put_or_delete_request ) {
|
||||
|
||||
/* TODO (high): Restructure this function */
|
||||
|
||||
@@ -60,15 +60,15 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
int truncated;
|
||||
#if !defined(NO_CGI)
|
||||
char *p;
|
||||
#endif
|
||||
#else
|
||||
(void)filename_buf_len; /* unused if NO_FILES is defined */
|
||||
#endif
|
||||
#endif /* !NO_CGI */
|
||||
#else /* NO_FILES */
|
||||
UNUSED_PARAMETER( filename_buf_len );
|
||||
#endif /* NO_FILES */
|
||||
|
||||
memset(filep, 0, sizeof(*filep));
|
||||
*filename = 0;
|
||||
*is_found = 0;
|
||||
*is_script_resource = 0;
|
||||
*filename = 0;
|
||||
*is_found = false;
|
||||
*is_script_resource = false;
|
||||
*is_put_or_delete_request = XX_httplib_is_put_or_delete_method(conn);
|
||||
|
||||
#if defined(USE_WEBSOCKET)
|
||||
@@ -79,7 +79,7 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
}
|
||||
#endif /* !NO_FILES */
|
||||
#else /* USE_WEBSOCKET */
|
||||
*is_websocket_request = 0;
|
||||
*is_websocket_request = false;
|
||||
#endif /* USE_WEBSOCKET */
|
||||
|
||||
#if !defined(NO_FILES)
|
||||
@@ -134,7 +134,7 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
*is_script_resource = !*is_put_or_delete_request;
|
||||
}
|
||||
#endif /* !defined(NO_CGI) */
|
||||
*is_found = 1;
|
||||
*is_found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
if (XX_httplib_stat(conn, gz_path, filep)) {
|
||||
if (filep) {
|
||||
filep->gzipped = 1;
|
||||
*is_found = 1;
|
||||
*is_found = true;
|
||||
}
|
||||
/* Currently gz files can not be scripts. */
|
||||
return;
|
||||
@@ -184,7 +184,7 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
|
||||
* trailing \0 */
|
||||
p[1] = '/';
|
||||
*is_script_resource = 1;
|
||||
*is_script_resource = true;
|
||||
break;
|
||||
} else *p = '/';
|
||||
}
|
||||
@@ -196,12 +196,13 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename,
|
||||
#if !defined(NO_FILES)
|
||||
/* Reset all outputs */
|
||||
interpret_cleanup:
|
||||
memset(filep, 0, sizeof(*filep));
|
||||
*filename = 0;
|
||||
*is_found = 0;
|
||||
*is_script_resource = 0;
|
||||
*is_websocket_request = 0;
|
||||
*is_put_or_delete_request = 0;
|
||||
memset( filep, 0, sizeof(*filep) );
|
||||
|
||||
*filename = 0;
|
||||
*is_found = false;
|
||||
*is_script_resource = false;
|
||||
*is_websocket_request = false;
|
||||
*is_put_or_delete_request = false;
|
||||
#endif /* !defined(NO_FILES) */
|
||||
|
||||
} /* XX_httplib_interpret_uri */
|
||||
|
@@ -22,26 +22,36 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
int XX_httplib_is_file_in_memory( const struct httplib_connection *conn, const char *path, struct file *filep ) {
|
||||
/*
|
||||
* bool XX_httplib_is_file_in_memory( const struct httplib_connection *conn, const char *path, struct file *filep );
|
||||
*
|
||||
* The function XX_httplib_is_file_in_memory() returns true, if a file defined
|
||||
* by a specific path is located in memory.
|
||||
*/
|
||||
|
||||
size_t size = 0;
|
||||
bool XX_httplib_is_file_in_memory( const struct httplib_connection *conn, const char *path, struct file *filep ) {
|
||||
|
||||
if (!conn || !filep) return 0;
|
||||
size_t size;
|
||||
|
||||
if (conn->ctx->callbacks.open_file) {
|
||||
filep->membuf = conn->ctx->callbacks.open_file(conn, path, &size);
|
||||
if (filep->membuf != NULL) {
|
||||
/* NOTE: override filep->size only on success. Otherwise, it might
|
||||
* break constructs like if (!XX_httplib_stat() || !XX_httplib_fopen()) ... */
|
||||
filep->size = size;
|
||||
}
|
||||
if ( conn == NULL || filep == NULL ) return false;
|
||||
|
||||
size = 0;
|
||||
|
||||
if ( conn->ctx->callbacks.open_file ) {
|
||||
|
||||
filep->membuf = conn->ctx->callbacks.open_file( conn, path, & size );
|
||||
|
||||
/* NOTE: override filep->size only on success. Otherwise, it might
|
||||
* break constructs like if (!XX_httplib_stat() || !XX_httplib_fopen()) ... */
|
||||
|
||||
if ( filep->membuf != NULL ) filep->size = size;
|
||||
}
|
||||
|
||||
return filep->membuf != NULL;
|
||||
return ( filep->membuf != NULL );
|
||||
|
||||
} /* XX_httplib_is_file_in_memory */
|
||||
|
@@ -22,15 +22,20 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
/*
|
||||
* bool XX_httplib_is_file_opened( const struct file *filep );
|
||||
*
|
||||
* The function XX_httplib_is_file_opened() returns true if the file of the
|
||||
* passed file pointer is opened and false otherwise.
|
||||
*/
|
||||
|
||||
bool XX_httplib_is_file_opened( const struct file *filep ) {
|
||||
|
||||
if ( filep == NULL ) return false;
|
||||
|
||||
return ( filep->membuf != NULL || filep->fp != NULL );
|
||||
return ( filep != NULL && ( filep->membuf != NULL || filep->fp != NULL ) );
|
||||
|
||||
} /* XX_httplib_is_file_opened */
|
||||
|
@@ -22,13 +22,13 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
/*
|
||||
* int XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep );
|
||||
* bool XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep );
|
||||
*
|
||||
* The function XX_httplib_is_not_modified() returns true, if a resource has
|
||||
* not been modified sinze a given datetime and a 304 response should therefore
|
||||
@@ -37,15 +37,17 @@
|
||||
|
||||
#if !defined(NO_CACHING)
|
||||
|
||||
int XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep ) {
|
||||
bool XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep ) {
|
||||
|
||||
char etag[64];
|
||||
const char *ims = httplib_get_header( conn, "If-Modified-Since" );
|
||||
const char *inm = httplib_get_header( conn, "If-None-Match" );
|
||||
|
||||
XX_httplib_construct_etag( etag, sizeof(etag), filep );
|
||||
if ( filep == NULL ) return 0;
|
||||
return (inm != NULL && !httplib_strcasecmp(etag, inm)) || (ims != NULL && (filep->last_modified <= XX_httplib_parse_date_string(ims)));
|
||||
if ( filep == NULL ) return false;
|
||||
|
||||
return (inm != NULL && ! httplib_strcasecmp( etag, inm ) ) ||
|
||||
(ims != NULL && ( filep->last_modified <= XX_httplib_parse_date_string( ims ) ) ) ;
|
||||
|
||||
} /* XX_httplib_is_not_modified */
|
||||
|
||||
|
@@ -27,9 +27,9 @@
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
int XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn ) {
|
||||
bool XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn ) {
|
||||
|
||||
if ( conn == NULL ) return 0;
|
||||
if ( conn == NULL ) return false;
|
||||
|
||||
const char *s = conn->request_info.request_method;
|
||||
|
||||
|
@@ -22,7 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
@@ -32,32 +32,33 @@
|
||||
*
|
||||
* The function XX_httplib_is_valid_http_method() checks if the method in a
|
||||
* request is a valid method.
|
||||
*
|
||||
* PTRACE is not supported for security reasons. Further more the following
|
||||
* WEBDAV methods have not been implemented:
|
||||
*
|
||||
* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518)
|
||||
* + 11 methods from RFC 3253
|
||||
* ORDERPATCH (RFC 3648)
|
||||
* ACL (RFC 3744)
|
||||
* SEARCH (RFC 5323)
|
||||
* + MicroSoft extensions
|
||||
* https://msdn.microsoft.com/en-us/library/aa142917.aspx
|
||||
*
|
||||
* The PATCH method is only supported for CGI and other scripts and for
|
||||
* callbacks.
|
||||
*/
|
||||
|
||||
bool XX_httplib_is_valid_http_method( const char *method ) {
|
||||
|
||||
return !strcmp(method, "GET") /* HTTP (RFC 2616) */
|
||||
|| !strcmp(method, "POST") /* HTTP (RFC 2616) */
|
||||
|| !strcmp(method, "HEAD") /* HTTP (RFC 2616) */
|
||||
|| !strcmp(method, "PUT") /* HTTP (RFC 2616) */
|
||||
|| !strcmp(method, "DELETE") /* HTTP (RFC 2616) */
|
||||
|| !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */
|
||||
/* TRACE method (RFC 2616) is not supported for security reasons */
|
||||
|| !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */
|
||||
|
||||
|| !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */
|
||||
|| !strcmp(method, "MKCOL") /* WEBDAV (RFC 2518) */
|
||||
|
||||
/* Unsupported WEBDAV Methods: */
|
||||
/* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
|
||||
/* + 11 methods from RFC 3253 */
|
||||
/* ORDERPATCH (RFC 3648) */
|
||||
/* ACL (RFC 3744) */
|
||||
/* SEARCH (RFC 5323) */
|
||||
/* + MicroSoft extensions
|
||||
* https://msdn.microsoft.com/en-us/library/aa142917.aspx */
|
||||
|
||||
/* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
|
||||
|| !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */
|
||||
return ( ! strcmp( method, "GET" ) ||
|
||||
! strcmp( method, "POST" ) ||
|
||||
! strcmp( method, "HEAD" ) ||
|
||||
! strcmp( method, "PUT" ) ||
|
||||
! strcmp( method, "DELETE" ) ||
|
||||
! strcmp( method, "OPTIONS" ) ||
|
||||
! strcmp( method, "CONNECT" ) ||
|
||||
! strcmp( method, "PROPFIND" ) ||
|
||||
! strcmp( method, "MKCOL" ) ||
|
||||
! strcmp( method, "PATCH" ) );
|
||||
|
||||
} /* XX_httplib_is_valid_http_method */
|
||||
|
@@ -35,7 +35,7 @@
|
||||
* to see if the connection is a valid websocket protocol.
|
||||
*/
|
||||
|
||||
int XX_httplib_is_websocket_protocol( const struct httplib_connection *conn ) {
|
||||
bool XX_httplib_is_websocket_protocol( const struct httplib_connection *conn ) {
|
||||
|
||||
#if defined(USE_WEBSOCKET)
|
||||
const char *upgrade;
|
||||
@@ -48,14 +48,14 @@ int XX_httplib_is_websocket_protocol( const struct httplib_connection *conn ) {
|
||||
*/
|
||||
|
||||
upgrade = httplib_get_header(conn, "Upgrade");
|
||||
if (upgrade == NULL) return 0; /* fail early, don't waste time checking other header * fields */
|
||||
if (upgrade == NULL) return false; /* fail early, don't waste time checking other header * fields */
|
||||
|
||||
if (!XX_httplib_strcasestr(upgrade, "websocket")) return 0;
|
||||
if (!XX_httplib_strcasestr(upgrade, "websocket")) return false;
|
||||
|
||||
connection = httplib_get_header(conn, "Connection");
|
||||
if (connection == NULL) return 0;
|
||||
if (connection == NULL) return false;
|
||||
|
||||
if (!XX_httplib_strcasestr(connection, "upgrade")) return 0;
|
||||
if (!XX_httplib_strcasestr(connection, "upgrade")) return false;
|
||||
|
||||
/* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
|
||||
* "Sec-WebSocket-Version" are also required.
|
||||
@@ -64,13 +64,13 @@ int XX_httplib_is_websocket_protocol( const struct httplib_connection *conn ) {
|
||||
* request). It will fail later in handle_websocket_request.
|
||||
*/
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
#else /* defined(USE_WEBSOCKET) */
|
||||
|
||||
(void)conn;
|
||||
UNUSED_PARAMETER(conn);
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
#endif /* defined(USE_WEBSOCKET) */
|
||||
|
||||
|
@@ -64,7 +64,7 @@ void XX_httplib_log_access( const struct httplib_connection *conn ) {
|
||||
if (tm != NULL) {
|
||||
strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
|
||||
} else {
|
||||
XX_httplib_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
|
||||
httplib_strlcpy( date, "01/Jan/1970:00:00:00 +0000", sizeof(date) );
|
||||
date[sizeof(date) - 1] = '\0';
|
||||
}
|
||||
|
||||
|
@@ -814,7 +814,7 @@ struct httplib_connection * XX_httplib_fc( struct httplib_context *ctx );
|
||||
void XX_httplib_fclose( struct file *filep );
|
||||
void XX_httplib_fclose_on_exec( struct file *filep, struct httplib_connection *conn );
|
||||
const char * XX_httplib_fgets( char *buf, size_t size, struct file *filep, char **p );
|
||||
int XX_httplib_fopen( const struct httplib_connection *conn, const char *path, const char *mode, struct file *filep );
|
||||
bool XX_httplib_fopen( const struct httplib_connection *conn, const char *path, const char *mode, struct file *filep );
|
||||
int XX_httplib_forward_body_data( struct httplib_connection *conn, FILE *fp, SOCKET sock, SSL *ssl );
|
||||
void XX_httplib_free_context( struct httplib_context *ctx );
|
||||
const char * XX_httplib_get_header( const struct httplib_request_info *ri, const char *name );
|
||||
@@ -837,15 +837,15 @@ void XX_httplib_handle_ssi_file_request( struct httplib_connection *conn, cons
|
||||
void XX_httplib_handle_static_file_request( struct httplib_connection *conn, const char *path, struct file *filep, const char *mime_type, const char *additional_headers );
|
||||
void XX_httplib_handle_websocket_request( struct httplib_connection *conn, const char *path, int is_callback_resource, httplib_websocket_connect_handler ws_connect_handler, httplib_websocket_ready_handler ws_ready_handler, httplib_websocket_data_handler ws_data_handler, httplib_websocket_close_handler ws_close_handler, void *cbData );
|
||||
int XX_httplib_header_has_option( const char *header, const char *option );
|
||||
void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, size_t filename_buf_len, struct file *filep, int *is_found, int *is_script_resource, int *is_websocket_request, int *is_put_or_delete_request );
|
||||
void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, size_t filename_buf_len, struct file *filep, bool *is_found, bool *is_script_resource, bool *is_websocket_request, bool *is_put_or_delete_request );
|
||||
int XX_httplib_is_authorized_for_put( struct httplib_connection *conn );
|
||||
int XX_httplib_is_file_in_memory( const struct httplib_connection *conn, const char *path, struct file *filep );
|
||||
bool XX_httplib_is_file_in_memory( const struct httplib_connection *conn, const char *path, struct file *filep );
|
||||
bool XX_httplib_is_file_opened( const struct file *filep );
|
||||
int XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep );
|
||||
int XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn );
|
||||
bool XX_httplib_is_not_modified( const struct httplib_connection *conn, const struct file *filep );
|
||||
bool XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn );
|
||||
bool XX_httplib_is_valid_http_method( const char *method );
|
||||
int XX_httplib_is_valid_port( unsigned long port );
|
||||
int XX_httplib_is_websocket_protocol( const struct httplib_connection *conn );
|
||||
bool XX_httplib_is_websocket_protocol( const struct httplib_connection *conn );
|
||||
int XX_httplib_join_thread( pthread_t threadid );
|
||||
void * XX_httplib_load_dll( struct httplib_context *ctx, const char *dll_name, struct ssl_func *sw );
|
||||
void XX_httplib_log_access( const struct httplib_connection *conn );
|
||||
@@ -875,7 +875,6 @@ int XX_httplib_read_request( FILE *fp, struct httplib_connection *conn, char *
|
||||
void XX_httplib_read_websocket( struct httplib_connection *conn, httplib_websocket_data_handler ws_data_handler, void *callback_data );
|
||||
void XX_httplib_redirect_to_https_port( struct httplib_connection *conn, int ssl_index );
|
||||
int XX_httplib_refresh_trust( struct httplib_connection *conn );
|
||||
int XX_httplib_remove( const struct httplib_connection *conn, const char *path );
|
||||
void XX_httplib_remove_bad_file( const struct httplib_connection *conn, const char *path );
|
||||
int XX_httplib_remove_directory( struct httplib_connection *conn, const char *dir );
|
||||
void XX_httplib_remove_double_dots_and_double_slashes( char *s );
|
||||
|
@@ -43,7 +43,7 @@ int XX_httplib_parse_auth_header(struct httplib_connection *conn, char *buf, siz
|
||||
if ((auth_header = httplib_get_header(conn, "Authorization")) == NULL || httplib_strncasecmp(auth_header, "Digest ", 7) != 0) return 0;
|
||||
|
||||
/* Make modifiable copy of the auth header */
|
||||
XX_httplib_strlcpy(buf, auth_header + 7, buf_size);
|
||||
httplib_strlcpy( buf, auth_header + 7, buf_size );
|
||||
s = buf;
|
||||
|
||||
/* Parse authorization header */
|
||||
|
@@ -52,7 +52,7 @@ void XX_httplib_print_dir_entry( struct de *de ) {
|
||||
if (tm != NULL) {
|
||||
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
|
||||
} else {
|
||||
XX_httplib_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
|
||||
httplib_strlcpy( mod, "01-Jan-1970 00:00", sizeof(mod) );
|
||||
mod[sizeof(mod) - 1] = '\0';
|
||||
}
|
||||
httplib_url_encode(de->file_name, href, sizeof(href));
|
||||
|
@@ -46,7 +46,7 @@ void XX_httplib_redirect_to_https_port( struct httplib_connection *conn, int ssl
|
||||
if (host_header != NULL) {
|
||||
char *pos;
|
||||
|
||||
XX_httplib_strlcpy(host, host_header, hostlen);
|
||||
httplib_strlcpy( host, host_header, hostlen );
|
||||
host[hostlen - 1] = '\0';
|
||||
pos = strchr(host, ':');
|
||||
if (pos != NULL) *pos = '\0';
|
||||
|
@@ -81,7 +81,7 @@ int XX_httplib_refresh_trust( struct httplib_connection *conn ) {
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == XX_httplib_atomic_inc(p_reload_lock)) {
|
||||
if (1 == httplib_atomic_inc(p_reload_lock)) {
|
||||
if (XX_httplib_ssl_use_pem_file(conn->ctx, pem) == 0) return 0;
|
||||
*p_reload_lock = 0;
|
||||
}
|
||||
|
@@ -22,24 +22,36 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
int XX_httplib_remove( const struct httplib_connection *conn, const char *path ) {
|
||||
/*
|
||||
* int httplib_remove( const char *path );
|
||||
*
|
||||
* The function httplib_remove() provides a platform independent way to remove
|
||||
* an entry from a directory. In Posix compliant environments this function is
|
||||
* a wrapper around the Posix remove() function. On other systems the Posix
|
||||
* remove functionality is emulated with own code.
|
||||
*
|
||||
* The function returns 0 when successful and -1 if an error occurs.
|
||||
*/
|
||||
|
||||
LIBHTTP_API int httplib_remove( const char *path ) {
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
wchar_t wbuf[PATH_MAX];
|
||||
XX_httplib_path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
|
||||
return DeleteFileW(wbuf) ? 0 : -1;
|
||||
|
||||
XX_httplib_path_to_unicode( path, wbuf, ARRAY_SIZE(wbuf) );
|
||||
|
||||
return ( DeleteFileW( wbuf ) ) ? 0 : -1;
|
||||
|
||||
#else /* _WIN32 */
|
||||
|
||||
(void)conn;
|
||||
return remove( path );
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
} /* XX_httplib_remove */
|
||||
} /* httplib_remove */
|
||||
|
@@ -36,7 +36,7 @@
|
||||
|
||||
void XX_httplib_remove_bad_file( const struct httplib_connection *conn, const char *path ) {
|
||||
|
||||
int r = XX_httplib_remove( conn, path );
|
||||
int r = httplib_remove( path );
|
||||
|
||||
if (r != 0) httplib_cry(conn, "%s: Cannot remove invalid file %s", __func__, path);
|
||||
|
||||
|
@@ -71,7 +71,7 @@ int XX_httplib_remove_directory( struct httplib_connection *conn, const char *di
|
||||
if (de.file.is_directory) {
|
||||
if (XX_httplib_remove_directory(conn, path) == 0) ok = 0;
|
||||
} else {
|
||||
if (XX_httplib_remove(conn, path) == 0) ok = 0;
|
||||
if (httplib_remove( path ) == 0) ok = 0;
|
||||
}
|
||||
} else {
|
||||
/* file is in memory. It can not be deleted. */
|
||||
|
@@ -22,11 +22,22 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
/*
|
||||
* We can optionally set TCP_USER_TIMEOUT
|
||||
*
|
||||
* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
|
||||
* max. time waiting for the acknowledged of TCP data before the connection
|
||||
* will be forcefully closed and ETIMEDOUT is returned to the application.
|
||||
* If this option is not set, the default timeout of 20-30 minutes is used.
|
||||
*/
|
||||
|
||||
// #define TCP_USER_TIMEOUT (18)
|
||||
|
||||
/*
|
||||
* int XX_httplib_set_sock_timeout( SOCKET sock, int milliseconds );
|
||||
*
|
||||
@@ -36,41 +47,39 @@
|
||||
|
||||
int XX_httplib_set_sock_timeout( SOCKET sock, int milliseconds ) {
|
||||
|
||||
int r0 = 0;
|
||||
int r0;
|
||||
int r1;
|
||||
int r2;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Windows specific */
|
||||
|
||||
DWORD tv = (DWORD)milliseconds;
|
||||
|
||||
#else
|
||||
/* Linux, ... (not Windows) */
|
||||
#else /* _WIN32 */
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
/* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
|
||||
* max. time waiting for the acknowledged of TCP data before the connection
|
||||
* will be forcefully closed and ETIMEDOUT is returned to the application.
|
||||
* If this option is not set, the default timeout of 20-30 minutes is used.
|
||||
*/
|
||||
/* #define TCP_USER_TIMEOUT (18) */
|
||||
|
||||
#if defined(TCP_USER_TIMEOUT)
|
||||
unsigned int uto = (unsigned int)milliseconds;
|
||||
r0 = setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
|
||||
#endif
|
||||
|
||||
memset(&tv, 0, sizeof(tv));
|
||||
tv.tv_sec = milliseconds / 1000;
|
||||
unsigned int uto;
|
||||
|
||||
uto = (unsigned int)milliseconds;
|
||||
r0 = setsockopt( sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto) );
|
||||
|
||||
#else
|
||||
r0 = 0;
|
||||
|
||||
#endif /* TCP_USER_TIMEOUT */
|
||||
|
||||
memset( & tv, 0, sizeof(tv) );
|
||||
tv.tv_sec = milliseconds / 1000;
|
||||
tv.tv_usec = (milliseconds * 1000) % 1000000;
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
r1 = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
|
||||
r2 = setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv));
|
||||
r1 = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv) );
|
||||
r2 = setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv) );
|
||||
|
||||
return r0 || r1 || r2;
|
||||
return (r0 || r1 || r2);
|
||||
|
||||
} /* XX_httplib_set_sock_timeout */
|
||||
|
@@ -66,7 +66,7 @@ unsigned long XX_httplib_ssl_id_callback( void ) {
|
||||
*/
|
||||
tls = (struct httplib_workerTLS *)XX_httplib_malloc(sizeof(struct httplib_workerTLS));
|
||||
tls->is_master = -2; /* -2 means "3rd party thread" */
|
||||
tls->thread_idx = (unsigned)XX_httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
tls->thread_idx = (unsigned)httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
pthread_setspecific(XX_httplib_sTlsKey, tls);
|
||||
}
|
||||
return tls->thread_idx;
|
||||
|
@@ -64,7 +64,7 @@ struct httplib_context *httplib_start( const struct httplib_callbacks *callbacks
|
||||
/* Random number generator will initialize at the first call */
|
||||
ctx->auth_nonce_mask = (uint64_t)XX_httplib_get_random() ^ (uint64_t)(ptrdiff_t)(options);
|
||||
|
||||
if (XX_httplib_atomic_inc(&XX_httplib_sTlsInit) == 1) {
|
||||
if (httplib_atomic_inc(&XX_httplib_sTlsInit) == 1) {
|
||||
|
||||
#if defined(_WIN32)
|
||||
InitializeCriticalSection(&global_log_file_lock);
|
||||
@@ -78,7 +78,7 @@ struct httplib_context *httplib_start( const struct httplib_callbacks *callbacks
|
||||
/* Fatal error - abort start. However, this situation should
|
||||
* never
|
||||
* occur in practice. */
|
||||
XX_httplib_atomic_dec(&XX_httplib_sTlsInit);
|
||||
httplib_atomic_dec(&XX_httplib_sTlsInit);
|
||||
httplib_cry( XX_httplib_fc(ctx), "Cannot initialize thread local storage");
|
||||
XX_httplib_free(ctx);
|
||||
return NULL;
|
||||
@@ -90,7 +90,7 @@ struct httplib_context *httplib_start( const struct httplib_callbacks *callbacks
|
||||
}
|
||||
|
||||
tls.is_master = -1;
|
||||
tls.thread_idx = (unsigned)XX_httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
tls.thread_idx = (unsigned)httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
#if defined(_WIN32)
|
||||
tls.pthread_cond_helper_mutex = NULL;
|
||||
#endif
|
||||
|
@@ -50,10 +50,10 @@ void httplib_stop( struct httplib_context *ctx ) {
|
||||
ctx->stop_flag = 1;
|
||||
|
||||
/* Wait until everything has stopped. */
|
||||
while ( ctx->stop_flag != 2 ) httplib_sleep(10);
|
||||
while ( ctx->stop_flag != 2 ) httplib_sleep( 10 );
|
||||
|
||||
XX_httplib_join_thread(mt);
|
||||
XX_httplib_free_context(ctx);
|
||||
XX_httplib_join_thread( mt );
|
||||
XX_httplib_free_context( ctx );
|
||||
|
||||
#if defined(_WIN32)
|
||||
WSACleanup();
|
||||
|
@@ -28,15 +28,15 @@
|
||||
#include "httplib_main.h"
|
||||
|
||||
/*
|
||||
* long long httplib_store_body( struct httplib_connection *conn, const char *path );
|
||||
* int64_t httplib_store_body( struct httplib_connection *conn, const char *path );
|
||||
*
|
||||
* The function httplib_store_body() stores in incoming body for future processing.
|
||||
*/
|
||||
|
||||
long long httplib_store_body( struct httplib_connection *conn, const char *path ) {
|
||||
int64_t httplib_store_body( struct httplib_connection *conn, const char *path ) {
|
||||
|
||||
char buf[MG_BUF_LEN];
|
||||
long long len = 0;
|
||||
int64_t len = 0;
|
||||
int ret;
|
||||
int n;
|
||||
struct file fi;
|
||||
|
@@ -30,6 +30,6 @@
|
||||
|
||||
char * XX_httplib_strdup( const char *str ) {
|
||||
|
||||
return XX_httplib_strndup(str, strlen(str));
|
||||
return httplib_strndup(str, strlen( str ) );
|
||||
|
||||
} /* XX_httplib_strdup */
|
||||
|
@@ -25,7 +25,5 @@
|
||||
void XX_httplib_snprintf( const struct httplib_connection *conn, int *truncated, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt), ... ) PRINTF_ARGS(5, 6);
|
||||
const char * XX_httplib_strcasestr( const char *big_str, const char *small_str );
|
||||
char * XX_httplib_strdup( const char *str );
|
||||
void XX_httplib_strlcpy( register char *dst, register const char *src, size_t n );
|
||||
char * XX_httplib_strndup( const char *ptr, size_t len );
|
||||
int XX_httplib_vprintf( struct httplib_connection *conn, const char *fmt, va_list ap );
|
||||
void XX_httplib_vsnprintf( const struct httplib_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, va_list ap );
|
||||
|
@@ -22,15 +22,38 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
#include "httplib_string.h"
|
||||
|
||||
void XX_httplib_strlcpy( register char *dst, register const char *src, size_t n ) {
|
||||
/*
|
||||
* void httplib_strlcpy( char *dst, const char *src, size_t len );
|
||||
*
|
||||
* The function httplib_strlcpy() provides a platform independent safe way to
|
||||
* copy a string from one memory location to another. The size of the receiving
|
||||
* buffer is provided as a parameter and the function ensures that no more than
|
||||
* the number of the characters fitting in that buffer will be copied. The
|
||||
* function also ensures that if the destination buffer is not NULL and the
|
||||
* size is at least one byte long that the resulting string is terminated with
|
||||
* a NUL character.
|
||||
*
|
||||
* If the source string is longer than will fit in the receiving buffer, the
|
||||
* remaining characters will be ignored.
|
||||
*/
|
||||
|
||||
for (; *src != '\0' && n > 1; n--) { *dst++ = *src++; }
|
||||
*dst = '\0';
|
||||
LIBHTTP_API void httplib_strlcpy( char *dst, const char *src, size_t len ) {
|
||||
|
||||
} /* XX_httplib_strlcpy */
|
||||
if ( dst == NULL || len == 0 ) return;
|
||||
if ( src == NULL ) { *dst = 0; return; }
|
||||
|
||||
while ( len > 1 && *src ) {
|
||||
|
||||
*dst++ = *src++;
|
||||
len--;
|
||||
}
|
||||
|
||||
*dst = 0;
|
||||
|
||||
} /* httplib_strlcpy */
|
||||
|
@@ -22,19 +22,36 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
#include "httplib_memory.h"
|
||||
#include "httplib_string.h"
|
||||
|
||||
char * XX_httplib_strndup( const char *ptr, size_t len ) {
|
||||
/*
|
||||
* char *httplib_strndup( const char *ptr, size_t len );
|
||||
*
|
||||
* The function strndup() duplicates a string with a maximum given length to a
|
||||
* new string in a newly allocated block of memory. The function is equivalent
|
||||
* to the Posix function strndup() with the difference that LibHTTP memory
|
||||
* allocation functions are used which allow for tracking of memory leaks
|
||||
* through a monitor hook. The size of the allocated memory block is the given
|
||||
* length plus one byte for the terminating NUL character.
|
||||
*
|
||||
* If the duplicate of the string is no longer used, the allocated memory
|
||||
* should be returned to the heap with a call to httplib_free.
|
||||
*
|
||||
* If the function fails, the value NULL is returned, otherwise a pointer to
|
||||
* the duplicate.
|
||||
*/
|
||||
|
||||
LIBHTTP_API char *httplib_strndup( const char *ptr, size_t len ) {
|
||||
|
||||
char *p;
|
||||
|
||||
if ((p = (char *)XX_httplib_malloc(len + 1)) != NULL) XX_httplib_strlcpy(p, ptr, len + 1);
|
||||
if ( (p = XX_httplib_malloc(len+1)) != NULL ) httplib_strlcpy( p, ptr, len+1 );
|
||||
|
||||
return p;
|
||||
|
||||
} /* XX_httplib_strndup */
|
||||
} /* httplib_strndup */
|
||||
|
@@ -62,7 +62,7 @@ int XX_httplib_substitute_index_file( struct httplib_connection *conn, char *pat
|
||||
if (filename_vec.len > path_len - (n + 2)) continue;
|
||||
|
||||
/* Prepare full path to the index file */
|
||||
XX_httplib_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
|
||||
httplib_strlcpy( path + n + 1, filename_vec.ptr, filename_vec.len + 1 );
|
||||
|
||||
/* Does it exist? */
|
||||
if (XX_httplib_stat(conn, path, &file)) {
|
||||
|
@@ -44,7 +44,7 @@ void XX_httplib_uninitialize_ssl( struct httplib_context *ctx ) {
|
||||
int i;
|
||||
(void)ctx;
|
||||
|
||||
if (XX_httplib_atomic_dec(&XX_httplib_cryptolib_users) == 0) {
|
||||
if (httplib_atomic_dec(&XX_httplib_cryptolib_users) == 0) {
|
||||
|
||||
/* Shutdown according to
|
||||
* https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
|
||||
|
@@ -27,13 +27,14 @@
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
|
||||
|
||||
int httplib_url_decode( const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded ) {
|
||||
|
||||
int i;
|
||||
int j;
|
||||
int a;
|
||||
int b;
|
||||
#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
|
||||
|
||||
for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
|
||||
if (i < src_len - 2 && src[i] == '%'
|
||||
|
@@ -23,8 +23,6 @@
|
||||
|
||||
|
||||
void XX_httplib_addenv( struct cgi_environment *env, PRINTF_FORMAT_STRING(const char *fmt), ... ) PRINTF_ARGS(2, 3);
|
||||
int XX_httplib_atomic_dec( volatile int *addr );
|
||||
int XX_httplib_atomic_inc( volatile int *addr );
|
||||
void XX_httplib_base64_encode( const unsigned char *src, int src_len, char *dst );
|
||||
double XX_httplib_difftimespec( const struct timespec *ts_now, const struct timespec *ts_before );
|
||||
uint64_t XX_httplib_get_random( void );
|
||||
|
@@ -73,7 +73,7 @@ static void *worker_thread_run( struct worker_thread_args *thread_args ) {
|
||||
XX_httplib_set_thread_name("worker");
|
||||
|
||||
tls.is_master = 0;
|
||||
tls.thread_idx = (unsigned)XX_httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
tls.thread_idx = (unsigned)httplib_atomic_inc(&XX_httplib_thread_idx_max);
|
||||
#if defined(_WIN32)
|
||||
tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
#endif
|
||||
|
@@ -22,13 +22,22 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
|
||||
/*
|
||||
* int rename( const char *a, const char *b );
|
||||
*
|
||||
* The function rename() provides a Windows CE specific implementation to
|
||||
* rename a file. As the kernel does not provide support for the Posix rename()
|
||||
* function this emulation function should provide equivalent functionality.
|
||||
* If renaming succeeds, 0 is returned, otherwise -1.
|
||||
*/
|
||||
|
||||
int rename( const char *a, const char *b ) {
|
||||
|
||||
wchar_t wa[PATH_MAX];
|
||||
|
@@ -22,7 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
@@ -34,16 +34,26 @@ struct stat {
|
||||
time_t st_mtime;
|
||||
};
|
||||
|
||||
/*
|
||||
* int stat( const char *name, struct stat *st );
|
||||
*
|
||||
* Windows CE does not provide all of the common system functions available on
|
||||
* other platforms. One missing function is the stat() function which is
|
||||
* implemented here using other existing functions in the kernel. The
|
||||
* functionality should be largely compatible with the Posix stat() version.
|
||||
*/
|
||||
|
||||
int stat( const char *name, struct stat *st ) {
|
||||
|
||||
wchar_t wbuf[PATH_MAX];
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
time_t creation_time, write_time;
|
||||
time_t creation_time;
|
||||
time_t write_time;
|
||||
|
||||
XX_httplib_path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
XX_httplib_path_to_unicode( name, wbuf, ARRAY_SIZE(wbuf) );
|
||||
memset( & attr, 0, sizeof(attr) );
|
||||
|
||||
GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
|
||||
GetFileAttributesExW( wbuf, GetFileExInfoStandard, &attr );
|
||||
st->st_size = (((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
|
||||
|
||||
write_time = SYS2UNIX_TIME( attr.ftLastWriteTime.dwLowDateTime, attr.ftLastWriteTime.dwHighDateTime );
|
||||
|
@@ -22,17 +22,415 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
|
||||
size_t strftime( char *dst, size_t dst_size, const char *fmt, const struct tm *tm ) {
|
||||
#define BUFLEN 64
|
||||
|
||||
static const char *weekday_l[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
|
||||
static const char *weekday_s[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
|
||||
static const char *month_l[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
|
||||
static const char *month_s[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
|
||||
/*
|
||||
* size_t strftime( char *dst, size_t dst_size, const char *fmt, const struct tm *tmm );
|
||||
*
|
||||
* On Windows CE systems not all common system functions are available. One of
|
||||
* the missing functions is strftime() which is emulated here using other
|
||||
* functions available in the Windows CE kernel.
|
||||
*
|
||||
* Note that this is a rudimentary implementation which doesn't take any
|
||||
* regional settings into account. It provides more or less the functionality
|
||||
* needed in the library but shouldn't be used for other purposes if an
|
||||
* accurate strftime implementation is needed.
|
||||
*/
|
||||
|
||||
size_t strftime( char *dst, size_t dst_size, const char *fmt, const struct tm *tmm ) {
|
||||
|
||||
bool neg_offset;
|
||||
long sec_offset;
|
||||
long min_offset;
|
||||
long hour_offset;
|
||||
size_t index;
|
||||
const char *ptr;
|
||||
char buffer[BUFLEN];
|
||||
|
||||
if ( dst == NULL || dst_size == 0 || fmt == NULL || tmm == NULL ) return 0;
|
||||
|
||||
index = 0;
|
||||
|
||||
while ( *fmt && index < dst_size ) {
|
||||
|
||||
if ( *fmt != '%' ) { dst[index++] = *fmt++; continue; }
|
||||
|
||||
fmt++;
|
||||
|
||||
switch ( *fmt ) {
|
||||
|
||||
case '%' :
|
||||
dst[index++] = *fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'A' :
|
||||
if ( tmm.tm_wday < 0 || tmm.tm_wday > 6 ) return 0;
|
||||
ptr = weekday_l[tmm.tm_wday];
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'a' :
|
||||
if ( tmm.tm_wday < 0 || tmm.tm_wday > 6 ) return 0;
|
||||
ptr = weekday_s[tmm.tm_wday];
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'B' :
|
||||
if ( tmm.tm_mon < 0 || tmm.tm_mon > 11 ) return 0;
|
||||
ptr = month_l[tmm.tm_mon];
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'C' :
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", (year+1900) / 100 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'b' :
|
||||
case 'h' :
|
||||
if ( tmm.tm_mon < 0 || tmm.tm_mon > 11 ) return 0;
|
||||
ptr = month_s[tmm.tm_mon];
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'd' :
|
||||
|
||||
if ( tmm.tm_mday < 1 || tmm.tm_mday > 31 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", tmm.tm_mday );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'D' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%m/%d/%y", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'e' :
|
||||
|
||||
if ( tmm.tm_mday < 1 || tmm.tm_mday > 31 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%2d", tmm.tm_mday );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'F' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%Y-%m-%d", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'H' :
|
||||
|
||||
if ( tmm.tm_hour < 0 || tmm.tm_hour > 23 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", tmm.tm_hour );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'I' :
|
||||
|
||||
if ( tmm.tm_hour < 0 || tmm.tm_hour > 23 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", ((tmm.tm_hour+11) % 12) + 1 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'j' :
|
||||
|
||||
if ( tmm.tm_yday < 0 || tmm.tm_yday > 365 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%03d", tmm.tm_yday+1 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'k' :
|
||||
|
||||
if ( tmm.tm_hour < 0 || tmm.tm_hour > 23 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%2d", tmm.tm_hour );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'l' :
|
||||
|
||||
if ( tmm.tm_hour < 0 || tmm.tm_hour > 23 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%2d", ((tmm.tm_hour+11) % 12) + 1 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'M' :
|
||||
|
||||
if ( tmm.tm_min < 0 || tmm.tm_min > 59 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", tmm.tm_min );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'm' :
|
||||
|
||||
if ( tmm.tm_mon < 0 || tmm.tm_mon > 11 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%02d", tmm.tm_mon+1 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'n' :
|
||||
|
||||
dst[index++] = '\n';
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'p' :
|
||||
|
||||
if ( tmm.tm_hour < 0 || tmm.tm_hour > 23 ) return 0;
|
||||
|
||||
if ( tmm.tm_hour < 12 ) ptr = "AM";
|
||||
else ptr = "PM";
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'R' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%H:%M", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'r' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%I:%M:%S %p", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'S' :
|
||||
|
||||
if ( tmm.tm_sec < 0 || tmm.tm_sec > 61 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%d", tmm.tm_sec );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'T' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%H:%M:%S", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 't' :
|
||||
|
||||
dst[index++] = '\t';
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'u' :
|
||||
|
||||
if ( tmm.tm_wday < 0 || tmm.tm_wday > 6 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%d", ((tmm.tm_wday+6) % 7) + 1 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'v' :
|
||||
|
||||
retval = strftime( &dst[index], dst_size-index, "%e-%b-%Y", tmm );
|
||||
if ( retval == 0 ) return 0;
|
||||
|
||||
index += retval-1;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
case 'w' :
|
||||
|
||||
if ( tmm.tm_wday < 0 || tmm.tm_wday > 6 ) return 0;
|
||||
|
||||
snprintf( buffer, BUFLEN, "%d", tmm.tm_wday );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'Y' :
|
||||
|
||||
snprintf( buffer, BUFLEN, "%d", tmm.tm_year + 1900 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
case 'y' :
|
||||
|
||||
snprintf( buffer, BUFLEN, "%d", tmm.tm_year % 100 );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'Z' :
|
||||
|
||||
if ( tmm.tm_zone == NULL ) return 0;
|
||||
|
||||
ptr = tmm.tm_zone;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 'z' :
|
||||
|
||||
if ( tmm.tm_gmt_off < 0 ) { neg_offset = true; sec_offset = -tmm.tm_gmt_off; }
|
||||
else { neg_offset = false; sec_offset = tmm.tm_gmt_off; }
|
||||
|
||||
sec_offset += 30;
|
||||
sec_offset /= 60;
|
||||
min_offset = sec_offset % 60;
|
||||
sec_offset /= 60;
|
||||
hour_offset = sec_offset;
|
||||
|
||||
if ( hour_offset > 14 ) return 0;
|
||||
|
||||
snprintf( buffer, "%c%02ld%02ld", (neg_offset) ? "-" : "+", hour_offset, min_offset );
|
||||
ptr = buffer;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ( *ptr && index < dst_size ) dst[index++] = *ptr++;
|
||||
if ( index >= dst_size ) return 0;
|
||||
|
||||
fmt++;
|
||||
}
|
||||
|
||||
if ( index < dst_size ) { dst[index++] = 0; return index; }
|
||||
|
||||
/* TODO */ //(void)XX_httplib_snprintf(NULL, dst, dst_size, "implement strftime()
|
||||
// for WinCE");
|
||||
return 0;
|
||||
|
||||
} /* strftime */
|
||||
|
@@ -22,24 +22,32 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* ============
|
||||
* Release: 1.8
|
||||
* Release: 2.0
|
||||
*/
|
||||
|
||||
#include "httplib_main.h"
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
|
||||
/*
|
||||
* time_t time( time_t *ptime );
|
||||
*
|
||||
* On WinCE systems not all of the common system functions are available. This
|
||||
* time() function provides an equivalent for time() based on time functions
|
||||
* which are available in the WinCE kernel.
|
||||
*/
|
||||
|
||||
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);
|
||||
GetSystemTime( & st );
|
||||
SystemTimeToFileTime( & st, & ft );
|
||||
t = SYS2UNIX_TIME( ft.dwLowDateTime, ft.dwHighDateTime );
|
||||
|
||||
if (ptime != NULL) *ptime = t;
|
||||
if ( ptime != NULL ) *ptime = t;
|
||||
|
||||
return t;
|
||||
|
||||
|
@@ -1767,7 +1767,7 @@ static const int myfile_content_rep = 50000;
|
||||
|
||||
|
||||
static int
|
||||
field_store(const char *path, long long file_size, void *user_data)
|
||||
field_store(const char *path, int64_t file_size, void *user_data)
|
||||
{
|
||||
FILE *f;
|
||||
ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
|
||||
|
Reference in New Issue
Block a user