mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-24 21:16:50 +03:00
ebe1b05114
remove unecessary inclusion of desktop search header in content header which has knock on effect of not having ctype or string system headers dragged in unecessarily. Futher this highlighted use of ctype API where internal ascii processing ought to be used.
602 lines
14 KiB
C
602 lines
14 KiB
C
/*
|
|
* Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* \brief core web search facilities implementation.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "utils/utils.h"
|
|
#include "utils/log.h"
|
|
#include "utils/url.h"
|
|
#include "utils/nsoption.h"
|
|
#include "netsurf/content.h"
|
|
#include "content/hlcache.h"
|
|
|
|
#include "desktop/searchweb.h"
|
|
#include "desktop/gui_internal.h"
|
|
|
|
struct search_provider {
|
|
char *name; /**< readable name such as 'google', 'yahoo', etc */
|
|
char *hostname; /**< host address such as www.google.com */
|
|
char *searchstring; /** < such as "www.google.com?search=%s" */
|
|
char *ico; /** < location of domain's favicon */
|
|
hlcache_handle *ico_handle;
|
|
};
|
|
|
|
static struct search_web_ctx_s {
|
|
struct search_provider *providers; /* web search providers */
|
|
size_t providers_count; /* number of providers */
|
|
|
|
size_t current; /* current provider */
|
|
|
|
hlcache_handle *default_ico_handle;
|
|
|
|
} search_web_ctx;
|
|
|
|
|
|
static const char *default_providers = "Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|\n";
|
|
|
|
static const char *default_search_icon_url = "resource:icons/search.png";
|
|
|
|
|
|
/**
|
|
* Read providers file.
|
|
*
|
|
* Allocates storage of sufficient size for the providers file and
|
|
* reads the entire file in.
|
|
*
|
|
* \param fname The filename to read.
|
|
* \param providers_out A pointer to place the result buffer in.
|
|
* \param providers_size_out Size of buffer.
|
|
* \return NSERROR_OK and providers_out updated or appropriate error code.
|
|
*/
|
|
static nserror
|
|
read_providers(const char *fname,
|
|
char **providers_out,
|
|
size_t *providers_size_out)
|
|
{
|
|
FILE *providersf;
|
|
long ftellsize;
|
|
size_t fsize;
|
|
char *providersd;
|
|
|
|
if (fname == NULL) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
providersf = fopen(fname, "r");
|
|
if (providersf == NULL) {
|
|
return NSERROR_NOT_FOUND;
|
|
}
|
|
|
|
if (fseek(providersf, 0, SEEK_END) != 0) {
|
|
fclose(providersf);
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
ftellsize = ftell(providersf);
|
|
if (ftellsize < 0) {
|
|
fclose(providersf);
|
|
return NSERROR_INVALID;
|
|
}
|
|
fsize = ftellsize;
|
|
|
|
if (fseek(providersf, 0, SEEK_SET) != 0) {
|
|
fclose(providersf);
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
providersd = malloc(fsize + 1);
|
|
if (providersd == NULL) {
|
|
fclose(providersf);
|
|
return NSERROR_NOMEM;
|
|
}
|
|
|
|
if (fread(providersd, 1, fsize, providersf) != fsize) {
|
|
fclose(providersf);
|
|
free(providersd);
|
|
return NSERROR_BAD_SIZE;
|
|
}
|
|
providersd[fsize] = 0; /* ensure null terminated */
|
|
|
|
fclose(providersf);
|
|
|
|
*providers_out = providersd;
|
|
*providers_size_out = fsize;
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/**
|
|
* parse search providers from a memory block.
|
|
*
|
|
* \param providersd The provider info data.
|
|
* \param providers_size The size of the provider data.
|
|
* \param providers_out The resulting provider array.
|
|
* \param providers_count The number of providers in the output array.
|
|
* \return NSERROR_OK on success or error code on failure.
|
|
*/
|
|
static nserror
|
|
parse_providers(char *providersd,
|
|
size_t providers_size,
|
|
struct search_provider **providers_out,
|
|
size_t *providers_count)
|
|
{
|
|
size_t pcount = 0; /* number of providers */
|
|
size_t pidx;
|
|
char *nl = providersd;
|
|
struct search_provider *providers;
|
|
|
|
/* count newlines */
|
|
while (nl != NULL) {
|
|
nl = strchr(nl, '\n');
|
|
if (nl != NULL) {
|
|
nl++;
|
|
pcount+=1;
|
|
}
|
|
}
|
|
|
|
if (pcount == 0) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
providers = malloc(pcount * sizeof(*providers));
|
|
if (providers == NULL) {
|
|
return NSERROR_NOMEM;
|
|
}
|
|
|
|
nl = providersd;
|
|
for (pidx = 0; pidx < pcount; pidx++) {
|
|
providers[pidx].name = nl;
|
|
nl = strchr(nl, '|');
|
|
if (nl == NULL) {
|
|
free(providers);
|
|
return NSERROR_INVALID;
|
|
}
|
|
*nl = 0;
|
|
nl++;
|
|
|
|
providers[pidx].hostname = nl;
|
|
nl = strchr(nl, '|');
|
|
if (nl == NULL) {
|
|
free(providers);
|
|
return NSERROR_INVALID;
|
|
}
|
|
*nl = 0;
|
|
nl++;
|
|
|
|
providers[pidx].searchstring = nl;
|
|
nl = strchr(nl, '|');
|
|
if (nl == NULL) {
|
|
free(providers);
|
|
return NSERROR_INVALID;
|
|
}
|
|
*nl = 0;
|
|
nl++;
|
|
|
|
providers[pidx].ico = nl;
|
|
nl = strchr(nl, '|');
|
|
if (nl == NULL) {
|
|
free(providers);
|
|
return NSERROR_INVALID;
|
|
}
|
|
*nl = 0;
|
|
nl++;
|
|
|
|
/* skip newline */
|
|
nl = strchr(nl, '\n');
|
|
if (nl == NULL) {
|
|
free(providers);
|
|
return NSERROR_INVALID;
|
|
}
|
|
nl++;
|
|
|
|
providers[pidx].ico_handle = NULL;
|
|
}
|
|
|
|
*providers_out = providers;
|
|
*providers_count = pcount;
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/**
|
|
* create a url for a search provider and a term
|
|
*
|
|
* \param provider The provider to use.
|
|
* \param term The term being searched for.
|
|
* \param url_out The resulting url.
|
|
* \return NSERROR_OK on success or appropriate error code.
|
|
*/
|
|
static nserror
|
|
make_search_nsurl(struct search_provider *provider,
|
|
const char *term,
|
|
nsurl **url_out)
|
|
{
|
|
nserror ret;
|
|
nsurl *url;
|
|
char *eterm; /* escaped term */
|
|
char *searchstr; /* the providers search string */
|
|
char *urlstr; /* the escaped term substituted into the provider */
|
|
char *urlstro;
|
|
size_t urlstr_len;
|
|
|
|
/* escape the search term and join it to the search url */
|
|
ret = url_escape(term, true, NULL, &eterm);
|
|
if (ret != NSERROR_OK) {
|
|
return ret;
|
|
}
|
|
|
|
searchstr = provider->searchstring;
|
|
|
|
urlstr_len = strlen(searchstr) + strlen(eterm) + 1;
|
|
urlstro = urlstr = malloc(urlstr_len);
|
|
if (urlstr == NULL) {
|
|
free(eterm);
|
|
return NSERROR_NOMEM;
|
|
}
|
|
|
|
/* composite search url */
|
|
for ( ; *searchstr != 0; searchstr++, urlstro++) {
|
|
*urlstro = *searchstr;
|
|
if ((*searchstr == '%') && (searchstr[1] == 's')) {
|
|
searchstr++; /* skip % */
|
|
memcpy(urlstro, eterm, strlen(eterm));
|
|
urlstro += strlen(eterm) - 1;
|
|
}
|
|
}
|
|
free(eterm);
|
|
*urlstro = '\0'; /* ensure string is NULL-terminated */
|
|
|
|
ret = nsurl_create(urlstr, &url);
|
|
free(urlstr);
|
|
if (ret != NSERROR_OK) {
|
|
return ret;
|
|
}
|
|
|
|
*url_out = url;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/**
|
|
* callback for hlcache icon fetch events.
|
|
*/
|
|
static nserror
|
|
search_web_ico_callback(hlcache_handle *ico,
|
|
const hlcache_event *event,
|
|
void *pw)
|
|
{
|
|
struct search_provider *provider = pw;
|
|
|
|
switch (event->type) {
|
|
|
|
case CONTENT_MSG_DONE:
|
|
NSLOG(netsurf, INFO, "icon '%s' retrieved",
|
|
nsurl_access(hlcache_handle_get_url(ico)));
|
|
guit->search_web->provider_update(provider->name,
|
|
content_get_bitmap(ico));
|
|
break;
|
|
|
|
case CONTENT_MSG_ERROR:
|
|
NSLOG(netsurf, INFO, "icon %s error: %s",
|
|
nsurl_access(hlcache_handle_get_url(ico)),
|
|
event->data.errordata.errormsg);
|
|
|
|
hlcache_handle_release(ico);
|
|
/* clear reference to released handle */
|
|
provider->ico_handle = NULL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
nserror
|
|
search_web_omni(const char *term,
|
|
enum search_web_omni_flags flags,
|
|
struct nsurl **url_out)
|
|
{
|
|
nserror ret;
|
|
nsurl *url;
|
|
char *eterm; /* encoded/altered search term */
|
|
|
|
if ((flags & SEARCH_WEB_OMNI_SEARCHONLY) == 0) {
|
|
|
|
/* first check to see if the term is a url */
|
|
ret = nsurl_create(term, &url);
|
|
if (ret == NSERROR_OK) {
|
|
*url_out = url;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* try with adding default scheme */
|
|
eterm = malloc(strlen(term) + SLEN("http://") + 1);
|
|
if (eterm == NULL) {
|
|
return NSERROR_NOMEM;
|
|
}
|
|
sprintf(eterm, "http://%s", term);
|
|
ret = nsurl_create(eterm, &url);
|
|
free(eterm);
|
|
if (ret == NSERROR_OK) {
|
|
*url_out = url;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* do not pass to search if user has disabled the option */
|
|
if (nsoption_bool(search_url_bar) == false) {
|
|
return NSERROR_BAD_URL;
|
|
}
|
|
}
|
|
|
|
/* must be initialised */
|
|
if (search_web_ctx.providers == NULL) {
|
|
return NSERROR_INIT_FAILED;
|
|
}
|
|
|
|
/* turn search into a nsurl */
|
|
ret = make_search_nsurl(&search_web_ctx.providers[search_web_ctx.current], term, &url);
|
|
if (ret != NSERROR_OK) {
|
|
return ret;
|
|
}
|
|
|
|
*url_out = url;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
nserror search_web_get_provider_bitmap(struct bitmap **bitmap_out)
|
|
{
|
|
struct search_provider *provider;
|
|
struct bitmap *ico_bitmap = NULL;
|
|
|
|
/* must be initialised */
|
|
if (search_web_ctx.providers == NULL) {
|
|
return NSERROR_INIT_FAILED;
|
|
}
|
|
|
|
provider = &search_web_ctx.providers[search_web_ctx.current];
|
|
|
|
/* set the icon now (if we can) at least to the default */
|
|
if (provider->ico_handle != NULL) {
|
|
ico_bitmap = content_get_bitmap(provider->ico_handle);
|
|
}
|
|
if ((ico_bitmap == NULL) &&
|
|
(search_web_ctx.default_ico_handle != NULL)) {
|
|
ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle);
|
|
}
|
|
|
|
*bitmap_out = ico_bitmap;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
nserror search_web_select_provider(int selection)
|
|
{
|
|
struct search_provider *provider;
|
|
struct bitmap *ico_bitmap = NULL;
|
|
|
|
/* must be initialised */
|
|
if (search_web_ctx.providers == NULL) {
|
|
return NSERROR_INIT_FAILED;
|
|
}
|
|
|
|
/* negative value just selects whatevers current */
|
|
if (selection >= 0) {
|
|
/* ensure selection lies within acceptable range */
|
|
if ((size_t)selection < search_web_ctx.providers_count) {
|
|
search_web_ctx.current = selection;
|
|
} else {
|
|
/* out of range */
|
|
search_web_ctx.current = 0;
|
|
}
|
|
}
|
|
|
|
provider = &search_web_ctx.providers[search_web_ctx.current];
|
|
|
|
/* set the icon now (if we can) at least to the default */
|
|
if (provider->ico_handle != NULL) {
|
|
ico_bitmap = content_get_bitmap(provider->ico_handle);
|
|
}
|
|
if ((ico_bitmap == NULL) &&
|
|
(search_web_ctx.default_ico_handle != NULL)) {
|
|
ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle);
|
|
}
|
|
/* update the callback with the provider change. Bitmap may
|
|
* be NULL at this point.
|
|
*/
|
|
guit->search_web->provider_update(provider->name, ico_bitmap);
|
|
|
|
|
|
/* if the providers icon has not been retrieved get it now */
|
|
if (provider->ico_handle == NULL) {
|
|
nsurl *icon_nsurl;
|
|
nserror ret;
|
|
|
|
/* create search icon url */
|
|
ret = nsurl_create(provider->ico, &icon_nsurl);
|
|
if (ret != NSERROR_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = hlcache_handle_retrieve(icon_nsurl, 0, NULL, NULL,
|
|
search_web_ico_callback,
|
|
provider,
|
|
NULL, CONTENT_IMAGE,
|
|
&provider->ico_handle);
|
|
nsurl_unref(icon_nsurl);
|
|
if (ret != NSERROR_OK) {
|
|
provider->ico_handle = NULL;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/**
|
|
* callback for hlcache icon fetch events.
|
|
*/
|
|
static nserror
|
|
default_ico_callback(hlcache_handle *ico,
|
|
const hlcache_event *event,
|
|
void *pw)
|
|
{
|
|
struct search_web_ctx_s *ctx = pw;
|
|
|
|
switch (event->type) {
|
|
|
|
case CONTENT_MSG_DONE:
|
|
NSLOG(netsurf, INFO, "default icon '%s' retrieved",
|
|
nsurl_access(hlcache_handle_get_url(ico)));
|
|
|
|
/* only set to default icon if providers icon has no handle */
|
|
if (ctx->providers[search_web_ctx.current].ico_handle == NULL) {
|
|
guit->search_web->provider_update(
|
|
ctx->providers[search_web_ctx.current].name,
|
|
content_get_bitmap(ico));
|
|
}
|
|
break;
|
|
|
|
case CONTENT_MSG_ERROR:
|
|
NSLOG(netsurf, INFO, "icon %s error: %s",
|
|
nsurl_access(hlcache_handle_get_url(ico)),
|
|
event->data.errordata.errormsg);
|
|
|
|
hlcache_handle_release(ico);
|
|
/* clear reference to released handle */
|
|
ctx->default_ico_handle = NULL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
ssize_t search_web_iterate_providers(ssize_t from, const char **name)
|
|
{
|
|
if (from < 0)
|
|
return -1;
|
|
|
|
if ((size_t)from >= search_web_ctx.providers_count)
|
|
return -1;
|
|
|
|
*name = search_web_ctx.providers[from].name;
|
|
|
|
return from + 1;
|
|
}
|
|
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
nserror search_web_init(const char *provider_fname)
|
|
{
|
|
nserror ret;
|
|
char *providers;
|
|
size_t providers_size;
|
|
nsurl *icon_nsurl;
|
|
|
|
/* create search icon url */
|
|
ret = nsurl_create(default_search_icon_url, &icon_nsurl);
|
|
if (ret != NSERROR_OK) {
|
|
return ret;
|
|
}
|
|
|
|
/* get a list of providers */
|
|
ret = read_providers(provider_fname, &providers, &providers_size);
|
|
if (ret != NSERROR_OK) {
|
|
providers = strdup(default_providers);
|
|
if (providers == NULL) {
|
|
return NSERROR_NOMEM;
|
|
}
|
|
providers_size = strlen(providers);
|
|
}
|
|
|
|
/* parse list of providers */
|
|
ret = parse_providers(providers,
|
|
providers_size,
|
|
&search_web_ctx.providers,
|
|
&search_web_ctx.providers_count);
|
|
if (ret != NSERROR_OK) {
|
|
free(providers);
|
|
return ret;
|
|
}
|
|
|
|
/* get default search icon */
|
|
ret = hlcache_handle_retrieve(icon_nsurl,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
default_ico_callback,
|
|
&search_web_ctx,
|
|
NULL,
|
|
CONTENT_IMAGE,
|
|
&search_web_ctx.default_ico_handle);
|
|
nsurl_unref(icon_nsurl);
|
|
if (ret != NSERROR_OK) {
|
|
search_web_ctx.default_ico_handle = NULL;
|
|
free(search_web_ctx.providers);
|
|
search_web_ctx.providers = NULL;
|
|
free(providers);
|
|
return ret;
|
|
}
|
|
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
/* exported interface documented in desktop/searchweb.h */
|
|
nserror search_web_finalise(void)
|
|
{
|
|
size_t pidx;
|
|
|
|
/* must be initialised */
|
|
if (search_web_ctx.providers == NULL) {
|
|
return NSERROR_INIT_FAILED;
|
|
}
|
|
|
|
if (search_web_ctx.default_ico_handle != NULL) {
|
|
hlcache_handle_release(search_web_ctx.default_ico_handle);
|
|
}
|
|
for (pidx = 0; pidx < search_web_ctx.providers_count; pidx++) {
|
|
if (search_web_ctx.providers[pidx].ico_handle != NULL) {
|
|
hlcache_handle_release(search_web_ctx.providers[pidx].ico_handle);
|
|
}
|
|
}
|
|
|
|
/* All the search provider data is held in a single block for
|
|
* efficiency.
|
|
*/
|
|
free(search_web_ctx.providers[0].name);
|
|
|
|
free(search_web_ctx.providers);
|
|
search_web_ctx.providers = NULL;
|
|
|
|
return NSERROR_OK;
|
|
}
|