mirror of
https://github.com/rkd77/elinks.git
synced 2025-06-30 22:19:29 -04:00
Merge branch 'top/0.13/bug1008' into elinks-0.13
This commit is contained in:
commit
b8b54a5325
6
NEWS
6
NEWS
@ -25,6 +25,12 @@ Miscellaneous:
|
|||||||
* bug 963: New option document.css.ignore_display_none.
|
* bug 963: New option document.css.ignore_display_none.
|
||||||
* bug 977: Fixed crash when opening in new tab a non link with onclick
|
* bug 977: Fixed crash when opening in new tab a non link with onclick
|
||||||
attribute.
|
attribute.
|
||||||
|
* bug 1008: File upload fields in HTML forms now stream the files to
|
||||||
|
the server, instead of reading them to memory in advance. This lets
|
||||||
|
you upload larger files. The downsides are that ELinks may use a
|
||||||
|
cached response even if you have modified a file between requests,
|
||||||
|
and that ELinks can send inconsistent data if you modify a file
|
||||||
|
while it is being uploaded.
|
||||||
* Really retry forever when connection.retries = 0.
|
* Really retry forever when connection.retries = 0.
|
||||||
* enhancement: Session-specific options. Any options changed with
|
* enhancement: Session-specific options. Any options changed with
|
||||||
toggle-* actions no longer affect other tabs or other terminals.
|
toggle-* actions no longer affect other tabs or other terminals.
|
||||||
|
@ -15,10 +15,9 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
|
static unsigned char *
|
||||||
unsigned char *
|
get_progress_msg_2(struct progress *progress, struct terminal *term,
|
||||||
get_progress_msg(struct progress *progress, struct terminal *term,
|
int wide, int full, unsigned char *separator, unsigned char *type)
|
||||||
int wide, int full, unsigned char *separator)
|
|
||||||
{
|
{
|
||||||
struct string msg;
|
struct string msg;
|
||||||
int newlines = separator[strlen(separator) - 1] == '\n';
|
int newlines = separator[strlen(separator) - 1] == '\n';
|
||||||
@ -29,7 +28,7 @@ get_progress_msg(struct progress *progress, struct terminal *term,
|
|||||||
* one, _("of")-like pearls are a nightmare. Format strings need to
|
* one, _("of")-like pearls are a nightmare. Format strings need to
|
||||||
* be introduced to this fuggy corner of code as well. --pasky */
|
* be introduced to this fuggy corner of code as well. --pasky */
|
||||||
|
|
||||||
add_to_string(&msg, _("Received", term));
|
add_to_string(&msg, type);
|
||||||
add_char_to_string(&msg, ' ');
|
add_char_to_string(&msg, ' ');
|
||||||
add_xnum_to_string(&msg, progress->pos);
|
add_xnum_to_string(&msg, progress->pos);
|
||||||
if (progress->size >= 0) {
|
if (progress->size >= 0) {
|
||||||
@ -90,6 +89,20 @@ get_progress_msg(struct progress *progress, struct terminal *term,
|
|||||||
return msg.source;
|
return msg.source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
get_upload_progress_msg(struct progress *progress, struct terminal *term,
|
||||||
|
int wide, int full, unsigned char *separator)
|
||||||
|
{
|
||||||
|
return get_progress_msg_2(progress, term, wide, full, separator, _("Sent", term));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
get_progress_msg(struct progress *progress, struct terminal *term,
|
||||||
|
int wide, int full, unsigned char *separator)
|
||||||
|
{
|
||||||
|
return get_progress_msg_2(progress, term, wide, full, separator, _("Received", term));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
draw_progress_bar(struct progress *progress, struct terminal *term,
|
draw_progress_bar(struct progress *progress, struct terminal *term,
|
||||||
int x, int y, int width,
|
int x, int y, int width,
|
||||||
|
@ -8,6 +8,11 @@ unsigned char *
|
|||||||
get_progress_msg(struct progress *progress, struct terminal *term,
|
get_progress_msg(struct progress *progress, struct terminal *term,
|
||||||
int wide, int full, unsigned char *separator);
|
int wide, int full, unsigned char *separator);
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
get_upload_progress_msg(struct progress *progress, struct terminal *term,
|
||||||
|
int wide, int full, unsigned char *separator);
|
||||||
|
|
||||||
/* Draws a progress bar meter or progress coloured text depending on whether
|
/* Draws a progress bar meter or progress coloured text depending on whether
|
||||||
* @text is NULL. If @meter_color is NULL dialog.meter color is used. */
|
* @text is NULL. If @meter_color is NULL dialog.meter color is used. */
|
||||||
void
|
void
|
||||||
|
@ -55,6 +55,9 @@ get_download_msg(struct download *download, struct terminal *term,
|
|||||||
&& download->conn->uri->protocol == PROTOCOL_BITTORRENT)
|
&& download->conn->uri->protocol == PROTOCOL_BITTORRENT)
|
||||||
return get_bittorrent_message(download, term, wide, full, separator);
|
return get_bittorrent_message(download, term, wide, full, separator);
|
||||||
#endif
|
#endif
|
||||||
|
if (download->conn && download->conn->http_upload_progress)
|
||||||
|
return get_upload_progress_msg(download->conn->http_upload_progress,
|
||||||
|
term, wide, full, separator);
|
||||||
|
|
||||||
return get_progress_msg(download->progress, term, wide, full, separator);
|
return get_progress_msg(download->progress, term, wide, full, separator);
|
||||||
}
|
}
|
||||||
@ -229,6 +232,7 @@ display_status_bar(struct session *ses, struct terminal *term, int tabs_count)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
int full = term->width > 130;
|
int full = term->width > 130;
|
||||||
int wide = term->width > 80;
|
int wide = term->width > 80;
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "network/progress.h"
|
#include "network/progress.h"
|
||||||
#include "network/socket.h"
|
#include "network/socket.h"
|
||||||
#include "network/ssl/ssl.h"
|
#include "network/ssl/ssl.h"
|
||||||
|
#include "protocol/http/http.h"
|
||||||
#include "protocol/protocol.h"
|
#include "protocol/protocol.h"
|
||||||
#include "protocol/proxy.h"
|
#include "protocol/proxy.h"
|
||||||
#include "protocol/uri.h"
|
#include "protocol/uri.h"
|
||||||
@ -64,9 +65,8 @@ static INIT_LIST_OF(struct host_connection, host_connections);
|
|||||||
static INIT_LIST_OF(struct keepalive_connection, keepalive_connections);
|
static INIT_LIST_OF(struct keepalive_connection, keepalive_connections);
|
||||||
|
|
||||||
/* Prototypes */
|
/* Prototypes */
|
||||||
static void notify_connection_callbacks(struct connection *conn);
|
|
||||||
static void check_keepalive_connections(void);
|
static void check_keepalive_connections(void);
|
||||||
|
static void notify_connection_callbacks(struct connection *conn);
|
||||||
|
|
||||||
static /* inline */ enum connection_priority
|
static /* inline */ enum connection_priority
|
||||||
get_priority(struct connection *conn)
|
get_priority(struct connection *conn)
|
||||||
@ -327,9 +327,9 @@ update_connection_progress(struct connection *conn)
|
|||||||
update_progress(conn->progress, conn->received, conn->est_length, conn->from);
|
update_progress(conn->progress, conn->received, conn->est_length, conn->from);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Progress timer callback for @conn->progress. As explained in
|
/** Progress timer callback for @a conn->progress. As explained in
|
||||||
* @start_update_progress, this function must erase the expired timer
|
* start_update_progress(), this function must erase the expired timer
|
||||||
* ID from @conn->progress->timer. */
|
* ID from @a conn->progress->timer. */
|
||||||
static void
|
static void
|
||||||
stat_timer(struct connection *conn)
|
stat_timer(struct connection *conn)
|
||||||
{
|
{
|
||||||
@ -338,17 +338,48 @@ stat_timer(struct connection *conn)
|
|||||||
notify_connection_callbacks(conn);
|
notify_connection_callbacks(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Progress timer callback for @a conn->upload_progress. As explained
|
||||||
|
* in start_update_progress(), this function must erase the expired timer
|
||||||
|
* ID from @a conn->upload_progress->timer. */
|
||||||
|
static void
|
||||||
|
upload_stat_timer(struct connection *conn)
|
||||||
|
{
|
||||||
|
struct http_connection_info *http = conn->info;
|
||||||
|
|
||||||
|
assert(conn->http_upload_progress);
|
||||||
|
if_assert_failed return;
|
||||||
|
assert(http);
|
||||||
|
if_assert_failed {
|
||||||
|
conn->http_upload_progress->timer = TIMER_ID_UNDEF;
|
||||||
|
/* The expired timer ID has now been erased. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_progress(conn->http_upload_progress, http->post.uploaded,
|
||||||
|
http->post.total_upload_length, http->post.uploaded);
|
||||||
|
/* The expired timer ID has now been erased. */
|
||||||
|
notify_connection_callbacks(conn);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
set_connection_state(struct connection *conn, enum connection_state state)
|
set_connection_state(struct connection *conn, enum connection_state state)
|
||||||
{
|
{
|
||||||
struct download *download;
|
struct download *download;
|
||||||
struct progress *progress = conn->progress;
|
struct progress *progress = conn->progress;
|
||||||
|
struct progress *upload_progress = conn->http_upload_progress;
|
||||||
|
|
||||||
if (is_in_result_state(conn->state) && is_in_progress_state(state))
|
if (is_in_result_state(conn->state) && is_in_progress_state(state))
|
||||||
conn->prev_error = conn->state;
|
conn->prev_error = conn->state;
|
||||||
|
|
||||||
conn->state = state;
|
conn->state = state;
|
||||||
if (conn->state == S_TRANS) {
|
if (conn->state == S_TRANS) {
|
||||||
|
if (upload_progress && upload_progress->timer == TIMER_ID_UNDEF) {
|
||||||
|
start_update_progress(upload_progress,
|
||||||
|
(void (*)(void *)) upload_stat_timer, conn);
|
||||||
|
upload_stat_timer(conn);
|
||||||
|
if (connection_disappeared(conn))
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (progress->timer == TIMER_ID_UNDEF) {
|
if (progress->timer == TIMER_ID_UNDEF) {
|
||||||
start_update_progress(progress, (void (*)(void *)) stat_timer, conn);
|
start_update_progress(progress, (void (*)(void *)) stat_timer, conn);
|
||||||
update_connection_progress(conn);
|
update_connection_progress(conn);
|
||||||
@ -358,6 +389,7 @@ set_connection_state(struct connection *conn, enum connection_state state)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
kill_timer(&progress->timer);
|
kill_timer(&progress->timer);
|
||||||
|
if (upload_progress) kill_timer(&upload_progress->timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (download, conn->downloads) {
|
foreach (download, conn->downloads) {
|
||||||
@ -430,6 +462,15 @@ free_connection_data(struct connection *conn)
|
|||||||
shutdown_connection_stream(conn);
|
shutdown_connection_stream(conn);
|
||||||
|
|
||||||
mem_free_set(&conn->info, NULL);
|
mem_free_set(&conn->info, NULL);
|
||||||
|
/* If conn->done is not NULL, it probably points to a function
|
||||||
|
* that expects conn->info to be a specific kind of structure.
|
||||||
|
* Such a function should not be called if a different pointer
|
||||||
|
* is later assigned to conn->info. Actually though, each
|
||||||
|
* free_connection_data() call seems to be soon followed by
|
||||||
|
* done_connection() so that conn->done would not be called
|
||||||
|
* again in any case. However, this assignment costs little
|
||||||
|
* and may make things a bit safer. */
|
||||||
|
conn->done = NULL;
|
||||||
|
|
||||||
kill_timer(&conn->timer);
|
kill_timer(&conn->timer);
|
||||||
|
|
||||||
@ -437,7 +478,7 @@ free_connection_data(struct connection *conn)
|
|||||||
done_host_connection(conn);
|
done_host_connection(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
notify_connection_callbacks(struct connection *conn)
|
notify_connection_callbacks(struct connection *conn)
|
||||||
{
|
{
|
||||||
enum connection_state state = conn->state;
|
enum connection_state state = conn->state;
|
||||||
@ -470,6 +511,8 @@ done_connection(struct connection *conn)
|
|||||||
mem_free(conn->socket);
|
mem_free(conn->socket);
|
||||||
mem_free(conn->data_socket);
|
mem_free(conn->data_socket);
|
||||||
done_progress(conn->progress);
|
done_progress(conn->progress);
|
||||||
|
if (conn->http_upload_progress)
|
||||||
|
done_progress(conn->http_upload_progress);
|
||||||
mem_free(conn);
|
mem_free(conn);
|
||||||
check_queue_bugs();
|
check_queue_bugs();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,15 @@ struct connection {
|
|||||||
LIST_OF(struct download) downloads;
|
LIST_OF(struct download) downloads;
|
||||||
struct progress *progress;
|
struct progress *progress;
|
||||||
|
|
||||||
|
/** Progress of sending the request and attached files to the
|
||||||
|
* server. This happens before any download.
|
||||||
|
*
|
||||||
|
* Currently, ELinks supports file uploads only in HTTP and
|
||||||
|
* local CGI. Therefore, upload_stat_timer() in connection.c
|
||||||
|
* assumes that #info points to struct http_connection_info
|
||||||
|
* whenever @c http_upload_progress is not NULL. */
|
||||||
|
struct progress *http_upload_progress;
|
||||||
|
|
||||||
/* If no proxy is used uri and proxied_uri are the same. */
|
/* If no proxy is used uri and proxied_uri are the same. */
|
||||||
struct uri *uri;
|
struct uri *uri;
|
||||||
struct uri *proxied_uri;
|
struct uri *proxied_uri;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#elif defined(CONFIG_GNUTLS)
|
#elif defined(CONFIG_GNUTLS)
|
||||||
|
#include <gcrypt.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#else
|
#else
|
||||||
#error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
|
#error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
#include "util/conv.h"
|
#include "util/conv.h"
|
||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
#include "util/random.h"
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: As you can see, SSL is currently implemented in very, erm,
|
/* FIXME: As you can see, SSL is currently implemented in very, erm,
|
||||||
@ -282,3 +284,17 @@ get_ssl_connection_cipher(struct socket *socket)
|
|||||||
|
|
||||||
return str.source;
|
return str.source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When CONFIG_SSL is defined, this implementation replaces the one in
|
||||||
|
* src/util/random.c. */
|
||||||
|
void
|
||||||
|
random_nonce(unsigned char buf[], size_t size)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_OPENSSL
|
||||||
|
RAND_pseudo_bytes(buf, size);
|
||||||
|
#elif defined(CONFIG_GNUTLS)
|
||||||
|
gcry_create_nonce(buf, size);
|
||||||
|
#else
|
||||||
|
# error unsupported SSL library
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -70,6 +70,7 @@ static const struct s_msg_dsc msg_dsc[] = {
|
|||||||
|
|
||||||
{S_HTTP_ERROR, N_("Bad HTTP response")},
|
{S_HTTP_ERROR, N_("Bad HTTP response")},
|
||||||
{S_HTTP_204, N_("No content")},
|
{S_HTTP_204, N_("No content")},
|
||||||
|
{S_HTTP_UPLOAD_RESIZED, N_("File was resized during upload")},
|
||||||
|
|
||||||
{S_FILE_TYPE, N_("Unknown file type")},
|
{S_FILE_TYPE, N_("Unknown file type")},
|
||||||
{S_FILE_ERROR, N_("Error opening file")},
|
{S_FILE_ERROR, N_("Error opening file")},
|
||||||
|
@ -72,6 +72,7 @@ enum connection_state {
|
|||||||
|
|
||||||
S_HTTP_ERROR = -100100,
|
S_HTTP_ERROR = -100100,
|
||||||
S_HTTP_204 = -100101,
|
S_HTTP_204 = -100101,
|
||||||
|
S_HTTP_UPLOAD_RESIZED = -100102,
|
||||||
|
|
||||||
S_FILE_TYPE = -100200,
|
S_FILE_TYPE = -100200,
|
||||||
S_FILE_ERROR = -100201,
|
S_FILE_ERROR = -100201,
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "util/conv.h"
|
#include "util/conv.h"
|
||||||
#include "util/md5.h"
|
#include "util/md5.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/random.h"
|
||||||
|
|
||||||
|
|
||||||
/* Hexes a binary md5 digest. Taken from RFC 2617 */
|
/* Hexes a binary md5 digest. Taken from RFC 2617 */
|
||||||
@ -31,18 +32,14 @@ convert_to_md5_digest_hex_T(md5_digest_bin_T bin, md5_digest_hex_T hex)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initializes a random cnonce that is also a hexed md5 digest. */
|
/* Initializes a random cnonce that has the same format as a hexed md5
|
||||||
|
* digest. */
|
||||||
static void
|
static void
|
||||||
init_cnonce_digest(md5_digest_hex_T cnonce)
|
init_cnonce_digest(md5_digest_hex_T cnonce)
|
||||||
{
|
{
|
||||||
md5_digest_bin_T md5;
|
md5_digest_bin_T md5;
|
||||||
int random;
|
|
||||||
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
random = rand();
|
|
||||||
MD5((const unsigned char *) &random, sizeof(random), md5);
|
|
||||||
|
|
||||||
|
random_nonce(md5, MD5_DIGEST_LENGTH);
|
||||||
convert_to_md5_digest_hex_T(md5, cnonce);
|
convert_to_md5_digest_hex_T(md5, cnonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/lists.h"
|
#include "util/lists.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/random.h"
|
||||||
#include "util/sha1.h"
|
#include "util/sha1.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "util/snprintf.h"
|
#include "util/snprintf.h"
|
||||||
@ -167,13 +168,10 @@ init_bittorrent_peer_id(bittorrent_id_T peer_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Hmm, sizeof(peer_id) don't work here. */
|
/* Hmm, sizeof(peer_id) don't work here. */
|
||||||
|
random_nonce(peer_id + i, sizeof(bittorrent_id_T) - i);
|
||||||
while (i < sizeof(bittorrent_id_T)) {
|
while (i < sizeof(bittorrent_id_T)) {
|
||||||
int random = rand();
|
peer_id[i] = hx(peer_id[i] & 0xF);
|
||||||
|
i++;
|
||||||
while (i < sizeof(bittorrent_id_T) && (random & 0xF)) {
|
|
||||||
peer_id[i++] = hx(random & 0xF);
|
|
||||||
random >>= 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +250,7 @@ done_bittorrent_connection(struct connection *conn)
|
|||||||
struct bittorrent_peer_connection *peer, *next;
|
struct bittorrent_peer_connection *peer, *next;
|
||||||
|
|
||||||
assert(bittorrent);
|
assert(bittorrent);
|
||||||
|
assert(conn->done == done_bittorrent_connection);
|
||||||
|
|
||||||
/* We don't want the tracker to see the fetch. */
|
/* We don't want the tracker to see the fetch. */
|
||||||
if (bittorrent->fetch)
|
if (bittorrent->fetch)
|
||||||
@ -270,6 +271,7 @@ done_bittorrent_connection(struct connection *conn)
|
|||||||
free_list(bittorrent->peer_pool);
|
free_list(bittorrent->peer_pool);
|
||||||
|
|
||||||
mem_free_set(&conn->info, NULL);
|
mem_free_set(&conn->info, NULL);
|
||||||
|
conn->done = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bittorrent_connection *
|
static struct bittorrent_connection *
|
||||||
@ -277,12 +279,17 @@ init_bittorrent_connection(struct connection *conn)
|
|||||||
{
|
{
|
||||||
struct bittorrent_connection *bittorrent;
|
struct bittorrent_connection *bittorrent;
|
||||||
|
|
||||||
|
assert(conn->info == NULL);
|
||||||
|
assert(conn->done == NULL);
|
||||||
|
if_assert_failed return NULL;
|
||||||
|
|
||||||
bittorrent = mem_calloc(1, sizeof(*bittorrent));
|
bittorrent = mem_calloc(1, sizeof(*bittorrent));
|
||||||
if (!bittorrent) return NULL;
|
if (!bittorrent) return NULL;
|
||||||
|
|
||||||
init_list(bittorrent->peers);
|
init_list(bittorrent->peers);
|
||||||
init_list(bittorrent->peer_pool);
|
init_list(bittorrent->peer_pool);
|
||||||
|
|
||||||
|
/* conn->info and conn->done were asserted as NULL above. */
|
||||||
conn->info = bittorrent;
|
conn->info = bittorrent;
|
||||||
conn->done = done_bittorrent_connection;
|
conn->done = done_bittorrent_connection;
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/lists.h"
|
#include "util/lists.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/random.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
|
|
||||||
@ -165,7 +166,7 @@ find_random_in_bittorrent_piece_cache(struct bittorrent_piece_cache *cache,
|
|||||||
|
|
||||||
assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces);
|
assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces);
|
||||||
|
|
||||||
srand(time(NULL));
|
seed_rand_once();
|
||||||
|
|
||||||
foreachback_bitfield_set (piece, peer->bitfield) {
|
foreachback_bitfield_set (piece, peer->bitfield) {
|
||||||
assertm(cache->entries[piece].rarity,
|
assertm(cache->entries[piece].rarity,
|
||||||
@ -238,7 +239,7 @@ find_rarest_in_bittorrent_piece_cache(struct bittorrent_piece_cache *cache,
|
|||||||
|
|
||||||
assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces);
|
assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces);
|
||||||
|
|
||||||
srand(time(NULL));
|
seed_rand_once();
|
||||||
|
|
||||||
/* Try to randomize the piece picking using the strategy from the random
|
/* Try to randomize the piece picking using the strategy from the random
|
||||||
* piece selection. */
|
* piece selection. */
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "intl/gettext/libintl.h"
|
#include "intl/gettext/libintl.h"
|
||||||
#include "mime/backend/common.h"
|
#include "mime/backend/common.h"
|
||||||
#include "network/connection.h"
|
#include "network/connection.h"
|
||||||
|
#include "network/progress.h"
|
||||||
#include "network/socket.h"
|
#include "network/socket.h"
|
||||||
#include "osdep/osdep.h"
|
#include "osdep/osdep.h"
|
||||||
#include "osdep/sysname.h"
|
#include "osdep/sysname.h"
|
||||||
@ -80,58 +81,50 @@ close_pipe_and_read(struct socket *data_socket)
|
|||||||
read_from_socket(conn->socket, rb, S_SENT, http_got_header);
|
read_from_socket(conn->socket, rb, S_SENT, http_got_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define POST_BUFFER_SIZE 32768
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_more_post_data(struct socket *socket)
|
||||||
|
{
|
||||||
|
struct connection *conn = socket->conn;
|
||||||
|
struct http_connection_info *http = conn->info;
|
||||||
|
unsigned char buffer[POST_BUFFER_SIZE];
|
||||||
|
int got;
|
||||||
|
enum connection_state error;
|
||||||
|
|
||||||
|
got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error);
|
||||||
|
if (got < 0) {
|
||||||
|
abort_connection(conn, error);
|
||||||
|
} else if (got > 0) {
|
||||||
|
write_to_socket(socket, buffer, got, S_TRANS,
|
||||||
|
send_more_post_data);
|
||||||
|
} else { /* got == 0, meaning end of data */
|
||||||
|
close_pipe_and_read(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef POST_BUFFER_SIZE
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_post_data(struct connection *conn)
|
send_post_data(struct connection *conn)
|
||||||
{
|
{
|
||||||
#define POST_BUFFER_SIZE 4096
|
struct http_connection_info *http = conn->info;
|
||||||
unsigned char *post = conn->uri->post;
|
unsigned char *post = conn->uri->post;
|
||||||
unsigned char *postend;
|
unsigned char *postend;
|
||||||
unsigned char buffer[POST_BUFFER_SIZE];
|
enum connection_state error;
|
||||||
struct string data;
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
if (!init_string(&data)) {
|
|
||||||
abort_connection(conn, S_OUT_OF_MEM);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
postend = strchr(post, '\n');
|
postend = strchr(post, '\n');
|
||||||
if (postend) post = postend + 1;
|
if (postend) post = postend + 1;
|
||||||
|
|
||||||
/* FIXME: Code duplication with protocol/http/http.c! --witekfl */
|
if (!open_http_post(&http->post, post, &error))
|
||||||
while (post[0] && post[1]) {
|
abort_connection(conn, error);
|
||||||
int h1, h2;
|
else {
|
||||||
|
if (!conn->http_upload_progress && http->post.file_count)
|
||||||
h1 = unhx(post[0]);
|
conn->http_upload_progress = init_progress(0);
|
||||||
assert(h1 >= 0 && h1 < 16);
|
send_more_post_data(conn->data_socket);
|
||||||
if_assert_failed h1 = 0;
|
|
||||||
|
|
||||||
h2 = unhx(post[1]);
|
|
||||||
assert(h2 >= 0 && h2 < 16);
|
|
||||||
if_assert_failed h2 = 0;
|
|
||||||
|
|
||||||
buffer[n++] = (h1<<4) + h2;
|
|
||||||
post += 2;
|
|
||||||
if (n == POST_BUFFER_SIZE) {
|
|
||||||
add_bytes_to_string(&data, buffer, n);
|
|
||||||
n = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (n)
|
|
||||||
add_bytes_to_string(&data, buffer, n);
|
|
||||||
|
|
||||||
|
|
||||||
/* If we're submitting a form whose controls do not have
|
|
||||||
* names, then the POST has a Content-Type but empty data,
|
|
||||||
* and an assertion would fail in write_to_socket. */
|
|
||||||
if (data.length)
|
|
||||||
write_to_socket(conn->data_socket, data.source, data.length,
|
|
||||||
S_SENT, close_pipe_and_read);
|
|
||||||
else
|
|
||||||
close_pipe_and_read(conn->data_socket);
|
|
||||||
|
|
||||||
done_string(&data);
|
|
||||||
#undef POST_BUFFER_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_request(struct connection *conn)
|
send_request(struct connection *conn)
|
||||||
|
@ -678,6 +678,13 @@ add_file_cmd_to_str(struct connection *conn)
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(conn->info == NULL);
|
||||||
|
assert(conn->done == NULL);
|
||||||
|
if_assert_failed {
|
||||||
|
abort_connection(conn, S_INTERNAL);
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* This will be reallocated below when we know how long the
|
/* This will be reallocated below when we know how long the
|
||||||
* command string should be. Error handling could be
|
* command string should be. Error handling could be
|
||||||
* simplified a little by allocating this initial structure on
|
* simplified a little by allocating this initial structure on
|
||||||
@ -689,6 +696,7 @@ add_file_cmd_to_str(struct connection *conn)
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* conn->info and conn->done were asserted as NULL above. */
|
||||||
conn->info = ftp; /* Freed when connection is destroyed. */
|
conn->info = ftp; /* Freed when connection is destroyed. */
|
||||||
|
|
||||||
if (!init_string(&command)
|
if (!init_string(&command)
|
||||||
|
@ -310,6 +310,13 @@ init_gopher_connection_info(struct connection *conn)
|
|||||||
* wazzup! */
|
* wazzup! */
|
||||||
assert(command.length >= 2);
|
assert(command.length >= 2);
|
||||||
|
|
||||||
|
assert(conn->info == NULL);
|
||||||
|
assert(conn->done == NULL);
|
||||||
|
if_assert_failed {
|
||||||
|
done_string(&command);
|
||||||
|
return S_INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
size = sizeof(*gopher) + command.length;
|
size = sizeof(*gopher) + command.length;
|
||||||
gopher = mem_calloc(1, size);
|
gopher = mem_calloc(1, size);
|
||||||
if (!gopher) {
|
if (!gopher) {
|
||||||
|
@ -3,6 +3,6 @@ include $(top_builddir)/Makefile.config
|
|||||||
|
|
||||||
OBJS-$(CONFIG_GSSAPI) += http_negotiate.o
|
OBJS-$(CONFIG_GSSAPI) += http_negotiate.o
|
||||||
|
|
||||||
OBJS = blacklist.o codes.o http.o
|
OBJS = blacklist.o codes.o http.o post.o
|
||||||
|
|
||||||
include $(top_srcdir)/Makefile.lib
|
include $(top_srcdir)/Makefile.lib
|
||||||
|
@ -8,12 +8,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#ifdef HAVE_UNISTD_H
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_FCNTL_H
|
|
||||||
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_LIMITS_H
|
#ifdef HAVE_LIMITS_H
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#endif
|
#endif
|
||||||
@ -51,11 +45,7 @@
|
|||||||
#include "http_negotiate.h"
|
#include "http_negotiate.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct http_version {
|
/* These macros concern the struct http_version defined in the http.h */
|
||||||
int major;
|
|
||||||
int minor;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HTTP_0_9(x) ((x).major == 0 && (x).minor == 9)
|
#define HTTP_0_9(x) ((x).major == 0 && (x).minor == 9)
|
||||||
#define HTTP_1_0(x) ((x).major == 1 && (x).minor == 0)
|
#define HTTP_1_0(x) ((x).major == 1 && (x).minor == 0)
|
||||||
#define HTTP_1_1(x) ((x).major == 1 && (x).minor == 1)
|
#define HTTP_1_1(x) ((x).major == 1 && (x).minor == 1)
|
||||||
@ -65,26 +55,13 @@ struct http_version {
|
|||||||
#define POST_HTTP_1_1(x) ((x).major > 1 || ((x).major == 1 && (x).minor > 1))
|
#define POST_HTTP_1_1(x) ((x).major > 1 || ((x).major == 1 && (x).minor > 1))
|
||||||
|
|
||||||
|
|
||||||
struct http_connection_info {
|
|
||||||
enum blacklist_flags bl_flags;
|
|
||||||
struct http_version recv_version;
|
|
||||||
struct http_version sent_version;
|
|
||||||
|
|
||||||
int close;
|
|
||||||
|
|
||||||
#define LEN_CHUNKED -2 /* == we get data in unknown number of chunks */
|
#define LEN_CHUNKED -2 /* == we get data in unknown number of chunks */
|
||||||
#define LEN_FINISHED 0
|
#define LEN_FINISHED 0
|
||||||
int length;
|
|
||||||
|
|
||||||
/* Either bytes coming in this chunk yet or "parser state". */
|
/* Either bytes coming in this chunk yet or "parser state". */
|
||||||
#define CHUNK_DATA_END -3
|
#define CHUNK_DATA_END -3
|
||||||
#define CHUNK_ZERO_SIZE -2
|
#define CHUNK_ZERO_SIZE -2
|
||||||
#define CHUNK_SIZE -1
|
#define CHUNK_SIZE -1
|
||||||
int chunk_remaining;
|
|
||||||
|
|
||||||
int code;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static struct auth_entry proxy_auth;
|
static struct auth_entry proxy_auth;
|
||||||
|
|
||||||
@ -487,9 +464,17 @@ static void
|
|||||||
http_end_request(struct connection *conn, enum connection_state state,
|
http_end_request(struct connection *conn, enum connection_state state,
|
||||||
int notrunc)
|
int notrunc)
|
||||||
{
|
{
|
||||||
|
struct http_connection_info *http;
|
||||||
|
|
||||||
shutdown_connection_stream(conn);
|
shutdown_connection_stream(conn);
|
||||||
|
|
||||||
if (conn->info && !((struct http_connection_info *) conn->info)->close
|
/* shutdown_connection_stream() should not change conn->info,
|
||||||
|
* but in case it does, read conn->info only after the call. */
|
||||||
|
http = conn->info;
|
||||||
|
if (http)
|
||||||
|
done_http_post(&http->post);
|
||||||
|
|
||||||
|
if (http && !http->close
|
||||||
&& (!conn->socket->ssl) /* We won't keep alive ssl connections */
|
&& (!conn->socket->ssl) /* We won't keep alive ssl connections */
|
||||||
&& (!get_opt_bool("protocol.http.bugs.post_no_keepalive", NULL)
|
&& (!get_opt_bool("protocol.http.bugs.post_no_keepalive", NULL)
|
||||||
|| !conn->uri->post)) {
|
|| !conn->uri->post)) {
|
||||||
@ -528,6 +513,19 @@ proxy_protocol_handler(struct connection *conn)
|
|||||||
#define connection_is_https_proxy(conn) \
|
#define connection_is_https_proxy(conn) \
|
||||||
(IS_PROXY_URI((conn)->uri) && (conn)->proxied_uri->protocol == PROTOCOL_HTTPS)
|
(IS_PROXY_URI((conn)->uri) && (conn)->proxied_uri->protocol == PROTOCOL_HTTPS)
|
||||||
|
|
||||||
|
/** connection.done points to this function if connection.info points
|
||||||
|
* to a struct http_connection_info. */
|
||||||
|
static void
|
||||||
|
done_http_connection(struct connection *conn)
|
||||||
|
{
|
||||||
|
struct http_connection_info *http = conn->info;
|
||||||
|
|
||||||
|
done_http_post(&http->post);
|
||||||
|
mem_free(http);
|
||||||
|
conn->info = NULL;
|
||||||
|
conn->done = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct http_connection_info *
|
struct http_connection_info *
|
||||||
init_http_connection_info(struct connection *conn, int major, int minor, int close)
|
init_http_connection_info(struct connection *conn, int major, int minor, int close)
|
||||||
{
|
{
|
||||||
@ -543,6 +541,8 @@ init_http_connection_info(struct connection *conn, int major, int minor, int clo
|
|||||||
http->sent_version.minor = minor;
|
http->sent_version.minor = minor;
|
||||||
http->close = close;
|
http->close = close;
|
||||||
|
|
||||||
|
init_http_post(&http->post);
|
||||||
|
|
||||||
/* The CGI code uses this too and blacklisting expects a host name. */
|
/* The CGI code uses this too and blacklisting expects a host name. */
|
||||||
if (conn->proxied_uri->protocol != PROTOCOL_FILE)
|
if (conn->proxied_uri->protocol != PROTOCOL_FILE)
|
||||||
http->bl_flags = get_blacklist_flags(conn->proxied_uri);
|
http->bl_flags = get_blacklist_flags(conn->proxied_uri);
|
||||||
@ -555,7 +555,12 @@ init_http_connection_info(struct connection *conn, int major, int minor, int clo
|
|||||||
|
|
||||||
/* If called from HTTPS proxy connection the connection info might have
|
/* If called from HTTPS proxy connection the connection info might have
|
||||||
* already been allocated. */
|
* already been allocated. */
|
||||||
|
if (conn->done) {
|
||||||
|
conn->done(conn);
|
||||||
|
conn->done = NULL;
|
||||||
|
}
|
||||||
mem_free_set(&conn->info, http);
|
mem_free_set(&conn->info, http);
|
||||||
|
conn->done = done_http_connection;
|
||||||
|
|
||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
@ -587,6 +592,39 @@ accept_encoding_header(struct string *header)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define POST_BUFFER_SIZE 32768
|
||||||
|
#define BIG_READ 655360
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_more_post_data(struct socket *socket)
|
||||||
|
{
|
||||||
|
struct connection *conn = socket->conn;
|
||||||
|
struct http_connection_info *http = conn->info;
|
||||||
|
unsigned char buffer[POST_BUFFER_SIZE];
|
||||||
|
int got;
|
||||||
|
enum connection_state error;
|
||||||
|
|
||||||
|
got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error);
|
||||||
|
if (got < 0) {
|
||||||
|
http_end_request(conn, error, 0);
|
||||||
|
} else if (got > 0) {
|
||||||
|
write_to_socket(socket, buffer, got, S_TRANS,
|
||||||
|
send_more_post_data);
|
||||||
|
} else { /* got == 0, meaning end of data */
|
||||||
|
/* Can't use request_from_socket() because there's no
|
||||||
|
* more data to write. */
|
||||||
|
struct read_buffer *rb = alloc_read_buffer(socket);
|
||||||
|
|
||||||
|
socket->state = SOCKET_END_ONCLOSE;
|
||||||
|
if (rb)
|
||||||
|
read_from_socket(socket, rb, S_SENT, http_got_header);
|
||||||
|
else
|
||||||
|
http_end_request(conn, S_OUT_OF_MEM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
http_send_header(struct socket *socket)
|
http_send_header(struct socket *socket)
|
||||||
{
|
{
|
||||||
@ -932,6 +970,7 @@ http_send_header(struct socket *socket)
|
|||||||
* as set by get_form_uri(). This '\n' is dropped if any
|
* as set by get_form_uri(). This '\n' is dropped if any
|
||||||
* and replaced by correct '\r\n' termination here. */
|
* and replaced by correct '\r\n' termination here. */
|
||||||
unsigned char *postend = strchr(uri->post, '\n');
|
unsigned char *postend = strchr(uri->post, '\n');
|
||||||
|
enum connection_state error;
|
||||||
|
|
||||||
if (postend) {
|
if (postend) {
|
||||||
add_to_string(&header, "Content-Type: ");
|
add_to_string(&header, "Content-Type: ");
|
||||||
@ -940,9 +979,15 @@ http_send_header(struct socket *socket)
|
|||||||
}
|
}
|
||||||
|
|
||||||
post_data = postend ? postend + 1 : uri->post;
|
post_data = postend ? postend + 1 : uri->post;
|
||||||
add_to_string(&header, "Content-Length: ");
|
if (!open_http_post(&http->post, post_data, &error)) {
|
||||||
add_long_to_string(&header, strlen(post_data) / 2);
|
http_end_request(conn, error, 0);
|
||||||
add_crlf_to_string(&header);
|
done_string(&header);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
add_format_to_string(&header, "Content-Length: "
|
||||||
|
"%" OFF_PRINT_FORMAT "\x0D\x0A",
|
||||||
|
(off_print_T)
|
||||||
|
http->post.total_upload_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COOKIES
|
#ifdef CONFIG_COOKIES
|
||||||
@ -965,42 +1010,21 @@ http_send_header(struct socket *socket)
|
|||||||
* This was already checked above and post_data is NULL
|
* This was already checked above and post_data is NULL
|
||||||
* in that case. Verified with an assertion below. */
|
* in that case. Verified with an assertion below. */
|
||||||
if (post_data) {
|
if (post_data) {
|
||||||
#define POST_BUFFER_SIZE 4096
|
|
||||||
unsigned char *post = post_data;
|
|
||||||
unsigned char buffer[POST_BUFFER_SIZE];
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
assert(!use_connect); /* see comment above */
|
assert(!use_connect); /* see comment above */
|
||||||
|
|
||||||
while (post[0] && post[1]) {
|
socket->state = SOCKET_END_ONCLOSE;
|
||||||
int h1, h2;
|
if (!conn->http_upload_progress && http->post.file_count)
|
||||||
|
conn->http_upload_progress = init_progress(0);
|
||||||
h1 = unhx(post[0]);
|
write_to_socket(socket, header.source, header.length, S_TRANS,
|
||||||
assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]);
|
send_more_post_data);
|
||||||
if_assert_failed h1 = 0;
|
} else
|
||||||
|
|
||||||
h2 = unhx(post[1]);
|
|
||||||
assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[1], post[1]);
|
|
||||||
if_assert_failed h2 = 0;
|
|
||||||
|
|
||||||
buffer[n++] = (h1<<4) + h2;
|
|
||||||
post += 2;
|
|
||||||
if (n == POST_BUFFER_SIZE) {
|
|
||||||
add_bytes_to_string(&header, buffer, n);
|
|
||||||
n = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n)
|
|
||||||
add_bytes_to_string(&header, buffer, n);
|
|
||||||
#undef POST_BUFFER_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
request_from_socket(socket, header.source, header.length, S_SENT,
|
request_from_socket(socket, header.source, header.length, S_SENT,
|
||||||
SOCKET_END_ONCLOSE, http_got_header);
|
SOCKET_END_ONCLOSE, http_got_header);
|
||||||
done_string(&header);
|
done_string(&header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef POST_BUFFER_SIZE
|
||||||
|
|
||||||
|
|
||||||
/* This function decompresses the data block given in @data (if it was
|
/* This function decompresses the data block given in @data (if it was
|
||||||
* compressed), which is long @len bytes. The decompressed data block is given
|
* compressed), which is long @len bytes. The decompressed data block is given
|
||||||
@ -1027,7 +1051,6 @@ decompress_data(struct connection *conn, unsigned char *data, int len,
|
|||||||
int *length_of_block;
|
int *length_of_block;
|
||||||
unsigned char *output = NULL;
|
unsigned char *output = NULL;
|
||||||
|
|
||||||
#define BIG_READ 65536
|
|
||||||
|
|
||||||
if (http->length == LEN_CHUNKED) {
|
if (http->length == LEN_CHUNKED) {
|
||||||
if (http->chunk_remaining == CHUNK_ZERO_SIZE)
|
if (http->chunk_remaining == CHUNK_ZERO_SIZE)
|
||||||
@ -1116,6 +1139,7 @@ decompress_data(struct connection *conn, unsigned char *data, int len,
|
|||||||
if (state == FINISHING) shutdown_connection_stream(conn);
|
if (state == FINISHING) shutdown_connection_stream(conn);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
#undef BIG_READ
|
||||||
|
|
||||||
static int
|
static int
|
||||||
is_line_in_buffer(struct read_buffer *rb)
|
is_line_in_buffer(struct read_buffer *rb)
|
||||||
|
@ -3,13 +3,34 @@
|
|||||||
#define EL__PROTOCOL_HTTP_HTTP_H
|
#define EL__PROTOCOL_HTTP_HTTP_H
|
||||||
|
|
||||||
#include "main/module.h"
|
#include "main/module.h"
|
||||||
|
#include "protocol/http/blacklist.h"
|
||||||
|
#include "protocol/http/post.h"
|
||||||
#include "protocol/protocol.h"
|
#include "protocol/protocol.h"
|
||||||
|
|
||||||
struct connection;
|
struct connection;
|
||||||
struct http_connection_info;
|
|
||||||
struct read_buffer;
|
struct read_buffer;
|
||||||
struct socket;
|
struct socket;
|
||||||
|
|
||||||
|
/* Macros related to this struct are defined in the http.c. */
|
||||||
|
struct http_version {
|
||||||
|
int major;
|
||||||
|
int minor;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** connection.info points to this in HTTP and local CGI connections. */
|
||||||
|
struct http_connection_info {
|
||||||
|
enum blacklist_flags bl_flags;
|
||||||
|
struct http_version recv_version;
|
||||||
|
struct http_version sent_version;
|
||||||
|
|
||||||
|
int close;
|
||||||
|
int length;
|
||||||
|
int chunk_remaining;
|
||||||
|
int code;
|
||||||
|
|
||||||
|
struct http_post post;
|
||||||
|
};
|
||||||
|
|
||||||
extern struct module http_protocol_module;
|
extern struct module http_protocol_module;
|
||||||
|
|
||||||
extern protocol_handler_T http_protocol_handler;
|
extern protocol_handler_T http_protocol_handler;
|
||||||
|
305
src/protocol/http/post.c
Normal file
305
src/protocol/http/post.c
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/** Parsing uri.post and uploading files in a POST request.
|
||||||
|
* @file */
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "elinks.h"
|
||||||
|
|
||||||
|
#include "protocol/http/post.h"
|
||||||
|
#include "protocol/uri.h"
|
||||||
|
#include "util/conv.h"
|
||||||
|
#include "util/error.h"
|
||||||
|
|
||||||
|
/** Initialize *@a http_post so that done_http_post() can be safely
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
void
|
||||||
|
init_http_post(struct http_post *http_post)
|
||||||
|
{
|
||||||
|
http_post->total_upload_length = 0;
|
||||||
|
http_post->uploaded = 0;
|
||||||
|
http_post->post_data = NULL;
|
||||||
|
http_post->post_fd = -1;
|
||||||
|
http_post->file_index = 0;
|
||||||
|
http_post->file_count = 0;
|
||||||
|
http_post->file_read = 0;
|
||||||
|
http_post->files = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Free all resources owned by *@a http_post, but do not free the
|
||||||
|
* structure itself. It is safe to call this multiple times.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
void
|
||||||
|
done_http_post(struct http_post *http_post)
|
||||||
|
{
|
||||||
|
http_post->total_upload_length = 0;
|
||||||
|
http_post->uploaded = 0;
|
||||||
|
http_post->post_data = NULL;
|
||||||
|
if (http_post->post_fd != -1) {
|
||||||
|
close(http_post->post_fd);
|
||||||
|
http_post->post_fd = -1;
|
||||||
|
}
|
||||||
|
http_post->file_index = 0;
|
||||||
|
http_post->file_count = 0;
|
||||||
|
http_post->file_read = 0;
|
||||||
|
mem_free_set(&http_post->files, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prepare to read POST data from a URI and possibly to upload files.
|
||||||
|
*
|
||||||
|
* @param http_post
|
||||||
|
* Must have been initialized with init_http_post().
|
||||||
|
* @param[in] post_data
|
||||||
|
* The body of the POST request as formatted by get_form_uri().
|
||||||
|
* However, unlike uri.post, @a post_data must not contain any
|
||||||
|
* Content-Type. The caller must ensure that the @a post_data
|
||||||
|
* pointer remains valid until done_http_post().
|
||||||
|
* @param[out] error
|
||||||
|
* If the function fails, it writes the error state here so that
|
||||||
|
* the caller can pass that on to abort_connection(). If the
|
||||||
|
* function succeeds, the value of *@a error is undefined.
|
||||||
|
*
|
||||||
|
* This function does not parse the Content-Type from uri.post; the
|
||||||
|
* caller must do that. This is because in local CGI, the child
|
||||||
|
* process handles the Content-Type (saving it to an environment
|
||||||
|
* variable before exec) but the parent process handles the body of
|
||||||
|
* the request (feeding it to the child process via a pipe).
|
||||||
|
*
|
||||||
|
* @return nonzero on success, zero on error.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
int
|
||||||
|
open_http_post(struct http_post *http_post, unsigned char *post_data,
|
||||||
|
enum connection_state *error)
|
||||||
|
{
|
||||||
|
off_t size = 0;
|
||||||
|
size_t length = strlen(post_data);
|
||||||
|
unsigned char *end = post_data;
|
||||||
|
|
||||||
|
done_http_post(http_post);
|
||||||
|
http_post->post_data = end;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct stat sb;
|
||||||
|
unsigned char *begin;
|
||||||
|
int res;
|
||||||
|
struct http_post_file *new_files;
|
||||||
|
|
||||||
|
begin = strchr(end, FILE_CHAR);
|
||||||
|
if (!begin) break;
|
||||||
|
end = strchr(begin + 1, FILE_CHAR);
|
||||||
|
if (!end) break;
|
||||||
|
*end = '\0';
|
||||||
|
res = stat(begin + 1, &sb);
|
||||||
|
*end = FILE_CHAR;
|
||||||
|
if (res) break;
|
||||||
|
|
||||||
|
/* This use of mem_realloc() in a loop consumes O(n^2)
|
||||||
|
* time but how many files are you really going to
|
||||||
|
* upload in one request? */
|
||||||
|
new_files = mem_realloc(http_post->files,
|
||||||
|
(http_post->file_count + 1)
|
||||||
|
* sizeof(*new_files));
|
||||||
|
if (new_files == NULL) {
|
||||||
|
done_http_post(http_post);
|
||||||
|
*error = S_OUT_OF_MEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
http_post->files = new_files;
|
||||||
|
new_files[http_post->file_count].size = sb.st_size;
|
||||||
|
http_post->file_count++;
|
||||||
|
|
||||||
|
size += sb.st_size;
|
||||||
|
length -= (end - begin + 1);
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
size += (length / 2);
|
||||||
|
http_post->total_upload_length = size;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return -2 if no data was read but the caller should retry;
|
||||||
|
* -1 if an error occurred and *@a error was set; 0 at end of data;
|
||||||
|
* a positive number if that many bytes were read.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
static int
|
||||||
|
read_http_post_inline(struct http_post *http_post,
|
||||||
|
unsigned char buffer[], int max,
|
||||||
|
enum connection_state *error)
|
||||||
|
{
|
||||||
|
unsigned char *post = http_post->post_data;
|
||||||
|
unsigned char *end = strchr(post, FILE_CHAR);
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
assert(http_post->post_fd < 0);
|
||||||
|
if_assert_failed { *error = S_INTERNAL; return -1; }
|
||||||
|
|
||||||
|
if (!end)
|
||||||
|
end = strchr(post, '\0');
|
||||||
|
|
||||||
|
while (post < end && total < max) {
|
||||||
|
int h1, h2;
|
||||||
|
|
||||||
|
h1 = unhx(post[0]);
|
||||||
|
assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]);
|
||||||
|
if_assert_failed h1 = 0;
|
||||||
|
|
||||||
|
h2 = unhx(post[1]);
|
||||||
|
assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[0], post[0]);
|
||||||
|
if_assert_failed h2 = 0;
|
||||||
|
|
||||||
|
buffer[total++] = (h1<<4) + h2;
|
||||||
|
post += 2;
|
||||||
|
}
|
||||||
|
if (post != end || *end != FILE_CHAR) {
|
||||||
|
http_post->post_data = post;
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_post->file_read = 0;
|
||||||
|
end = strchr(post + 1, FILE_CHAR);
|
||||||
|
assert(end);
|
||||||
|
*end = '\0';
|
||||||
|
http_post->post_fd = open(post + 1, O_RDONLY);
|
||||||
|
/* Be careful not to change errno here. */
|
||||||
|
*end = FILE_CHAR;
|
||||||
|
if (http_post->post_fd < 0) {
|
||||||
|
http_post->post_data = post;
|
||||||
|
if (total > 0)
|
||||||
|
return total; /* retry the open on the next call */
|
||||||
|
else {
|
||||||
|
*error = -errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http_post->post_data = end + 1;
|
||||||
|
return total ? total : -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return -2 if no data was read but the caller should retry;
|
||||||
|
* -1 if an error occurred and *@a error was set; 0 at end of data;
|
||||||
|
* a positive number if that many bytes were read.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
static int
|
||||||
|
read_http_post_fd(struct http_post *http_post,
|
||||||
|
unsigned char buffer[], int max,
|
||||||
|
enum connection_state *error)
|
||||||
|
{
|
||||||
|
const struct http_post_file *const file
|
||||||
|
= &http_post->files[http_post->file_index];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* safe_read() would set errno = EBADF anyway, but check this
|
||||||
|
* explicitly to make any such bugs easier to detect. */
|
||||||
|
assert(http_post->post_fd >= 0);
|
||||||
|
if_assert_failed { *error = S_INTERNAL; return -1; }
|
||||||
|
|
||||||
|
ret = safe_read(http_post->post_fd, buffer, max);
|
||||||
|
if (ret <= 0) {
|
||||||
|
const int errno_from_read = errno;
|
||||||
|
|
||||||
|
close(http_post->post_fd);
|
||||||
|
http_post->post_fd = -1;
|
||||||
|
http_post->file_index++;
|
||||||
|
/* http_post->file_read is used below so don't clear it here.
|
||||||
|
* It will be cleared when the next file is opened. */
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
*error = -errno_from_read;
|
||||||
|
return -1;
|
||||||
|
} else if (http_post->file_read != file->size) {
|
||||||
|
/* ELinks already sent a Content-Length header
|
||||||
|
* based on the size of this file, but the
|
||||||
|
* file has since been shrunk. Abort the
|
||||||
|
* connection because ELinks can no longer get
|
||||||
|
* enough data to fill the Content-Length.
|
||||||
|
* (Well, it could pad with zeroes, but that
|
||||||
|
* would be just weird.) */
|
||||||
|
*error = S_HTTP_UPLOAD_RESIZED;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
/* The upload file ended but there may still
|
||||||
|
* be more data in uri.post. If not,
|
||||||
|
* read_http_post_inline() will return 0 to
|
||||||
|
* indicate the final end of file. */
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http_post->file_read += ret;
|
||||||
|
if (http_post->file_read > file->size) {
|
||||||
|
/* ELinks already sent a Content-Length header based
|
||||||
|
* on the size of this file, but the file has since
|
||||||
|
* been extended. Abort the connection because ELinks
|
||||||
|
* can no longer fit the entire file in the original
|
||||||
|
* Content-Length. */
|
||||||
|
*error = S_HTTP_UPLOAD_RESIZED;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read data from connection.uri->post or from the files to which it
|
||||||
|
* refers.
|
||||||
|
*
|
||||||
|
* @return >0 if read that many bytes; 0 if EOF; -1 on error and set
|
||||||
|
* *@a error.
|
||||||
|
*
|
||||||
|
* @relates http_post */
|
||||||
|
int
|
||||||
|
read_http_post(struct http_post *http_post,
|
||||||
|
unsigned char buffer[], int max,
|
||||||
|
enum connection_state *error)
|
||||||
|
{
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
while (total < max) {
|
||||||
|
int chunk;
|
||||||
|
|
||||||
|
if (http_post->post_fd < 0)
|
||||||
|
chunk = read_http_post_inline(http_post,
|
||||||
|
buffer + total,
|
||||||
|
max - total,
|
||||||
|
error);
|
||||||
|
else
|
||||||
|
chunk = read_http_post_fd(http_post,
|
||||||
|
buffer + total,
|
||||||
|
max - total,
|
||||||
|
error);
|
||||||
|
|
||||||
|
if (chunk > 0) {
|
||||||
|
total += chunk;
|
||||||
|
http_post->uploaded += chunk;
|
||||||
|
} else if (chunk != -2) {
|
||||||
|
assert(chunk == -1 || chunk == 0);
|
||||||
|
/* If some data has already been successfully
|
||||||
|
* read to buffer[], tell the caller about
|
||||||
|
* that and forget about the error. The next
|
||||||
|
* read_http_post() call will retry the
|
||||||
|
* operation that failed. */
|
||||||
|
if (total != 0)
|
||||||
|
return total;
|
||||||
|
else
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
62
src/protocol/http/post.h
Normal file
62
src/protocol/http/post.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/** Parsing uri.post and uploading files in a POST request.
|
||||||
|
* @file */
|
||||||
|
|
||||||
|
#ifndef EL__PROTOCOL_HTTP_POST_H
|
||||||
|
#define EL__PROTOCOL_HTTP_POST_H
|
||||||
|
|
||||||
|
#include "network/state.h"
|
||||||
|
|
||||||
|
/** Information about a file to be uploaded in a POST request.
|
||||||
|
* open_http_post() collects this information and done_http_post()
|
||||||
|
* discards it. */
|
||||||
|
struct http_post_file {
|
||||||
|
/** The size of the file. */
|
||||||
|
off_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** State of reading POST data from connection.uri->post and related
|
||||||
|
* files. */
|
||||||
|
struct http_post {
|
||||||
|
/** Total size of the POST body to be uploaded */
|
||||||
|
off_t total_upload_length;
|
||||||
|
|
||||||
|
/** Amount of POST body data uploaded so far.
|
||||||
|
* read_http_post() increments this. */
|
||||||
|
off_t uploaded;
|
||||||
|
|
||||||
|
/** Points to the next byte to be read from connection.uri->post.
|
||||||
|
* Does not point to const because http_read_post() momentarily
|
||||||
|
* substitutes a null character for the FILE_CHAR at the end of
|
||||||
|
* each file name. */
|
||||||
|
unsigned char *post_data;
|
||||||
|
|
||||||
|
/** File descriptor from which data is being read, or -1 if
|
||||||
|
* none. */
|
||||||
|
int post_fd;
|
||||||
|
|
||||||
|
/** Current position in the #files array. This is the file
|
||||||
|
* that is currently being read (when post_fd != -1) or would
|
||||||
|
* be read next (when post_fd == -1). */
|
||||||
|
size_t file_index;
|
||||||
|
|
||||||
|
/** Number of files to be uploaded, i.e. the number of
|
||||||
|
* elements in the #files array. */
|
||||||
|
size_t file_count;
|
||||||
|
|
||||||
|
/** Number of bytes read from the current file so far.
|
||||||
|
* The value makes sense only when post_fd != -1. */
|
||||||
|
off_t file_read;
|
||||||
|
|
||||||
|
/** Array of information about files to be uploaded. */
|
||||||
|
struct http_post_file *files;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_http_post(struct http_post *http_post);
|
||||||
|
void done_http_post(struct http_post *http_post);
|
||||||
|
int open_http_post(struct http_post *http_post, unsigned char *post_data,
|
||||||
|
enum connection_state *error);
|
||||||
|
int read_http_post(struct http_post *http_post,
|
||||||
|
unsigned char buffer[], int max,
|
||||||
|
enum connection_state *error);
|
||||||
|
|
||||||
|
#endif
|
@ -103,6 +103,13 @@ init_nntp_connection_info(struct connection *conn)
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
int datalen;
|
int datalen;
|
||||||
|
|
||||||
|
assert(conn->info == NULL);
|
||||||
|
assert(conn->done == NULL);
|
||||||
|
if_assert_failed {
|
||||||
|
abort_connection(conn, S_INTERNAL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
nntp = mem_calloc(1, sizeof(*nntp));
|
nntp = mem_calloc(1, sizeof(*nntp));
|
||||||
if (!nntp) {
|
if (!nntp) {
|
||||||
abort_connection(conn, S_OUT_OF_MEM);
|
abort_connection(conn, S_OUT_OF_MEM);
|
||||||
@ -194,6 +201,13 @@ nntp_quit(struct connection *conn)
|
|||||||
{
|
{
|
||||||
struct nntp_connection_info *info;
|
struct nntp_connection_info *info;
|
||||||
|
|
||||||
|
assert(conn->info == NULL);
|
||||||
|
assert(conn->done == NULL);
|
||||||
|
if_assert_failed {
|
||||||
|
abort_connection(conn, S_INTERNAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
info = mem_calloc(1, sizeof(*info));
|
info = mem_calloc(1, sizeof(*info));
|
||||||
if (!info) {
|
if (!info) {
|
||||||
abort_connection(conn, S_OUT_OF_MEM);
|
abort_connection(conn, S_OUT_OF_MEM);
|
||||||
|
@ -7,6 +7,7 @@ struct string;
|
|||||||
|
|
||||||
#define POST_CHAR 1
|
#define POST_CHAR 1
|
||||||
#define POST_CHAR_S "\001"
|
#define POST_CHAR_S "\001"
|
||||||
|
#define FILE_CHAR '\002'
|
||||||
|
|
||||||
/* The uri structure is used to store the start position and length of commonly
|
/* The uri structure is used to store the start position and length of commonly
|
||||||
* used uri fields. It is initialized by parse_uri(). It is possible that the
|
* used uri fields. It is initialized by parse_uri(). It is possible that the
|
||||||
@ -54,6 +55,9 @@ struct uri {
|
|||||||
unsigned int datalen:16;
|
unsigned int datalen:16;
|
||||||
unsigned int fragmentlen:16;
|
unsigned int fragmentlen:16;
|
||||||
|
|
||||||
|
/* Number of POSTED files */
|
||||||
|
unsigned int big_files:8;
|
||||||
|
|
||||||
/* Flags */
|
/* Flags */
|
||||||
unsigned int ipv6:1; /* URI contains IPv6 host */
|
unsigned int ipv6:1; /* URI contains IPv6 host */
|
||||||
unsigned int form:1; /* URI originated from form */
|
unsigned int form:1; /* URI originated from form */
|
||||||
|
@ -32,6 +32,7 @@ OBJS = \
|
|||||||
hash.o \
|
hash.o \
|
||||||
memlist.o \
|
memlist.o \
|
||||||
memory.o \
|
memory.o \
|
||||||
|
random.o \
|
||||||
secsave.o \
|
secsave.o \
|
||||||
snprintf.o \
|
snprintf.o \
|
||||||
string.o \
|
string.o \
|
||||||
|
114
src/util/random.c
Normal file
114
src/util/random.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/** Random numbers.
|
||||||
|
* @file */
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "elinks.h"
|
||||||
|
|
||||||
|
#include "util/random.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
seed_rand_once(void)
|
||||||
|
{
|
||||||
|
static int seeded = 0;
|
||||||
|
|
||||||
|
if (!seeded) {
|
||||||
|
srand(time(NULL));
|
||||||
|
seeded = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_SSL
|
||||||
|
|
||||||
|
static void
|
||||||
|
pseudorandom_nonce(unsigned char buf[], size_t size)
|
||||||
|
{
|
||||||
|
static int initialized = 0;
|
||||||
|
static int accept_bits;
|
||||||
|
static int accept_mask;
|
||||||
|
unsigned int got_mask;
|
||||||
|
unsigned int got_random;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
unsigned int shift;
|
||||||
|
|
||||||
|
seed_rand_once();
|
||||||
|
|
||||||
|
/* 32767 <= RAND_MAX <= INT_MAX. Find the largest
|
||||||
|
* accept_mask such that accept_mask <= RAND_MAX and
|
||||||
|
* accept_mask + 1 is a power of two. */
|
||||||
|
shift = RAND_MAX;
|
||||||
|
accept_bits = 0U;
|
||||||
|
accept_mask = 0U;
|
||||||
|
while (shift != 0U) {
|
||||||
|
shift >>= 1;
|
||||||
|
accept_bits++;
|
||||||
|
accept_mask = (accept_mask << 1) + 1U;
|
||||||
|
}
|
||||||
|
if (accept_mask > (unsigned int) RAND_MAX) {
|
||||||
|
accept_bits--;
|
||||||
|
accept_mask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
got_mask = got_random = 0U;
|
||||||
|
for (index = 0; index < size; ) {
|
||||||
|
if (got_mask >= UCHAR_MAX) {
|
||||||
|
buf[index++] = (unsigned char) got_random;
|
||||||
|
got_mask >>= CHAR_BIT;
|
||||||
|
got_random >>= CHAR_BIT;
|
||||||
|
} else {
|
||||||
|
unsigned int candidate;
|
||||||
|
|
||||||
|
do {
|
||||||
|
candidate = rand();
|
||||||
|
} while (candidate > accept_mask);
|
||||||
|
|
||||||
|
/* These shifts can discard some bits. */
|
||||||
|
got_mask = (got_mask << accept_bits) | accept_mask;
|
||||||
|
got_random = (got_random << accept_bits) | candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fill a buffer with random bytes. The bytes are not
|
||||||
|
* cryptographically random enough to be used in a key, but they
|
||||||
|
* should be good enough for a nonce or boundary string that may
|
||||||
|
* be sent in cleartext.
|
||||||
|
*
|
||||||
|
* If CONFIG_SSL is defined, then this function is instead defined in
|
||||||
|
* src/network/ssl/ssl.c, and it gets random numbers directly from the
|
||||||
|
* selected SSL library. */
|
||||||
|
void
|
||||||
|
random_nonce(unsigned char buf[], size_t size)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
FILE *f = fopen("/dev/urandom", "rb");
|
||||||
|
|
||||||
|
if (!f) f = fopen("/dev/prandom", "rb"); /* OpenBSD */
|
||||||
|
if (f) {
|
||||||
|
i = fread(data, 1, length, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the random device did not exist or could not provide
|
||||||
|
* enough data, then fill the buffer with rand(). The
|
||||||
|
* resulting numbers may be predictable but they provide
|
||||||
|
* ELinks with at least some way to generate boundary strings
|
||||||
|
* for multipart uploads. A more secure algorithm and entropy
|
||||||
|
* collection could be implemented, but there doesn't seem to
|
||||||
|
* be much point as SSL libraries already provide this
|
||||||
|
* facility. */
|
||||||
|
if (i < size)
|
||||||
|
pseudorandom_nonce(buf + i, size - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ndef CONFIG_SSL */
|
11
src/util/random.h
Normal file
11
src/util/random.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/** Random numbers.
|
||||||
|
* @file */
|
||||||
|
|
||||||
|
#ifndef EL__UTIL_RANDOM_H
|
||||||
|
#define EL__UTIL_RANDOM_H
|
||||||
|
|
||||||
|
void seed_rand_once(void);
|
||||||
|
|
||||||
|
void random_nonce(unsigned char buf[], size_t size);
|
||||||
|
|
||||||
|
#endif
|
@ -45,6 +45,7 @@
|
|||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/random.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "viewer/action.h"
|
#include "viewer/action.h"
|
||||||
#include "viewer/text/draw.h"
|
#include "viewer/text/draw.h"
|
||||||
@ -58,7 +59,19 @@
|
|||||||
/* TODO: Some of these (particulary those encoding routines) would feel better
|
/* TODO: Some of these (particulary those encoding routines) would feel better
|
||||||
* in viewer/common/. --pasky */
|
* in viewer/common/. --pasky */
|
||||||
|
|
||||||
|
struct files_offset {
|
||||||
|
LIST_HEAD(struct files_offset);
|
||||||
|
/* offset of the filename in the data generated by encode_multipart.
|
||||||
|
* data[begin] is the FILE_CHAR, data + begin + 1 is the filename. */
|
||||||
|
int begin;
|
||||||
|
/* end of filename. data[end] is the FILE_CHAR. In normal strings
|
||||||
|
* it would be a nul char. */
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** @relates submitted_value */
|
/** @relates submitted_value */
|
||||||
|
|
||||||
struct submitted_value *
|
struct submitted_value *
|
||||||
init_submitted_value(unsigned char *name, unsigned char *value, enum form_type type,
|
init_submitted_value(unsigned char *name, unsigned char *value, enum form_type type,
|
||||||
struct form_control *fc, int position)
|
struct form_control *fc, int position)
|
||||||
@ -817,12 +830,29 @@ struct boundary_info {
|
|||||||
unsigned char string[BOUNDARY_LENGTH];
|
unsigned char string[BOUNDARY_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @relates boundary_info */
|
||||||
|
static void
|
||||||
|
randomize_boundary(unsigned char *data, int length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
random_nonce(data, length);
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
/* Only [0-9A-Za-z]. */
|
||||||
|
data[i] = data[i] & 63;
|
||||||
|
if (data[i] < 10) data[i] += '0';
|
||||||
|
else if (data[i] < 36) data[i] = data[i] - 10 + 'A';
|
||||||
|
else if (data[i] < 62) data[i] = data[i] - 36 + 'a';
|
||||||
|
else data[i] = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @relates boundary_info */
|
/** @relates boundary_info */
|
||||||
static inline void
|
static inline void
|
||||||
init_boundary(struct boundary_info *boundary)
|
init_boundary(struct boundary_info *boundary)
|
||||||
{
|
{
|
||||||
memset(boundary, 0, sizeof(*boundary));
|
memset(boundary, 0, sizeof(*boundary));
|
||||||
memset(boundary->string, '0', BOUNDARY_LENGTH);
|
randomize_boundary(boundary->string, BOUNDARY_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add boundary to string and save the offset
|
/** Add boundary to string and save the offset
|
||||||
@ -838,76 +868,12 @@ add_boundary(struct string *data, struct boundary_info *boundary)
|
|||||||
add_bytes_to_string(data, boundary->string, BOUNDARY_LENGTH);
|
add_bytes_to_string(data, boundary->string, BOUNDARY_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @relates boundary_info */
|
|
||||||
static inline unsigned char *
|
|
||||||
increment_boundary_counter(struct boundary_info *boundary)
|
|
||||||
{
|
|
||||||
int j;
|
|
||||||
|
|
||||||
/* This is just a decimal string incrementation */
|
|
||||||
for (j = BOUNDARY_LENGTH - 1; j >= 0; j--) {
|
|
||||||
if (boundary->string[j]++ < '9')
|
|
||||||
return boundary->string;
|
|
||||||
|
|
||||||
boundary->string[j] = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL("Form data boundary counter overflow");
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @relates boundary_info */
|
|
||||||
static inline void
|
|
||||||
check_boundary(struct string *data, struct boundary_info *boundary)
|
|
||||||
{
|
|
||||||
unsigned char *bound = boundary->string;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Search between all boundaries. There is a starting and an ending
|
|
||||||
* boundary so only check the range of chars after the current offset
|
|
||||||
* and before the next offset. If some string in the form data matches
|
|
||||||
* the boundary string it is changed. */
|
|
||||||
for (i = 0; i < boundary->count - 1; i++) {
|
|
||||||
/* Start after the boundary string and also jump past the
|
|
||||||
* "\r\nContent-Disposition: form-data; name=\"" string added
|
|
||||||
* before any form data. */
|
|
||||||
int start_offset = boundary->offsets[i] + BOUNDARY_LENGTH + 40;
|
|
||||||
|
|
||||||
/* End so that there is atleast BOUNDARY_LENGTH chars to
|
|
||||||
* compare. Subtract 2 char because there is no need to also
|
|
||||||
* compare the '--' prefix that is part of the boundary. */
|
|
||||||
int end_offset = boundary->offsets[i + 1] - BOUNDARY_LENGTH - 2;
|
|
||||||
unsigned char *pos = data->source + start_offset;
|
|
||||||
unsigned char *end = data->source + end_offset;
|
|
||||||
|
|
||||||
for (; pos <= end; pos++) {
|
|
||||||
if (memcmp(pos, bound, BOUNDARY_LENGTH))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* If incrementing causes overflow bail out. There is
|
|
||||||
* no need to reset the boundary string with '0' since
|
|
||||||
* that is already done when incrementing. */
|
|
||||||
if (!increment_boundary_counter(boundary))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Else start checking all boundaries using the new
|
|
||||||
* boundary string */
|
|
||||||
i = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now update all the boundaries with the unique boundary string */
|
|
||||||
for (i = 0; i < boundary->count; i++)
|
|
||||||
memcpy(data->source + boundary->offsets[i], bound, BOUNDARY_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
|
/** @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
|
||||||
static void
|
static void
|
||||||
encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
||||||
struct string *data,
|
struct string *data, struct boundary_info *boundary,
|
||||||
struct boundary_info *boundary, int cp_from, int cp_to)
|
LIST_OF(struct files_offset) *bfs, int cp_from, int cp_to)
|
||||||
{
|
{
|
||||||
struct conv_table *convert_table = NULL;
|
struct conv_table *convert_table = NULL;
|
||||||
struct submitted_value *sv;
|
struct submitted_value *sv;
|
||||||
@ -936,9 +902,6 @@ encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
|||||||
add_char_to_string(data, '"');
|
add_char_to_string(data, '"');
|
||||||
|
|
||||||
if (sv->type == FC_FILE) {
|
if (sv->type == FC_FILE) {
|
||||||
#define F_BUFLEN 1024
|
|
||||||
int fh;
|
|
||||||
unsigned char buffer[F_BUFLEN];
|
|
||||||
unsigned char *extension;
|
unsigned char *extension;
|
||||||
|
|
||||||
add_to_string(data, "; filename=\"");
|
add_to_string(data, "; filename=\"");
|
||||||
@ -968,39 +931,59 @@ encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
|||||||
|
|
||||||
if (*sv->value) {
|
if (*sv->value) {
|
||||||
unsigned char *filename;
|
unsigned char *filename;
|
||||||
|
struct files_offset *bfs_new;
|
||||||
|
|
||||||
if (get_cmd_opt_bool("anonymous")) {
|
if (get_cmd_opt_bool("anonymous")) {
|
||||||
errno = EPERM;
|
errno = EPERM;
|
||||||
goto encode_error;
|
goto encode_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
|
|
||||||
filename = expand_tilde(sv->value);
|
filename = expand_tilde(sv->value);
|
||||||
if (!filename) goto encode_error;
|
if (!filename) goto encode_error;
|
||||||
|
|
||||||
fh = open(filename, O_RDONLY);
|
/* Do not allow FILE_CHAR in file
|
||||||
|
* names. It would make the resulting
|
||||||
|
* *data string ambiguous.
|
||||||
|
*
|
||||||
|
* Because FILE_CHAR is a control
|
||||||
|
* character, the user cannot directly
|
||||||
|
* type it in a file upload field.
|
||||||
|
* ELinks also does not let scripts
|
||||||
|
* modify such fields, for security
|
||||||
|
* reasons. It seems impossible to
|
||||||
|
* get FILE_CHAR here, so use assert.
|
||||||
|
*
|
||||||
|
* In uri.post, the first '\n' also
|
||||||
|
* has special meaning. However, '\n'
|
||||||
|
* in a file name does not cause any
|
||||||
|
* ambiguity, because get_form_uri()
|
||||||
|
* always adds a content-type and '\n'
|
||||||
|
* to the beginning of the encoded
|
||||||
|
* POST data. */
|
||||||
|
assert(strchr(filename, FILE_CHAR) == NULL);
|
||||||
|
if_assert_failed {
|
||||||
mem_free(filename);
|
mem_free(filename);
|
||||||
|
errno = EINVAL;
|
||||||
if (fh == -1) goto encode_error;
|
|
||||||
set_bin(fh);
|
|
||||||
while (1) {
|
|
||||||
ssize_t rd = safe_read(fh, buffer, F_BUFLEN);
|
|
||||||
|
|
||||||
if (rd) {
|
|
||||||
if (rd == -1) {
|
|
||||||
close(fh);
|
|
||||||
goto encode_error;
|
goto encode_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_bytes_to_string(data, buffer, rd);
|
if (access(filename, R_OK)) {
|
||||||
|
mem_free(filename);
|
||||||
} else {
|
goto encode_error;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
bfs_new = mem_calloc(1, sizeof(*bfs_new));
|
||||||
close(fh);
|
if (!bfs_new) {
|
||||||
|
mem_free(filename);
|
||||||
|
goto encode_error;
|
||||||
|
}
|
||||||
|
bfs_new->begin = data->length;
|
||||||
|
add_char_to_string(data, FILE_CHAR);
|
||||||
|
add_to_string(data, filename);
|
||||||
|
add_char_to_string(data, FILE_CHAR);
|
||||||
|
bfs_new->end = data->length;
|
||||||
|
add_to_list_end(*bfs, bfs_new);
|
||||||
|
mem_free(filename);
|
||||||
}
|
}
|
||||||
#undef F_BUFLEN
|
|
||||||
} else {
|
} else {
|
||||||
add_crlf_to_string(data);
|
add_crlf_to_string(data);
|
||||||
add_crlf_to_string(data);
|
add_crlf_to_string(data);
|
||||||
@ -1035,12 +1018,11 @@ encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
|||||||
add_boundary(data, boundary);
|
add_boundary(data, boundary);
|
||||||
add_to_string(data, "--\r\n");
|
add_to_string(data, "--\r\n");
|
||||||
|
|
||||||
check_boundary(data, boundary);
|
|
||||||
|
|
||||||
mem_free_if(boundary->offsets);
|
mem_free_if(boundary->offsets);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
encode_error:
|
encode_error:
|
||||||
|
free_list(*bfs);
|
||||||
mem_free_if(boundary->offsets);
|
mem_free_if(boundary->offsets);
|
||||||
done_string(data);
|
done_string(data);
|
||||||
|
|
||||||
@ -1151,6 +1133,7 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
|
|||||||
{
|
{
|
||||||
struct boundary_info boundary;
|
struct boundary_info boundary;
|
||||||
INIT_LIST_OF(struct submitted_value, submit);
|
INIT_LIST_OF(struct submitted_value, submit);
|
||||||
|
INIT_LIST_OF(struct files_offset, bfs);
|
||||||
struct string data;
|
struct string data;
|
||||||
struct string go;
|
struct string go;
|
||||||
int cp_from, cp_to;
|
int cp_from, cp_to;
|
||||||
@ -1184,7 +1167,8 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FORM_METHOD_POST_MP:
|
case FORM_METHOD_POST_MP:
|
||||||
encode_multipart(ses, &submit, &data, &boundary, cp_from, cp_to);
|
encode_multipart(ses, &submit, &data, &boundary,
|
||||||
|
&bfs, cp_from, cp_to);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORM_METHOD_POST_TEXT_PLAIN:
|
case FORM_METHOD_POST_TEXT_PLAIN:
|
||||||
@ -1236,7 +1220,6 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
|
|||||||
{
|
{
|
||||||
/* Note that we end content type here by a simple '\n',
|
/* Note that we end content type here by a simple '\n',
|
||||||
* replaced later by correct '\r\n' in http_send_header(). */
|
* replaced later by correct '\r\n' in http_send_header(). */
|
||||||
int i;
|
|
||||||
|
|
||||||
add_to_string(&go, form->action);
|
add_to_string(&go, form->action);
|
||||||
add_char_to_string(&go, POST_CHAR);
|
add_char_to_string(&go, POST_CHAR);
|
||||||
@ -1256,12 +1239,36 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
|
|||||||
add_char_to_string(&go, '\n');
|
add_char_to_string(&go, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list_empty(bfs)) {
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < data.length; i++) {
|
for (i = 0; i < data.length; i++) {
|
||||||
unsigned char p[3];
|
unsigned char p[3];
|
||||||
|
|
||||||
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
||||||
add_to_string(&go, p);
|
add_to_string(&go, p);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct files_offset *b;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (b, bfs) {
|
||||||
|
for (; i < b->begin; i++) {
|
||||||
|
unsigned char p[3];
|
||||||
|
|
||||||
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
||||||
|
add_to_string(&go, p);
|
||||||
|
}
|
||||||
|
add_bytes_to_string(&go, data.source + i, b->end - b->begin);
|
||||||
|
i = b->end;
|
||||||
|
}
|
||||||
|
for (; i < data.length; i++) {
|
||||||
|
unsigned char p[3];
|
||||||
|
|
||||||
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
||||||
|
add_to_string(&go, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1269,7 +1276,10 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
|
|||||||
|
|
||||||
uri = get_uri(go.source, 0);
|
uri = get_uri(go.source, 0);
|
||||||
done_string(&go);
|
done_string(&go);
|
||||||
if (uri) uri->form = 1;
|
if (uri) {
|
||||||
|
uri->form = 1;
|
||||||
|
}
|
||||||
|
free_list(bfs);
|
||||||
|
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user