Example #1
0
File: fsp.c Project: Efreak/elinks
static void
fsp_directory(FSP_SESSION *ses, struct uri *uri)
{
	struct string buf;
	FSP_DIR *dir;
	unsigned char *data = get_uri_string(uri, URI_DATA);
	unsigned char dircolor[8] = "";

	if (!data)
		fsp_error(connection_state(S_OUT_OF_MEM));
	decode_uri(data);
	if (!is_in_state(init_directory_listing(&buf, uri), S_OK))
		fsp_error(connection_state(S_OUT_OF_MEM));

	dir = fsp_opendir(ses, data);
	if (!dir) fsp_error(connection_state_for_errno(errno));

	fprintf(stderr, "text/html");
	fclose(stderr);

	puts(buf.source);

	if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
		color_to_string(get_opt_color("document.colors.dirs", NULL),
				dircolor);
	}

	sort_and_display_entries(dir, dircolor);
	fsp_closedir(dir);
	puts("</pre><hr/></body></html>");
	fsp_close_session(ses);
	exit(0);
}
Example #2
0
int
decode_contact (struct sip_msg *msg,char *unused1,char *unused2)
{

	str uri;
	str newUri;
	char separator;
	int res;

	uri.s = 0;
	uri.len = 0;

#ifdef DEBUG
	fprintf (stdout,"---START--------DECODE CONTACT-----------------\n");
        fprintf (stdout,"%.*s\n",50,msg->buf);
	fprintf (stdout, "INITIAL.s=[%.*s]\n", uri.len, uri.s);
#endif

	separator = DEFAULT_SEPARATOR[0];
	if (contact_flds_separator != NULL)
		if (strlen(contact_flds_separator)>=1)
			separator = contact_flds_separator[0];
		
	if ((msg->new_uri.s == NULL) || (msg->new_uri.len == 0))
	{
		uri = msg->first_line.u.request.uri;
		if (uri.s == NULL) 
			return -1;
	}
	else
	{
		uri = msg->new_uri;
	}
	
	res = decode_uri (uri, separator, &newUri);
	
#ifdef DEBUG
	if (res == 0) 
		fprintf (stdout, "newuri.s=[%.*s]\n", newUri.len, newUri.s);
#endif
	if (res != 0)
	{
		LM_ERR("failed decoding contact.Code %d\n", res);
#ifdef STRICT_CHECK
		return res;
#endif
	}
	else
	{
		/* we do not modify the original first line */
		if ((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) 
			msg->new_uri = newUri;
		else
		{
			pkg_free(msg->new_uri.s);
			msg->new_uri = newUri;
		}		
	}
	return 1;
}
Example #3
0
void
data_protocol_handler(struct connection *conn)
{
	struct uri *uri = conn->uri;
	struct cache_entry *cached = get_cache_entry(uri);
	unsigned char *data_start, *data;
	int base64 = 0;

	if (!cached) {
		abort_connection(conn, connection_state(S_OUT_OF_MEM));
		return;
	}

	conn->cached = cached;

	data_start = parse_data_protocol_header(conn, &base64);
	if (!data_start) {
		abort_connection(conn, connection_state(S_OUT_OF_MEM));
		return;
	}

	/* Allocate the data string because URI decoding will possibly modify
	 * it. */
	data = memacpy(data_start, uri->datalen - (data_start - uri->data));
	if (!data) {
		abort_connection(conn, connection_state(S_OUT_OF_MEM));
		return;
	}

	if (base64) {
		unsigned char *decoded = base64_encode(data);

		if (!decoded) {
			abort_connection(conn, connection_state(S_OUT_OF_MEM));
			return;
		}

		mem_free_set(&data, decoded);
	} else {
		decode_uri(data);
	}

	{
		/* Use strlen() to get the correct decoded length */
		int datalen = strlen((const char *)data);

		add_fragment(cached, conn->from, data, datalen);
		conn->from += datalen;
	}

	mem_free(data);

	abort_connection(conn, connection_state(S_OK));
}
Example #4
0
static unsigned char *
get_file_download_text(struct listbox_item *item, struct terminal *term)
{
	struct file_download *file_download = item->udata;
	unsigned char *uristring;

	uristring = get_uri_string(file_download->uri, URI_PUBLIC);
	if (uristring) {
#ifdef CONFIG_UTF8
		if (term->utf8_cp)
			decode_uri(uristring);
		else
#endif /* CONFIG_UTF8 */
			decode_uri_for_display(uristring);
	}

	return uristring;
}
Example #5
0
int
decode_contact (struct sip_msg *msg,char *unused1,char *unused2)
{

	str* uri;
	str newUri;
	str dst_uri;
	char separator;
	int res;
	

	separator = DEFAULT_SEPARATOR[0];
	if (contact_flds_separator != NULL)
		if (strlen(contact_flds_separator)>=1)
			separator = contact_flds_separator[0];
		
	if ((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) {
		uri = &msg->first_line.u.request.uri;
	}else{
		uri = &msg->new_uri;
	}
	
	res = decode_uri (uri, separator, &newUri, &dst_uri);
	
	if (res != 0) {
			LOG (L_ERR,"ERROR: decode_contact:Failed decoding contact."
						"Code %d\n", res);
			return res;
	} else {
		/* we do not modify the original first line */
		if (msg->new_uri.s)
			pkg_free(msg->new_uri.s);
		msg->new_uri = newUri;
		msg->parsed_uri_ok=0;
		msg->dst_uri = dst_uri;
		ruri_mark_new();
	}
	return 1;
}
Example #6
0
int
decode_contact_header (struct sip_msg *msg,char *unused1,char *unused2)
{

	contact_body_t *cb;
	contact_t *c;
	str uri;
	str newUri;
	char separator;
	int res;
	
	
#ifdef DEBUG
	str* ruri;
	fprintf (stdout,"---START--------DECODE CONTACT HEADER-----------------\n");
#endif

	if ((msg->contact == NULL)&&((parse_headers(msg,HDR_CONTACT_F,0) == -1) ||
				(msg->contact== NULL) ))
		{
		LM_ERR("no Contact header present\n");
		return -1;
		}

	separator = DEFAULT_SEPARATOR[0];
	if (contact_flds_separator != NULL)
		if (strlen(contact_flds_separator)>=1)
			separator = contact_flds_separator[0];

#ifdef DEBUG
	fprintf (stdout,"Using separator %c\n",separator);
	ruri = GET_RURI(msg);
	fprintf (stdout,"[len = %d]New uri is->%.*s\n",
			ruri->len,ruri->len,ruri->s);
	ruri = &msg->first_line.u.request.uri;
	fprintf (stdout, "INITIAL.s=[%.*s]\n", ruri->len, ruri->s);
#endif
		
	if (msg->contact->parsed == NULL) parse_contact (msg->contact);
	if (msg->contact->parsed != NULL)
	{
		cb = (contact_body_t *) msg->contact->parsed;
		c = cb->contacts;
		// we visit each contact 
	 if (c != NULL)
	  {
		uri = c->uri;

		res = decode_uri (uri, separator, &newUri);
#ifdef DEBUG
		fprintf (stdout, "newuri.s=[%.*s]\n", newUri.len, newUri.s);
#endif
		if (res != 0)
		{
			LM_ERR("failed decoding contact.Code %d\n", res);
#ifdef STRICT_CHECK
				return res;
#endif
		}
		else
		if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0)
		{
			LM_ERR("lumping failed in mangling port \n");
			return -2;
		}

#ifdef DECODE_ALL_CONTACTS
		while (c->next != NULL)
		{
			c = c->next;
			uri = c->uri;

			res = decode_uri (uri, separator, &newUri);
			if (res != 0)
				{
				LM_ERR("failed decoding contact.Code %d\n",res);
#ifdef STRICT_CHECK
				return res;
#endif
				}
			else
			if (patch (msg, uri.s, uri.len, newUri.s, newUri.len) < 0)
			{
				LM_ERR("lumping failed in mangling port \n");
				return -3;
			}
		} // end while 
#endif
	   } // if c!= NULL 
	} // end if 
	else // after parsing still NULL 
		{
			LM_ERR("unable to parse Contact header\n");
			return -4;
		}
#ifdef DEBUG
	fprintf (stdout,"---END--------DECODE CONTACT HEADER-----------------\n");fflush(stdout);
#endif
	return 1;
}
Example #7
0
int
decode_contact_header (struct sip_msg *msg,char *unused1,char *unused2)
{

	contact_body_t *cb;
	contact_t *c;
	str* uri;
	str newUri;
	char separator;
	int res;
	
	

	if ((msg->contact == NULL)&&((parse_headers(msg,HDR_CONTACT_F,0) == -1) || 
				(msg->contact== NULL) ))
		{
		LOG(L_ERR,"ERROR: decode_contact_header: no Contact header present\n");
		return -1;
		}

	separator = DEFAULT_SEPARATOR[0];
	if (contact_flds_separator != NULL)
		if (strlen(contact_flds_separator)>=1)
			separator = contact_flds_separator[0];
		
	if (msg->contact->parsed == NULL) parse_contact (msg->contact);
	if (msg->contact->parsed != NULL)
	{
		cb = (contact_body_t *) msg->contact->parsed;
		c = cb->contacts;
		// we visit each contact 
	 if (c != NULL)
	  {
		uri = &c->uri;

		res = decode_uri (uri, separator, &newUri, 0);
		if (res != 0)
		{
			LOG (L_ERR,"ERROR: decode_contact_header:Failed decoding contact.Code %d\n", res);
#ifdef STRICT_CHECK
				return res;
#endif
		}
		else
		if (patch (msg, uri->s, uri->len, newUri.s, newUri.len) < 0)
		{
			LOG (L_ERR,"ERROR: decode_contact:lumping failed in mangling port \n");
			return -2;
		}

#ifdef DECODE_ALL_CONTACTS
		while (c->next != NULL)
		{
			c = c->next;
			uri = &c->uri;

			res = decode_uri (uri, separator, &newUri, 0);
			if (res != 0)
				{
				LOG (L_ERR,"ERROR: decode_contact: Failed decoding contact.Code %d\n",res);
#ifdef STRICT_CHECK
				return res;
#endif
				}
			else
			if (patch (msg, uri->s, uri->len, newUri.s, newUri.len) < 0)
			{
				LOG (L_ERR,"ERROR: decode_contact:lumping failed in mangling port \n");
				return -3;
			}
		} // end while 
#endif
	   } // if c!= NULL 
	} // end if 
	else // after parsing still NULL 
		{
			LOG(L_ERR,"ERROR: decode_contact: Unable to parse Contact header\n");
			return -4;
		}
	return 1;
}
Example #8
0
File: cmd.c Project: 9612jhf/webdis
cmd_response_t
cmd_run(struct worker *w, struct http_client *client,
		const char *uri, size_t uri_len,
		const char *body, size_t body_len) {

	char *qmark = memchr(uri, '?', uri_len);
	char *slash;
	const char *p, *cmd_name = uri;
	int cmd_len;
	int param_count = 0, cur_param = 1;

	struct cmd *cmd;
	formatting_fun f_format;

	/* count arguments */
	if(qmark) {
		uri_len = qmark - uri;
	}
	for(p = uri; p && p < uri + uri_len; param_count++) {
		p = memchr(p+1, '/', uri_len - (p+1-uri));
	}

	if(body && body_len) { /* PUT request */
		param_count++;
	}
	if(param_count == 0) {
		return CMD_PARAM_ERROR;
	}

	cmd = cmd_new(param_count);
	cmd->fd = client->fd;
	cmd->database = w->s->cfg->database;

	/* get output formatting function */
	uri_len = cmd_select_format(client, cmd, uri, uri_len, &f_format);

	/* add HTTP info */
	cmd_setup(cmd, client);

	/* check if we only have one command or more. */
	slash = memchr(uri, '/', uri_len);
	if(slash) {

		/* detect DB number by checking if first arg is only numbers */
		int has_db = 1;
		int db_num = 0;
		for(p = uri; p < slash; ++p) {
			if(*p < '0' || *p > '9') {
				has_db = 0;
				break;
			}
			db_num = db_num * 10 + (*p - '0');
		}

		/* shift to next arg if a db was set up */
		if(has_db) {
			char *next;
			cmd->database = db_num;
			cmd->count--; /* overcounted earlier */
			cmd_name = slash + 1;

			if((next = memchr(cmd_name, '/', uri_len - (slash - uri)))) {
				cmd_len = next - cmd_name;
			} else {
				cmd_len = uri_len - (slash - uri + 1);
			}
		} else {
			cmd_len = slash - uri;
		}
	} else {
		cmd_len = uri_len;
	}

	/* there is always a first parameter, it's the command name */
	cmd->argv[0] = malloc(cmd_len);
	memcpy(cmd->argv[0], cmd_name, cmd_len);
	cmd->argv_len[0] = cmd_len;

	/* check that the client is able to run this command */
	if(!acl_allow_command(cmd, w->s->cfg, client)) {
		cmd_free(cmd);
		return CMD_ACL_FAIL;
	}

	if(cmd_is_subscribe(cmd)) {
		/* create a new connection to Redis */
		cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0);

		/* register with the client, used upon disconnection */
		client->pub_sub = cmd;
		cmd->pub_sub_client = client;
	} else if(cmd->database != w->s->cfg->database) {
		/* create a new connection to Redis for custom DBs */
		cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0);
	} else {
		/* get a connection from the pool */
		cmd->ac = (redisAsyncContext*)pool_get_context(w->pool);
	}

	/* no args (e.g. INFO command) */
	if(!slash) {
		if(!cmd->ac) {
			cmd_free(cmd);
			return CMD_REDIS_UNAVAIL;
		}
		redisAsyncCommandArgv(cmd->ac, f_format, cmd, 1,
				(const char **)cmd->argv, cmd->argv_len);
		return CMD_SENT;
	}
	p = cmd_name + cmd_len + 1;
	while(p < uri + uri_len) {

		const char *arg = p;
		int arg_len;
		char *next = memchr(arg, '/', uri_len - (arg-uri));
		if(!next || next > uri + uri_len) { /* last argument */
			p = uri + uri_len;
			arg_len = p - arg;
		} else { /* found a slash */
			arg_len = next - arg;
			p = next + 1;
		}

		/* record argument */
		cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1);
		cur_param++;
	}

	if(body && body_len) { /* PUT request */
		cmd->argv[cur_param] = malloc(body_len);
		memcpy(cmd->argv[cur_param], body, body_len);
		cmd->argv_len[cur_param] = body_len;
	}

	/* send it off! */
	if(cmd->ac) {
		cmd_send(cmd, f_format);
		return CMD_SENT;
	}
	/* failed to find a suitable connection to Redis. */
	cmd_free(cmd);
	client->pub_sub = NULL;
	return CMD_REDIS_UNAVAIL;
}
Example #9
0
static void
download_dialog_layouter(struct dialog_data *dlg_data)
{
	struct file_download *file_download = dlg_data->dlg->udata;
	struct terminal *term = dlg_data->win->term;
	int w = dialog_max_width(term);
	int rw = w;
	int x, y = 0;
	int url_len;
	unsigned char *url;
	struct download *download = &file_download->download;
	struct color_pair *dialog_text_color = get_bfu_color(term, "dialog.text");
	unsigned char *msg = get_download_msg(download, term, 1, 1, "\n");
	int show_meter = (download_is_progressing(download)
			  && download->progress->size >= 0);
#if CONFIG_BITTORRENT
	int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT
			  && (show_meter || is_in_state(download->state, S_RESUME)));
#endif

	redraw_windows(REDRAW_BEHIND_WINDOW, dlg_data->win);
	file_download->dlg_data = dlg_data;

	if (!msg) return;

	url = get_uri_string(file_download->uri, URI_PUBLIC);
	if (!url) {
		mem_free(msg);
		return;
	}
#ifdef CONFIG_UTF8
	if (term->utf8_cp)
		decode_uri(url);
	else
#endif /* CONFIG_UTF8 */
		decode_uri_for_display(url);
	url_len = strlen(url);

	if (show_meter) {
		int_lower_bound(&w, DOWN_DLG_MIN);
	}

	dlg_format_text_do(dlg_data, url, 0, &y, w, &rw,
			dialog_text_color, ALIGN_LEFT, 1);

	y++;
	if (show_meter) y += 2;

#if CONFIG_BITTORRENT
	if (bittorrent) y += 2;
#endif
	dlg_format_text_do(dlg_data, msg, 0, &y, w, &rw,
			dialog_text_color, ALIGN_LEFT, 1);

	y++;
	dlg_format_buttons(dlg_data, dlg_data->widgets_data,
			   dlg_data->number_of_widgets, 0, &y, w,
			   &rw, ALIGN_CENTER, 1);

	draw_dialog(dlg_data, w, y);

	w = rw;
	if (url_len > w) {
		/* Truncate too long urls */
		url_len = w;
		url[url_len] = '\0';
		if (url_len > 4) {
			url[--url_len] = '.';
			url[--url_len] = '.';
			url[--url_len] = '.';
		}
	}

	y = dlg_data->box.y + DIALOG_TB + 1;
	x = dlg_data->box.x + DIALOG_LB;
	dlg_format_text_do(dlg_data, url, x, &y, w, NULL,
			dialog_text_color, ALIGN_LEFT, 0);

	if (show_meter) {
		y++;
		draw_progress_bar(download->progress, term, x, y, w, NULL, NULL);
		y++;
	}

#if CONFIG_BITTORRENT
	if (bittorrent) {
		y++;
		draw_bittorrent_piece_progress(download, term, x, y, w, NULL, NULL);
		y++;
	}
#endif
	y++;
	dlg_format_text_do(dlg_data, msg, x, &y, w, NULL,
			dialog_text_color, ALIGN_LEFT, 0);

	y++;
	dlg_format_buttons(dlg_data, dlg_data->widgets_data,
			   dlg_data->number_of_widgets, x, &y, w,
			   NULL, ALIGN_CENTER, 0);

	mem_free(url);
	mem_free(msg);
}
Example #10
0
/** 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, const unsigned char *post_data,
	       struct connection_state *error)
{
	off_t size = 0;
	size_t length = strlen((const char *)post_data);
	const unsigned char *end = post_data;

	done_http_post(http_post);
	http_post->post_data = end;

	while (1) {
		struct stat sb;
		const unsigned char *begin;
		int res;
		struct http_post_file *new_files;
		unsigned char *filename;

		begin = (const unsigned char *)strchr((char *)end, FILE_CHAR);
		if (!begin) break;
		end = (const unsigned char *)strchr((char *)(begin + 1), FILE_CHAR);
		if (!end) break;
		filename = memacpy(begin + 1, end - begin - 1); /* adds '\0' */
		if (!filename) {
			done_http_post(http_post);
			*error = connection_state(S_OUT_OF_MEM);
			return 0;
		}
		decode_uri(filename);
		res = stat((const char *)filename, &sb);
		if (res) {
			*error = connection_state_for_errno(errno);
			done_http_post(http_post);
			return 0;
		}

		/* 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 = (struct http_post_file *)mem_realloc(http_post->files,
					(http_post->file_count + 1)
					* sizeof(*new_files));
		if (new_files == NULL) {
			mem_free(filename);
			done_http_post(http_post);
			*error = connection_state(S_OUT_OF_MEM);
			return 0;
		}
		http_post->files = new_files;
		new_files[http_post->file_count].name = filename;
		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;
}
Example #11
0
File: cmd.c Project: orls/webdis
int
cmd_run(struct server *s, struct evhttp_request *rq,
		const char *uri, size_t uri_len, const char *body, size_t body_len) {

	char *qmark = strchr(uri, '?');
	char *slash;
	const char *p;
	int cmd_len;
	int param_count = 0, cur_param = 1, i;

	struct cmd *cmd;

	formatting_fun f_format;

	/* count arguments */
	if(qmark) {
		uri_len = qmark - uri;
	}
	for(p = uri; p && p < uri + uri_len; param_count++) {
		p = strchr(p+1, '/');
	}

	if(body && body_len) { /* PUT request */
		param_count++;
	}

	cmd = cmd_new(rq, param_count);

	/* parse URI parameters */
	evhttp_parse_query(uri, &cmd->uri_params);

	/* get output formatting function */
	uri_len = cmd_select_format(cmd, uri, uri_len, &f_format);

	/* check if we only have one command or more. */
	slash = memchr(uri, '/', uri_len);
	if(slash) {
		cmd_len = slash - uri;
	} else {
		cmd_len = uri_len;
	}

	/* there is always a first parameter, it's the command name */
	cmd->argv[0] = uri;
	cmd->argv_len[0] = cmd_len;


	/* check that the client is able to run this command */
	if(!acl_allow_command(cmd, s->cfg, rq)) {
		return -1;
	}

	/* check if we have to split the connection */
	if(cmd_is_subscribe(cmd)) {
		struct pubsub_client *ps;

		ps = calloc(1, sizeof(struct pubsub_client));
		ps->s = s = server_copy(s);
		ps->cmd = cmd;
		ps->rq = rq;
		evhttp_connection_set_closecb(rq->evcon, on_http_disconnect, ps);
	}

	/* no args (e.g. INFO command) */
	if(!slash) {
		redisAsyncCommandArgv(s->ac, f_format, cmd, 1, cmd->argv, cmd->argv_len);
		return 0;
	}
	p = slash + 1;
	while(p < uri + uri_len) {

		const char *arg = p;
		int arg_len;
		char *next = strchr(arg, '/');
		if(!next || next > uri + uri_len) { /* last argument */
			p = uri + uri_len;
			arg_len = p - arg;
		} else { /* found a slash */
			arg_len = next - arg;
			p = next + 1;
		}

		/* record argument */
		cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1);
		cur_param++;
	}

	if(body && body_len) { /* PUT request */
		cmd->argv[cur_param] = body;
		cmd->argv_len[cur_param] = body_len;
	}

	/* push command to Redis. */
	redisAsyncCommandArgv(s->ac, f_format, cmd, cmd->count, cmd->argv, cmd->argv_len);

	for(i = 1; i < cur_param; ++i) {
		free((char*)cmd->argv[i]);
	}

	return 0;
}
Example #12
0
File: fsp.c Project: Efreak/elinks
static void
do_fsp(struct connection *conn)
{
	FSP_SESSION *ses;
	struct stat sb;
	struct uri *uri = conn->uri;
	struct auth_entry *auth;
	unsigned char *host = get_uri_string(uri, URI_HOST);
	unsigned char *data = get_uri_string(uri, URI_DATA);
	unsigned short port = (unsigned short)get_uri_port(uri);
	unsigned char *password = NULL;

	decode_uri(data);
	if (uri->passwordlen) {
		password = get_uri_string(uri, URI_PASSWORD);
	} else {
		auth = find_auth(uri);
		if (auth) password = auth->password;
	}

	/* fsp_open_session may not set errno if getaddrinfo fails
	 * https://sourceforge.net/tracker/index.php?func=detail&aid=2036798&group_id=93841&atid=605738
	 * Try to detect this bug and use an ELinks-specific error
	 * code instead, so that we can display a message anyway.  */
	errno = 0;
	ses = fsp_open_session(host, port, password);
	if (!ses) {
		if (errno)
			fsp_error(connection_state_for_errno(errno));
		else
			fsp_error(connection_state(S_FSP_OPEN_SESSION_UNKN));
	}

	/* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS
	 * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738
	 * If ELinks and fsplib are using different values of
	 * _FILE_OFFSET_BITS, then they get different definitions of
	 * struct stat, and the st_size stored by fsp_stat is
	 * typically not the same as the st_size read by ELinks.
	 * Fortunately, st_mode seems to have the same offset and size
	 * in both versions of struct stat.
	 *
	 * If all the bytes used by the 32-bit st_size are also used
	 * by the 64-bit st_size, then ELinks may be able to guess
	 * which ones they are, because the current version 2 of FSP
	 * supports only 32-bit file sizes in protocol packets.  Begin
	 * by filling struct stat with 0xAA so that it's easier to
	 * detect which bytes fsp_stat has left unchanged.  (Only
	 * sb.st_size really needs to be filled, but filling the rest
	 * too helps viewing the data with a debugger.)  */
	memset(&sb, 0xAA, sizeof(sb));
	if (fsp_stat(ses, data, &sb)) fsp_error(connection_state_for_errno(errno));

	if (S_ISDIR(sb.st_mode)) {
		fsp_directory(ses, uri);
	} else { /* regular file */
		char buf[READ_SIZE];
		FSP_FILE *file = fsp_fopen(ses, data, "r");
		int r;

		if (!file) {
			fsp_error(connection_state_for_errno(errno));
		}

#if SIZEOF_OFF_T >= 8
		if (sb.st_size < 0 || sb.st_size > 0xFFFFFFFF) {
			/* Probably a _FILE_OFFSET_BITS mismatch as
			 * described above.  Try to detect which half
			 * of st_size is the real size.  This may
			 * depend on the endianness of the processor
			 * and on the padding in struct stat.  */
			if ((sb.st_size & 0xFFFFFFFF00000000ULL) == 0xAAAAAAAA00000000ULL)
				sb.st_size = sb.st_size & 0xFFFFFFFF;
			else if ((sb.st_size & 0xFFFFFFFF) == 0xAAAAAAAA)
				sb.st_size = (sb.st_size >> 32) & 0xFFFFFFFF;
			else	/* Can't figure it out. */
				sb.st_size = 1;
		}
#endif

		/* Send filesize */
		fprintf(stderr, "%" OFF_PRINT_FORMAT "\n",
			(off_print_T) sb.st_size);
		fclose(stderr);

		while ((r = fsp_fread(buf, 1, READ_SIZE, file)) > 0) {
			int off = 0;

			while (r) {
				int w = safe_write(STDOUT_FILENO, buf + off, r);

				if (w == -1) goto out;
				off += w;
				r -= w;
			}
		}
out:
		fsp_fclose(file);
		fsp_close_session(ses);
		exit(0);
	}