<?php

/**
 * choose.php - ChooSE your search engine
 *
 * Let users choose their favorite search engine! Instead of providing
 * a link to a particular web search one can use choose.php which allows
 * users to choose a preferred search engine.
 * The script is meant to be used for instance in blog posts and comments
 * (e.g. "Have you tried to look it up? Most search engines provide good
 * results, see https://wox.at/s/choose.php?q=what+does+RTFM+mean") or in
 * electronic mail communication (for this purpose the script produces a
 * a link to itself to be copied into the message).
 *
 * (License + History: see also end of file)
 *
 * @author     Andreas Schamanek <https://andreas.schamanek.net>
 * @license    GPL <http://www.gnu.org/licenses/gpl.html>
 * @copyright  (c) 2009-2023 Andreas Schamanek
 *
 */

error_reporting(0);

$version '2023-09-27';

// file names and paths (example if NO rewriting rules are set)
// $me='https://wox.at/s/choose.php'; // Link to your installation of choose.php
// $mesuffix='?q=';                   // query suffix which choose.php parses
// $c_action="$me";                   // form action of main form ("c_choose")

// file names and paths (example if rewriting rules are set)
// see https://wox.at/s/readme.htm for Apache rewriting rules
$me='https://wox.at/s';               // Link to your installation of choose.php
$mesuffix='/';                        // suffix is done by rewriting rules
$c_action="$me/";                     // form action of main form ("c_choose")

// CSS for ChooSE (requires absolute addressing if rewriting is used)
$c_css="$me/choose.css";

// provide a link for users to suggest another search engine
// e.g. a mail address 'mailto:pleaseadd@example.net' or
// a web page 'https://example.com/suggest-another-se'
$c_suggest='mailto:as2013+choose2add@schamanek.net';

// link to opensearch description file, also for Firefox "add to your search engines"
// cf. https://developer.mozilla.org/en-US/docs/Web/OpenSearch
// sadly, on Google Chrome/Chromium this works only if ChooSE is served from the
// root of a domain, cf. https://stackoverflow.com/a/52607598/196133
$c_ffseadd="$me/opensearch.xml";

// $formkey is a secret string to check if our own form was used
$formkey='1669d087'// you might want to choose your own string here
$formkey=$formkey.date('Y-m-d'); // makes 1 form valid for up to 24 hours
// we generate a sufficiently safe yet short checksum string
$formkey=str_pad(dechex(abs(crc32($formkey))),8'0'STR_PAD_LEFT);

// homepage of choose.php with source code and instructions
$choose='https://wox.at/s/readme.htm'// if in doubt leave unchanged

// -------------------------------------
// List of definitions of search engines

$engine['duckduckgo']['text']='Duckduckgo';
$engine['duckduckgo']['title']='Privacy-aware meta &amp; and crowd-sourced search';
$engine['duckduckgo']['url']='https://duckduckgo.com/?q=';
$engine['duckduckgo']['accesskey']='D';

$engine['google']['text']='Google';
$engine['google']['title']='Google.com verbatim search';
$engine['google']['url']='https://www.google.com/search?tbs=li:1&q=';
$engine['google']['accesskey']='G'// F and S are not recommended

$engine['bing']['text']='Bing';
$engine['bing']['title']='Microsoft Bing (former MSN Search)';
$engine['bing']['url']='https://www.bing.com/search?q=';
$engine['bing']['accesskey']='B';

$engine['mojeek']['text']='moJeek';
$engine['mojeek']['title']='UK based privacy-aware search engine with its own index';
$engine['mojeek']['url']='https://www.mojeek.com/search?q=';
$engine['mojeek']['accesskey']='J';

// Ask removed 2022-10-08 b/c I never use it and I don't know anyone who does
//$engine['ask']['text']='Ask';
//$engine['ask']['title']='Ask.com Search';
//$engine['ask']['url']='http://www.ask.com/web?q=';
//$engine['ask']['accesskey']='A';

$engine['startpage']['text']='startPage';
$engine['startpage']['title']='Privacy-aware search engine powered by Google and Microsoft Bing';
$engine['startpage']['url']='https://startpage.com/do/search?q=';
$engine['startpage']['accesskey']='P';

$engine['qwant']['text']='Qwant';
$engine['qwant']['title']='France based, privacy-aware search engine';
$engine['qwant']['url']='https://www.qwant.com/?q=';
$engine['qwant']['accesskey']='Q';

// SearX: Much recommended open-source, self-hostable meta search engine
// Run it yourself [ https://searx.github.io/searx/ ] or use one of the
// public instances [ https://searx.space/ ]
// Consider SearXNG - https://github.com/searxng/searxng (a. 2022-04-03)
$engine['searx']['text']='searX<sup>m</sup>';
$engine['searx']['title']='<a href="https://searx.github.io/searx/">Open-source, self-hostable meta search engine</a>';
$engine['searx']['url']='https://searx.be/?q=';
$engine['searx']['accesskey']='X';

//$engine['dogpile']['text']='dOgpile<sup>m</sup>';
//$engine['dogpile']['title']='Meta search; searches Google, Yahoo, Bing, and Ask';
//$engine['dogpile']['url']='https://www.dogpile.com/search/web?q=';
//$engine['dogpile']['accesskey']='O';

$engine['metager']['text']='MetaGer<sup>m</sup>';
$engine['metager']['title']='German meta search engine';
$engine['metager']['url']='https://metager.de/meta/meta.ger3?sprueche=off&eingabe=';
$engine['metager']['accesskey']='M';

$engine['ecosia']['text']='Ecosia<sup>e</sup>';
$engine['ecosia']['title']='Eco friendly search powered by Microsoft Bing';
$engine['ecosia']['url']='https://www.ecosia.org/search?q=';
$engine['ecosia']['accesskey']='E';

$engine['blackle']['text']='Blackle<sup>e</sup>';
$engine['blackle']['title']='Black Google Custom web search';
$engine['blackle']['url']='http://www.google.com/cse?cx=013269018370076798483%3A8eec3papwpi&q=';
$engine['blackle']['accesskey']='H';

$engine['exalead']['text']='exaLead';
$engine['exalead']['title']='France based search engine';
$engine['exalead']['url']='http://www.exalead.com/search?q=';
$engine['exalead']['accesskey']='L';

$engine['yandex']['text']='Yandex';
$engine['yandex']['title']='Popular Russian search engine';
$engine['yandex']['url']='https://yandex.com/yandsearch?text=';
$engine['yandex']['accesskey']='Y';

$engine['yahoo']['text']='yahoo';
$engine['yahoo']['title']='Yahoo.com Yahoo! Search powered by Microsoft Bing';
$engine['yahoo']['url']='https://search.yahoo.com/search?p=';
$engine['yahoo']['accesskey']='';

$engine['etools']['text']='eTools<sup>m</sup>';
$engine['etools']['title']='Swiss meta search (also offers results as PDF)';
$engine['etools']['url']='https://www.etools.ch/searchSubmit.do?query=';
$engine['etools']['accesskey']='T';

$engine['carrot2']['text']='Carrot2<sup>m</sup>';
$engine['carrot2']['title']='Clustering meta search powered by eTools.ch';
$engine['carrot2']['url']='https://search.carrot2.org/#/search/web/';
$engine['carrot2']['accesskey']='C';

// peekier redirects to kagi which wants users to sign up
//$engine['peekier']['text']='peekIer';
//$engine['peekier']['title']='Search engine that anonymously shows previews of results';
//$engine['peekier']['url']='https://peekier.com/#!';
//$engine['peekier']['accesskey']='I';

// Swisscows removed 2022-10-08
// cf. https://forum.awesomealternatives.org/d/501-removed-swisscows-search-engine
// + https://github.com/privacyguides/privacyguides.org/discussions/1047
//$engine['swisscows']['text']='swisscOws';
//$engine['swisscows']['title']='Swiss privacy-aware search engine powered by Microsoft Bing';
//$engine['swisscows']['url']='https://swisscows.com/web?query=';
//$engine['swisscows']['accesskey']='O';

$engine['brave']['text']='bRave';
$engine['brave']['title']='Privacy-aware search engine';
$engine['brave']['url']='https://search.brave.com/search?q=';
$engine['brave']['accesskey']='R';

// to consider
//
// infotiger: very new? very small? (2022-04-30)
//$engine['infotiger']['text']='infotiger';
//$engine['infotiger']['title']='infotiger';
//$engine['infotiger']['url']='https://infotiger.com/search?query=';
//$engine['infotiger']['accesskey']='X'; // F and S are not recommended
//
//$engine['gigablast']['text']='gigablast';
//$engine['gigablast']['title']='gigablast';
//$engine['gigablast']['url']='https://www.gigablast.com/search?q=';
//$engine['gigablast']['accesskey']='X'; // F and S are not recommended
//
// presearch: meta search which links to many others like ChooSE but many more
//$engine['presearch']['text']='presearch';
//$engine['presearch']['title']='presearch';
//$engine['presearch']['url']='https://presearch.com/search?q=';
//$engine['presearch']['accesskey']='X'; // F and S are not recommended
//
//$engine['you']['text']='You.com';
//$engine['you']['title']='';
//$engine['you']['url']='https://you.com/search?q=';
//$engine['you']['accesskey']='X'; // F and S are not recommended
//
//$engine['neeva']['text']='Neeva';
//$engine['neeva']['title']='';
//$engine['neeva']['url']='https://neeva.com/search?q=';
//$engine['neeva']['accesskey']='X'; // F and S are not recommended
//
//$engine['fireball']['text']='fireball';
//$engine['fireball']['title']='Germany based';
//$engine['fireball']['url']='https://fireball.de/search?q=';
//$engine['fireball']['accesskey']='X'; // F and S are not recommended

/*
$engine['']['text']='';
$engine['']['title']='';
$engine['']['url']='';
$engine['']['accesskey']='X'; // F and S are not recommended
*/

// removed 2014-05-07, doesn't work for me, neither Yippy nor Clusty
// 2016-03-30: Yippy reappeared
// 2016-09-11: Yippy removed since it does not provide GET
// 2020-12-12: Considered to re-add, at least works, has clustering, uses https
// but ultimately decided that it's not worth as long as there are alternatives
//$engine['yippy']['text']='yippy<sup>m</sup>';
//$engine['yippy']['title']='Clustering meta search';
//$engine['yippy']['url']='https://yippy.com/search?query=';
//$engine['yippy']['accesskey']='';

// unbubble is sadly gone for good in March 2019
//$engine['unbubble']['text']='unbubble<sup>m</sup>';
//$engine['unbubble']['title']='Privacy-aware meta search engine';
//$engine['unbubble']['url']='https://www.unbubble.eu/?q=';
//$engine['unbubble']['accesskey']='';

$enginedefault='duckduckgo';

// the field to set a focus on (w/ Javascript) if ?q= is set
// (if ?q= is empty focus is on "c_query")
$focus='c_engine_1';

function 
sl($str) { return get_magic_quotes_gpc() ? stripslashes($str) : $str; }
function 
hsc($str) { return htmlspecialchars($strENT_NOQUOTES); }
function 
hscq($str) { return htmlspecialchars($str); }

isset(
$_REQUEST['c_query']) && $query hsc(sl($_REQUEST['c_query']));
isset(
$_REQUEST['c_engine']) && $chosen $_REQUEST['c_engine'];

// if a search engine has been chosen and all is set
// redirect user to chosen engine

if (!empty($query) && ($_POST['c_submit'] == 'Search')
    && (
$_POST['c_key'] == $formkey) && (!empty($chosen)) ) {
    
$redirect $engine[$chosen]['url']
        . 
urlencode(sl($_REQUEST['c_query']));
    
// dirty workaround for carrot2
    
if ($chosen=="carrot2"$redirect str_replace('+',' ',$redirect);
    
header("Location: $redirect");
    exit();
}

// if a generic search was requested strip slashes (sl), encode + prepare,
// else reset variables to point to the script itself

$q = isset($_REQUEST['q']) ? sl($_REQUEST['q']) : '';
if (!empty(
$q)) {
    
$qu urlencode(hsc($q));
    
$linktext strlen($qu) > 49 ?
        
"$me$mesuffix".substr($qu,0,48).'...'
        
"$me$mesuffix".$qu;
    
$link "$me$mesuffix".$qu;
} else {
    
$linktext $me;
    
$link $me;
    
// while we are here
    
$focus 'c_query';
}
$q hscq($q);

// build list of options/search engines

$options ''$engine_number 0;
// see if user has a favorite engine
$e = isset($_REQUEST['e']) ? sl($_REQUEST['e']) : $enginedefault;
// check that it is valid
(!isset($engine[$e]) || empty($engine[$e])) && $e $enginedefault;

foreach (
$engine as $engine_k => $engine_v) {
    
$engine_number++;
    
$checked = ($e == $engine_k) ? ' checked="checked"' '';

    if (isset(
$_REQUEST['ext'])) {
        
// show extended list of options
        
$engine_text preg_replace('/<su[bp]>.<\/su[bp]>/','',$engine_v['text']);
        
$options .= '<label class="choice" for="c_engine_' $engine_number
        
"\"><span class=\"option\"\n"
        
'><input id="c_engine_' $engine_number '" name="c_engine" '
        
"type=\"radio\" value=\"$engine_k\""
        
. (($engine_v['accesskey'] == '') ? '' " accesskey=\"$engine_v[accesskey]\"")
        . 
"$checked\n/> <a href=\"$engine_v[url]\"> $engine_text</a>: $engine_v[title] "
        
"[<a href=\"$c_action?e=$engine_k\" title=\"Access key + URL with "
        
"pre-selection for $engine_text\">"
        
. (($engine_v['accesskey'] == '') ? '<span class="nokey">no key</span>' $engine_v['accesskey'])
        . 
"</a>]</span></label><br>\n";
    } else {
        
// show default compact list of options
        
$options .= '<label class="choice" for="c_engine_' $engine_number
        
'" title="' strip_tags($engine_v['title'])
        . ((
$engine_v['accesskey'] == '') ? '">' " [$engine_v[accesskey]]\">")
        . 
"<span class=\"option\"\n"
        
'><input id="c_engine_' $engine_number '" name="c_engine" '
        
"type=\"radio\" value=\"$engine_k\""
        
. (($engine_v['accesskey'] == '') ? '' " accesskey=\"$engine_v[accesskey]\"")
        . 
"$checked\n/> $engine_v[text]</span></label>\n";
    }
}

// prepare rest of HTML form

// $c_suggest: ignore the whole thing if not set
if (isset($c_suggest) and !empty($c_suggest)) {
    
$c_suggesttext="<b>&middot;</b> <a href=\"$c_suggest\">suggest another</a>";
} else {
    
$c_suggesttext='';
}

// write out all the HTML from head to footer

echo <<<EOT
<!doctype html><html lang="en">
<head>
 <meta charset="utf-8">
 <title>ChooSE your search engine</title>
 <meta name="viewport" content="width=device-width,initial-scale=1.0">
 <link rel="stylesheet" type="text/css" href="
$c_css" media="all">
 <link title="ChooSE" type="application/opensearchdescription+xml" rel="search" href="
$c_ffseadd">
 <script type="text/javascript">
  function AddFFse(url){window.external.AddSearchProvider(url);}
 </script>
</head>
<body onLoad="updateLink()">

<center><div class="choose">
 <h1>ChooSE your search engine</h1>
 <form id="c_choose" name="c_choose" method="post" action="
$c_action">
 <table><tr class="query">
  <td class="l">Search for</td>
  <td><input id="c_query" name="c_query" type="text" size="59" maxlength="510" value="
$q" accesskey="F" onKeyUp="updateLink()" onMouseMove="updateLink()"
  /><!--[if IE]><input type="text" style="display: none;" disabled="disabled" size="1" /><![endif]--></td>
  </tr><tr class="engine">
  <td class="l">with</td>
  <td class="engine">
$options
  </td></tr></table>
  <input type="hidden" name="c_key" value="
$formkey" />
  <p><input class="button" type="submit" name="c_submit" value="Search" accesskey="S" /></p>
 </form>
</div>

<div id="c_link"><a href="
$link"><code>$linktext</code></a></div>

<div id="footer">
 press alt-shift-g for google, alt-shift-b for bing, ... enter to search
<br />
 <a href="
$me" title="ChooSE your search engine" rel="sidebar">bookmark</a>
 <b>&middot;</b> <a href="javascript:AddFFse('
$c_ffseadd')">add to your search engines</a>
 <b>&middot;</b> <a id="c_linkext" href="
$me?ext">extended list</a>
 
$c_suggesttext
<br />
 <a href="
$choose" title="ChooSE">ChooSE</a> v$version
 (c) <a href="https://wox.at/as/" title="Go to homepage of Andreas Schamanek">andreas schamanek</a>
</div>
</center>
<script type="text/javascript">
document.forms['c_choose'].elements['
$focus'].focus();
function updateLink() {
 q = new String();
 q = encodeURI(document.c_choose.c_query.value);
 q = q.replace(/\+/g,'%2B').replace(/%20/g,'+').replace(/\?/g,'%3F').replace(/&/g,'%26').replace(/#/g,'%23');
 if(q.length == 0) return;
 if(q.length > 49) {t=q.substring(0,48)+"...";} else {t=q;}
 // the following lines should correspond to the affected HTML code
 t1 = '<a href="
$me$mesuffix'+q+'"><code>$me$mesuffix'+t+'</code></a>';
 document.getElementById('c_link').innerHTML = t1;
 document.getElementById('c_linkext').href = '
$me$mesuffix'+q+'?ext';
}
</script>

</body></html>

EOT;

/*
 * License
 *
 * This script 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This script 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 script; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA

 * History
 *
 * 2009-11-30  first buggy "punish me" alpha release candidate
 * 2010-05-30  code cleanup, added ask.com, made source available too
 * 2010-06-22  fixed and cleaned up some paths and file names
 * 2010-06-23  added sl() to stripslashes() only if magic_quotes_gpc is set
 * 2010-07-05  improved handling of '&'
 * 2010-07-19  fixed handling of '&' and '+', removed Clusty search option
 * 2010-07-27  fixed fix relating handling '+' (bug introduced 2010-07-19)
 * 2010-11-05  re-added Clusty as Yippy search, added blekko.com search
 * 2010-11-05  added support for search options with no accesskey defined
 * 2010-11-05  fixed handling of '#'
 * 2011-01-21  Forestle search superseded by Ecosia.org; updated Blackle
 * 2011-05-25  added Yandex - popular Russian search engine
 * 2011-06-06  added encoding of "?"; added https://encrypted.google.com/
 * 2012-01-22  update ?ext link dynamically to include search text
 * 2012-03-02  removed Scroogle + Yauba, added DuckDuckGo + StartPage
 * 2012-04-01  added <link type=opensearchdescription+xml to headers
 * 2015-03-17  fixed encoding bug, changed URL for dogpile
 * 2015-05-22  Blekko is gone
 * 2016-03-30  added Qwant, updated Ixquick, re-added Yippy
 * 2016-09-11  added Searx provided by searx.me, re-removed Yippy
 * 2016-10-10  changed several links to https
 * 2017-04-18  replaced JavaScript bookmark me code with rel="sidebar"
 * 2018-03-05  added Unbubble + FindX
 * 2018-05-24  removed Dogpile because it tracks users' clicks
 * 2018-11-16  ixquick forwarding to startpage, findx closed down
 * 2018-12-26  updated URL for metager.de
 * 2019-03-15  Added eTools.ch, removed Unbubble
 * 2019-10-13  Fixed URL for ecosia.org
 * 2020-12-12  Various tweaks, added Carrot2 + Mojeek, fixed Qwant; fixed opensearch
 * 2021-10-06  Added Swisscows.com
 * 2022-01-16  Added Brave.com
 * 2022-10-08  Removed Ask.com b/c it won't be missed, removed Swisscows
 * 2023-06-23  Removed peekier.com b/c it redirects to kagi.com which requires signup
 *
 */