QQSearch
From NarcWiki
QQSearch is a projlet inspired by Jon Eveland's qqint, and serves approximately the same purpose, only better.
Contents |
[edit] Rationale
QQSearch, and qqint, on which it is based, function similarly to a "Unix Shell of the Internet" crossed with centralized bookmark storage. In a nutshell, one can type in commands which expand to URLs, which your browser is then redirected to.
The idea is you should be able to type in, for example, "google search term", and arrive at http://google.com/search?q=search+term. However, an interesting extension of this format is using Firefox QuickSearches:
- right-click the search field and click "Add a Keyword for this Search..."
- type anything in the Name field and "qq" in the Keyword field, then click "Add"
- open a new tab and type "qq google search term", it should behave the same as the example above.
Something similar can be done in most other browsers, generally by defining the form in question as a "Search Engine" and using the search input instead of the address bar.
[edit] The Problem with qqint
There isn't really a big problem with qqint, just a few minor niggles:
First of all, qqint lives on Mr. Eveland's server, and writes logs in there. If he wanted, he could grep the logs and form an idea of what each IP that used qqint was searching for.
Secondly, qqint is not easily extensible; this makes perfect sense for a public service, but is terrible for personal use, since everyone has a different set of websites they visit.
[edit] How QQSearch fixes these problems
Instead of being hosted by someone else, QQSearch is built to be hosted by yourself -- whoever you are. The install process will consist of extracting and uploading a few pieces of code to your hosting provider, and then configuring as needed. For privacy, the hosting provider can be your own computer -- just download and install Apache and PHP and you'll be good to go.
[edit] Specification
[edit] File Structure
qq: . .. builtins/ inc/ index.php qq/builtins: . .. add.inc del.inc list.inc help.inc [etc] qq/inc: adodb/ base.inc base_class.inc conf.inc engine.inc sql.inc
[edit] Database structure
- Database: qqsearch
- qqsearch.commands:
Create Table url_mappings ( keyword Varchar(20) Not Null Primary Key, url Varchar(1023) Not Null -- or equivalent );
- (optional addon) qqsearch.aliases:
Create Table aliases ( keyword Varchar(20) Not Null Primary Key, command Varchar(255) Not Null );
[edit] Database notes
The database connection will use the ADOdb abstraction layer to allow for deployment in mostly any web serving environment that supports PHP. A good portable recommendation would be to use it with a SQLite database that stays together with the code at all times.
[edit] Implementation details
There are three types of commands:
- built-ins: these exist as .php files in the builtins/ directory. They should define important bits such as "add", "list", "delete", and so on.
- URL mappings: exist in the url_mappings table of the database. These are pure associative keys -- keyword -> url, with some argument interpolation (see below).
- aliases: defined in the aliases table, these allow a few tricks similar to bash aliases, like redefining a command to mean "command + parameter". They will however not support any argument interpolation -- instead, the arguments will be passed to the command actually executed.
[edit] Argument interpolation
This is what allows you to have a "google" keyword that translates to "http://www.google.com/search?q={{*}}".
Examples:
- url/script?parameters={{*}} -- to pass all parameters as a single (URL-encoded) string.
- url/script?param1={{1}}¶m2={{2}} -- to pass each parameter separately (e.g. http://www.narc.ro/ottd-tun-calc.php?tl={{1}}&gl={{2}})
- url/script?param1={{1}}&others={{2-}} -- the first parameter is passed separately, the remainder are lumped together.
- url/script?params={{1-4}}&other={{5}} -- the first four parameters are passed together, the fifth separately.
[edit] List of default built-ins
- add: add a new command-url mapping. Syntax: add <command> <url>. Adding an existing command should replace it.
- del: delete a command. Syntax: del <command>
- list: list all commands (built-ins, URL mappings and aliases). Syntax: list
- alias: add a new alias, if the aliases table exists. Syntax: alias <alias_name> <command (with or without (static!) parameters)>. New alias with the same name replaces the old one. An alias can refer to a command or a builtin, but not another alias.
- unalias: delete an alias, if the aliases table exists.
- help: Gives details about a built-in, command, or alias. Syntax: help <something>. Will list multiple entries with the same name (such as an alias with a same-named command). Will also follow aliases and add "help alias_target" to the output.
[edit] Search order
Since it's potentially possible to have a built-in, a command, and an alias, all with the same names, disambiguization is necessary. Thus, when looking up a command, the search order will be as follows:
- if a builtin by the given name exists, it is executed; else,
- if an alias by the given name exists, it is expanded; if the resulting command is a built-in, it is executed; else,
- if a URL mapping by the given name exists, it is executed;
- otherwise, an error is returned.
Note that this gives the following properties:
- Built-ins cannot be shadowed by anything.
- Aliases cannot point to other aliases (preventing an infinite loop for cases like "alias google google something", which is intended to make "google" translate to "google something <rest of parameters>").
- Aliases can shadow commands by the same name, enabling the above hack, which can be handy sometimes.
[edit] Implementing a built-in
There are two possible schools of thought here: the object-oriented approach, and the other one. In the case of the OO approach, there would be a base class called "BuiltinCommand", or similar, and all the built-ins would be either subclasses, or (more likely) instances of that class.
The other approach would be to have each built-in command define a struct such as $command, with [name], [short_desc], and other values.
These two approaches are identical in that they both require that the commands eventually be listed in a global array, and so I will pick the OO way of doing it for this case. The class will be QQBuiltinCommand and the global array $qq_builtins.
Thus, a skeleton built-in would look like this:
// leading <? and trailing ?> omitted
// skel.inc -- skeleton built-in
class QQBuiltinSkel extends QQBuiltinCommand
{
public function __construct()
{
// Get my short name from the current filename.
$this->name = basename(__FILE__,'.inc');
$this->shortdesc = 'A skeleton built-in command for QQSearch. Does nothing.';
$this->longdesc = <<<EOS
{$this->name} is just an example command, showing a pure skeleton built-in.
It does nothing, but its source shows how to implement a built-in command for
QQSearch.
EOS;
}
public function execute($_ARGS)
//*f This is the workhorse function of the skel built-in.
//* Normally, you'd put your clever whiz-bang smart thing here.
{
echo 'I do nothing.';
}
}
$my_obj = new QQBuiltinSkel();
$qq_builtins[$my_obj->get_name()] = $my_obj;

