diff --git a/include/libretro.h b/include/libretro.h index 8d6a166..54c05ca 100644 --- a/include/libretro.h +++ b/include/libretro.h @@ -1833,6 +1833,22 @@ enum retro_mod * input devices does not need to take any action on its own. */ +#define RETRO_ENVIRONMENT_GET_DEVICE_POWER (77 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_device_power * -- + * Returns the device's current power state as reported by the frontend. + * This is useful for emulating the battery level in handheld consoles, + * or for reducing power consumption when on battery power. + * + * The return value indicates whether the frontend can provide this information, + * even if the parameter is NULL. + * + * If the frontend does not support this functionality, + * then the provided argument will remain unchanged. + * + * Note that this environment call describes the power state for the entire device, + * not for individual peripherals like controllers. + */ + /* VFS functionality */ /* File paths: @@ -3133,7 +3149,7 @@ typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id /** * A callback interface for giving a core the ability to send and receive custom - * network packets during a multiplayer session between two or more instances + * network packets during a multiplayer session between two or more instances * of a libretro frontend. * * @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE @@ -4153,6 +4169,86 @@ struct retro_microphone_interface retro_read_mic_t read_mic; }; +/** + * Describes how a device is being powered. + * @see RETRO_ENVIRONMENT_GET_DEVICE_POWER + */ +enum retro_power_state +{ + /** + * Indicates that the frontend cannot report its power state at this time, + * most likely due to a lack of support. + * + * \c RETRO_ENVIRONMENT_GET_DEVICE_POWER will not return this value; + * instead, the environment callback will return \c false. + */ + RETRO_POWERSTATE_UNKNOWN = 0, + + /** + * Indicates that the device is running on its battery. + * Usually applies to portable devices such as handhelds, laptops, and smartphones. + */ + RETRO_POWERSTATE_DISCHARGING, + + /** + * Indicates that the device's battery is currently charging. + */ + RETRO_POWERSTATE_CHARGING, + + /** + * Indicates that the device is connected to a power source + * and that its battery has finished charging. + */ + RETRO_POWERSTATE_CHARGED, + + /** + * Indicates that the device is connected to a power source + * and that it does not have a battery. + * This usually suggests a desktop computer or a non-portable game console. + */ + RETRO_POWERSTATE_PLUGGED_IN +}; + +/** + * Indicates that an estimate is not available for the battery level or time remaining, + * even if the actual power state is known. + */ +#define RETRO_POWERSTATE_NO_ESTIMATE (-1) + +/** + * Describes the power state of the device running the frontend. + * @see RETRO_ENVIRONMENT_GET_DEVICE_POWER + */ +struct retro_device_power +{ + /** + * The current state of the frontend's power usage. + */ + enum retro_power_state state; + + /** + * A rough estimate of the amount of time remaining (in seconds) + * before the device powers off. + * This value depends on a variety of factors, + * so it is not guaranteed to be accurate. + * + * Will be set to \c RETRO_POWERSTATE_NO_ESTIMATE if \c state does not equal \c RETRO_POWERSTATE_DISCHARGING. + * May still be set to \c RETRO_POWERSTATE_NO_ESTIMATE if the frontend is unable to provide an estimate. + */ + int seconds; + + /** + * The approximate percentage of battery charge, + * ranging from 0 to 100 (inclusive). + * The device may power off before this reaches 0. + * + * The user might have configured their device + * to stop charging before the battery is full, + * so do not assume that this will be 100 in the \c RETRO_POWERSTATE_CHARGED state. + */ + int8_t percent; +}; + /* Callbacks */ /* Environment callback. Gives implementations a way of performing diff --git a/include/net/net_http.h b/include/net/net_http.h index 8523145..c328d42 100644 --- a/include/net/net_http.h +++ b/include/net/net_http.h @@ -51,6 +51,9 @@ void net_http_connection_set_user_agent(struct http_connection_t *conn, const ch void net_http_connection_set_headers(struct http_connection_t *conn, const char *headers); +void net_http_connection_set_content(struct http_connection_t *conn, const char *content_type, + size_t content_length, const void *content); + const char *net_http_connection_url(struct http_connection_t *conn); const char* net_http_connection_method(struct http_connection_t* conn); @@ -93,6 +96,17 @@ int net_http_status(struct http_t *state); **/ bool net_http_error(struct http_t *state); +/** + * net_http_headers: + * + * Leaf function. + * + * @return the response headers. The returned buffer is owned by the + * caller of net_http_new; it is not freed by net_http_delete. + * If the status is not 20x and accept_error is false, it returns NULL. + **/ +struct string_list *net_http_headers(struct http_t *state); + /** * net_http_data: * diff --git a/net/net_http.c b/net/net_http.c index a57c793..7205ee5 100644 --- a/net/net_http.c +++ b/net/net_http.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ struct http_socket_state_t struct http_t { char *data; + struct string_list *headers; struct http_socket_state_t sock_state; /* ptr alignment */ size_t pos; size_t len; @@ -81,10 +83,11 @@ struct http_connection_t char *scan; char *methodcopy; char *contenttypecopy; - char *postdatacopy; + void *postdatacopy; char *useragentcopy; char *headerscopy; struct http_socket_state_t sock_state; /* ptr alignment */ + size_t contentlength; int port; }; @@ -530,7 +533,10 @@ struct http_connection_t *net_http_connection_new(const char *url, conn->methodcopy = strdup(method); if (data) + { conn->postdatacopy = strdup(data); + conn->contentlength = strlen(data); + } if (!(conn->urlcopy = strdup(url))) goto error; @@ -703,6 +709,25 @@ void net_http_connection_set_headers( conn->headerscopy = headers ? strdup(headers) : NULL; } +void net_http_connection_set_content( + struct http_connection_t *conn, const char *content_type, + size_t content_length, const void *content) + +{ + if (conn->contenttypecopy) + free(conn->contenttypecopy); + if (conn->postdatacopy) + free(conn->postdatacopy); + + conn->contenttypecopy = content_type ? strdup(content_type) : NULL; + conn->contentlength = content_length; + if (content_length) + { + conn->postdatacopy = malloc(content_length); + memcpy(conn->postdatacopy, content, content_length); + } +} + const char *net_http_connection_url(struct http_connection_t *conn) { return conn->urlcopy; @@ -767,8 +792,7 @@ struct http_t *net_http_new(struct http_connection_t *conn) if (conn->headerscopy) net_http_send_str(&conn->sock_state, &error, conn->headerscopy, strlen(conn->headerscopy)); - /* This is not being set anywhere yet */ - else if (conn->contenttypecopy) + if (conn->contenttypecopy) { net_http_send_str(&conn->sock_state, &error, "Content-Type: ", STRLEN_CONST("Content-Type: ")); @@ -778,12 +802,12 @@ struct http_t *net_http_new(struct http_connection_t *conn) STRLEN_CONST("\r\n")); } - if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST"))) + if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST") || string_is_equal(conn->methodcopy, "PUT"))) { size_t post_len, len; char *len_str = NULL; - if (!conn->postdatacopy) + if (!conn->postdatacopy && !string_is_equal(conn->methodcopy, "PUT")) goto err; if (!conn->headerscopy) @@ -799,7 +823,7 @@ struct http_t *net_http_new(struct http_connection_t *conn) net_http_send_str(&conn->sock_state, &error, "Content-Length: ", STRLEN_CONST("Content-Length: ")); - post_len = strlen(conn->postdatacopy); + post_len = conn->contentlength; #ifdef _WIN32 len = snprintf(NULL, 0, "%" PRIuPTR, post_len); len_str = (char*)malloc(len + 1); @@ -836,9 +860,9 @@ struct http_t *net_http_new(struct http_connection_t *conn) net_http_send_str(&conn->sock_state, &error, "\r\n", STRLEN_CONST("\r\n")); - if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST"))) + if (conn->postdatacopy && conn->contentlength) net_http_send_str(&conn->sock_state, &error, conn->postdatacopy, - strlen(conn->postdatacopy)); + conn->contentlength); if (!error) { @@ -854,7 +878,12 @@ struct http_t *net_http_new(struct http_connection_t *conn) state->buflen = 512; if ((state->data = (char*)malloc(state->buflen))) - return state; + { + if ((state->headers = string_list_new()) && + string_list_initialize(state->headers)) + return state; + string_list_free(state->headers); + } free(state); } @@ -865,6 +894,8 @@ err: free(conn->methodcopy); if (conn->contenttypecopy) free(conn->contenttypecopy); + if (conn->postdatacopy) + free(conn->postdatacopy); conn->methodcopy = NULL; conn->contenttypecopy = NULL; conn->postdatacopy = NULL; @@ -981,12 +1012,24 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked")) state->bodytype = T_CHUNK; - /* TODO: save headers somewhere */ if (state->data[0]=='\0') { - state->part = P_BODY; - if (state->bodytype == T_CHUNK) - state->part = P_BODY_CHUNKLEN; + if (state->status == 100) + { + state->part = P_HEADER_TOP; + } + else + { + state->part = P_BODY; + if (state->bodytype == T_CHUNK) + state->part = P_BODY_CHUNKLEN; + } + } + else + { + union string_list_elem_attr attr; + attr.i = 0; + string_list_append(state->headers, state->data, attr); } } @@ -1007,7 +1050,7 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) { if (state->error) newlen = -1; - else + else if (state->len) { #ifdef HAVE_SSL if (state->sock_state.ssl && state->sock_state.ssl_ctx) @@ -1158,6 +1201,26 @@ int net_http_status(struct http_t *state) return state->status; } +/** + * net_http_headers: + * + * Leaf function. + * + * @return the response headers. The returned buffer is owned by the + * caller of net_http_new; it is not freed by net_http_delete(). + * If the status is not 20x and accept_error is false, it returns NULL. + **/ +struct string_list *net_http_headers(struct http_t *state) +{ + if (!state) + return NULL; + + if (state->error) + return NULL; + + return state->headers; +} + /** * net_http_data: * diff --git a/net/net_socket_ssl_mbed.c b/net/net_socket_ssl_mbed.c index 48bed20..a0a524f 100644 --- a/net/net_socket_ssl_mbed.c +++ b/net/net_socket_ssl_mbed.c @@ -234,11 +234,24 @@ int ssl_socket_send_all_blocking(void *state_data, mbedtls_net_set_block(&state->net_ctx); - while ((ret = mbedtls_ssl_write(&state->ctx, data, size)) <= 0) + while (size) { - if ( ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) - return false; + ret = mbedtls_ssl_write(&state->ctx, data, size); + + if (!ret) + continue; + + if (ret < 0) + { + if ( ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) + return false; + } + else + { + data += ret; + size -= ret; + } } return true;