1 /*
2 * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * \file
21 * \brief core web search facilities implementation.
22 */
23
24 #include <stdlib.h>
25
26 #include "utils/utils.h"
27 #include "utils/log.h"
28 #include "utils/url.h"
29 #include "utils/nsoption.h"
30 #include "netsurf/content.h"
31 #include "content/hlcache.h"
32
33 #include "desktop/searchweb.h"
34 #include "desktop/gui_internal.h"
35
36 struct search_provider {
37 char *name; /**< readable name such as 'google', 'yahoo', etc */
38 char *hostname; /**< host address such as www.google.com */
39 char *searchstring; /** < such as "www.google.com?search=%s" */
40 char *ico; /** < location of domain's favicon */
41 hlcache_handle *ico_handle;
42 };
43
44 static struct search_web_ctx_s {
45 struct search_provider *providers; /* web search providers */
46 size_t providers_count; /* number of providers */
47
48 size_t current; /* current provider */
49
50 hlcache_handle *default_ico_handle;
51
52 } search_web_ctx;
53
54
55 static const char *default_providers = "Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|\n";
56
57 static const char *default_search_icon_url = "resource:icons/search.png";
58
59
60 /**
61 * Read providers file.
62 *
63 * Allocates storage of sufficient size for the providers file and
64 * reads the entire file in.
65 *
66 * \param fname The filename to read.
67 * \param providers_out A pointer to place the result buffer in.
68 * \param providers_size_out Size of buffer.
69 * \return NSERROR_OK and providers_out updated or appropriate error code.
70 */
71 static nserror
read_providers(const char * fname,char ** providers_out,size_t * providers_size_out)72 read_providers(const char *fname,
73 char **providers_out,
74 size_t *providers_size_out)
75 {
76 FILE *providersf;
77 long ftellsize;
78 size_t fsize;
79 char *providersd;
80
81 if (fname == NULL) {
82 return NSERROR_BAD_PARAMETER;
83 }
84
85 providersf = fopen(fname, "r");
86 if (providersf == NULL) {
87 return NSERROR_NOT_FOUND;
88 }
89
90 if (fseek(providersf, 0, SEEK_END) != 0) {
91 fclose(providersf);
92 return NSERROR_INVALID;
93 }
94
95 ftellsize = ftell(providersf);
96 if (ftellsize < 0) {
97 fclose(providersf);
98 return NSERROR_INVALID;
99 }
100 fsize = ftellsize;
101
102 if (fseek(providersf, 0, SEEK_SET) != 0) {
103 fclose(providersf);
104 return NSERROR_INVALID;
105 }
106
107 providersd = malloc(fsize + 1);
108 if (providersd == NULL) {
109 fclose(providersf);
110 return NSERROR_NOMEM;
111 }
112
113 if (fread(providersd, 1, fsize, providersf) != fsize) {
114 fclose(providersf);
115 free(providersd);
116 return NSERROR_BAD_SIZE;
117 }
118 providersd[fsize] = 0; /* ensure null terminated */
119
120 fclose(providersf);
121
122 *providers_out = providersd;
123 *providers_size_out = fsize;
124
125 return NSERROR_OK;
126 }
127
128 /**
129 * parse search providers from a memory block.
130 *
131 * \param providersd The provider info data.
132 * \param providers_size The size of the provider data.
133 * \param providers_out The resulting provider array.
134 * \param providers_count The number of providers in the output array.
135 * \return NSERROR_OK on success or error code on failure.
136 */
137 static nserror
parse_providers(char * providersd,size_t providers_size,struct search_provider ** providers_out,size_t * providers_count)138 parse_providers(char *providersd,
139 size_t providers_size,
140 struct search_provider **providers_out,
141 size_t *providers_count)
142 {
143 size_t pcount = 0; /* number of providers */
144 size_t pidx;
145 char *nl = providersd;
146 struct search_provider *providers;
147
148 /* count newlines */
149 while (nl != NULL) {
150 nl = strchr(nl, '\n');
151 if (nl != NULL) {
152 nl++;
153 pcount+=1;
154 }
155 }
156
157 if (pcount == 0) {
158 return NSERROR_INVALID;
159 }
160
161 providers = malloc(pcount * sizeof(*providers));
162 if (providers == NULL) {
163 return NSERROR_NOMEM;
164 }
165
166 nl = providersd;
167 for (pidx = 0; pidx < pcount; pidx++) {
168 providers[pidx].name = nl;
169 nl = strchr(nl, '|');
170 if (nl == NULL) {
171 free(providers);
172 return NSERROR_INVALID;
173 }
174 *nl = 0;
175 nl++;
176
177 providers[pidx].hostname = nl;
178 nl = strchr(nl, '|');
179 if (nl == NULL) {
180 free(providers);
181 return NSERROR_INVALID;
182 }
183 *nl = 0;
184 nl++;
185
186 providers[pidx].searchstring = nl;
187 nl = strchr(nl, '|');
188 if (nl == NULL) {
189 free(providers);
190 return NSERROR_INVALID;
191 }
192 *nl = 0;
193 nl++;
194
195 providers[pidx].ico = nl;
196 nl = strchr(nl, '|');
197 if (nl == NULL) {
198 free(providers);
199 return NSERROR_INVALID;
200 }
201 *nl = 0;
202 nl++;
203
204 /* skip newline */
205 nl = strchr(nl, '\n');
206 if (nl == NULL) {
207 free(providers);
208 return NSERROR_INVALID;
209 }
210 nl++;
211
212 providers[pidx].ico_handle = NULL;
213 }
214
215 *providers_out = providers;
216 *providers_count = pcount;
217
218 return NSERROR_OK;
219 }
220
221 /**
222 * create a url for a search provider and a term
223 *
224 * \param provider The provider to use.
225 * \param term The term being searched for.
226 * \param url_out The resulting url.
227 * \return NSERROR_OK on success or appropriate error code.
228 */
229 static nserror
make_search_nsurl(struct search_provider * provider,const char * term,nsurl ** url_out)230 make_search_nsurl(struct search_provider *provider,
231 const char *term,
232 nsurl **url_out)
233 {
234 nserror ret;
235 nsurl *url;
236 char *eterm; /* escaped term */
237 char *searchstr; /* the providers search string */
238 char *urlstr; /* the escaped term substituted into the provider */
239 char *urlstro;
240 size_t urlstr_len;
241
242 /* escape the search term and join it to the search url */
243 ret = url_escape(term, true, NULL, &eterm);
244 if (ret != NSERROR_OK) {
245 return ret;
246 }
247
248 searchstr = provider->searchstring;
249
250 urlstr_len = strlen(searchstr) + strlen(eterm) + 1;
251 urlstro = urlstr = malloc(urlstr_len);
252 if (urlstr == NULL) {
253 free(eterm);
254 return NSERROR_NOMEM;
255 }
256
257 /* composite search url */
258 for ( ; *searchstr != 0; searchstr++, urlstro++) {
259 *urlstro = *searchstr;
260 if ((*searchstr == '%') && (searchstr[1] == 's')) {
261 searchstr++; /* skip % */
262 memcpy(urlstro, eterm, strlen(eterm));
263 urlstro += strlen(eterm) - 1;
264 }
265 }
266 free(eterm);
267 *urlstro = '\0'; /* ensure string is NULL-terminated */
268
269 ret = nsurl_create(urlstr, &url);
270 free(urlstr);
271 if (ret != NSERROR_OK) {
272 return ret;
273 }
274
275 *url_out = url;
276 return NSERROR_OK;
277 }
278
279 /**
280 * callback for hlcache icon fetch events.
281 */
282 static nserror
search_web_ico_callback(hlcache_handle * ico,const hlcache_event * event,void * pw)283 search_web_ico_callback(hlcache_handle *ico,
284 const hlcache_event *event,
285 void *pw)
286 {
287 struct search_provider *provider = pw;
288
289 switch (event->type) {
290
291 case CONTENT_MSG_DONE:
292 NSLOG(netsurf, INFO, "icon '%s' retrieved",
293 nsurl_access(hlcache_handle_get_url(ico)));
294 guit->search_web->provider_update(provider->name,
295 content_get_bitmap(ico));
296 break;
297
298 case CONTENT_MSG_ERROR:
299 NSLOG(netsurf, INFO, "icon %s error: %s",
300 nsurl_access(hlcache_handle_get_url(ico)),
301 event->data.errordata.errormsg);
302
303 hlcache_handle_release(ico);
304 /* clear reference to released handle */
305 provider->ico_handle = NULL;
306 break;
307
308 default:
309 break;
310 }
311
312 return NSERROR_OK;
313 }
314
315 /* exported interface documented in desktop/searchweb.h */
316 nserror
search_web_omni(const char * term,enum search_web_omni_flags flags,struct nsurl ** url_out)317 search_web_omni(const char *term,
318 enum search_web_omni_flags flags,
319 struct nsurl **url_out)
320 {
321 nserror ret;
322 nsurl *url;
323 char *eterm; /* encoded/altered search term */
324
325 if ((flags & SEARCH_WEB_OMNI_SEARCHONLY) == 0) {
326
327 /* first check to see if the term is a url */
328 ret = nsurl_create(term, &url);
329 if (ret == NSERROR_OK) {
330 *url_out = url;
331 return NSERROR_OK;
332 }
333
334 /* try with adding default scheme */
335 eterm = malloc(strlen(term) + SLEN("http://") + 1);
336 if (eterm == NULL) {
337 return NSERROR_NOMEM;
338 }
339 sprintf(eterm, "http://%s", term);
340 ret = nsurl_create(eterm, &url);
341 free(eterm);
342 if (ret == NSERROR_OK) {
343 *url_out = url;
344 return NSERROR_OK;
345 }
346
347 /* do not pass to search if user has disabled the option */
348 if (nsoption_bool(search_url_bar) == false) {
349 return NSERROR_BAD_URL;
350 }
351 }
352
353 /* must be initialised */
354 if (search_web_ctx.providers == NULL) {
355 return NSERROR_INIT_FAILED;
356 }
357
358 /* turn search into a nsurl */
359 ret = make_search_nsurl(&search_web_ctx.providers[search_web_ctx.current], term, &url);
360 if (ret != NSERROR_OK) {
361 return ret;
362 }
363
364 *url_out = url;
365 return NSERROR_OK;
366 }
367
368 /* exported interface documented in desktop/searchweb.h */
search_web_get_provider_bitmap(struct bitmap ** bitmap_out)369 nserror search_web_get_provider_bitmap(struct bitmap **bitmap_out)
370 {
371 struct search_provider *provider;
372 struct bitmap *ico_bitmap = NULL;
373
374 /* must be initialised */
375 if (search_web_ctx.providers == NULL) {
376 return NSERROR_INIT_FAILED;
377 }
378
379 provider = &search_web_ctx.providers[search_web_ctx.current];
380
381 /* set the icon now (if we can) at least to the default */
382 if (provider->ico_handle != NULL) {
383 ico_bitmap = content_get_bitmap(provider->ico_handle);
384 }
385 if ((ico_bitmap == NULL) &&
386 (search_web_ctx.default_ico_handle != NULL)) {
387 ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle);
388 }
389
390 *bitmap_out = ico_bitmap;
391 return NSERROR_OK;
392 }
393
394
395 /* exported interface documented in desktop/searchweb.h */
search_web_select_provider(int selection)396 nserror search_web_select_provider(int selection)
397 {
398 struct search_provider *provider;
399 struct bitmap *ico_bitmap = NULL;
400
401 /* must be initialised */
402 if (search_web_ctx.providers == NULL) {
403 return NSERROR_INIT_FAILED;
404 }
405
406 /* negative value just selects whatevers current */
407 if (selection >= 0) {
408 /* ensure selection lies within acceptable range */
409 if ((size_t)selection < search_web_ctx.providers_count) {
410 search_web_ctx.current = selection;
411 } else {
412 /* out of range */
413 search_web_ctx.current = 0;
414 }
415 }
416
417 provider = &search_web_ctx.providers[search_web_ctx.current];
418
419 /* set the icon now (if we can) at least to the default */
420 if (provider->ico_handle != NULL) {
421 ico_bitmap = content_get_bitmap(provider->ico_handle);
422 }
423 if ((ico_bitmap == NULL) &&
424 (search_web_ctx.default_ico_handle != NULL)) {
425 ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle);
426 }
427 /* update the callback with the provider change. Bitmap may
428 * be NULL at this point.
429 */
430 guit->search_web->provider_update(provider->name, ico_bitmap);
431
432
433 /* if the providers icon has not been retrieved get it now */
434 if (provider->ico_handle == NULL) {
435 nsurl *icon_nsurl;
436 nserror ret;
437
438 /* create search icon url */
439 ret = nsurl_create(provider->ico, &icon_nsurl);
440 if (ret != NSERROR_OK) {
441 return ret;
442 }
443
444 ret = hlcache_handle_retrieve(icon_nsurl, 0, NULL, NULL,
445 search_web_ico_callback,
446 provider,
447 NULL, CONTENT_IMAGE,
448 &provider->ico_handle);
449 nsurl_unref(icon_nsurl);
450 if (ret != NSERROR_OK) {
451 provider->ico_handle = NULL;
452 return ret;
453 }
454 }
455
456 return NSERROR_OK;
457 }
458
459 /**
460 * callback for hlcache icon fetch events.
461 */
462 static nserror
default_ico_callback(hlcache_handle * ico,const hlcache_event * event,void * pw)463 default_ico_callback(hlcache_handle *ico,
464 const hlcache_event *event,
465 void *pw)
466 {
467 struct search_web_ctx_s *ctx = pw;
468
469 switch (event->type) {
470
471 case CONTENT_MSG_DONE:
472 NSLOG(netsurf, INFO, "default icon '%s' retrieved",
473 nsurl_access(hlcache_handle_get_url(ico)));
474
475 /* only set to default icon if providers icon has no handle */
476 if (ctx->providers[search_web_ctx.current].ico_handle == NULL) {
477 guit->search_web->provider_update(
478 ctx->providers[search_web_ctx.current].name,
479 content_get_bitmap(ico));
480 }
481 break;
482
483 case CONTENT_MSG_ERROR:
484 NSLOG(netsurf, INFO, "icon %s error: %s",
485 nsurl_access(hlcache_handle_get_url(ico)),
486 event->data.errordata.errormsg);
487
488 hlcache_handle_release(ico);
489 /* clear reference to released handle */
490 ctx->default_ico_handle = NULL;
491 break;
492
493 default:
494 break;
495 }
496
497 return NSERROR_OK;
498 }
499
500 /* exported interface documented in desktop/searchweb.h */
search_web_iterate_providers(ssize_t from,const char ** name)501 ssize_t search_web_iterate_providers(ssize_t from, const char **name)
502 {
503 if (from < 0)
504 return -1;
505
506 if ((size_t)from >= search_web_ctx.providers_count)
507 return -1;
508
509 *name = search_web_ctx.providers[from].name;
510
511 return from + 1;
512 }
513
514
515 /* exported interface documented in desktop/searchweb.h */
search_web_init(const char * provider_fname)516 nserror search_web_init(const char *provider_fname)
517 {
518 nserror ret;
519 char *providers;
520 size_t providers_size;
521 nsurl *icon_nsurl;
522
523 /* create search icon url */
524 ret = nsurl_create(default_search_icon_url, &icon_nsurl);
525 if (ret != NSERROR_OK) {
526 return ret;
527 }
528
529 /* get a list of providers */
530 ret = read_providers(provider_fname, &providers, &providers_size);
531 if (ret != NSERROR_OK) {
532 providers = strdup(default_providers);
533 if (providers == NULL) {
534 return NSERROR_NOMEM;
535 }
536 providers_size = strlen(providers);
537 }
538
539 /* parse list of providers */
540 ret = parse_providers(providers,
541 providers_size,
542 &search_web_ctx.providers,
543 &search_web_ctx.providers_count);
544 if (ret != NSERROR_OK) {
545 free(providers);
546 return ret;
547 }
548
549 /* get default search icon */
550 ret = hlcache_handle_retrieve(icon_nsurl,
551 0,
552 NULL,
553 NULL,
554 default_ico_callback,
555 &search_web_ctx,
556 NULL,
557 CONTENT_IMAGE,
558 &search_web_ctx.default_ico_handle);
559 nsurl_unref(icon_nsurl);
560 if (ret != NSERROR_OK) {
561 search_web_ctx.default_ico_handle = NULL;
562 free(search_web_ctx.providers);
563 search_web_ctx.providers = NULL;
564 free(providers);
565 return ret;
566 }
567
568
569 return NSERROR_OK;
570 }
571
572 /* exported interface documented in desktop/searchweb.h */
search_web_finalise(void)573 nserror search_web_finalise(void)
574 {
575 size_t pidx;
576
577 /* must be initialised */
578 if (search_web_ctx.providers == NULL) {
579 return NSERROR_INIT_FAILED;
580 }
581
582 if (search_web_ctx.default_ico_handle != NULL) {
583 hlcache_handle_release(search_web_ctx.default_ico_handle);
584 }
585 for (pidx = 0; pidx < search_web_ctx.providers_count; pidx++) {
586 if (search_web_ctx.providers[pidx].ico_handle != NULL) {
587 hlcache_handle_release(search_web_ctx.providers[pidx].ico_handle);
588 }
589 }
590
591 /* All the search provider data is held in a single block for
592 * efficiency.
593 */
594 free(search_web_ctx.providers[0].name);
595
596 free(search_web_ctx.providers);
597 search_web_ctx.providers = NULL;
598
599 return NSERROR_OK;
600 }
601