Beispiel #1
0
enum uri_errno
parse_uri(struct uri *uri, unsigned char *uristring)
{
	unsigned char *prefix_end, *host_end;
#ifdef CONFIG_IPV6
	unsigned char *lbracket, *rbracket;
#endif

	assertm(uristring != NULL, "No uri to parse.");
	memset(uri, 0, sizeof(*uri));

	/* Nothing to do for an empty url. */
	if_assert_failed return 0;
	if (!*uristring) return URI_ERRNO_EMPTY;

	uri->string = uristring;
	uri->protocollen = get_protocol_length(uristring);

	/* Invalid */
	if (!uri->protocollen) return URI_ERRNO_INVALID_PROTOCOL;

	/* Figure out whether the protocol is known */
	uri->protocol = get_protocol(struri(uri), uri->protocollen);

	prefix_end = uristring + uri->protocollen; /* ':' */

	/* Check if there's a digit after the protocol name. */
	if (isdigit(*prefix_end)) {
		uri->ip_family = uristring[uri->protocollen] - '0';
		prefix_end++;
	}
	if (*prefix_end != ':')
		return URI_ERRNO_INVALID_PROTOCOL;
	prefix_end++;

	/* Skip slashes */

	if (prefix_end[0] == '/' && prefix_end[1] == '/') {
		if (prefix_end[2] == '/'
		    && get_protocol_need_slash_after_host(uri->protocol))
			return URI_ERRNO_TOO_MANY_SLASHES;

		prefix_end += 2;

	} else if (get_protocol_need_slashes(uri->protocol)) {
		return URI_ERRNO_NO_SLASHES;
	}

	if (get_protocol_free_syntax(uri->protocol)) {
		uri->data = prefix_end;
		uri->datalen = strlen(prefix_end);
		return URI_ERRNO_OK;

	} else if (uri->protocol == PROTOCOL_FILE) {
		int datalen = strcspn(prefix_end, "#" POST_CHAR_S);
		unsigned char *frag_or_post = prefix_end + datalen;

		/* Extract the fragment part. */
		if (datalen >= 0) {
			if (*frag_or_post == '#') {
				uri->fragment = frag_or_post + 1;
				uri->fragmentlen = strcspn(uri->fragment, POST_CHAR_S);
				frag_or_post = uri->fragment + uri->fragmentlen;
			}
			if (*frag_or_post == POST_CHAR) {
				uri->post = frag_or_post + 1;
			}
		} else {
			datalen = strlen(prefix_end);
		}

		/* A bit of a special case, but using the "normal" host
		 * parsing seems a bit scary at this point. (see bug 107). */
		if (datalen > 9 && !c_strncasecmp(prefix_end, "localhost/", 10)) {
			prefix_end += 9;
			datalen -= 9;
		}

		uri->data = prefix_end;
		uri->datalen = datalen;

		return URI_ERRNO_OK;
	}

	/* Isolate host */

#ifdef CONFIG_IPV6
	/* Get brackets enclosing IPv6 address */
	lbracket = strchr((const char *)prefix_end, '[');
	if (lbracket) {
		rbracket = strchr((const char *)lbracket, ']');
		/* [address] is handled only inside of hostname part (surprisingly). */
		if (rbracket && rbracket < prefix_end + strcspn(prefix_end, "/"))
			uri->ipv6 = 1;
		else
			lbracket = rbracket = NULL;
	} else {
		rbracket = NULL;
	}
#endif

	/* Possibly skip auth part */
	host_end = prefix_end + strcspn(prefix_end, "@");

	if (prefix_end + strcspn(prefix_end, "/") > host_end
	    && *host_end) { /* we have auth info here */
		unsigned char *user_end;

		/* Allow '@' in the password component */
		while (strcspn(host_end + 1, "@") < strcspn(host_end + 1, "/?"))
			host_end = host_end + 1 + strcspn(host_end + 1, "@");

		user_end = strchr((const char *)prefix_end, ':');

		if (!user_end || user_end > host_end) {
			uri->user = prefix_end;
			uri->userlen = host_end - prefix_end;
		} else {
			uri->user = prefix_end;
			uri->userlen = user_end - prefix_end;
			uri->password = user_end + 1;
			uri->passwordlen = host_end - user_end - 1;
		}
		prefix_end = host_end + 1;
	}

#ifdef CONFIG_IPV6
	if (uri->ipv6)
		host_end = rbracket + strcspn(rbracket, ":/?");
	else
#endif
		host_end = prefix_end + strcspn(prefix_end, ":/?");

#ifdef CONFIG_IPV6
	if (uri->ipv6) {
		int addrlen = rbracket - lbracket - 1;

		/* Check for valid length.
		 * addrlen >= sizeof(hostbuf) is theorically impossible
		 * but i keep the test in case of... Safer, imho --Zas */
		assertm(addrlen >= 0 && addrlen < NI_MAXHOST,
			"parse_uri(): addrlen value is bad (%d) for URL '%s'. "
			"Problems are likely to be encountered. Please report "
			"this, it is a security bug!", addrlen, uristring);
		if_assert_failed return URI_ERRNO_IPV6_SECURITY;

		uri->host = lbracket + 1;
		uri->hostlen = addrlen;
	} else
Beispiel #2
0
static void S_ADECL(check_handle)(void *P)
{
//     ARRAY_LOG("checking %p",P);
    if(P && !HashPtr_Find(&S_ADECL(ABARRAY_tracker),P))
        assertm(0,"unknown handle %p passed to Array",P);
}
Beispiel #3
0
void
draw_textarea(struct terminal *term, struct form_state *fs,
	      struct document_view *doc_view, struct link *link)
{
	struct line_info *line, *linex;
	struct form_control *fc;
	struct box *box;
	int vx, vy;
	int sl, ye;
	int x, y;

	assert(term && doc_view && doc_view->document && doc_view->vs && link);
	if_assert_failed return;

#ifdef CONFIG_UTF8
	if (term->utf8_cp) {
		draw_textarea_utf8(term, fs, doc_view, link);
		return;
	}
#endif /* CONFIG_UTF8 */
	fc = get_link_form_control(link);
	assertm(fc != NULL, "link %d has no form control", (int) (link - doc_view->document->links));
	if_assert_failed return;

	box = &doc_view->box;
	vx = doc_view->vs->x;
	vy = doc_view->vs->y;

	if (!link->npoints) return;
#ifdef CONFIG_UTF8
	area_cursor(fc, fs, 0);
#else
	area_cursor(fc, fs);
#endif /* CONFIG_UTF8 */
	linex = format_text(fs->value, fc->cols, fc->wrap, 0);
	if (!linex) return;
	line = linex;
	sl = fs->vypos;
	while (line->start != -1 && sl) sl--, line++;

	x = link->points[0].x + box->x - vx;
	y = link->points[0].y + box->y - vy;
	ye = y + fc->rows;

	for (; line->start != -1 && y < ye; line++, y++) {
		int i;

		if (!row_is_in_box(box, y)) continue;

		for (i = 0; i < fc->cols; i++) {
			unsigned char data;
			int xi = x + i;

			if (!col_is_in_box(box, xi))
				continue;

			if (i >= -fs->vpos
			    && i + fs->vpos < line->end - line->start)
				data = fs->value[line->start + i + fs->vpos];
			else
				data = '_';

			draw_char_data(term, xi, y, data);
		}
	}

	for (; y < ye; y++) {
		int i;

		if (!row_is_in_box(box, y)) continue;

		for (i = 0; i < fc->cols; i++) {
			int xi = x + i;

			if (col_is_in_box(box, xi))
				draw_char_data(term, xi, y, '_');
		}
	}

	mem_free(linex);
}
Beispiel #4
0
Datei: task.c Projekt: ezc/elinks
void
ses_goto(struct session *ses, struct uri *uri, unsigned char *target_frame,
	 struct location *target_location, enum cache_mode cache_mode,
	 enum task_type task_type, int redir)
{
	/* [gettext_accelerator_context(ses_goto)] */
	struct task *task;
	int referrer_incomplete = 0;
	int malicious_uri = 0;
	int confirm_submit = uri->form && get_opt_bool("document.browse.forms"
	                                               ".confirm_submit", ses);
	unsigned char *m1 = NULL, *message = NULL;
	struct memory_list *mlist = NULL;

	if (ses->doc_view
	    && ses->doc_view->document
	    && ses->doc_view->document->refresh) {
		kill_document_refresh(ses->doc_view->document->refresh);
	}

	assertm(!ses->loading_uri, "Buggy URI reference counting");

	/* Reset the redirect counter if this is not a redirect. */
	if (!redir) {
		ses->redirect_cnt = 0;
	}

	/* Figure out whether to confirm submit or not */

	/* Only confirm submit if we are posting form data or a misleading URI
	 * was detected. */
	/* Note uri->post might be empty here but we are still supposely
	 * posting form data so this should be more correct. */

	if (uri->user && uri->userlen
	    && get_opt_bool("document.browse.links.warn_malicious", ses)
	    && check_malicious_uri(uri)) {
		malicious_uri = 1;
		confirm_submit = 1;

	} else if (uri->form) {
		/* First check if the referring URI was incomplete. It
		 * indicates that the posted form data might be incomplete too.
		 * See bug 460. */
		if (ses->referrer) {
			struct cache_entry *cached;

			cached = find_in_cache(ses->referrer);
			referrer_incomplete = (cached && cached->incomplete);
		}

		if (referrer_incomplete) {
			confirm_submit = 1;

		} else if (get_validated_cache_entry(uri, cache_mode)) {
			confirm_submit = 0;
		}
	}

	if (!confirm_submit) {
		ses_load(ses, get_uri_reference(uri), target_frame,
		         target_location, cache_mode, task_type);
		return;
	}

	task = mem_alloc(sizeof(*task));
	if (!task) return;

	task->ses = ses;
	task->uri = get_uri_reference(uri);
	task->cache_mode = cache_mode;
	task->session_task.type = task_type;
	task->session_task.target.frame = null_or_stracpy(target_frame);
	task->session_task.target.location = target_location;

	if (malicious_uri) {
		unsigned char *host = memacpy(uri->host, uri->hostlen);
		unsigned char *user = memacpy(uri->user, uri->userlen);
		unsigned char *uristring = get_uri_string(uri, URI_PUBLIC);

		message = msg_text(ses->tab->term,
			N_("The URL you are about to follow might be maliciously "
			"crafted in order to confuse you. By following the URL "
			"you will be connecting to host \"%s\" as user \"%s\".\n\n"
			"Do you want to go to URL %s?"), host, user, uristring);

		mem_free_if(host);
		mem_free_if(user);
		mem_free_if(uristring);

	} else if (redir) {
		m1 = N_("Do you want to follow the redirect and post form data "
			"to URL %s?");

	} else if (referrer_incomplete) {
		m1 = N_("The form data you are about to post might be incomplete.\n"
			"Do you want to post to URL %s?");

	} else if (task_type == TASK_FORWARD) {
		m1 = N_("Do you want to post form data to URL %s?");

	} else {
		m1 = N_("Do you want to repost form data to URL %s?");
	}

	if (!message && m1) {
		unsigned char *uristring = get_uri_string(uri, URI_PUBLIC);

		message = msg_text(ses->tab->term, m1, uristring);
		mem_free_if(uristring);
	}

	add_to_ml(&mlist, task, (void *) NULL);
	if (task->session_task.target.frame)
		add_to_ml(&mlist, task->session_task.target.frame, (void *) NULL);
	msg_box(ses->tab->term, mlist, MSGBOX_FREE_TEXT,
		N_("Warning"), ALIGN_CENTER,
		message,
		task, 2,
		MSG_BOX_BUTTON(N_("~Yes"), post_yes, B_ENTER),
		MSG_BOX_BUTTON(N_("~No"), post_no, B_ESC));
}
Beispiel #5
0
static void
draw_textarea_utf8(struct terminal *term, struct form_state *fs,
	      struct document_view *doc_view, struct link *link)
{
	struct line_info *line, *linex;
	struct form_control *fc;
	struct box *box;
	int vx, vy;
	int sl, ye;
	int x, xbase, y;

	assert(term && doc_view && doc_view->document && doc_view->vs && link);
	if_assert_failed return;
	fc = get_link_form_control(link);
	assertm(fc != NULL, "link %d has no form control", (int) (link - doc_view->document->links));
	if_assert_failed return;

	box = &doc_view->box;
	vx = doc_view->vs->x;
	vy = doc_view->vs->y;

	if (!link->npoints) return;
	area_cursor(fc, fs, 1);
	linex = format_textutf8(fs->value, fc->cols, fc->wrap, 0);
	if (!linex) return;
	line = linex;
	sl = fs->vypos;
	while (line->start != -1 && sl) sl--, line++;

	xbase = link->points[0].x + box->x - vx;
	y = link->points[0].y + box->y - vy;
	ye = y + fc->rows;

	for (; line->start != -1 && y < ye; line++, y++) {
		int i;
		unsigned char *text, *end;

		text = fs->value + line->start;
		end = fs->value + line->end;

		text += utf8_cells2bytes(text, fs->vpos, end);

		if (!row_is_in_box(box, y)) continue;

		for (i = 0, x = xbase; i < fc->cols; i++, x++) {
			unicode_val_T data;

			if (i >= -fs->vpos && text < end) {
				/* utf8_to_unicode will increment text. */
				data = utf8_to_unicode(&text, end);
			} else
				data = '_';

			if (col_is_in_box(box, x)) {
				int cell = unicode_to_cell(data);

				if (cell == 2) {
					draw_char_data(term, x++, y, data);
					i++;
					data = UCS_NO_CHAR;
				}

				draw_char_data(term, x, y, data);
			}
		}
	}

	for (; y < ye; y++) {
		int i;

		if (!row_is_in_box(box, y)) continue;

		for (i = 0, x = xbase; i < fc->cols; i++, x++) {
			if (col_is_in_box(box, x))
				draw_char_data(term, x, y, '_');
		}
	}

	mem_free(linex);
}
Beispiel #6
0
static void
read_select(struct socket *socket)
{
	struct read_buffer *rb = socket->read_buffer;
	ssize_t rd;

	assertm(rb != NULL, "read socket has no buffer");
	if_assert_failed {
		socket->ops->done(socket, connection_state(S_INTERNAL));
		return;
	}

	/* We are making some progress, therefore reset the timeout; we do this
	 * for read_select() to avoid that the periodic calls to user handlers
	 * has to do it. */
	socket->ops->set_timeout(socket, connection_state(0));

	if (!socket->duplex)
		clear_handlers(socket->fd);

	if (!rb->freespace) {
		int size = RD_SIZE(rb, rb->length);

		rb = mem_realloc(rb, size);
		if (!rb) {
			socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
			return;
		}
		rb->freespace = size - sizeof(*rb) - rb->length;
		assert(rb->freespace > 0);
		socket->read_buffer = rb;
	}

#ifdef CONFIG_SSL
	if (socket->ssl) {
		rd = ssl_read(socket, rb->data + rb->length, rb->freespace);
	} else
#endif
	{
		rd = generic_read(socket, rb->data + rb->length, rb->freespace);
	}

	switch (rd) {
#ifdef CONFIG_SSL
	case SOCKET_SSL_WANT_READ:
		read_from_socket(socket, rb, connection_state(S_TRANS), rb->done);
		break;
#endif
	case SOCKET_CANT_READ:
		if (socket->state != SOCKET_RETRY_ONCLOSE) {
			socket->state = SOCKET_CLOSED;
			rb->done(socket, rb);
			break;
		}

		socket->ops->retry(socket, connection_state(S_CANT_READ));
		break;

	case SOCKET_SYSCALL_ERROR:
		socket->ops->retry(socket, connection_state_for_errno(errno));
		break;

	case SOCKET_INTERNAL_ERROR:
		/* The global errno variable is used for passing
		 * internal connection_state error value. */
		socket->ops->done(socket, connection_state(errno));
		break;

	default:
		debug_transfer_log(rb->data + rb->length, rd);

		rb->length += rd;
		rb->freespace -= rd;
		assert(rb->freespace >= 0);

		rb->done(socket, rb);
	}
}
Beispiel #7
0
static void
write_select(struct socket *socket)
{
	struct write_buffer *wb = socket->write_buffer;
	int wr;

	assertm(wb != NULL, "write socket has no buffer");
	if_assert_failed {
		socket->ops->done(socket, connection_state(S_INTERNAL));
		return;
	}

	/* We are making some progress, therefore reset the timeout; ie.  when
	 * uploading large files the time needed for all the data to be sent can
	 * easily exceed the timeout. */
	socket->ops->set_timeout(socket, connection_state(0));

#if 0
	printf("ws: %d\n",wb->length-wb->pos);
	for (wr = wb->pos; wr < wb->length; wr++) printf("%c", wb->data[wr]);
	printf("-\n");
#endif

#ifdef CONFIG_SSL
	if (socket->ssl) {
		wr = ssl_write(socket, wb->data + wb->pos, wb->length - wb->pos);
	} else
#endif
	{
		assert(wb->length - wb->pos > 0);
		wr = generic_write(socket, wb->data + wb->pos, wb->length - wb->pos);
	}

	switch (wr) {
	case SOCKET_CANT_WRITE:
		socket->ops->retry(socket, connection_state(S_CANT_WRITE));
		break;

	case SOCKET_SYSCALL_ERROR:
		socket->ops->retry(socket, connection_state_for_errno(errno));
		break;

	case SOCKET_INTERNAL_ERROR:
		/* The global errno variable is used for passing
		 * internal connection_state error value. */
		socket->ops->done(socket, connection_state(errno));
		break;

	default:
		if (wr < 0) break;

		/*printf("wr: %d\n", wr);*/
		wb->pos += wr;

		if (wb->pos == wb->length) {
			socket_write_T done = wb->done;

			if (!socket->duplex) {
				clear_handlers(socket->fd);

			} else {
				select_handler_T read_handler;
				select_handler_T error_handler;

				read_handler  = get_handler(socket->fd, SELECT_HANDLER_READ);
				error_handler = read_handler
					      ? (select_handler_T) exception
					      : NULL;

				set_handlers(socket->fd, read_handler, NULL,
					     error_handler, socket);
			}

			mem_free_set(&socket->write_buffer, NULL);
			done(socket);
		}
	}
}
Beispiel #8
0
/** Parse one event from itrm_in.queue and append to itrm_out.queue.
 * @pre On entry, @a *itrm must not be blocked.
 * @returns the number of bytes removed from itrm->in.queue; at least 0.
 * @post If this function leaves the queue not full, it also reenables
 * reading from itrm->in.std.  (Because it does not add to the queue,
 * it never need disable reading.)  */
static int
process_queue(struct itrm *itrm)
{
	struct interlink_event ev;
	int el = 0;

	if (!itrm->in.queue.len) goto return_without_event;
	assert(!itrm->blocked);
	if_assert_failed return 0; /* unlike goto, don't enable reading */

	set_kbd_interlink_event(&ev, KBD_UNDEF, KBD_MOD_NONE);

#ifdef DEBUG_ITRM_QUEUE
	{
		int i;

		/* Dump current queue in a readable form to stderr. */
		for (i = 0; i < itrm->in.queue.len; i++)
			if (itrm->in.queue.data[i] == ASCII_ESC)
				fprintf(stderr, "ESC ");
			else if (isprint(itrm->in.queue.data[i]) && !isspace(itrm->in.queue.data[i]))
				fprintf(stderr, "%c ", itrm->in.queue.data[i]);
			else
				fprintf(stderr, "0x%02x ", itrm->in.queue.data[i]);

		fprintf(stderr, "\n");
		fflush(stderr);
	}
#endif /* DEBUG_ITRM_QUEUE */

	/* el == -1 means itrm->in.queue appears to be the beginning of an
	 *          escape sequence but it is not yet complete.  Set a timer;
	 *          if it times out, then assume it wasn't an escape sequence
	 *          after all.
	 * el == 0 means this function has not yet figured out what the data
	 *         in itrm->in.queue is, but some possibilities remain.
	 *         One of them will be chosen before returning.
	 * el > 0 means some bytes were successfully parsed from the beginning
	 *        of itrm->in.queue and should now be removed from there.
	 *        However, this does not always imply an event will be queued.
	 */

	/* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER
	 * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as
	 * meaning the same as ESC 0x4F, but those cannot yet be implemented
	 * because of bug 777: the UTF-8 decoder is run too late.  */
	if (itrm->in.queue.data[0] == ASCII_ESC) {
		if (itrm->in.queue.len < 2) {
			el = -1;
		} else if (itrm->in.queue.data[1] == 0x5B /* CSI */) {
			el = decode_terminal_escape_sequence(itrm, &ev);
		} else if (itrm->in.queue.data[1] == 0x4F /* SS3 */) {
			el = decode_terminal_application_key(itrm, &ev);
		} else if (itrm->in.queue.data[1] == ASCII_ESC) {
			/* ESC ESC can be either Alt-Esc or the
			 * beginning of e.g. ESC ESC 0x5B 0x41,
			 * which we should parse as Esc Up.  */
			if (itrm->in.queue.len < 3) {
				/* Need more data to figure it out.  */
				el = -1;
			} else if (itrm->in.queue.data[2] == 0x5B
				   || itrm->in.queue.data[2] == 0x4F) {
				/* The first ESC appears to be followed
				 * by an escape sequence.  Treat it as
				 * a standalone Esc.  */
				el = 1;
				set_kbd_event(itrm, &ev,
					      itrm->in.queue.data[0],
					      KBD_MOD_NONE);
			} else {
				/* The second ESC of ESC ESC is not the
				 * beginning of any known escape sequence.
				 * This must be Alt-Esc, then.  */
				el = 2;
				set_kbd_event(itrm, &ev,
					      itrm->in.queue.data[1],
					      KBD_MOD_ALT);
			}
		}
		if (el == 0) {	/* Begins with ESC, but none of the above */
			el = 2;
			set_kbd_event(itrm, &ev, itrm->in.queue.data[1],
				      KBD_MOD_ALT);
		}

	} else if (itrm->in.queue.data[0] == 0) {
		static const struct term_event_keyboard os2xtd[256] = {
#include "terminal/key.inc"
		};

		if (itrm->in.queue.len < 2)
			el = -1;
		else {
			el = 2;
			set_kbd_interlink_event(&ev,
						os2xtd[itrm->in.queue.data[1]].key,
						os2xtd[itrm->in.queue.data[1]].modifier);
		}
	}

	if (el == 0) {
		el = 1;
		set_kbd_event(itrm, &ev, itrm->in.queue.data[0],
			      itrm->bracketed_pasting ? KBD_MOD_PASTE : KBD_MOD_NONE);
	}

	/* The call to decode_terminal_escape_sequence() might have changed the
	 * keyboard event to a mouse event. */
	if (ev.ev == EVENT_MOUSE || ev.info.keyboard.key != KBD_UNDEF) {
		itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
		itrm->bracketed_pasting = 
			(ev.ev == EVENT_KBD
			 && (ev.info.keyboard.modifier & KBD_MOD_PASTE));
	}

return_without_event:
	if (el == -1) {
		install_timer(&itrm->timer, ESC_TIMEOUT, (void (*)(void *)) kbd_timeout,
			      itrm);
		return 0;
	} else {
		assertm(itrm->in.queue.len >= el, "event queue underflow");
		if_assert_failed { itrm->in.queue.len = el; }

		itrm->in.queue.len -= el;
		if (itrm->in.queue.len)
			memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);

		if (itrm->in.queue.len < ITRM_IN_QUEUE_SIZE)
			handle_itrm_stdin(itrm);

		return el;
	}
}
Beispiel #9
0
//----------------------------------------
//  receive helper.
// this really could use a dose of win32 
// optimizations. I should figure out how
// to schedule and defer receives...
// @todo -AB: get this optimized for async reads :09/24/08
//----------------------------------------
BOOL sock_recv(SOCKET sock, char **hrecv_frame, BOOL *closed)
{
    int total_read = 0;    
    if(closed)
        *closed = FALSE;
    if(sock == INVALID_SOCKET || !hrecv_frame)
        return FALSE;
    // apparently there is no way to know how much data is available to read...
    // I find this very hard to believe
#if 0
    {
        long cur = ftell(sock);
        long n_avil;
        int cur_size = achr_size(hrecv_frame);
        int n_read;
        
        assertm(0==fseek(sock,0,SEEK_END),"failed to seek.");
        n_avil = ftell(sock);
        assertm(0==fseek(sock,cur,SEEK_CUR));
        achr_setsize(hrecv_frame,cur_size+n_avil);
        n_read = recv(c->sock,(*hrecv_frame)+cur_size,n_avil,0);
        if(n_read<0)
        {
            if(last_error() != EWOULDBLOCK) 
                return FALSE;
        }
        else if(n_read == 0)                // closed on other end
        {
            if(closed)
                *closed = TRUE;
        }
        return TRUE;
    }
#endif
    for(;;)
    {
        int n_read;
        int cur_size = achr_size(hrecv_frame); 
        achr_setsize(hrecv_frame,cur_size+1024);
        n_read = recv(sock,(*hrecv_frame)+cur_size,1024,0);
        if(n_read<0)
        {
            if(last_error() != EWOULDBLOCK) 
                return FALSE;
            else
            {
                achr_setsize(hrecv_frame,cur_size);
                break;                      // out of data to read.
            }
        }
        else if(n_read == 0)                // closed on other end
        {
            if(closed)
                *closed = TRUE;
            achr_setsize(hrecv_frame,cur_size);
            break;
        }        
        achr_setsize(hrecv_frame,cur_size+n_read);
        total_read += n_read;
    }
    return TRUE;
}