Beispiel #1
0
void tn_func(struct session *ses, unsigned char *url, struct list_head *prog, unsigned char *t1, unsigned char *t2)
{
	unsigned char *m;
	unsigned char *h, *p;
	int hl, pl;
	unsigned char *hh, *pp;
	int f = 1;
	if (parse_url(url, NULL, NULL, NULL, NULL, NULL, &h, &hl, &p, &pl, NULL, NULL, NULL) || !hl) goto fail;
	if (!(hh = memacpy(h, hl))) goto fail;
	if (pl && !(pp = memacpy(p, pl))) goto fail1;
	check_shell_security(&hh);
	if (pl) check_shell_security(&pp);
	m = mem_alloc(strlen(hh) + (pl ? strlen(pp) : 0) + 2);
	f = 0;
	strcpy(m, hh);
	if (pl) {
		strcat(m, " ");
		strcat(m, pp);
		m[hl + 1 + pl] = 0;
	}
	prog_func(ses->term, prog, m, t1);
	mem_free(m);
	if (pl) mem_free(pp);
	fail1:
	mem_free(hh);
	fail:
	if (f) msg_box(ses->term, NULL, TEXT(T_BAD_URL_SYNTAX), AL_CENTER, t2, NULL, 1, TEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
}
Beispiel #2
0
unsigned char *get_host_name(unsigned char *url)
{
	unsigned char *h;
	int hl;
	if (parse_url(url, NULL, NULL, NULL, NULL, NULL, &h, &hl, NULL, NULL, NULL, NULL, NULL)) return stracpy(cast_uchar "");
	return memacpy(h, hl);
}
Beispiel #3
0
unsigned char *get_pass(unsigned char *url)
{
	unsigned char *h;
	int hl;
	if (parse_url(url, NULL,NULL,  NULL, &h, &hl, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) return NULL;
	return memacpy(h, hl);
}
Beispiel #4
0
/* Inspired by Vim this is used to hook into the stdout write method so written
 * data is displayed in a nice message box. */
static VALUE
erb_module_message(VALUE self, VALUE str)
{
	unsigned char *message, *line_end;
	struct terminal *term;

	str = rb_obj_as_string(str);
	message = memacpy(RSTRING(str)->ptr, RSTRING(str)->len);
	if (!message) return Qnil;

	line_end = strchr(message, '\n');
	if (line_end) *line_end = '\0';

	term = get_default_terminal();
	if (!term) {
		usrerror("[Ruby] %s", message);
		mem_free(message);
		return Qnil;
	}

	info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
		 N_("Ruby Message"), ALIGN_LEFT, message);

	return Qnil;
}
Beispiel #5
0
/* Get image filename from its src attribute. */
static unsigned char *
get_image_filename_from_src(int max_len, unsigned char *src)
{
	unsigned char *text = NULL;
	unsigned char *start, *filename;
	int len;

	if (!src) return NULL;
	/* We can display image as [foo.gif]. */

	len = strcspn((const char *)src, "?");

	for (start = src + len; start > src; start--)
		if (dir_sep(start[-1])) {
			break;
		}

	len -= start - src;

	filename = memacpy(start, len);
	if (filename) {
		/* XXX: Due to a compatibility alias (added: 2004-12-15 in
		 * 0.10pre3.CVS for document.browse.images.file_tags) this can
		 * return a negative @max_len. */
		text = truncate_label(filename, max_len);
		mem_free(filename);
	}

	return text;
}
Beispiel #6
0
unsigned char *get_port_str(unsigned char *url)
{
	unsigned char *h;
	int hl;
	if (parse_url(url, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &h, &hl, NULL, NULL, NULL)) return NULL;
	return hl ? memacpy(h, hl) : NULL;
}
Beispiel #7
0
static unsigned char *
get_fragment_content_type(struct cache_entry *cached)
{
	struct fragment *fragment;
	size_t length;
	unsigned char *sample;
	unsigned char *ctype = NULL;

	if (list_empty(cached->frag))
		return NULL;

	fragment = cached->frag.next;
	if (fragment->offset)
		return NULL;

	length = fragment->length > 1024 ? 1024 : fragment->length;
	sample = memacpy(fragment->data, length);
	if (!sample)
		return NULL;

	if (c_strcasestr((const char *)sample, "<html>"))
		ctype = stracpy("text/html");

	mem_free(sample);

	return ctype;
}
Beispiel #8
0
unsigned char *get_next_path_filename( unsigned char **path_ptr, unsigned char separator )
{
  int eax;
  int ecx;
  unsigned char *path;
  unsigned char *filename;
  int filenamelen;
  path[0] = path_ptr[0];
{
  unsigned char *tmp;
  filenamelen = 0;
  if ( path[0] & 255 )
  {
    if ( path[0] != separator )
    {
      do
      {
        path[0] = path[1];
        if ( path[0] == 0 )
          filenamelen = path[0] - path[0];
        else
        {
          path_ptr[0] = path_ptr[0];
        }
      }
      while ( path[0] != separator );
      filenamelen = path[0] - path[0];
    }
    path_ptr[0] = &path[1];
    if ( filenamelen >= 1 )
    {
      if ( memacpy( &path[0], filenamelen ) )
      {
        mem_free( (void*)memacpy( &path[0], filenamelen ) );
        return expand_tilde( memacpy( &path[0], filenamelen ) );
      }
      filename[0] = 0;
    }
  }
  path_ptr[0] = &path[0];
  if ( filenamelen >= 1 )
  {
  }
  path_ptr[0] = &path[1];
}
}
Beispiel #9
0
unsigned char *
stracpy(const unsigned char *src)
{
	assertm(src, "[stracpy]");
	if_assert_failed return NULL;

	return memacpy(src, strlen((const char *)src));
}
Beispiel #10
0
unsigned char *get_host_and_pass(unsigned char *url)
{
	unsigned char *u, *h, *p, *z, *k;
	int hl, pl;
	if (parse_url(url, NULL, &u, NULL, NULL, NULL, &h, &hl, &p, &pl, NULL, NULL, NULL)) return NULL;
	z = u ? u : h;
	k = p ? p + pl : h + hl;
	return memacpy(z, k - z);
}
Beispiel #11
0
void parse_mimetypes_file( unsigned char *filename )
{
  int edx;
  FILE *file = fopen64( &filename[0], "rb" );
  unsigned char line[1024];
  if ( file )
  {
    do
    {
      if ( fgets( line, 1023, file ) )
      {
        unsigned char *ctype;
        unsigned char *token;
        if ( strchr( line, 35 ) )
          *(char*)(strchr( line, 35 )) = 0;
        ctype[0] = line[0];
        if ( ( ctype[0] & 255 ) && token[0] )
        {
          token[0] = 0;
          if ( strchr( &ctype[0], 47 ) )
          {
            token[0] = token[1];
            while ( token[0] == 0 || token[0] == 0 )
            {
              token[0] = 0;
              if ( !get_hash_item( mimetypes_map, &token[0], strlen( &token[0] ) ) && get_hash_item( mimetypes_map, &token[0], strlen( &token[0] ) ) )
              {
                *ebp_1084 = memacpy( &ctype[0], strlen( &ctype[0] ) );
                if ( ebp_1084 )
                {
                  memcpy( &token[0], &edi, edx + 4 );
                  if ( eax == 0 )
                    goto B34;
                  else
                  {
                    token[0] = token[0] + 1;
                  }
                }
B34:                done_mimetypes_entry( &edx );
              }
            }
          }
        }
      }
      else
        fclose( file );
    }
    while ( token[0] );
  }
  if ( 0 ^ 0 )
  {
    __stack_chk_fail(  );
  }
  return;
}
Beispiel #12
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));
}
Beispiel #13
0
unsigned char *get_keepalive_id(unsigned char *url)
{
	unsigned char *h, *p, *k, *d;
	int hl, pl;
	if (parse_url(url, NULL, NULL, NULL, NULL, NULL, &h, &hl, &p, &pl, &d, NULL, NULL)) return NULL;
	if (!casecmp(url, cast_uchar "proxy://", 8) && !casecmp(d, cast_uchar "https://", 8)) {
		if (parse_url(d, NULL, NULL, NULL, NULL, NULL, &h, &hl, &p, &pl, NULL, NULL, NULL)) return NULL;
	}
	k = p ? p + pl : h + hl;
	if (!k) return stracpy(cast_uchar "");
	return memacpy(url, k - url);
}
Beispiel #14
0
static enum evhook_status
script_hook_goto_url(va_list ap, void *data)
{
	unsigned char **url = va_arg(ap, unsigned char **);
	struct session *ses = va_arg(ap, struct session *);
	int error;
	VALUE args[2];
	VALUE result;

	if (*url == NULL)
		return EVENT_HOOK_STATUS_NEXT;

	args[0] = rb_str_new(*url, strlen((const char *)*url));

	if (!ses || !have_location(ses)) {
		args[1] = Qnil;
	} else {
		args[1] = rb_str_new(struri(cur_loc(ses)->vs.uri), strlen((const char *)struri(cur_loc(ses)->vs.uri)));
	}

	result = erb_protected_method_call("goto_url_hook", 2, args, &error);
	if (error) {
		erb_report_error(ses, error);
		return EVENT_HOOK_STATUS_NEXT;
	}

	switch (rb_type(result)) {
	case T_STRING:
	{
		unsigned char *new_url;

		new_url = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
		if (new_url) {
			mem_free_set(url, new_url);
		}
		break;
	}
	case T_NIL:
		break;

	default:
		alert_ruby_error(ses, "goto_url_hook must return a string or nil");
	}

	return EVENT_HOOK_STATUS_NEXT;
}
Beispiel #15
0
static enum evhook_status
script_hook_follow_url(va_list ap, void *data)
{
	unsigned char **url = va_arg(ap, unsigned char **);
	struct session *ses = va_arg(ap, struct session *);
	int error;
	VALUE args[1];
	VALUE result;

	evhook_use_params(url && ses);

	if (*url == NULL)
		return EVENT_HOOK_STATUS_NEXT;

	args[0] = rb_str_new(*url, strlen((const char *)*url));

	result = erb_protected_method_call("follow_url_hook", 1, args, &error);
	if (error) {
		erb_report_error(ses, error);
		return EVENT_HOOK_STATUS_NEXT;
	}

	switch (rb_type(result)) {
	case T_STRING:
	{
		unsigned char *new_url;

		new_url = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
		if (new_url) {
			mem_free_set(url, new_url);
		}
		break;
	}
	case T_NIL:
		break;

	default:
		alert_ruby_error(ses, "follow_url_hook must return a string or nil");
	}

	return EVENT_HOOK_STATUS_NEXT;
}
Beispiel #16
0
Datei: task.c Projekt: ezc/elinks
/** Check if the URI is obfuscated (bug 382). The problem is said to occur when
 * a URI designed to pass access a specific location with a supplied username,
 * contains misleading chars prior to the @ symbol.
 *
 * An attacker can exploit this issue by supplying a malicious URI pointing to
 * a page designed to mimic that of a trusted site, and tricking a victim who
 * follows a link into believing they are actually at the trusted location.
 *
 * Only the user ID (and not also the password) is checked because only the
 * user ID is displayed in the status bar. */
static int
check_malicious_uri(struct uri *uri)
{
	unsigned char *user, *pos;
	int warn = 0;

	assert(uri->user && uri->userlen);

	user = pos = memacpy(uri->user, uri->userlen);
	if (!user) return 0;

	decode_uri_for_display(user);

	while (*pos) {
		int length, trailing_dots;

		for (length = 0; pos[length] != '\0'; length++)
			if (!(isalnum(pos[length]) || pos[length] == '.'))
				break;

		/* Wind back so that the TLD part is checked correctly. */
		for (trailing_dots = 0; trailing_dots < length; trailing_dots++)
			if (!length || pos[length - trailing_dots - 1] != '.')
				break;

		/* Not perfect, but I am clueless as how to do better. Besides
		 * I don't really think it is an issue for ELinks. --jonas */
		if (end_with_known_tld(pos, length - trailing_dots) != -1) {
			warn = 1;
			break;
		}

		pos += length;

		while (*pos && (!isalnum(*pos) || *pos == '.'))
			pos++;
	}

	mem_free(user);

	return warn;
}
Beispiel #17
0
static unsigned char *
init_data_protocol_header(struct cache_entry *cached,
			  unsigned char *type, int typelen)
{
	unsigned char *head;

	assert(typelen);

	type = memacpy(type, typelen);
	if (!type) return NULL;

	/* Set fake content type */
	head = straconcat((const unsigned char *)"\r\nContent-Type: ", type, "\r\n",
			  (unsigned char *) NULL);
	mem_free(type);
	if (!head) return NULL;

	mem_free_set(&cached->head, head);
	return head;
}
Beispiel #18
0
/* The Ruby function can return:
 *  - "PROXY:PORT" to use the specified proxy
 *  - ""           to not use any proxy
 *  - nil          to use the default proxies */
static enum evhook_status
script_hook_get_proxy(va_list ap, void *data)
{
	unsigned char **new_proxy_url = va_arg(ap, unsigned char **);
	unsigned char *url = va_arg(ap, unsigned char *);
	int error;
	VALUE args[1];
	VALUE result;

	if (!new_proxy_url || !url)
		return EVENT_HOOK_STATUS_NEXT;

	args[0] = rb_str_new(url, strlen((const char *)url));

	result = erb_protected_method_call("proxy_hook", 1, args, &error);
	if (error) {
		erb_report_error(NULL, error);
		return EVENT_HOOK_STATUS_NEXT;
	}

	switch (rb_type(result)) {
	case T_STRING:
	{
		unsigned char *proxy;

		proxy = memacpy(RSTRING(result)->ptr, RSTRING(result)->len);
		if (proxy) {
			mem_free_set(new_proxy_url, proxy);
		}
		break;
	}
	case T_NIL:
		break;

	default:
		alert_ruby_error(NULL, "proxy_hook must return a string or nil");
	}

	return EVENT_HOOK_STATUS_NEXT;
}
Beispiel #19
0
unsigned char *parse_header_param(unsigned char *x, unsigned char *e)
{
    unsigned char u;
    size_t le = strlen(e);
    int lp;
    unsigned char *y = x;
a:
    if (!(y = strchr(y, ';'))) return NULL;
    while (*y && (*y == ';' || *y <= ' ')) y++;
    if (strlen(y) < le) return NULL;
    if (casecmp(y, e, le)) goto a;
    y += le;
    while (*y && (*y <= ' ' || *y == '=')) y++;
    u = ';';
    if (*y == '\'' || *y == '"') u = *y++;
    lp = 0;
    while (y[lp] >= ' ' && y[lp] != u) {
        lp++;
        if (lp == MAXINT) overalloc();
    }
    return memacpy(y, lp);
}
Beispiel #20
0
/* Searches a word to find an email adress or an URI to add as a link. */
static inline struct link *
check_link_word(struct document *document, unsigned char *uri, int length,
		int x, int y)
{
	struct uri test;
	unsigned char *where = NULL;
	unsigned char *mailto = memchr(uri, '@', length);
	int keep = uri[length];
	struct link *new_link;

	assert(document);
	if_assert_failed return NULL;

	uri[length] = 0;

	if (mailto && mailto > uri && mailto - uri < length - 1) {
		where = straconcat("mailto:", uri, (unsigned char *) NULL);

	} else if (parse_uri(&test, uri) == URI_ERRNO_OK
		   && test.protocol != PROTOCOL_UNKNOWN
		   && (test.datalen || test.hostlen)) {
		where = memacpy(uri, length);
	}

	uri[length] = keep;

	if (!where) return NULL;

	/* We need to reparse the URI and normalize it so that the protocol and
	 * host part are converted to lowercase. */
	normalize_uri(NULL, where);

	new_link = add_document_link(document, where, length, x, y);

	if (!new_link) mem_free(where);

	return new_link;
}
Beispiel #21
0
/* sezere 1 cookie z retezce str, na zacatku nesmi byt zadne whitechars
 * na konci muze byt strednik nebo 0
 * cookie musi byt ve tvaru nazev=hodnota, kolem rovnase nesmi byt zadne mezery
 * (respektive mezery se budou pocitat do nazvu a do hodnoty)
 */
int set_cookie(struct terminal *term, unsigned char *url, unsigned char *str)
{
	int noval = 0;
	struct cookie *cookie;
	struct c_server *cs;
	unsigned char *p, *q, *s, *server, *date;
	d_opt = &dd_opt;
    int accept_cookies = dds.allow_cookies;
	if (accept_cookies == ACCEPT_NONE) {
		return 0;
	}
	for (p = str; *p != ';' && *p; p++) { /*if (WHITECHAR(*p)) return 0;*/ }
	for (q = str; *q != '='; q++) if (!*q || q >= p) {
		noval = 1;
		break;
	}
	if (str == q || q + 1 == p) return 0;
	cookie = mem_alloc(sizeof(struct cookie));
	server = get_host_name(url);
	cookie->name = memacpy(str, q - str);
	cookie->value = !noval ? memacpy(q + 1, p - q - 1) : NULL;
	cookie->server = stracpy(server);
	date = parse_header_param(str, cast_uchar "expires", 0);
	if (date) {
		cookie->expires = parse_http_date(date);
		/* kdo tohle napsal a proc ?? */
		/*if (! cookie->expires) cookie->expires++;*/ /* no harm and we can use zero then */
		mem_free(date);
	} else
		cookie->expires = 0;
	if (!(cookie->path = parse_header_param(str, cast_uchar "path", 0))) {
		/*unsigned char *w;*/
		cookie->path = stracpy(cast_uchar "/");
		/*
		add_to_strn(&cookie->path, document);
		for (w = cookie->path; *w; w++) if (end_of_dir(cookie->path, *w)) {
			*w = 0;
			break;
		}
		for (w = cookie->path + strlen(cast_const_char cookie->path) - 1; w >= cookie->path; w--)
			if (*w == '/') {
				w[1] = 0;
				break;
			}
		*/
	} else {
		if (cookie->path[0] != '/') {
			add_to_strn(&cookie->path, cast_uchar "x");
			memmove(cookie->path + 1, cookie->path, strlen(cast_const_char cookie->path) - 1);
			cookie->path[0] = '/';
		}
	}
	if (!(cookie->domain = parse_header_param(str, cast_uchar "domain", 0))) cookie->domain = stracpy(server);
	if (cookie->domain[0] == '.') memmove(cookie->domain, cookie->domain + 1, strlen(cast_const_char cookie->domain));
	if ((s = parse_header_param(str, cast_uchar "secure", 0))) {
		cookie->secure = 1;
		mem_free(s);
	} else cookie->secure = 0;
	if (check_domain_security(server, cookie->domain)) {
		mem_free(cookie->domain);
		cookie->domain = stracpy(server);
	}
	foreach (cs, c_servers) if (!strcasecmp(cast_const_char cs->server, cast_const_char server)) {
		if (cs->accpt) goto ok;
		else {
			free_cookie(cookie);
			mem_free(cookie);
			mem_free(server);
			return 0;
		}
	}
	if (accept_cookies != ACCEPT_ALL) {
		free_cookie(cookie);
		mem_free(cookie);
		mem_free(server);
		return 1;
	}
	ok:
	accept_cookie(cookie);
	mem_free(server);
	return 0;
}
Beispiel #22
0
unsigned char *translate_url(unsigned char *url, unsigned char *cwd)
{
	unsigned char *ch;
	unsigned char *nu, *da;
	unsigned char *prefix;
	int sl;
	while (*url == ' ') url++;
	if (*url && url[strlen(cast_const_char url) - 1] == ' ') {
		nu = stracpy(url);
		while (*nu && nu[strlen(cast_const_char nu) - 1] == ' ') nu[strlen(cast_const_char nu) - 1] = 0;
		ch = translate_url(nu, cwd);
		mem_free(nu);
		return ch;
	}
	if (!casecmp(cast_uchar "proxy://", url, 8)) return NULL;
	if (!parse_url(url, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &da, NULL, NULL)) {
		nu = stracpy(url);
		goto return_nu;
	}
	if (strchr(cast_const_char url, POST_CHAR)) return NULL;
	if (strstr(cast_const_char url, "://")) {
		nu = stracpy(url);
		extend_str(&nu, 1);
		ch = cast_uchar strrchr(cast_const_char nu, '#');
		if (!ch || strchr(cast_const_char ch, '/')) ch = nu + strlen(cast_const_char nu);
		memmove(ch + 1, ch, strlen(cast_const_char ch) + 1);
		*ch = '/';
		if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
		mem_free(nu);
	}
	prefix = cast_uchar "file://";
	if (url[0] == '[' && strchr(cast_const_char url, ']')) {
		ch = url;
		goto http;
	}
	ch = url + strcspn(cast_const_char url, ".:/@");
	sl = 0;
#ifdef SPAD
	if (strchr(cast_const_char url, ':') && _is_local(cast_const_char url)) goto set_prefix;
#endif
	if (*ch != ':' || *(url + strcspn(cast_const_char url, "/@")) == '@') {
		if (*url != '.' && *ch == '.') {
			unsigned char *e, *f, *g;
			int tl;
			for (e = ch + 1; *(f = e + strcspn(cast_const_char e, ".:/")) == '.'; e = f + 1)
				;
			g = memacpy(e, f - e);
			tl = is_tld(g);
			mem_free(g);
			if (tl)
				http: prefix = cast_uchar "http://", sl = 1;
		}
		if (*ch == '@' || *ch == ':' || !cmpbeg(url, cast_uchar "ftp.")) prefix = cast_uchar "ftp://", sl = 1;
		goto set_prefix;
		set_prefix:
		nu = stracpy(prefix);
		add_to_strn(&nu, url);
		if (sl && !strchr(cast_const_char url, '/')) add_to_strn(&nu, cast_uchar "/");
		if (parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
			mem_free(nu);
			return NULL;
		}
		goto return_nu;
	}
#ifdef DOS_FS
	if (ch == url + 1) goto set_prefix;
#endif
	if (!(nu = memacpy(url, ch - url + 1))) return NULL;
	add_to_strn(&nu, cast_uchar "//");
	add_to_strn(&nu, ch + 1);
	if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
	add_to_strn(&nu, cast_uchar "/");
	if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
	mem_free(nu);
	return NULL;

	return_nu:
	insert_wd(&nu, cwd);
	extend_str(&nu, 1);
	translate_directories(nu);
	nu = translate_hashbang(nu);
	return nu;
}
Beispiel #23
0
/*
 * U funkce join_urls musi byt prvni url absolutni (takove, co projde funkci
 * parse_url bez chyby --- pokud neni absolutni, tak to spatne na internal) a
 * druhe url je relativni cesta vuci nemu nebo taky absolutni url. Pokud je
 * druhe url absolutni, vrati se to; pokud je relativni, tak se spoji prvni a
 * druhe url.
 */
unsigned char *join_urls(unsigned char *base, unsigned char *rel)
{
	unsigned char *p, *n, *pp, *ch;
	int l;
	int lo = !casecmp(base, cast_uchar "file://", 7);
	int data = !casecmp(base, cast_uchar "data:", 5);
	if (rel[0] == '#' || !rel[0]) {
		n = stracpy(base);
		for (p = n; *p && *p != POST_CHAR && *p != '#'; p++)
			;
		*p = 0;
		add_to_strn(&n, rel);
		goto return_n;
	}
	if (rel[0] == '?' || rel[0] == '&') {
		unsigned char rj[3];
		unsigned char *d = get_url_data(base);
		if (!d) goto bad_base;
		rj[0] = rel[0];
		rj[1] = POST_CHAR;
		rj[2] = 0;
		d += strcspn(cast_const_char d, cast_const_char rj);
		n = memacpy(base, d - base);
		add_to_strn(&n, rel);
		goto return_n;
	}
	if (rel[0] == '/' && rel[1] == '/' && !data) {
		unsigned char *s;
		if (!(s = cast_uchar strstr(cast_const_char base, "//"))) {
			if (!(s = cast_uchar strchr(cast_const_char base, ':'))) {
				bad_base:
				internal("bad base url: %s", base);
				return NULL;
			}
			s++;
		}
		n = memacpy(base, s - base);
		add_to_strn(&n, rel);
		if (!parse_url(n, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_n;
		add_to_strn(&n, cast_uchar "/");
		if (!parse_url(n, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_n;
		mem_free(n);
	}
	if (!casecmp(cast_uchar "proxy://", rel, 8)) goto prx;
	if (!parse_url(rel, &l, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
		n = stracpy(rel);
		goto return_n;
	}
	n = stracpy(rel);
	while (n[0] && n[strlen(cast_const_char n) - 1] <= ' ') n[strlen(cast_const_char n) - 1] = 0;
	extend_str(&n, 1);
	ch = cast_uchar strrchr(cast_const_char n, '#');
	if (!ch || strchr(cast_const_char ch, '/')) ch = n + strlen(cast_const_char n);
	memmove(ch + 1, ch, strlen(cast_const_char ch) + 1);
	*ch = '/';
	if (!parse_url(n, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_n;
	mem_free(n);
	prx:
	if (parse_url(base, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &p, NULL, NULL) || !p) {
		goto bad_base;
	}
	if (!dsep(*p)) p--;
	if (!data) {
		if (end_of_dir(base, rel[0])) for (; *p; p++) {
			if (end_of_dir(base, *p)) break;
		} else if (!dsep(rel[0])) for (pp = p; *pp; pp++) {
			if (end_of_dir(base, *pp)) break;
			if (dsep(*pp)) p = pp + 1;
		}
	}
	n = memacpy(base, p - base);
	add_to_strn(&n, rel);
	goto return_n;

	return_n:
	extend_str(&n, 1);
	translate_directories(n);
	return n;
}
Beispiel #24
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 #25
0
static enum parse_error
parse_set_common(struct option *opt_tree, struct conf_parsing_state *state,
		 struct string *mirror, int is_system_conf, int want_domain)
{
	const unsigned char *domain_orig = NULL;
	size_t domain_len = 0;
	unsigned char *domain_copy = NULL;
	const unsigned char *optname_orig;
	size_t optname_len;
	unsigned char *optname_copy;

	skip_white(&state->pos);
	if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);

	if (want_domain) {
		domain_orig = state->pos.look;
		while (isident(*state->pos.look) || *state->pos.look == '*'
		       || *state->pos.look == '.' || *state->pos.look == '+')
			state->pos.look++;
		domain_len = state->pos.look - domain_orig;

		skip_white(&state->pos);
	}

	/* Option name */
	optname_orig = state->pos.look;
	while (is_option_name_char(*state->pos.look)
	       || *state->pos.look == '.')
		state->pos.look++;
	optname_len = state->pos.look - optname_orig;

	skip_white(&state->pos);

	/* Equal sign */
	if (*state->pos.look != '=')
		return show_parse_error(state, ERROR_PARSE);
	state->pos.look++; /* '=' */
	skip_white(&state->pos);
	if (!*state->pos.look)
		return show_parse_error(state, ERROR_VALUE);

	optname_copy = memacpy(optname_orig, optname_len);
	if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
	if (want_domain) {
		domain_copy = memacpy(domain_orig, domain_len);
		if (!domain_copy) {
			mem_free(optname_copy);
			return show_parse_error(state, ERROR_NOMEM);
		}
	}

	/* Option value */
	{
		struct option *opt;
		unsigned char *val;
		const struct conf_parsing_pos pos_before_value = state->pos;

		if (want_domain && *domain_copy) {
			struct option *domain_tree;

			domain_tree = get_domain_tree(domain_copy);
			if (!domain_tree) {
				mem_free(domain_copy);
				mem_free(optname_copy);
				skip_option_value(&state->pos);
				return show_parse_error(state, ERROR_NOMEM);
			}

			if (mirror) {
				opt = get_opt_rec_real(domain_tree,
						       optname_copy);
			} else {
				opt = get_opt_rec(opt_tree, optname_copy);
				if (opt) {
					opt = get_option_shadow(opt, opt_tree,
								domain_tree);
					if (!opt) {
						mem_free(domain_copy);
						mem_free(optname_copy);
						skip_option_value(&state->pos);
						return show_parse_error(state,
									ERROR_NOMEM);
					}
				}
			}
		} else {
			opt = mirror
				? get_opt_rec_real(opt_tree, optname_copy)
				: get_opt_rec(opt_tree, optname_copy);
		}
		if (want_domain)
			mem_free(domain_copy);
		domain_copy = NULL;
		mem_free(optname_copy);
		optname_copy = NULL;

		if (!opt || (opt->flags & OPT_HIDDEN)) {
			show_parse_error(state, ERROR_OPTION);
			skip_option_value(&state->pos);
			return ERROR_OPTION;
			/* TODO: Distinguish between two scenarios:
			 * - A newer version of ELinks has saved an
			 *   option that this version does not recognize.
			 *   The option must be preserved.  (This works.)
			 * - The user has added an option, saved
			 *   elinks.conf, restarted ELinks, deleted the
			 *   option, and is now saving elinks.conf again.
			 *   The option should be rewritten to "unset".
			 *   (This does not work yet.)
			 * In both cases, ELinks has no struct option
			 * for that name.  Possible fixes:
			 * - If the tree has OPT_AUTOCREATE, then
			 *   assume the user had created that option,
			 *   and rewrite it to "unset".  Otherwise,
			 *   keep it.
			 * - When the user deletes an option, just mark
			 *   it with OPT_DELETED, and keep it in memory
			 *   as long as OPT_TOUCHED is set.  */
		}

		if (!option_types[opt->type].read) {
			show_parse_error(state, ERROR_VALUE);
			skip_option_value(&state->pos);
			return ERROR_VALUE;
		}

		val = option_types[opt->type].read(opt, &state->pos.look,
						   &state->pos.line);
		if (!val) {
			/* The reader function failed.  Jump back to
			 * the beginning of the value and skip it with
			 * the generic code.  For the error message,
			 * use the line number at the beginning of the
			 * value, because the ending position is not
			 * interesting if there is an unclosed quote.  */
			state->pos = pos_before_value;
			show_parse_error(state, ERROR_VALUE);
			skip_option_value(&state->pos);
			return ERROR_VALUE;
		}

		if (!mirror) {
			/* loading a configuration file */
			if (!option_types[opt->type].set
			    || !option_types[opt->type].set(opt, val)) {
				mem_free(val);
				return show_parse_error(state, ERROR_VALUE);
			}
		} else if (is_system_conf) {
			/* scanning a file that will not be rewritten */
			struct option *flagsite = indirect_option(opt);

			if (!(flagsite->flags & OPT_DELETED)
			    && option_types[opt->type].equals
			    && option_types[opt->type].equals(opt, val))
				flagsite->flags &= ~OPT_MUST_SAVE;
			else
				flagsite->flags |= OPT_MUST_SAVE;
		} else {
			/* rewriting a configuration file */
			struct option *flagsite = indirect_option(opt);

			if (flagsite->flags & OPT_DELETED) {
				/* Replace the "set" command with an
				 * "unset" command.  */
				add_to_string(mirror, "unset ");
				add_bytes_to_string(mirror, optname_orig,
						    optname_len);
				state->mirrored = state->pos.look;
			} else if (option_types[opt->type].write) {
				add_bytes_to_string(mirror, state->mirrored,
						    pos_before_value.look
						    - state->mirrored);
				option_types[opt->type].write(opt, mirror);
				state->mirrored = state->pos.look;
			}
			/* Remember that the option need not be
			 * written to the end of the file.  */
			flagsite->flags &= ~OPT_MUST_SAVE;
		}
		mem_free(val);
	}

	return ERROR_NONE;
}
Beispiel #26
0
unsigned char *get_protocol_name(unsigned char *url)
{
	int l;
	if (parse_url(url, &l, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) return NULL;
	return memacpy(url, l);
}
Beispiel #27
0
unsigned char *translate_url(unsigned char *url, unsigned char *cwd)
{
	unsigned char *ch;
	unsigned char *nu, *da;
	unsigned char *prefix;
	int sl;
	while (*url == ' ') url++;
	if (*url && url[strlen(cast_const_char url) - 1] == ' ') {
		nu = stracpy(url);
		while (*nu && nu[strlen(cast_const_char nu) - 1] == ' ') nu[strlen(cast_const_char nu) - 1] = 0;
		ch = translate_url(nu, cwd);
		mem_free(nu);
		return ch;
	}
	if (!casecmp(cast_uchar "proxy://", url, 8)) return NULL;
	if (!parse_url(url, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &da, NULL, NULL)) {
		nu = stracpy(url);
		goto return_nu;
	}
	if (strchr(cast_const_char url, POST_CHAR)) return NULL;
	if (strstr(cast_const_char url, "://")) {
		nu = stracpy(url);
		extend_str(&nu, 1);
		ch = cast_uchar strrchr(cast_const_char nu, '#');
		if (!ch || strchr(cast_const_char ch, '/')) ch = nu + strlen(cast_const_char nu);
		memmove(ch + 1, ch, strlen(cast_const_char ch) + 1);
		*ch = '/';
		if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
		mem_free(nu);
	}
	prefix = cast_uchar "file://";
	if (url[0] == '[' && strchr(cast_const_char url, ']')) {
		ch = url;
		goto http;
	}
	ch = url + strcspn(cast_const_char url, ".:/@");
	sl = 0;
#ifdef SPAD
	if (strchr(cast_const_char url, ':') && _is_local(cast_const_char url)) goto set_prefix;
#endif
	if (*ch != ':' || *(url + strcspn(cast_const_char url, "/@")) == '@') {
		if (*url != '.' && *ch == '.') {
			unsigned char *f, *e;
			int i;
			for (e = ch + 1; *(f = e + strcspn(cast_const_char e, ".:/")) == '.'; e = f + 1)
				;
			for (i = 0; i < f - e; i++) if (e[i] < '0' || e[i] > '9') goto noip;
			goto http;
			noip:
			if (f - e == 2 && casecmp(e, cast_uchar "gz", 2)) {
				http:
				prefix = cast_uchar "http://", sl = 1;
			} else {
				char *tld[] = {
					"com",
					"edu",
					"net",
					"org",
					"gov",
					"mil",
					"int",
					"arpa",
					"aero",
					"biz",
					"coop",
					"info",
					"museum",
					"name",
					"pro",
					"cat",
					"jobs",
					"mobi",
					"travel",
					"tel",
					"onion",
					"exit",
					NULL };
				for (i = 0; tld[i]; i++) if ((size_t)(f - e) == strlen(cast_const_char tld[i]) && !casecmp(cast_uchar tld[i], e, f - e)) goto http;
			}
		}
		if (*ch == '@' || *ch == ':' || !cmpbeg(url, cast_uchar "ftp.")) prefix = cast_uchar "ftp://", sl = 1;
		goto set_prefix;
		set_prefix:
		nu = stracpy(prefix);
		add_to_strn(&nu, url);
		if (sl && !strchr(cast_const_char url, '/')) add_to_strn(&nu, cast_uchar "/");
		if (parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
			mem_free(nu);
			return NULL;
		}
		goto return_nu;
	}
#ifdef DOS_FS
	if (ch == url + 1) goto set_prefix;
#endif
	if (!(nu = memacpy(url, ch - url + 1))) return NULL;
	add_to_strn(&nu, cast_uchar "//");
	add_to_strn(&nu, ch + 1);
	if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
	add_to_strn(&nu, cast_uchar "/");
	if (!parse_url(nu, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) goto return_nu;
	mem_free(nu);
	return NULL;

	return_nu:
	insert_wd(&nu, cwd);
	extend_str(&nu, 1);
	translate_directories(nu);
	nu = translate_hashbang(nu);
	return nu;
}
Beispiel #28
0
static void
smb_got_header(struct socket *socket, struct read_buffer *rb)
{
	struct connection *conn = socket->conn;
	struct read_buffer *buf;
	int error = 0;

	conn->cached = get_cache_entry(conn->uri);
	if (!conn->cached) {
		/* Even though these are pipes rather than real
		 * sockets, call close_socket instead of close, to
		 * ensure that abort_connection won't try to close the
		 * file descriptors again.  (Could we skip the calls
		 * and assume abort_connection will do them?)  */
		close_socket(socket);
		close_socket(conn->data_socket);
		abort_connection(conn, connection_state(S_OUT_OF_MEM));
		return;
	}
	socket->state = SOCKET_END_ONCLOSE;

	if (rb->length > 0) {
		unsigned char *ctype = memacpy(rb->data, rb->length);

		if (ctype && *ctype) {
			if (!strcmp(ctype, "text/x-error")) {
				error = 1;
				mem_free(ctype);
			} else {
				if (ctype[0] >= '0' && ctype[0] <= '9') {
#ifdef HAVE_ATOLL
					conn->est_length = (off_t)atoll(ctype);
#else
					conn->est_length = (off_t)atol(ctype);
#endif
					mem_free(ctype);

					/* avoid error */
					if (!conn->est_length) {
						abort_connection(conn, connection_state(S_OK));
						return;
					}
				}
				else mem_free_set(&conn->cached->content_type, ctype);
			}
		} else {
			mem_free_if(ctype);
		}
	}

	buf = alloc_read_buffer(conn->data_socket);
	if (!buf) {
		close_socket(socket);
		close_socket(conn->data_socket);
		abort_connection(conn, connection_state(S_OUT_OF_MEM));
		return;
	}
	if (error) {
		mem_free_set(&conn->cached->content_type, stracpy("text/html"));
		read_from_socket(conn->data_socket, buf,
				 connection_state(S_CONN), smb_got_error);
	} else {
		read_from_socket(conn->data_socket, buf,
				 connection_state(S_CONN), smb_got_data);
	}
}
Beispiel #29
0
static enum parse_error
parse_unset(struct option *opt_tree, struct conf_parsing_state *state,
	    struct string *mirror, int is_system_conf)
{
	const unsigned char *optname_orig;
	size_t optname_len;
	unsigned char *optname_copy;

	skip_white(&state->pos);
	if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);

	/* Option name */
	optname_orig = state->pos.look;
	while (is_option_name_char(*state->pos.look)
	       || *state->pos.look == '.')
		state->pos.look++;
	optname_len = state->pos.look - optname_orig;

	optname_copy = memacpy(optname_orig, optname_len);
	if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);

	{
		struct option *opt;

		opt = get_opt_rec_real(opt_tree, optname_copy);
		mem_free(optname_copy);
		optname_copy = NULL;

		if (!opt || (opt->flags & OPT_HIDDEN)) {
			/* The user wanted to delete the option, and
			 * it has already been deleted; this is not an
			 * error.  This might happen if a version of
			 * ELinks has a built-in URL rewriting rule,
			 * the user disables it, and a later version
			 * no longer has it.  */
			return ERROR_NONE;
		}

		if (!mirror) {
			/* loading a configuration file */
			if (opt->flags & OPT_ALLOC) delete_option(opt);
			else mark_option_as_deleted(opt);
		} else if (is_system_conf) {
			/* scanning a file that will not be rewritten */
			struct option *flagsite = indirect_option(opt);

			if (flagsite->flags & OPT_DELETED)
				flagsite->flags &= ~OPT_MUST_SAVE;
			else
				flagsite->flags |= OPT_MUST_SAVE;
		} else {
			/* rewriting a configuration file */
			struct option *flagsite = indirect_option(opt);

			if (flagsite->flags & OPT_DELETED) {
				/* The "unset" command is already in the file,
				 * and unlike with "set", there is no value
				 * to be updated.  */
			} else if (option_types[opt->type].write) {
				/* Replace the "unset" command with a
				 * "set" command.  */
				add_to_string(mirror, "set ");
				add_bytes_to_string(mirror, optname_orig,
						    optname_len);
				add_to_string(mirror, " = ");
				option_types[opt->type].write(opt, mirror);
				state->mirrored = state->pos.look;
			}
			/* Remember that the option need not be
			 * written to the end of the file.  */
			flagsite->flags &= ~OPT_MUST_SAVE;
		}
	}

	return ERROR_NONE;
}
int
html_parse_meta_refresh(const unsigned char *content,
			unsigned long *delay_out,
			unsigned char **url_out)
{
	const unsigned char *scan = content;
	char *delay_end;
	int saw_delay = 0;
	const unsigned char *url_begin;
	const unsigned char *url_end;

	*url_out = NULL;
	*delay_out = 0;

	while (LWS(*scan))
		++scan;

	/* TODO: Do we need to switch to the "C" locale and back?  */
	*delay_out = strtoul(scan, &delay_end, 10);
	saw_delay = (scan != (const unsigned char *) delay_end);
	scan = (const unsigned char *) delay_end;

	if (saw_delay) {
		/* Omit any fractional part.  */
		if (*scan == '.') {
			++scan;
			while (!(*scan == '\0' || LWS(*scan)
				 || *scan == ';' || *scan == ','))
				++scan;
		}

		if (!(*scan == '\0' || LWS(*scan)
		      || *scan == ';' || *scan == ',')) {
			/* The delay is followed by garbage.  Give up.  */
			return -1;
		}

		/* Between the delay and the URL, there must be at
		 * least one LWS, semicolon, or comma; optionally with
		 * more LWS around it.  */
		while (LWS(*scan))
			++scan;
		if (*scan == ';' || *scan == ',')
			++scan;
	} else {
		/* The delay was not specified.  The delimiter must be
		 * a semicolon or a comma, optionally with LWS.  LWS
		 * alone does not suffice.  */
		while (*scan != '\0' && *scan != ';' && *scan != ',')
			++scan;
		if (*scan == ';' || *scan == ',')
			++scan;
	}
	
	while (LWS(*scan))
		++scan;

	/* Presume the URL begins here...  */
	url_begin = scan;

	/* ..unless there is "URL=" with at least one equals sign,
	 * and optional spaces.  */
	if ((scan[0] == 'U' || scan[0] == 'u')
	    && (scan[1] == 'R' || scan[1] == 'r')
	    && (scan[2] == 'L' || scan[2] == 'l')) {
		scan += 3;
 		while (LWS(*scan))
			++scan;
		if (*scan == '=') {
			++scan;
			while (LWS(*scan))
				++scan;
			url_begin = scan;
		}
	}

	if (*url_begin == '"' || *url_begin == '\'') {
		unsigned char quote = *url_begin++;

		url_end = strchr(url_begin, quote);
		if (url_end == NULL)
			url_end = strchr(url_begin, '\0');
	} else {
		url_end = strchr(url_begin, '\0');
	}

	/* In any case, trim all spaces from the end of the URL.  */
	while (url_begin < url_end && LWS(url_end[-1]))
		--url_end;

	if (url_begin != url_end) {
		*url_out = memacpy(url_begin, url_end - url_begin);
		if (!*url_out)
			return -1;
	} else if (!saw_delay) {
		/* There is no delay and no URL.  */
		return -1;
	}
	
	return 0;
}