corz.org uses cookies to remember that you've seen this notice explaining that corz.org uses cookies, okay!
<?php /* --- ۞---> text { encoding:utf-8;bom:no;linebreaks:unix;tabs:4sp; } */
if (isset($_GET['zoogleimg'])) { zoogleimg(); die(); }
$cz_version = '1.0.2.3';
// if grabbing your prefs from a site-wide init, now is the time..
//require_once $_SERVER['DOCUMENT_ROOT'].'/inc/init.php';
/*
Welcome to corzoogle!
the real-time search engine for humans and their documents
(c) copyright corz.org 2004->tomorrow!
NOTE: corzoogle requires php5 to run.
v- -v- IMPORTANT -v- -v- -v- -v- FOR WEB SITES -v- -v- -v- -v- IMPORTANT -v- -v-
This software performs REAL-TIME searches on the raw data of your
filesystem. Unlike other search systems which "index" content on a
daily/weekly/manual basis, corzoogle searches LIVE. If a file is
altered and you then search, it will appear in the results
*immediately*.
This live raw searching is good, but you don't want it to happen to
a file with passwords in it. corzoogle comes with THREE ways to
protect your passwords. Firstly whole folders can be excluded..
/includes/ are generally excluded, ironically
You can also exclude specific named files from the search,
"config.php" files spring to mind. Lastly, including the word
"nosearch" *anywhere* in any file (a standard comment is ideal) and
that file will NOT be searched.
All these options are completely configurable in the preferences
section below.
-^- -^- IMPORTANT -^- -^- -^- -^- FOR WEB SITES -^- -^- -^- -^- IMPORTANT -^- -^-
If you're using corzoogle to search your own personal hard drive,
documents, mail archive, research notes, or whatever, you can
probably forget all this stuff and just drop this very file into
whatever folder you want to (recursively **) search. of course
you'll need to have a webserver running to use this. [I kneel in the
church of..] Apache is installed as standard on Mac OS X, most
Linux/*nix/BSD distros, and also real easy to install on Windows
boxes too, unless you're ronnie heh. remember, the smaller the area
corzoogle has to cover, the quicker the search will be, so if you
are using corzoogle to search HUGE (20,000+ docu- ments) archives,
you might want to copy corzoogles into the subfolders for rapid
local searches.
Any web server that can run php should have no trouble. even
[*tssssss*] IIs, I guess. it's "built" into most XP's & win2K's.
untested, syat. Apache runs great on windows, and the Apache team
have done a marvellous job with their Windows binary installers.
Windows users might also want to check out something called "WAMP".
With a webserver running on your desktop machine, corzoogle doubles
up as a rapid real-time 'desktop' search engine. Handy.
Feel free to mail me about anything corzoogle-related, putting
'corzoogle' somewhere in the subject header appeases my shitlist
daemon.
hey! it's cz!
;o)
** .. and all the folders inside it, and inside them, and so on.
ps.. hoping you might find customisation desirable, I've designed
and coded corzoogle considering readablility over cleverness,
although there are a couple of clever bits, too.
If you are new to php, maybe even if you're not, there's probably
something of value in my copious commantage. some gags, too. most
importantly, the routines are designed for speed. if something ugly
is faster than something cute, we go with ugly. with a built-in
timer, it's easy enough to test even minute performance variations.
if you see something slow, let me know!
FINAL NOTE: error_reporting is OFF by default. this saves us some
potential needless headaches, but also, of course, means that if for
some reason you can't get corzoogle working, you won't know why!
It's unlikely, but if you ever need to enable error reporting,
search for :errors: in this file and uncomment that line. Then MAIL
ME the error!
If you want a version with loads of debug output lines included,
drop me a mail. This would not only help you, but me!
Then, with my debugger handy, you have the option to spew out wads
of debugging information, which while slowing down your searches,
could prove invaluable in pinning down any server issues you might
be experiencing.
*/
//*> Preferences..
//*> Security Prefs..
/*
Ignore Folders
The important IGNORE folders. Their contents will NOT be searched. Security
aside, this is useful for avoiding image galleries, smilie folders,
etcetera. but you may wish to search for filenames in these places, so it's
up to you..
*/
$ignore_folders = 'err,inc,includes,cgi-bin,private';
/*
You only need to enter one instance of 'inc', (for example) to cover every
'inc/' folder in your entire website.
NOTE: You do not need to add slashes to the start or end of each entry,
though it will do no harm if you do.
ALSO NOTE: You can be more specific, too, eg. 'tools/secret'
*/
/*
Filenames to NEVER search inside, anywhere, ever.
*/
$private = 'config.php,robots.txt,links.php,readme.html,--Slideshow--.php,--Thumbs--.php'; // <-------‹‹ !!:!:!! IMPORTANT !!:!:!!
/* This is especially for files which would normally be *allowed* by
their $extensions, like config.php files */
// :!: note: invisible files (beginning with a dot '.') are NEVER searched :!:
/*
putting "nosearch" (no quotes) anywhere inside a file means it will NOT be scanned */
$dont_search = 'nosearch';
/*
..or some other word or phrase of your choosing.
A simple html comment.. <!-- nosearch --> ..is ideal. CaSe must mach
exactly. obviously, the file you are reading now will never appear in your
search results. of course, in reality, most all of these files are searched
otherwise, how would we know which ones to *display*?
*/
/*
Allowed File Extensions..
File types to search inside.
Separate extensions with a comma, the dot "." is not required.
*/
$extentions = 'htm,html,txt,phps,php,php4,php3,blog,comment,nfo,au3,pl,sh,bat,conf';
/*
You can include extensions for any file which has searchable content.
You can also add the "gz" extension, and have corzoogle examine inside
gzipped pages/archives (note: there is a slight speed penalty for this).
*/
//*> Search Preferences..
/*
searching will stop after this many results.
default: $max_hits = 200;
*/
$max_hits = 200;
/*
perhaps I will add a message along the lines of..
"consider narrowing your search by using more search terms"
/* search between..
[optional] default: $search_between = array('<body','</body>');
by default, corzoogle will search for content anywhere between the <body> and </body> tags.
you can alter this behaviour here, perhaps broadening searches to <html></html> tags, or
whatever you like. you could even make up your own <start-search><end-search> tags. using the
<html></html> tags will always get you more hits, generally, but will degrades the quality of
the results both at the search and result presentation stages.
This works independently of title scoring and descriptions. note: no closing '>' on the
first entry. this is important, particularly for body tags, which sometimes have extra
parameters. <tr> and <div> also come to mind.
if a file doesn't have this tag (and it's allowed by its extension, like a plain text file),
then the whole file is searched. comment out to disable search_between altogether.
*/
$search_between = array('<title', '</body>');
// note: you can mix tags.. $search_between = array('<title','</body>'); for instance
/*
this means "Search Between Tag is Case Insensitive". it's around 3% slower, but if you
have a mix of case in the html tags of your documents (*tsss*) you can enable this
*/
$sbtici = false; // (true/false) default: $sbtici = false;
/* you can leave the above at "false" generally, if you are searching between
<body></body> tags and some document has <BODY></BODY> tags instead,
corzoogle will just search the whole thing
*/
/*
or you can tell corzoogle to ONLY search documents that contain your
$search_between tags. enable this only if you understand the implications,
particularly if you (like me) split you page structures and body content
into separate files, the one with the <body> tags may have no content! this
is highly useful if you utilise custom search_between tags, or want to limit
your searching to only particular kinds of content, however. */
$enforce_between = false; // (true/false) default: $enforce_between = false;
/* filename searching..
[optional] default: $do_filenames = true;
corzoogle can return matches for filenames, too. users only need to
include a word with a dot "." to invoke this filename-searching
behaviour. searching for..
download.php
would return a list of all documents containing the term "download.php"
as usual, but also an additional list of *files* matching
"download.php".
secure index.php
would return documents containing the words "secure" AND "download.php",
and the very same list of matching files as our last query. most likely
successful file-name searching will be best achieved with single terms..
security.txt
you can use wildcard file matching too. a search for..
.html
will return a list of all files matching .html, in other words, all html
files in corzoole's search zone, or any file that contains the term
".html" in its name. it works the other way too, a search for..
html.
achieves exactly the same file search result, though probably with a
great deal less regular page hits above it. this is useful when
searching *only* for matching filenames.
To invoke this file searching behaviour, all you do is add a dot!
This directive also affects the +10 bonus that hits get for matching
each query string in their file name, which is a nice touch. A search
for "security" would match all documents containing that word, but push
those with the words "security" *anywhere* in their file name a little
closer to the top of the pile. 3 matching terms in the filename would be
+30, etc. You can set the bonus here too.
*/
$do_filenames = true;
$filename_bonus = 10;
/*
If you like, you can have corzoogle *always* return matching file names.
This is by request.
*/
$always_filenames = false;
// If $always_filenames is true, shall corzoogle automatically place the dot
// before or after the query? This won't affect the number of file hits returned,
// but will affect the regular results. See above for details?
$anf_hack_after = true;
/*
scoring prefs (ranking) ..
corzoogle ranks hits by the frequency of search terms contained within the document if
the user inputs multiple terms, the search is narrowed down to documents containing ALL
the terms. In other words, "boolean AND".
you can use individual boolean NOT's, for instance..
mac osx sudo box -apple -madness
searches for words containing "mac" AND "osx" AND "sudo" AND "box", but will NOT
return any hits for documents containing the word "apple", or the word "madness".
simple scoring:
each term scores 3 points per occurrence. in a search for..
mac osx sudo box
a document containing nine instances of the word "mac", six instances of the
word "osx", and three instances each of the words "sudo" and "box, would score..
(9*3)+(6*3)+(3*3)+(3*3) = 27+15+9+9 = 63
this simple scoring may be just what you need, but corzoogle's default is to use..
weighted_scoring..
[need] default: $weighted_scoring = true;
*/
$weighted_scoring = true;
/*
with weighted scoring the earlier words score more heavily..
that same search now scores (9*4)+(6*3)+(3*2)+(3*1) = 36+18+6+3 = 60. not 63. read on..
"mac"'s score is capped at $q_word_max (default is 33, set below). no one single
word can score more than $q_word_max points (in either simple or weighted scoring mode)
if there had been fifty hits for "mac", it still would have scored 33 points.
so the actual score is 60.
if someone had searched for the same terms in *this* order..
sudo box mac osx
that same document would have scored (3*4)+(3*3)+(9*2)+(3*1) = 12+9+18+3 = 42
normally the first term scores 4, the second 3, and so on until all terms
score one point.
You can set a higher weight here..
*/
$search_weight = 4; // (integer) default: $search_weight = 4;
/*
individual query term scoring continues up to a maximum of this amount..
(integer) default: $q_max_score = 100;
*/
$q_max_score = 100;
// if you lower this right down, "phrases" will get higher ranking.
// between these few numbers you can completely control your results ranking.
/* regardless of any scoring prefs, exact case-sensitive word matches always scores
an additional 1 point bonus. this is in addition to max scores. if two documents
contain the exact number of matched terms, the one with an exact case-sensitive
match will always pip the other at the post. */
/*
you can also set the value that one single word can achieve in the results. works
in simple or weighted mode. default is one third (33%) of $q_max_score. depending
on your document content, you may want to adjust this, anything in the 20-60%
works well. 33% is good for general usage. if you are doing a lot of single-word
queries, or are using a large $search_weight, you might want to set this a wee bit higher.
*/
$q_word_max = 33; // (integer) default: $q_word_max = 33;
/*
which is a simple cap, set to prevent runaway terms from affecting the results too
much. the examples above were cleverly chosen to produce the same results in both
modes. nah, it was just a "coincedence".
although we do check for "stop-words" (common words like "the", "and", etc), in
reality, users don't input these kinds of words, mostly they are genuinely
searching for real documents!
you can choose your own stopwords here, too.
*/
$stop_words = array ('' // some of these are probably not necessary
,' ',';o','a','A','all','and','are','as', 'at','be','but','by','can','do','don\'t'
,'for','got','have','he','here','I','in','if','is','it','like','me','my','n','no'
,'o','of','on','one','or','out','she','so','t','than','then','that','that\'s'
,'the','The','there','there\'s','these','this','to','too','was','we','with','you'
);
/*
if any of these stop-words are removed from the search, it will be reported back
to the user (optionally). try a few and you'll see. remember to "escape"
(put a backslash in front of) all single quotes inside stop-words here,
'won\'t', for instance. or else use double-quotes around those ones..
"won't", "can't", etc /* I still can't decide which is faster, single or
double quotes. my brain says
single quotes should *always* be faster, as there's less to check for. hmmph. */
// report stop words removed from search (handy for debugging, perhaps)
$report_stop_words = true;
// NOTE: stop-word reporting is only enabled when phrase matching is disabled..
/*
phrase matching..
*/
$match_phrases = true; // (true/false) default: $match_phrases = true;
/*
If the original query is a phrase (two words or more), and all the terms exist in a
document, it is then checked for matching "phrases"
searching for..
Wonkey Man Mac
matches "and I came upon The Wonkey Man Machine and gasped like a silly monkey"
jackpot! whole query phrase exists *in entirety* inside the document,
with the exact SaMe cAsE..
bonus = 20% + (10% * per query term in phrase) = 20% + 30% = 50%
exact matching query phrase of three words, this is a good match.
wonkey man mac
(a case insensitive match) scores 10% + 5%/term = 25%
the longer the matching phrase, the bigger the extra bonus. all these
scores are relative to..
$phrase_max_score
(integer) default: $phrase_max_score = 50;
*/
$phrase_max_score = 50;
/*
this scoring element is in *addition* to $q_max_score, and is in itself a
*relative* score, read on ...
*/
/*
titles scoring..
(true/false) default: $title_scoring = true;
IMPORTANT:
unlike query term scoring, these scores are *not* caps. The numbers given in the examples
below are correct at the default of $title_max_score = 100; If you alter $title_max_score,
you also alter those values, in other words, title scores are *relative* to whatever maximum
value you set here.
This enables us to set a convenient body-to-title ratio, adjusting the level of importance,
or "weight" of either aspect, depending on what _you_ need. you could set this so even the
best matching titles add only another 20, or make title searching THE most important factor,
setting it to 150, 200, or more.
this is how corzoogle scores a document for its document <title>..
if the whole query string is a phrase (two words or more) and matches the title
even in part, (case in-sensitive match), the document scores +20
if that same query string also matches the entire title *exactly* in a case-
insensitive manner, a further 60 points are awarded (80 for this title)
OR
title and query match *exacitaly*, CaSe AND Length, so it's +80. (maxed!)
OR (no "phrases" found)..
the page scores +10 for each query term that's in the title *somewhere*
in a search for..
Mac Madness on Mars
a document with the title "mars madness for your macintosh" would score +30
a document with the title "The mac madness on Mars" would score +80
a document with the title "Mac Madness on Mars" would score +100
*/
$title_scoring = true;
/*
you can set the maximum score (weight) a <title> can achieve. default is 100
*/
$title_max_score = 100;
/* score all titles..
(true/false) default: $score_all_titles = false;
by default, corzoogle will only take titles into consideration if there has already
been a match in the body of the document (or whatever you have set it to $search_between)
a document's <title> will only be scored if it has already been matched by its content.
if you are searching between <body> tags, the header part of the document simlply wont
be scanned for matches unless there has been FULL match somewhere in the body content of
the document (and that means ALL terms in a multiple query).
only documents that are hits because of matching <body> content will be scrutinized for
their title content, then scored and weighted according to its relevance, again giving
the possibility of more fine-tuned results.
if you want, you can direct corzoogle to look at ALL titles, regardless of whether
there was a body text match or not. if this is set to true, even a single term from a
multi-term query will trigger a hit. in some searches there will be a small (2-3%)
performance penalty for this, but if you need to score all titles, here you go..
*/
$score_all_titles = false;
/*
Content As Title
default: $contents_as_title = false;
under certain circumstances, you may want to have corzoogle use the body of the
text as the title for the hit, rather than its filename. This only comes into effect if
the document has no "real" title, and you have set $score_all_titles = true; (above)
this may be useful where you have a lot of text files with intelligible titles.
*/
$contents_as_title = false;
/*
"content as title" length
default: $cat_length = 33;
The title will truncate at this point.
*/
$cat_length = 33;
/*
note: in the absence of a <title> tag, the filename acts as the document's title
*/
/* show scores
(true/false) default: $show_scores = false;
mainly for debugging and tweaking your scoring system. fairly ugly
*/
$show_scores = false;
/*
Search inside php/html tags?
You can choose to not search for text inside html/php elements..
if you are using corzoogle as an onsite "grep", set this to true.
setting to false will generally speed up searches for words like "bottom"
but slow down most other searches slightly.
NOTE: this isn't 100% foolproof. If you have a document with malformed tags
corzoogle (or rather, the php strip_tags function) will err on the side of
caution, and search inside it, ignoring it's tagness, but not always.
this relies on the built-in php tag stripping mechanism which can sometimes
have *unexpected results*.
If your document contains unencoded < and > characters, this will fail.
note AGAIN: this IS NOT 100% foolproof!
If documents are inexplicably not showing up in results where you expect
them to, this is probably the cause. So set it to true. The 'snippets' will
still have the tags stripped from them.
*/
$search_in_tags = false;// (true/false) default: $search_in_tags = false;
// (we'll see!) $allowed_tags = '<pre>';
$allowed_tags = '';
// really, I need to write my own tag-stripping mechanism, the built-in one
// sometimes makes a right mess! (actually, it's getting better these days)
//*> Results Preferences..
/*
A note about how disabling search_between can alter results..
Contrary to what one might imagine, disabling search_between can wildly
alter the quality and quantity of your results. it all depends on how your
pages are designed, and wether or not you enable $search_in_tags (further
down the prefs). when you disable $search_between, corzoogle always scans
the whole document.
Consider this.. you have a php document, and the whole page is contained
within it, the html being output by "echo" commands. if you are searching
between <body> tags, corzoogle will find those and do as you expect. if,
however, you disable $search_between, you are asking corzoogle to scan the
entire document. as the whole document is inside one big <?php tag, if you
also disable $search_in_tags, the document will be ignored in its entirety!
outputing whole documents with echo commands isn't very clever, but still,
folks do it. they'd be better served moving the html to another file and
"including" it. easier to edit too. armed with this information you can
better tweak your searching. also consider custom $search_between tags.
*/
/* Show recent searches..
(true/false) default: $recent_hits = true;
whether to allow "most recent searches" at the foot of the results page.
this is fun, you can see what folks have been searching for. creates live
links, the unfollowed ones being "other people's" searches. interesting
data. I might make it "random recent searches"
*/
$recent_hits = true;
/*
:!: to do this, corzoogle creates a file called ".corzoogles" in the same
folder as this file. If the folder doesn't have write access (likely), you
will get an error. The error message will guide you into the solution.
Probably the best method for "live" sites would be to create the file
locally (by simply corzoogling once) and then upload the ".corzoogles" file
to your website, chmod it to 777 (or use FTP client to set its write
permissions to ALL)
*/
/*
how many recent hits?
we only want to show 'so many' past hits
default: $hits_length = 80; (enough hits to fill 80 characters)
coupled with the "recent-hits" css style, you can pretty much create
any effect here, position them elsewhere on the page, etc..
*/
$hits_length = 80;
/*
hits file
[file path] default: $hits_file = '.corzoogles';
Where to store the recent his information.
By default, it's a file called ".corzoogles" in the same directory as
corzoogle.php, but you can change that here, perhaps to your temp
directory.. $hits_file = '/tmp/.corzoogles';
NOTE: YOU MUST USE THE FULL SYSTEM PATH!!!
i.e. if your site *actually* lives in /var/www/vhosts/mysite/httpdocs/ and
you want your hits file in your http://mysite/inc/log/ folder, you would
use..
$hits_file = '/var/www/vhosts/mysite/httpdocs/inc/log/.corzoogles';
If you are using the system's temp folder, note two things:
* it may be periodically cleaned out by the system, and your recent hits
could occasionally reset - no biggie, and..
* on a shared server, there may be other corzoogle users, so it's a
good idea to use a unique name, like .ht_mydomain_corzoogles
*/
$hits_file = '.corzoogles';
// Use rel=nofollow on these links?
$no_follow_recent_hits = true;
/*
use preview snippets.. [default: $use_snippets = true;]
corzoogle grabs previews from the body of the text, one for each search
term, and strings them all together to create a nice preview snippet. this
is good.
*/
$use_snippets = true;
/*
Snippet Length
The total length of the preview can be configured here. the default is 300.
small is best. this remains constant regardless of the number of query
terms; corzoogle will grab a small chunk of text from the query term
onwards.
If there were five words in the query, it will create five snippets, each (
$snippet_length / 5 ) characters long and join them together.
*/
$snippet_length = 300;
/*
Descriptions in result text.. [bool][default: $use_descriptions = true;]
If a <description> meta tag exists in the document, we can optionally
instruct corzoogle to display it directly between the title and the preview
snippet of each result.
*/
$use_descriptions = true;
/*
Highlight Exact Hits [bool][default: $highlight_exact_hits = false;]
add fairly ugly highlights to exact case-matched words in the preview..
If you searched for BigBob, then "bigbob" and "BIGBOB" are still hits, of course,
and get their usual coloring in the preview, but "BigBob" gets a yellow highlighter-
pen type mark through it. *eew*
*/
$highlight_exact_hits = false;
/*
Preview has "context" [bool][default: $preview_has_context = true;]
Normally we return the preview snippet starting directly at the query term.
In a search for "mac", the preview might begin.. "mac news and information.
this is the most up-to-date...".
If we set this to true, the preview snippet begins *before* the query, and
the query is placed some way into the snippet, so now the same result might
read.. "click here for the latest mac news and information. this is the most
up-to-date..." which gives the result some context, aids clarity.
*/
$preview_has_context = true;
/*
Clean-Up Entities.. [bool][default: $clean_entities = true;]
Attempt to clean up entities inside the snippets - helps if you like your
results to validate no matter how messed up the searched documents are! Will
use utf-8 encoding, where available.
*/
$clean_entities = true;
/*
Note: if you run a non-english site, this may or may not preserve the
character encoding, depending on which version of php you run. Suck it and
see.
By default, corzoogle presents the query term one quarter of the way into
the snippet. this is expressed as 1/4, so the context ratio is 4. If you'd
like the term to appear earlier in the snippet use 5 or 6. To have the term
appear exactly half way through the snippet, use 2.
Because of the law of diminishing thingamybobs, it is impossible to get the
query term to appear right at the start by using some HUGE number**, just
make $preview_has_context = false!
*/
$context_ratio = 5;
// ** well, maybe a Really Big Number!
/* Deepness: [bool][default: $deepness = false;]
If you are running corzoogle inside a subfolder of the web server, but want to
search from the dir ABOVE that, you can specify that here. Your results will
be a adjusted accordingly. (dir = directory = folder)
remember: specifying deepness means you search from the dir ABOVE the dir that
corzoogle.php lives IN THE REAL FILE SYSTEM, symbolic links are followed.
*/
$deepness = false;
/*
okay, deepness examples..
1) I have a server.. "soho" (http://soho) .. this is a local webserver. In
it I have a copy of the php documentation, in a folder called "phpman". I
want to search only in the documentation
$deepness = false;
corzoogle URL = http://soho/phpman/corzoogle.php
2) Same server, I have a virtual host called "dev", inside that I have a
folder called "search". I have renamed corzoogle to "index.php" and dropped
it into the "search" folder. I want to search from http://dev (more
exciting developer documents)
$deepness = true;
corzoogle URL = http://dev/search/
3) I have a virtual host called "corzorg" (http://corzorg) and I want to
search it ALL
$deepness = false;
corzoogle URL = http://corzorg/corzoogle.php
Hopefully that makes things clear
:!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!:
:!: you can drop corzoogle into ANY folder at ANY depth and it will, by default, produce :!:
:!: correct results, correct URL's. "deepness" is only needed for searching from ABOVE. :!:
:!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!:
I just thought I'd do a cute notice box.
/*
embedded "corzoogle" image [string][default: $logo = 'embedded';]
Lots of folks don't realise that the image is embedded *inside* this script.
yes, this is funky, and does make corzoogle totally portable. however, if
you run corzoogle on a live website, remember, the image is generated for
*every* hit on corzoogle.php.
In other words, using a static image is preferable for live web sites
(download one from my site, or save it from any corzoogle page).
This way, the user's web browser cache will keep the image handy, save you
some bandwidth and CPU. Sure, not a lot, but it all mounts up. It will also
appear quicker to the user. On a local site/intranet, this isn't an issue.
Options are 'embedded' or '/path/to/image.png'..
$logo = 'embedded';
$logo = '/img/corzoogle_sm.png';
*/
//$logo = '/img/corzoogle_sm_blue.png';
$logo = 'embedded';
/*
Show user options?
Currently there are options for:
Match whole words.
*/
$do_user_options = true;
/*
User Option: Return only matching WHOLE Words.
If you search for "prefer", you will get results back for documents that
contain "preference", "preferred" and so on. If you prefer, you can have
corzoogle return only hits for matching whole words.
Depending on your site's content, this may be more useful.
If you don't want to enable your users to search for only whole words, set
this to false */
$option_whole_words = true;
/*
You can also override this behaviour and disable the whole_word option in
the process.. with this set to true, the previous setting is ignored. You
can still have user options, but "whole words" will not be among them. Set
to false to enable the user to choose.
*/
$always_whole_words = false;
/*
Currently Whole Words is the only user option, so these two preference are
redundant.
*/
//*> File preferences..
/*
Extension Mangling
Let's say you have a php site, and you keep your content in files ending
.htm, but if they are accessed directly, you redirect (with htaccess) to the
php container page. now you can have corzoogle alter the extension for you,
so the link goes directly to the php file.
Another use is for file.php.comment files, rather than the raw comment file,
you want the user to load the page it is attached to. It is also handy for
sending hits for /readme.htm and /header.html and such to plain "/".
uncomment to use this feature..
*/
//$mangle = array( '.htm'=>'.php' , '.php.comment'=>'.php#comments');
// note the double extension on the third entry. handy!
// also note, mangling doesn't operate on your ".blog" files, if you use that.
/*
corzblog..
If you run corzblog (my blogging system) and want your blogs and blog
archives to be searchable (or at least, have corzoogle return valid links to
them) alter this to whatever extension you use for your blogs, usually
'.blog'
*/
$blog_file_name = 'blogz.blog';
/*
corzoogle can translate a file hit such as..
/blog/arc/2003-nov.blog
into..
/blog/index.php?archive=2003-nov
you could use this mechanism for other generated pages, too, but you'd
need to slightly alter the code inside the look_in_file function.
Perhaps plug-ins are in corzoogle's future!
Enter the name of blog archive directory (inside blog folder)..
*/
$arc_name = 'arc';
/*
Blog Flat Links?
Are you using corzblog's flat-links capability (with mod_rewrite), enable this.
The above link would then be: /blog/2003-nov
*/
$blog_flatlinks = true;
//*> Hosting Preferences..
/*
Embedded corzoogle! [bool][default: $embedded = false;]
It's always been easy enough to run corzoogle from inside another page, but
now it's even easier! just set this to true, and include corzoogle in your
page...
include 'corzoogle.php';
Remember to enclose in php tags if it isn't already.
corzoogle will return its results in the same given space. Remember to set
"deepness" if you are in a sub-folder of your site, e.g..
http://mysite.org/search/index.php
And also to use a *real* image for your logo, or else the logo will be
disabled. You might also want to do some (small) CSS for the "#description"
text.
*/
$embedded = false;
/*
Page Styles
If NOT running embedded, you will probably want to include some styles sheets.
A sample is included.
*/
$page_styles = 'corzoogle.css';
/*
NOTE:
You can include multiple sheets, separating them with commas, e.g..
$page_styles = '/styles/site.css,corzoogle.css';
*/
/*
Bottom Fix Styles
corzoogle can include an extra sheet on pages where there is less than
a standard "page" of output, which is to say, the main search page, and
a results page with less than three $bfix_limit (next pref) hits returned.
*/
$bfix_styles = '/inc/css/bfix.css';
/*
Bottom Fix limit
*/
$bfix_limit = 3;
// more results than this gets the regular styles.
/* Site Notice.
You can put a notice under the corzoogle search form if you like.
This only appears on the front page, not with the results.
The css style (class) is named "corzoogle-notice".
As well as site notices, it's a fine place to put search tips!
(note: corzoogle tips are syndicated - feel free to include!)
*/
$notice = '';
/*
Custom Site Header (for when not embedded).
Enter the FULL path, e.g. $_SERVER['DOCUMENT_ROOT'].'/inc/header.php'
Leave empty to disable.
*/
$custom_header = '';
// Custom Site Footer (for when not embedded)..
// Leave empty to disable.
//
$custom_footer = '';
// If you wish to support old IE browsers, you can include an HTML5 shiv..
// (leave blank to disable)
$html_js = ''; //$html_js = '/inc/js/html5.js';
/*
This should be fine as is. You might know better.
This is used for checking referrer information (for hot-linking)
and also for building the file-paths of $filename matches..
*/
$domain = $_SERVER['HTTP_HOST'];
/*
Hot-Linking. FALSE! FALSE! FALSE!!! okay, maybe true.
You can even *allow* hot-linking, if you like (crazy person)..
I'm easing up about this. it's not so bad, you know, folks could search
your content from other sites. why not! okay dammit, true it is..
*/
$allow_hot_link = true;
/*
if you do *not* allow hot-linking, what message will be displayed
when someone attempts to hot-link? .. */
$hot_link_message = 'thanks for droppping by!';
/* Search Redirection..
[optional] default: $redirect = false;
By default, corzoogle sends the queries to itself, of course.
Optionally, you can have corzoogle redirect your queries to another corzoogle
engine, even one existing in a different domain or website. (that you own)
Remember, if the receiving corzoogle engine is in a different domain from the
sending corzoogle, you must allow hot-linking at the receiving end.
*/
$redirect = false;
$redirect_to = 'http://'.$domain.'/physics/search/index.php';
/* Of course, a simple form would suffice!
See inside echoform() for more details */
/*
Notify web master on searches [bool][default: $notify = false;]
Someone asked for this, a nice idea if you are one of those webmasters who
*really cares* about their visitors. email the webmaster on every search.
let me know if you want more features here currently corzoogle will report
what was searched for, and the number of hits they got.
*/
$notify = false;
/*
Mailer Line-Breaks [default: $mail_lb = PHP_EOL;]
What a pain, having to specify this.
Normally we use full windows (\r\n) linebreaks between (additional) x-headers
(when using postfix php wrapper you might be better to use PHP_EOL, to work
around a bug there) we always use regualar UNIX \n linebreaks for body text.
*/
$mail_lb = PHP_EOL;
/*
Notification Mail Prefs..
You might need to mess about with this.
The mail function will insert this into the mail..
The search query was.. "<search query here>" [XX hits]
*/
$to_addy = "me@example.com";
$from_addy = "corzoogle@example.com";
$email_subject = "corzoogler!";
$email_body = "There has been a new corzoogle search! @ http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']."\n\n";// optional information..
$email_body .= "Their IP was: ".$_SERVER['REMOTE_ADDR']."\n";
$email_body .= "Their Browser was: ".@$_SERVER['HTTP_USER_AGENT']."\n";
// it's usually best to limit extra headers to only one or two lines
$xmail_headers = 'X-Mailer: corzoogle realtime search engine v'.$cz_version.' (php v'.phpversion().")".$mail_lb;
//$xmail_headers .= "Reply-To: $from_addy".$mail_lb;
/*
bot hits (they follow our "latest hits", too.)
if the user agent is in this list, the webmaster won't get notified
$nomail_list = array(
'ai_archiver',
'almaden.ibm.com/cs/crawler',
'ia_archiver',
'Ask Jeeves/Teoma',
'BecomeBot',
'ConveraCrawler',
'Exabot@exava.com',
'FAST Enterprise Crawler',
'Feedster Crawler',
'FeedValidator',
'findlinks/',
'gazz/',
'Gigabot',
'Girafabot',
'globalspec.com/Ocelli',
'googlebot',
'Jetbot',
'larbin_',
'msnbot',
'NG/2.0',
'nhnbot@naver.com',
'slurp',
'statbot@gmail.com',
'Syndic8',
'Yahoo-MMCrawler',
'YahooFeedSeeker',
'ZyBorg'
);
*/
// alternatively, you can use an external list.
// uncomment this line and enter the full path from your server root..
//$nomail_list = '/inc/data/bot_agents.txt';
// The bot list is a simple plain text list of strings that will match bots separated by
// UNIX "\n" line breaks. At corz.org, other scripts also consult this list.
/*
:!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!:
:!: You are finished here now, please save this file and corzoogle for "pass" to :!:
:!: see if any of your passwords turn up. No really, please do that right now! :!:
:!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!::!:
That's it!
I hope you enjoy using corzoogle as much as I do!
for now..
;o)
*///:end: of ordinary user preferences
//*> Debugging Prefs..
/*
report no errors.
I made this the default, saves email.
:!: on real live webservers, PHP error reporting should ALWAYS be OFF, but
amazingly, often isn't. You can set this in your scripts, or in your root
.htaccess file. You'll definitely want to enable error *logging* instead.
*/
//error_reporting(~0); // :errors: for debugging, uncomment this AND PLEASE MAIL ME THE OUTPUT!
/*
Display All Paths
Have corzoogle spit out every folder it has searched inside.
For debugging only!
*/
$display_path = false;
/*
Maximum Execution Time
For HUGE sites/archives (TB's), use whatever you like.
(we are waiting for the filesystem to do a *full* text search)
On sites/regular sized archives (say, under 5000 documents), you can usually
ignore this, generally, times will be in the 0.01s-1.0s range, well within
php's default of 30 seconds.
Even my home dev server (an old laptop) can corzoogle 5000 documents
in under .5s!
*/
ini_set ('max_execution_time', 60);
/*
Safe Level
This has nothing to do with security default: $safe_level = 0;
Normally, you wont ever need to touch this setting, but if you are having
trouble getting corzoogle to work on your site, you could try increasing
this number to 1 or 2 and see if that fixes it. Generally there will be
slight performance losses for enabling these safety features. At any rate,
switch on error reporting and send me a copy of any errors you get. ta.
*/
$safe_level = 0;
//*> End Preferences
/*
okay, let's shake this thing.. */
//*> Begin..
ob_start();
if ($use_utf8 = true) ini_set('default_charset','utf-8');
global $corzoogle, $qc, $q_scored;
$score = $hit_name = array();
// Get user prefs into shape..
$extentions = array_flip(explode(',', $extentions));
// we flip it so we can use isset($extensions[extension]), which is waaaay
// faster than using in_array()
$ignore = explode(',', $ignore_folders);
$ifc = count($ignore);
for ($i=0; $i < $ifc; $i++) {
$ignore[$i] = '/'.trim($ignore[$i], ' \\/').'/';
}
// this will be filled with the query with stop words removed.
$stripped_q = '';
// corzblog..
$blog_ext = substr($blog_file_name, strrpos($blog_file_name, '.')); // ".blog"
//*> User Options..
$whole_words_only = false;
if ($always_whole_words) {
$option_whole_words = false;
$user_opt['wwo'] = true;
$whole_words_only = true;
}
if ($do_user_options) {
if ($option_whole_words) {
if (isset($_GET['wwo'])) {
if ($_GET['wwo'] == 1) {
$user_opt['wwo'] = true;
$whole_words_only = true;
}
}
}
}
// option-specifics..
if ($whole_words_only) {
$boundaries = explode('|', '!|@|#|$|%|^|&|*|(|)|[|]|,|..|/|"|{|}|\|~');
}
/* Start Timer..
I've seen sites with 20,000+ documents being corzoogled (on modern web servers)
in under two seconds. This is how we know..
*/
$search_time = explode(' ',microtime());
$start_time = $search_time[1].substr($search_time[0],1);
//*> The Query..
if (isset($_GET['q'])) {
if (get_magic_quotes_gpc()) {
$q = xssclean(stripslashes(trim($_GET['q']))); // clean user input!
} else {
$q = xssclean(trim($_GET['q']));
}
// remove quotes - phrase matching is automatic!
$q = str_replace('"', '', $q);
// this is inserted back in the query field, *usually* the last search string
$result_txt = htmlentities($q, ENT_COMPAT, 'UTF-8');
if ($always_filenames) {
if ($anf_hack_after) {
$q .= '.'; // optional hack to *always* search for file names (by request)
} else {
$q = '.'.$q;
}
}
// get the query string into shape..
//
// query is too short!
if (strlen($q) < 3) {
$stripped_q = '';
$result_txt = 'need three characters!';
} else {
// strip out the stop words; and, the, of, etc
$stripped_q = strip_stuffing($q);
$stripped_q = strip_stoppers($stripped_q);
if (strlen($stripped_q) == 0) {
$result_txt = 'more than common words!';
} else if (strlen($stripped_q) <= 2) {
$stripped_q = '';
$result_txt = 'need real words!';
}
}
// need more definite rules for strings of small "weird" words :2do:
/*
a search for "man+of+war" is now "man+war", "of" was removed. "of"
will still be taken into account when we match for "phrases", though.
*/
// create an array of query terms..
$corzoogle = explode(' ', $stripped_q);
// a few checks..
// remove duplicate query terms..
$corzoogle = array_unique($corzoogle);
// better do this again
$qc = count($corzoogle);
/*
-queries .. (boolean NOT)
don't report files containing query terms preceded by a '-' character
a search for something like "bandy-legged" would pass unmolested..
*/
$not_this = '';
for ($i=0; $i<$qc; $i++) {
$neg = @$corzoogle[$i];
if (substr($neg,0,1) == '-') {
// build an array of NOTs
$not_this[$i] = substr($corzoogle[$i],1);
// remove this term from the search array
unset($corzoogle[$i]);
} else { $not_this = ''; }
}
/*
query may contain boolean NOT's, this will prevent phrase matching of
"good" parts. when phrase matching, in a search for..
oh macness -drop
we'd be looking for "oh macness -drop" instead of just "oh macness".
this fixes that..
if someone puts there boolean NOT's in the middle of a query phrase
they DESERVE to find nothing! but we check for this anyway. *sigh*
*/
if (is_array($not_this)) {
foreach ($not_this as $stripper) {
$q = str_replace($stripper,'',$q);
$q = str_replace('-','',$q);
$q = str_replace(' ',' ',$q);
}
}
$q = trim($q);
// better do this again!
$qc = count($corzoogle);
/* Filename Searching..
Okay, this is neat. if a query term has a word with a dot, it looks like
what we have here is a filename, so let's also look for a matching file
name. the engine's running, so let's grab the first filename from the query
string and get outta here..
We can match "parts" of filenames too. ie. index. would return all
"index.whatevers", or any file with the term "index" in its name, as well as
the usual documents containing that term. .index would achieve the same
result, but with probably much less regular page hits.
*/
if ($do_filenames == true) {
foreach($corzoogle as $query_word) {
if (strstr($query_word, '.')) {
if ($query_word{0} == '.') {
$query_word = substr($query_word, 1);
} elseif (substr($query_word, -1) == '.') {
$query_word = substr($query_word, 0, -1);
}
$sys_filename = $query_word;
break 1;
}
}
}
// someone is searching your files from outside your website!
if (isset($_SERVER['HTTP_REFERER'])) {
if (!stristr(@$_SERVER['HTTP_REFERER'], $domain) and ($allow_hot_link == false)) {
$stripped_q = '';
$result_txt = $hot_link_message;
}
} elseif ($allow_hot_link == false) {
$stripped_q = '';
$result_txt = $hot_link_message;
}
/*
reset the counters
(you could add extra deepness here if you really needed it)
*/
if ($deepness) {
$path = array('../');
} else {
$path = array('./');
}
$hit = 0;
$fn_count = 0;
}
if ($embedded != true) {
do_header(); // this takes us up to "...<body>"
if (file_exists($custom_header)) { include $custom_header; }
}
if ($embedded != true) {
echo '
<section class="content corzoogle-container">';
}
// splurge out a nice search form
echoform();
/*
version info */
if (array_key_exists('version', $_GET)) {
echo '
<div class="centered">
corzoogle™ v',$cz_version,' © 2004 -> ',date("Y"),' corz.org
</div>';
}
/*
Let's search.. */
if (($stripped_q != '') and ($qc != 0)) {
burrow();
/*
The actual searching happens HERE! --->>> ! <<<--- ;o)
*/
//*> Report Hits..
//
// STOP THE CLOCK!!!
$search_time = explode(' ',microtime());
$total_time = ($search_time[1].substr($search_time[0],1)) - $start_time;
if ($hit < $bfix_limit and isset($bfix_styles) and !empty($bfix_styles)) {
echo '
<link rel="stylesheet" href="',$bfix_styles,'" type="text/css" media="screen" />';
}
// report the hits..
if ($q != '') {
$plu = '';
$hc = count($hit_name);
if ($hc != 1) { $plu = 's'; } // pluralise hit(s) text
// rank the results, according to $score..
if (count($score) != false) { // there might be no hits
array_multisort($score, SORT_DESC, $preview, $hit_name, $pop_title, $file_path); // php magic!
}
// useful corzoogle links. no one sees this unless they corzoogle first!
if (isset($_GET['q'])) do_links();
// print out query, timer and counter results..
//
$a = '';
echo '
<section class="corzoogle-info">
<div class="found-info ',$cz_version,'">
corzoogle found '.
$hit .' hit'. $plu .' for "';
// create the "q1+q2+q3" text..
foreach($corzoogle as $some_term) {
$a .= $some_term .'+';
}
// print out, removing the last "+"
echo substr($a, 0, -1);
// report boolean NOT words..
if ((is_array($not_this)) and (count($not_this) != 0)) {
echo '" (not ';
foreach ($not_this as $notword) {
echo ' "',$notword,'"';
}
echo ') ';
} else {
echo '"';
}
echo ' in '.substr($total_time,0,4),' seconds',$maxed,'<br />
<span class="search-items">(searching ',$file_count,' items)</span>
</div>
</section>';
//*> Print Results..
echo '
<section class="search-results">';
for ($i=0; $i<$hit; $i++) {
// if ($i>=$max_hits) break;
echo '
<div class="result">
<a href="', str_replace(' ', '%20', $file_path[$i]),'" title="',$pop_title[$i],'">',$hit_name[$i],'</a><br />
',$preview[$i],' <br />';
if ($show_scores) echo'
<span class="score">score: ',$score[$i], '</span>';
echo '
</div>
';
}
echo '
</section>';
/*
stop-words removed from the search? let's report that..
(they are taken into consideration for phrases)
*/
if (!$match_phrases and $report_stop_words) { // only report if phrase matching is disabled
$removed = trim($removed);
if ($removed != '') {
echo '
<div class="stop-words" id="zooglestops" >
The following common words were removed from the search: <span class="removed">'.$removed.'</span>
</div>';
}
}
/*
additionally, we found the following matching filenames..
*/
if ($sys_filename == true) {
if (count($filename_hit) > 0) {
echo '
<section class="file-results" id="corfile">
<div class="label-file-results">corzoogled ',$fn_count,' file names matching "'.$sys_filename .'".. </div><br />';
for ($i=0;$i<$fn_count;$i++) { // test speed of list/each combo
if ($filename_hit[$i] != '') {
echo '
<a class="file-result" href="',$filename_hit[$i],'" title="',basename($filename_hit_name[$i]),'">'
,$filename_hit_name[$i],'</a><br />';
}
}
echo '
</section>';
}
}
/*
show most recent searches, just for fun..
*/
if ($recent_hits) {
// add query string to the recent searches..
$past_hits = latest_hits($hit);
echo '
<aside>
<div class="recent-hits">
most recent searches<br />';
foreach ($past_hits as $previous) {
$nofollow = '';
if ($no_follow_recent_hits) {
$nofo = 'rel="nofollow"';
}
echo '
<a ',$nofollow,' href="'. array_shift(explode('?', $_SERVER['REQUEST_URI'], 2)),'?q=',
str_replace(' ', '+', $previous),'" title="Search for ',$previous .'">',$previous,'</a>';
}
echo '
</div>
</aside>';
}
}
}
// Whatever, we need a gap at the bottom. White space is important..
echo '
<!-- corzoogled! -->';
// :todo:
// perhaps put the menu (css) bottom left for first page visits (no searches yet)
// i spotted more than one person visit corzoogle, then come back again to the
// download page from a GOOGLE search for "corzoogle download". how mad is that! hahah
if (!$embedded) {
echo '
</section>';
if (($stripped_q != '') and ($qc != 0) and file_exists($_SERVER['DOCUMENT_ROOT'].'/inc/footer.php')) {
if (file_exists($custom_footer)) { include $custom_footer; }
}
echo '
</body>
</html>';
}
// feed the browser..
ob_end_flush();
// send mail to caring webmasters..
if (($notify == true) and ($stripped_q != '')) {
$user_agent = @$_SERVER['HTTP_USER_AGENT'];
$got_bot = false;
if (isset($nomail_list)) {
if (!is_array($nomail_list) and file_exists($_SERVER['DOCUMENT_ROOT'].$nomail_list)) {
$bot_agent_data = explode("\n", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'].$nomail_list)));
}
} else {
$nomail_list = NULL;
}
// run through the bot agent strings list..
if (is_array($nomail_list)) {
foreach ($nomail_list as $some_bot) {
if (stristr($user_agent, $some_bot)) {
$notify = false;
break;
}
}
}
if ($notify) {
notify_webmaster();
}
}
if (!empty($GLOBALS['do_debug'])) debug('out'); // :debug
/*
That's all folks! Now the funky functions.
I like them underneath..
*/
/*
burrow..
Fast File System Burrowing..
*/
function burrow() {
global $display_path, $domain, $extentions, $file_count, $sys_filename, $filename_hit,
$filename_hit_name, $fn_count, $hit, $ignore, $level, $maxed, $max_hits, $path, $private;
// build a string of the paths inside this root..
$search_path='';
for ($i=0 ; $i <= $level ; $i++) {
$search_path .= $path[$i];
// remove those important IGNORE directories from the search path..
$search_path = str_replace($ignore, '', $search_path);
}
// for debugging purposes..
if ($display_path) { echo "<pre>$search_path</pre>"; }
// go in and read the directory's contents..
$dirhandle = opendir($search_path);
while ($file = readdir($dirhandle)) {
// skip 'dir' entries and invisibles (esp' mac ._resource.frk files)
if ($file{0} != '.') { // a quick way to grab the 1st character
$file_count++; // totale grandé
// if it's a "regular file"..
if (is_file($search_path.$file)) {
// is this a $sys_filename I see before me?
if ($sys_filename == true) {
if (stristr($file, $sys_filename)) { // for exact matching filenames.. use: if ($file == $sys_filename) {
$filename_hit[$fn_count] = $search_path.$file;
$filename_hit_name[$fn_count++] = 'http://'.$domain.substr($search_path.$file,1);
}
}
// get the file extension..
$fext = substr($file, strrpos($file, '.')+1);
// if we are permitted to search this filetype, then do it..
// the answer could be '0' here, is why the triple ===
if (isset($extentions[$fext]) and strpos($private, $file) === false) {
look_in_file($search_path.$file);
}
if ($hit >= $max_hits) {
$maxed = ' (search limit reached)';
return;
}
// or is it a directory?
// if so, add it to the path and loop into it.
//
} elseif (is_dir($search_path.$file)) {
$path[++$level] = ($file.'/');
burrow();
$level--;
}
}
}
closedir($dirhandle);
}
/*
Examine File Contents..
*/
function look_in_file ($file) {
global $arc_name, $allowed_tags, $blog_ext, $blog_file_name, $blog_flatlinks, $blogz_path, $boundaries, $cat_length, $clean_entities, $contents_as_title,
$context_ratio, $corzoogle, $deepness, $default_boolean, $domain, $dont_search, $pop_title, $enforce_between, $file_path,
$hit_name, $sys_filename, $filename_hit, $filename_bonus, $highlight_exact_hits, $hit, $level, $mangle, $mangle_merge,
$match_phrases, $not_this, $path, $phrase_max_score, $preview, $preview_has_context, $q, $qc, $q_max_score, $q_word_max,
$weighted_scoring, $safe_level, $score, $score_all_titles, $search_between, $sbtici, $search_in_tags, $snippet_length,
$title_scoring, $title_max_score, $use_descriptions, $use_snippets, $search_weight, $whole_words_only; // yeah, it's gettin outta hand!
$term_tally = 0;
$title = '';
$in_title= false;
$search_path='';
for ($i=0; $i <= $level; $i++) {
$search_path .= $path[$i];
}
/*
Where corzoogle opens and reads the file..
$file_data = the raw text of the file, remains constant
$search_data = the (modified) text we will be searching through
*/
$fext = substr($file, strrpos($file, '.')); // ".html"
if ($safe_level > 0) {
$search_data = $file_data = implode('', file($file)); // 50% slower!
} else {
if ($fext !== '.gz') {
$file_handle = fopen($file, 'rb');
if (filesize($file) > 0) {
$search_data = $file_data = fread($file_handle, filesize($file));
} else {
$search_data = $file_data = '';
}
fclose($file_handle);
// gzipped files.. //:test: woopelganger
// If you use this, remember to add .gz to your list of allowed extensions.
// Note, there will be a slight speed penalty for enabling this functionality.
} else {
if (filesize($file) > 0) {
$search_data = $file_data = file_get_contents('compress.zlib://'.$file);
// this will also reveal file names inside tar.gz files
} else {
$search_data = $file_data = '';
}
}
}
$file_name = basename($file);
/*
boolean NOT checking..
multiple -not terms are checked..
if the file contains one of your -terms, it's damned!
*/
if (is_array($not_this)) {
foreach($not_this as $notz) {
if (stristr($search_data, $notz)) {
return;
}
}
}
/*
"nosearch" tag?..
Is the word "nosearch" (or your custom $dont_search word) inside this file?
If not, and it's not damned, we will investigate further..
*/
if (strpos($file_data, $dont_search)) {
return;
}
/*
we have a candidate! */
/* search_between..
if there's a $search_between tag in the document, use it to search between..
(tags configurable in prefs) default is search between <body></body> tags. */
/* neater, but slower..
if Search Between Tags Is Case Insensitive: */
if ($sbtici == true) {
if (count($search_between) != 0) {
if (!strpos($file_data, $search_between[0]) === false ) {
if ($between_str = stristr($file_data,$search_between[0])) {
$end_point = strpos($between_str, $search_between[1]);
if ($end_point == false) { $end_point = strlen($between_str) + 1; }
$search_data = substr($between_str, // <- the string
strpos($between_str,'>') + 1, // <- start (after starting tag)
$end_point - strpos($between_str,'>') - 1); // <- length
} elseif ($enforce_between == true) return;
}
}
// uglier, faster..
//
} else {
if (count($search_between) != 0) {
if (!strpos($file_data, $search_between[0]) === false ) {
$s_start = strpos ($file_data, $search_between[0]) + strlen($search_between[0]) + 1;
$end_point = strpos($file_data, $search_between[1]);
if ($end_point === false) $end_point = strlen($file_data) + 1;
// reconstruct the tag, it can be stripped away later with strip_tags()
$search_data = $search_between[0].'>'.substr($file_data, $s_start, $end_point - $s_start);
} elseif ($enforce_between == true) {
return;
}
}
}
/* if the file has a real <title>, we'll use it, and score for it..
if not, we'll use the filename in its place. getting the <title>
this early on causes a :speed: hit, usually worth it. */
$name_is_title = false;
if ($score_all_titles == true) {
if (!$title = get_title($file_data)) {
$name_is_title = true;
if ($contents_as_title == true) {
$title = substr(strip_tags($search_data),0,$cat_length).'...';
} else {
// if the filename becomes the title, we'll chop off the extension..
$title = substr($file_name,0,strrpos($file_name,'.'));
}
}
}
// regardless of the number of query terms, we want a $snippet_length
// character preview (default is 300)
$num_o_q = count($corzoogle); // number of query terms
$preview_length = ($snippet_length / $num_o_q); // simple arithmetic
$snippet = '';
$found = 0;
// score progressively less for 2nd, 3rd and 4th query terms..
if ($weighted_scoring) { $weight = $search_weight; } else { $weight = 3; }
/*
where corzoogle examines the file contents...
*/
//*> Check Query Terms..
// (walk through the $corzoogle array, one-by-one)
//
foreach($corzoogle as $query_word) {
if ($search_in_tags == false) {
$search_data = strip_tags($search_data, $allowed_tags);
}
// kill two birds with one stone..
// the most important line of the program
if ($match_str = stristr($search_data, $query_word)) { // $match_str now is all the data from {match} to EOF
if (!$whole_words_only) {
$match_pos = strpos($search_data, $match_str); //Case-SenSitiVe
} else {
// If whole word searching is enabled, check if it is a whole word, if not, return.
$t_search_data = ' '.str_replace($boundaries, ' ', $search_data).' '; // quicker than bundling, somehow.
$match_pos = stripos($t_search_data, ' '.$query_word.' ');
if ($match_pos === false) {
return;
}
}
// We got a match!
$found++;
// this makes the preview more understandable..
if ($preview_has_context == true) {
$snip_from = $match_pos - ($preview_length/$context_ratio);
if ($snip_from < 0) $snip_from = 0;
$snippet .= substr($search_data, $snip_from, $preview_length) .'.. ';
} else {
$snippet .= substr($match_str, 0, $preview_length) .'.. ';
}
// only on word boundries.. (what about entities?)
$snippet = substr(strip_tags($snippet), 0, strrpos($snippet, ' ') + 1);
// make that three birds
$term_scored = 0;
// $term_scored += ($weight * substr_count($search_data, $query_word));
// a wee bonus for case-sensitive exact match..
$exact_bonus = substr_count($search_data, $query_word);
if ($exact_bonus != 0) { $term_scored += 1; }
$term_scored += ($weight * $exact_bonus);
// case-matching..
if (ucfirst($query_word) !== $query_word) {
$term_scored += ($weight * substr_count($search_data, ucfirst($query_word))); // handy function
}
if (strtolower($query_word) !== $query_word) {
$term_scored += ($weight * substr_count($search_data, strtolower($query_word)));
}
if (strtoupper($query_word) !== $query_word) {
$term_scored += ($weight * substr_count($search_data, strtoupper($query_word)));
}
if ($term_scored == 0) {
$term_scored += ($weight * @substr_count($search_data, substr($match_str, 0, strpos($match_str, ' '))));
} // any @ is just for speed! if (strpos(.. - aarrgh! no! hahah
// max $q_word_max points per query term (set in prefs)..
if ($term_scored > $q_word_max) $term_scored = $q_word_max;
// if weighted, after 4th term, all matches score one point
// these terms are the bit-players in our search adventure
if ($weighted_scoring) {
$weight--;
if ($weight == 0) $weight++;
}
$term_tally += $term_scored; // add this query term's score to the tally
}
// you can only score so many points by way of query terms (set in prefs)..
if ($term_tally > $q_max_score) { $q_scored = $q_max_score; } else { $q_scored = $term_tally; }
// okay, four birds. if the file has a real <title>, we'll use it, and score for it..
if (($score_all_titles == true) and ($title_scoring == true) and (stristr($title, $query_word))) {
$in_title = true;
}
} // finished walking $corzoogle array now
/*
if *all* the query terms didn't appear inside this file, go back to burrow()ing
it is _crucial_ that we return at this point for NON-matching files. :speed:
*/
if (($found != $num_o_q) and ($in_title == false)) return;
/*
..or else continue, it's a $hit!
Filename bonus: if any of the query terms are in the filename
*/
if (($sys_filename) and ($name_is_title == false)) {
foreach($corzoogle as $query_word) {
if (stristr($file, $query_word)) {
$q_scored += $filename_bonus;
}
}
}
//*> Phrase Matching..
//
$phrase_scored = 0;
if ($match_phrases == true) {
// if the original query is a phrase (has a space inside it)
if (strrpos($q, ' ')) {
/*
even if "and", "am", "I" or whatever (stop words) were removed from the query array,
we still use the *entire* query string when looking for matching *phrases*.
you only get this far if we already have a document matching all terms.
*/
// query exists (with the SaMe cAsE) *in entirety* inside this file, +30% (jackpot!)
// fastest tests first..
if (strpos($search_data, $q)) {
$phrase_scored += ($phrase_max_score * (3/10));
// the longer the query, the higher the bonus..
$phrase_scored += str_word_count($q)*$phrase_max_score * (1.5/10); // previously 1/10
// or +20% for a phrase match with a different CaSe..
} elseif (stristr($search_data, $q)) {
// "man mac" would match "The BIG Man Machine ..."
$phrase_scored += ($phrase_max_score * (2/10));
$phrase_scored += str_word_count($q) * 5;
}
}
// up to the maximum set in the preferences..
if ($phrase_scored > $phrase_max_score) $phrase_scored = $phrase_max_score;
// consider "fuzzy" searching here. :2do:
// now we set the total score for terms..
$score[$hit] = $q_scored + $phrase_scored;
}
// we'll use this for our links..
$file_path[$hit] = $file;
//*> Create Preview Snippet..
//
if ($use_snippets == true) {
$text_preview = $snippet;
// sometimes we still have a tiny string. like when the query matches right at the end.
// so we'll just pad that out with some body text instead..
if (strlen($text_preview) < 33) {
$text_preview = strip_tags($search_data);
$text_preview .= substr($text_preview, 0, $snippet_length);
}
}
// Evil Entities..
if ($clean_entities) {
$text_preview = switch_entities(@html_entity_decode($text_preview, ENT_QUOTES, 'UTF-8'));
// this MUST be done after the main (walking corzoogle) loop, but *before* adding styling (next)..
}
// Description..
$description_tag = '';
// are we using these in results?
if ($use_descriptions == true) {
// if so, does this document have a <description> tag?
$description_tag = get_description($file_data);
// if all that worked out we'll put this description below our preview title
if (strlen($description_tag) > 0) {
$description_tag = '
<div class="description">
'.$description_tag.'..
</div>';
$text_preview = $description_tag.$text_preview;
}
}
/*
just for looks.. */
// this will prevent us getting <- W I D E -> previews..
$wrappers = array('>','<','_','.','(','-');
$splitter = array('> ',' <',' _','. ',' (','- ');
$text_preview = str_replace($wrappers, $splitter, $text_preview);
/* colour the found query terms by their capitalisation..
this technique works fine, looks nice, but it's a bit boring */
foreach ($corzoogle as $rep) {
$text_preview = str_replace(strtolower($rep),
'<span class="hit-lower">'. strtolower($rep) .'</span>', $text_preview);
$text_preview = str_replace(ucfirst($rep),
'<span class="hit-case">'. ucfirst($rep) .'</span>', $text_preview);
$text_preview = str_replace(strtoupper($rep),
'<span class="hit-upper">'. strtoupper($rep) .'</span>', $text_preview);
// you can add ugly highlights, too..
if ($highlight_exact_hits == true) {
$text_preview = str_replace($rep, '<span style="background: #FFFF33;">'.$rep.'</span>', $text_preview); //2do - use scheme
}
}
// the preview is ready..
$preview[$hit] = stripslashes($text_preview);
/*
corzblog
*/
if ($fext == $blog_ext) {
$is_blog = true;
$blogz_path = '';
$bfile = basename($file);
$hit_name[$hit] = str_replace($blog_ext, '', $bfile);
// work out the path parts we need..
$this_blog_path = dirname($file);
$blog_path = explode('/', $this_blog_path); // split it up
foreach ($blog_path as $element) {
if (stristr($element,$arc_name)) break;
$blogz_path .= $element.'/'; // put it back together
}
// I use flat links, these days..
if ($blog_flatlinks) {
if (stristr($this_blog_path, $arc_name)) {
$file_path[$hit] = $blogz_path . $hit_name[$hit];
$pop_title[$hit] = $hit_name[$hit] = 'blog archive: '.$hit_name[$hit];
} elseif ($bfile == $blog_file_name) {
$file_path[$hit] = $pop_title[$hit] = substr($blogz_path, 1);
$hit_name[$hit] = substr($file_path[$hit], 1, -1);
} elseif ($bfile == 'about'.$blog_ext) {
$file_path[$hit] = substr($blogz_path, 1).'about-blog';
$pop_title[$hit] = $hit_name[$hit] = 'about-blog';
}
} else {
$file_path[$hit] = $blogz_path .'index.php?archive='. $hit_name[$hit];
}
// end corzblog specific code
} else {
$is_blog = false;
}
//*> Extension Mangling..
//
if ($is_blog == false) {
if (count($mangle) != 0) {
reset($mangle);
// run through each mangled pair, switching key for value.. (we test entire path, can do lotta mangle!)
while (list($key, $value) = each($mangle)) {
if (substr($file_path[$hit], (0 - strlen($key))) == $key) {
$new_hit = str_replace($key,$value,$file_path[$hit]);
$file_path[$hit] = $new_hit;
}
}
}
// Mouse-Over title (full site path)
$pop_title[$hit] = substr($file_path[$hit], 1);
//*> <title> scoring..
//
if (!$score_all_titles == true) {
if (!$title = get_title($file_data)) $title = substr($file_name,0,strrpos($file_name,'.'));
}
if ($title_scoring) {
/*
an important small (stop) word may have been removed. but such a word
can be important in the "phrase", so we use the whole query string.
*/
// phrase in the title..
$t_scored = 0;
// check that q is a phrase. (another way to do this)
if ((strrpos($q, ' ') > 0)) {
/*
The whole query string matches a part of the title (case insensitive).
A search for "dog spot" would return the "my dog spot" page,
along with the usual dog spot documents. This is stylish, huh.
And what a punchline! obfuscated php..
if (stristr(substr($title,strpos($title,$q),strlen($q)),$title{0}) == $q) $t_scored+=20;
*ahem*, where was I.. ah yes, this three-stage approach works well..
*/
// the *direct hit* .. full query matches the title exacitaly!
if ($title == $q) {
$t_scored = $title_max_score;
// case-insensitive exact match
} elseif (strlen(stristr($title, $q)) == strlen($title)) {
$t_scored += ($title_max_score*(3/5));
// now a case-insensitive full title match..
} elseif (stristr($title, $q)) {
$t_scored += ($title_max_score*(2/5));
}
}
// no phrases, okay, check for individual words in the title..
$tmp_score = 0;
if ($t_scored == 0) {
// +20% for each query term that's in there *somewhere*..
foreach ($corzoogle as $query_word) {
if (stristr($title, $query_word)) $tmp_score += ($title_max_score/5);
}
// up to a maximum of 80% of $title_max_score, a direct hit will always score higher.
if ($tmp_score > ($title_max_score*(4/5))) $tmp_score = ($title_max_score*(4/5));
$t_scored += $tmp_score;
}
if ($t_scored > $title_max_score) $t_scored = $title_max_score;
// add the title score to the running total..
$score[$hit] += $t_scored;
} // finished title scoring
// if there is no title, or it contains a variable, the filename will do fine
if (strpos($title, '$') > -1 ) {
$hit_name[$hit] = $file_name; }
else {
$hit_name[$hit] = $title; }
} // not a blog
// increase our hit counter for the next (possible) hit..
$hit++;
}
/*
Web Master Email Notifications..
*/
function notify_webmaster() {
global $q, $to_addy, $email_subject, $email_body, $from_addy, $hit, $cz_version, $mail_lb, $xmail_headers;
$xmail_headers = "From: $from_addy".$mail_lb.$xmail_headers;
if (isset($_SERVER['HTTP_REFERER']) and ($_SERVER['HTTP_REFERER'] != $_SERVER['HTTP_HOST'] )) {
$email_body .= "\nReferred by.. ".$_SERVER['HTTP_REFERER']."\n\n"; }
$email_body .= "\nThe search query was.. \"$q\" [$hit hits]\n";
$email_body .= "\n-- \ncorzoogle mailer v$cz_version (c) corz.org\n"; // everything after "-- " becomes the sig
mail($to_addy, $email_subject, $email_body, $xmail_headers);
}
/*
Recent Searches..
Interestingly, this affects folks' search queries..
*/
function latest_hits ($add=true) {
global $hits_length, $hits_file, $q, $result_txt;
// nice to know that folk are searching for God at my site
$past_hits = array();
// get the "serialized" array from the ".corzoogles" file
if (file_exists($hits_file)) {
$grab_hits = file_get_contents($hits_file);
$existing_hits = @unserialize($grab_hits);
// add our query string to the top of the array past_hits array.
if ($add and !in_array($result_txt, $existing_hits)) {
array_push($past_hits, $result_txt); // this is the first element in the array
}
// join the unserialized array to our $past_hits array
if (is_array($existing_hits)) { // might be a new file
foreach ($existing_hits as $old_hit) {
// it's best to do it one element at a time here, the array is very small
if (strlen(implode(' ', $past_hits)) >= $hits_length) {
break 1;
} else {
array_push($past_hits, $old_hit);
}
}
}
}
// serialize and save the whole array back to a file for the next search. and so on ...
$sa = serialize($past_hits);
// attempt to create file if it doesn't exist
$fp = @fopen($hits_file, 'w');
if (is_writable($hits_file)) {
flock($fp, LOCK_EX);// not really necessary here, but still..
fwrite($fp, $sa);
flock($fp, LOCK_UN);
// fflush($fp);
fclose($fp);
} else {
// if we can't create the file, guide the user to a solution..
echo '
<div class="no-write">
<h3>Your recent searches file is not writable!<br />
<small>Recent seaches cannot be stored!</small></h3>
<p>Please manually create the "',$hits_file,'" file, and make it world-writeable, or else allow (temporary) write access to the directory that I live in, so I can create the file myself.
<p>You can give the folder write-access in a shell/terminal (<a title="or use sudo. e.g. sudo chmod 777 /path/to/folder">as root</a>)..
<blockquote>
<p><strong>chmod 777 /path/to/folder</strong><br />
</blockquote>
<p>or from your desktop, using the properties or "get info" for that folder, or with any decent text editor, save a blank ".corzoogles" file next to "corzoogle.php"
<p>You could do the whole lot in a shell, too (<a title="or use sudo. e.g. sudo chmod 777 /path/to/folder">as root</a>)..
<blockquote>
<p><strong>touch /path/to/folder/.corzoogles<br />
chmod 777 /path/to/folder/.corzoogles</strong>
</blockquote>
<p>Alternatively, switch off recent searches in the preferences!<br />
</div>';
}
return $past_hits;
}
/*
Print Out the Search Form..
With a slight alteration, you can easily use this on other pages, too
like your 404 page, for instance. With maybe something like..
$insert = substr($_SERVER['REQUEST_URI'], (strrpos($_SERVER['REQUEST_URI'], '/')+1));
form input value="'. $insert .'";
(pseudo code, you get the idea)
hit a 404 at corz.org for a demo. I use a "static" png logog onsite, btw.
In quotes because it will actually change color with the current scheme!)
You can download Clever-404.php from https://corz.org/engine
Note: if you use corzoogle on the web, you must leave the copyright notice
intact
Here's some HTML you can copy and paste directly into your page..
<form method="get" action="/corzoogle.php" id="corzoogle">
<div class="search-form">
<!-- corzoogle™ powered search (c) copyright corz.org 2004->tomorrow! -->
<!-- you cannot legally remove this copyright notice -->
<a href="https://corz.org/server/tools/corzoogle/" title="corzoogle™.. the realtime personal search engine, from corz.org">
<img src="',$logo,'" alt="logo for corzoogle; fast realtime personal search engine from corz.org" class="dim"></a><br>
<br>
<input name="q" maxlength="256" value="" id="cz" autofocus="autofocus" title="narrow the search with multiple terms
use - to exclude words
to search for files, add a dot, anywhere
click the 'tips' link for more info" type="text">
<input id="cz-search" value="do it!" title="corzoogle locates!" type="submit">
</div>
</form>
*/
function echoform() {
global $do_user_options, $embedded, $logo, $notice, $qc, $result_txt, $redirect_to, $redirect, $user_opt, $option_whole_words;
$do_image = false;
if ((!$embedded) or ($embedded and stristr($logo, 'embed'))) $do_image = true;// end with "png" for IE pngfix
if ($logo =='embedded') { $logo = $_SERVER['PHP_SELF'] .'?zoogleimg=do.png'; }
if ($redirect) { $target = $redirect_to; } else { $target = $_SERVER['REQUEST_URI']; }
echo'
<form method="get" action="',$target,'" id="corzoogle">
<div class="search-form">
<!-- corzoogle™ powered search (c) copyright corz.org 2004->tomorrow! -->
<!-- you cannot legally remove this link and copyright notice, though you may move them in the page -->
<!-- If you require a developer license, please contact me directly! -->
<a href="https://corz.org/server/tools/corzoogle/" title="corzoogle™.. the realtime personal search engine, from corz.org">';
if ($do_image == true) {
echo '
<img src="',$logo,'" alt="logo for corzoogle; fast realtime personal search engine from corz.org" class="dim" />';
}
echo '</a><br />
<br />
<input type="text" name="q" maxlength="256" value="',$result_txt,'" id="cz" autofocus="autofocus" title="Narrow the search with multiple terms.
Use -word to exclude words.
To search for files, add a dot, anywhere.
Click the \'tips\' link for more info." />
<input id="cz-search" type="submit" value="do it!" title="corzoogle locates!" />';
// User Options..
if ($do_user_options) {
// quicker to set regardless of enablement.
$opt_wwo = '';
if ($user_opt['wwo']) {
$opt_wwo = ' checked="checked"';
}
echo '<br />
<div class="corzoogle-options" title="corzoogle can return only pages containing the WHOLE word.
A search for "foo" will NOT return results for pages containing the word "food"
( unless they also contain the word "foo" ! )">';
if ($option_whole_words) {
echo '
<input type="checkbox" value="1" name="wwo" id="opt-wwo" class="check-box"',$opt_wwo,' />
<label for="opt-wwo" class="checkbox-label">whole words only</label>';
}
echo '
</div>';
}
if (!empty($notice) and !isset($qc)) {
//if (!empty($notice)) {
echo '
<div class="corzoogle-notice">
',$notice,'
</div>';
}
echo '
</div>
</form>';
}
/*
Print out Header..
*/
function do_header() {
$addstr = '';
if (isset($GLOBALS['q']) and $GLOBALS['q'] != '') {
$addstr = ' - searching for "'.$GLOBALS['q'].'" ';
}
echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<title>corzoogle - the fast, real-time search engine (v',$GLOBALS['cz_version'],')',$addstr,'</title>';
echo '
<meta name="description" content="corzoogle - Fast realtime search engine for home or website, from corz.org. Fast. Portable. Cool." />
<meta name="generator" content="corzoogle" />
<meta name="author" content="corz.org" />';
$style_sheets = explode(',', $GLOBALS['page_styles']);
foreach ($style_sheets as $my_sheet) {
echo '
<link rel="stylesheet" href="',$my_sheet,'" type="text/css" media="screen"/>';
}
echo '
</head>
<body>';
}
/*
corzoogle links..
Please leave these links intact!
Please do leave a link to the "tips" somewhere, which I keep updated.
https://corz.org/server/tools/corzoogle/tips.txt has minimal html formatting,
in simple divs, so you can load it in an iframe or whatever,users do deserve
the latest and best help available. In other words, I syndicate the tips. If
you think you can improve on the tips, or any other thing, by all means do.
*/
function do_links() {
global $hc;
echo '
<div class="corz-links">
<a href="https://corz.org/server/tools/corzoogle/index.php" title="what\'s it all about?">about</a>
•
<a href="https://corz.org/server/tools/corzoogle/download.php" title="download corzoogle">download</a>
•
<a href="https://corz.org/server/tools/corzoogle/tips.php" title="handy tips, ways to get the most from your corzoogling">tips</a>
</div>';
}
/*
Get Description()
we can (optionally) display these under the titles of results
*/
function get_description ($doc_string) {
$description_tag = '';
// The easy but mega-slow method.. $meta_tags = get_meta_tags($file); etc.
// The (much) quicker way.. not clever, but trés rapido!
// (must avoid regex at all costs). At least now everyone has stripos()!
// In actual fact, the old (even uglier!) method is still faster, but only *just*
if (stripos($doc_string, 'meta name="description"')) {
$start_at = stripos($doc_string, 'meta name="description"')+33;
$content = substr($doc_string, $start_at, 333); // 333 characters maximum description
$end_at = strpos($content, '"');
$description_tag = substr($content, 0, $end_at);
}
if (stripos($doc_string, "meta name='description'")) {
$start_at = stripos($doc_string, "meta name='description'")+33;
$content = substr($doc_string, $start_at, 333);
$end_at = strpos($content, "'");
$description_tag = substr($content, 0, $end_at);
}
if (stripos($doc_string, 'meta name=description')) {
$start_at = stripos($doc_string, 'meta name=description')+30;
$content = substr($doc_string, $start_at, 333);
$end_at = stripos($content, '>');
$description_tag = substr($content, 0, $end_at);
}
return $description_tag;
}
/*
Strip "stop-words" from the query..
Accepts $string, returns $string minus any stop-words
*/
function strip_stoppers($string) {
global $removed, $stop_words;
// stop-words themselves are up in the prefs now.
$words = explode (' ', $string);
$qs = count($stop_words);
reset($words);
while (list($key, $val) = each($words)) {
for($i=0;$i<$qs;$i++) {
if (($words[$key] == $stop_words[$i]) or ($words[$key] == ucfirst($stop_words[$i]))) {
unset($words[$key]);
$removed .=' '.$stop_words[$i];
break 1;
} elseif (strlen($words[$key]) < 2) {
// if ($report_stop_words) {
// $removed .=' '.$words[$key];
// }
unset($words[$key]);
break 1;
}
}
}
return implode(' ', $words);
}
/*
Strip other stuff from query..
*/
function strip_stuffing($some_string) {
// there are lots of silly people about, so we remove a few undesirables, there will be more.
$nonos = array('<','>','..',' .'.'. ',',',';','[',']','\\',' \\',
'\\ ','/',' /','/ ','*','~','#','•','°',
"\n","\r","\t","\r\n",'&','?','$','%','+','=','»','«'); // we leave :()" for now. might need those
$some_string = str_replace($nonos, '', $some_string); // remove undesirables
// in case folks have extra spaces in between the words..
$spacers = array(' ',' ',' ',' '); // goan remove one. I dare ye!
$some_string = str_replace($spacers, ' ', $some_string); // str_replace has back-to-front syntax.
$some_string = str_replace('+', ' ', $some_string); // yes, I know *why*. it's still annoying.
$some_string = trim($some_string); // might add "te rms" in the future, hmm..
return $some_string;
}
/*
Get The Title..
something like if (eregi ("<title>(.*)</title>", etc.. is less reliable
*/
function get_title($string) {
$t_end = 0;
if ($grab_this = stristr($string, '<title>')) {
$grab_this = substr($grab_this, 7, 248);
$t_end = strpos($grab_this, '<');
}
return substr($grab_this, 0, $t_end);
}
// damn php 5.3!
function switch_entities($string) {
if (!defined('PHP_VERSION_ID')) {
$version = explode('.', PHP_VERSION);
define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
}
if (PHP_VERSION_ID >= 50390) {
return htmlentities($string, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
} else {
return @htmlentities($string, ENT_NOQUOTES, 'UTF-8'); // @ in case it's a REALLY old php, will fall-back to Latin1
}
}
/*
xss clean-up
clean up against potential xss attacks
adapted from the bitflux xss prevention techniques..
http://blog.bitflux.ch/wiki/XSS_Prevention
any comments or suggestions about this to
security at corz dot org, ta.
*/
function xssclean($string) {
// skip any null or non string values
if (is_null($string) || !is_string($string)) {
return false;
}
if (get_magic_quotes_gpc()) {
$string = stripslashes($string);
}
// fix &entity\n;
$string = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $string);
// URL decode
$string = urldecode($string);
// convert Hexadecimals
$string = preg_replace('!(&#|\\\)[xX]([0-9a-fA-F]+);?!e','chr(hexdec("$2"))', $string);
// clean up entities
$string = preg_replace('!(�+[0-9]+)!','$1;',$string);
$string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
$string = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $string);
$string = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $string);
$string = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
// remove any attribute starting with "on" or xmlns
$string = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $string);
// remove javascript: and vbscript: protocols
$string = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $string);
$string = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $string);
$string = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $string);
// only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $string);
$string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $string);
$string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $string);
// remove namespaced elements (we do not need them)
$string = preg_replace('#</*\w+:\w[^>]*+>#i', '', $string);
do {
// remove really unwanted tags
$old_data = $string;
$string = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $string);
} while ($old_data !== $string);
// we are done...
return $string;
}
/*
function:zoogleimg() - embeded corzoogle image
Thanks to Rolf Holtsmark and Terje Monsen.. http://php.holtsmark.no
I knew that *out there* somewhere, would be tool for doing this.
*/
function zoogleimg() {
header("Content-type: image/png");
header("Content-length: 23915");
echo base64_decode(
'iVBORw0KGgoAAAANSUhEUgAAAR0AAABQCAYAAAFT3JHrAAAAB3RJTUUH1QkXACwYZKTZkwAAAAlwSFlz'.
'AAAewQAAHsEBw2lUUwAAAARnQU1BAACxjwv8YQUAAFz6SURBVHja7D0FeFTX0rPucSHBXUNxWgpFC8Wt'.
'uHuB4E6BYMHdg1vxQnDXEggSIBDi7r7ucv8zN7uw0KDt+1/fezt8l+zevTJnzpyxc84MgAPeAKu4k8x/'.
'ByaXwp5HkT81i/uNbf/l4OEHFP4dPLAJY9LS9XVGVir/XOnjq2zSvJETOc0YNH5y/Q5NSzyRfFMDOFS5'.
'TJVBIepZt6n7/luPbpv12mZms2X26E6tVuK14cEPLMBkwjddvmXgM1OiQylgMKFs1UbOKr3WTE7R73p1'.
'K4oKu/gKhq7tzXgHs4eh8XiBGzk45BCs/KYlfUOn3gManr5w837Y8zv4vYq1ZZJdl6+umBu8VLPhxKVx'.
'Lbv0HIXXnrofhtf4/FpyIGXrlqjrz6j4V/S9Xkeu3zyxZOehNZsvBUeQ736nV1ym1vffRU2oNo+a3mAp'.
'9Q5CNb5ZQNlRTHxm6nzbBcxN33WiNm70x+9lbNd7zJHgd0avwO0ZvmUqNsFzs/aeOt5t0IghW1rPffPw'.
'BWWH4mfXX3u7UAv6e9GfZx/aQSM0sXIAfucVy0Nc8c06DZssNS7deek2nk+6+wBPM1ZWbGie+PCCL4eQ'.
'e9XB4xOBfqKIGuaydET91bUtJ38dW8G5dY8QPH/yVkjv4EN7jnGdhfhVtGvZ0dzFKft/mL1idTcXCRO4'.
'XAYsGlquUKkrrLjqyOm5oaUe1pnfYrVux4ZLGZ36De38IZ6z70sWfD68f+2bxi4dXp6yfmcHzWtLvXcd'.
'A/4Ng6vCb/sWU4t3HkKG5/9/v9wBfwf8o7qNsfzUJupDP77h7iGTZpXbsfMOtf9q6G/4/dn+Y1TYrsN4'.
'Iz16khLDqccPz9Gy6kJ4WObViJcZ+x8dpx984Nof1MHrf7x5yd0Lj8yPDtygmrVr54Pfrzx5cScl5jHV'.
'vk8fFKwQm5VEXzchILDGreOPqfVHzwz9E2az5p6iH7h0929r/tgQhJ9F+D3hxl0aqbUrf6aFGjmc8fzG'.
'u2vxu2elQVPfIBIaGU/dO3wLv9fC76/Oh+Jn/v3zW+hrzgaNp4XpmO0z6e9L2m6kRvhOp6bWXWwTC2+A'.
'VbHaW+m6yKs6fSN+XuZaBT9zdm6f9Ob3kvM98HNL/OzT9+15Sfdx1Ia602g1gd/n+vTHz+UWDfSitm2Z'.
'o1iwbv0pPP/dnM50I+c0WPmnrrNhZXZ3F8KcteeSq9eeR52v5ltv/7rdWdg9Ffp2UeNfi5nGjzXn9JaH'.
'dX1aGvffCmnfbttPVK+W3+X27t271oXL13q0c7XMn/x8LX99s+k5eLHYgyZmCpfLBOmjHRJ4sqQn+S4o'.
'4+5Lv1TgwYVVo/ZTMxst/SBPfVDafopR4V0J/+ZZEwNWlBk5a/48/N3v22/rkb+137uO/RnP/3th+6E9'.
'T48cXPGGtxzggP8wCNiyr0RSTiq1/xYtx/3+3fj8I+HC02cR0/Yv/mIC/UnqHboZuuLEqScUi8XqQr66'.
'rTty48H4ib/hg50v33v6cn+Hfvj5R3K4k8M74uQ5KudlFK2wzjwKX5STnUhduxxErTt0/PzSBZ2pFUt6'.
'U1P2rTo/fvHibeSab8hR8Wz4Ff2626upYdNnriDfOUNXBVF+I2fjM1BiupCjWlhsEvVHZILaihb7yKiN'.
'1Na1h9GZK4fXrAw6vCkzIplac/DUaLzgVcwr9etH5/EZrfD3u1cPUndOr6GCLx414D3x2UnwOi3mTTv3'.
'XLi3YP/0Ezb9JyaHz9gKc6h1h4MDPkqwzVtvUn2G77Z3h2wg3t95ILVt2uICeFdFcK7PWESdGDyBVpZ7'.
'9v5KbVw75B13yQ5Ew38bRNVYVgl/r44nOsxbr+T+NBy/17O/cO2pKycnBx2jRsyYN3JniwXUitrj3yhs'.
'Gxwbu4U6MWYrni958+RK6swOGgdv/O3IwWXaTdOaUE5ubtiZsOrM8YjOgUNsHMRc2HIdNbnGIspK8FLk'.
'KG3F2ftjHGTZuOUGvH6egZ893vtNkxkZDdrrN9CxfePXDR0/2e/F6QsQ5s5GyluMRjOIxZ5/oky1wCqU'.
'069clSbUZUXk3Hi0XqNqLqtMyZ5caVCaGASDFq0/9i4nh/x8NTQM9qxe+qBS/waJLmU9wbdspZL210Re'.
'eQrxoRE0bvcubIeI+4fwcwNyVO4/eG6liWsf8BWFhdfxpEyjAKPF+KadOp0enN0l0K5XP7Qf08mRRo7U'.
'bRdulbJ/BwOKhzLL9/y+XafiNk5PD92yZ3UgIh+LD8beWr77yBIfmbKDXiyMH/PLIDQqXpMjD4qGXXUr'.
'ARGbROvLS1vPI7urre+VWK+5b/1ec2/wxQM6M/iUYBpO9eje/QA5h63XWnESlq1SpfmCsYF7mEwWdfz+'.
'iflXTh57aMXL3KRLF99upRIzEiU9drRvWt+Tp47omfziIkGYBVM3PGmm0+nw3XxrGxCvTOSeTdvPnhHp'.
'+XWyuLmn540btJyce0UOPfw3QdmyZV1W/lKD2jj1Wwre+lbc7XN/pHoOHz2XfBZ87bMZX3vjPxSwPTbZ'.
'aQJrMMwBDnCAA/6dEB0dPfZLrv//9+f/DYC+V6FKBssvoAEPe+EzVfe/Zb7n/xvKe5cRKzRKSMqlDV7L'.
'5973P0EcAmqZWgEmi+mLbip2WJ37IzInIyrXi89jA4PFhKEDm6CRRbPizbM3Vd56tcio1gCT/LYnOaHB'.
'pgVzwmz37r92L6C0u/BnN76lVnZSGFx+JGvQrHuj8xKB0IcyCTLZTBboTFrQmqW+GQnqDlN69bhMbhMF'.
'XbytEvG4oNRooYxPCejYsNabdyJsOnFxcFOm0wGwUGDUGkBTxSeh5Xc1MXxs4wReYuRDHYfDAYNeA+lm'.
'l/jm39RGPw4nATUZhVnEGHrXvLt+/aXSRWsRZ8blQnq2jBq/ujcXiuyk4uHZ8xRqxtyTtglB70HDdlPL'.
'957Yjr/dDtxAHZi0ABGuRA7n1t16dk17+JQ6dTv0OP6el5tKvY64R+3eMZEKOrKtYNnintTWPcu1P+5o'.
'js9rbX1m9YOP91MLLtKR5UqbTl7oUe+XX6nKfnWGQJEz6H465JkuOjUTf0fvHa6duKtY8+1k/P4DFIW/'.
'S8bcfEFlRabSUWhyMJOjHlL375xGlwH9pXJ3Tq+lrhxZTB05tR/dBs7gjROpJnO62q6HPZOPUaf238Pr'.
'0dURzWi9hhpXmcaJWyznnLj7/GR2thzWrR34ExT5MJCSeqrRoeFX1Lcv3pan7doHQ4IPNien42kuCv79'.
'nKzfIKjr5tqbfB3p6VXG9dSxxVKzqLx8TP9xJYsQ/V1r5QD08qndD4OomNwoCOy0EomVGBqbEvcqMQ2M'.
'r15cId/paHWP7+t5/fEqWn7mwXNp9yZ1eRlnHkoMVT3yIBQeWZ8lvZ6RsPgnLntBYNCBab4lvUGtyIeQ'.
'WOlJKPLtQJGfDAp5AQycsQBnfI3uYheQihV0O0NDwxaHbnoOWSeec2Y2DkxV5CrBSA6BhPZCkLiJfyJO'.
'0pPsnlqtCcx6fYrt3L2bV57g39zTZ52S7qLfB1l2t1DJUfFGjkyOvYEeb0Ji4ktwL09PH+htRLTBootL'.
'qOPPjoJHtN8q8hVnZ6nzD5+Dt5sLtkhmd6ny+N3HkCFTw7xNQR2y1j4F70mNQuyHmf/gnmuDp+xaUFLC'.
'nbPl8LpaZSa0WF5KJGpn/ZmRGvccdDo6VEQPkyxpLuQrC+kfM+/lzI+9mwLbMpYgJybZ7oEit6PA9o53'.
'BPKl6xGwZ+/DP51HeH3hKvCcJQDv+S2xl29SL478DjZ2FYs9gM398xxb8MPg4FV3F4GA6aM5unnzanJK'.
'hwhxiYzgslnvP5fadeE2hETEQG5mqsappDtwWPxy7z3SFHbiD+DrdManf1zLfG0odyz2/AL37dvmSX/t'.
'42opzM+A6Zuff0euU+HFCTkpIObTk1SQlZALTp4SEAgkBiun2cIY+NcWFXiXCJ0GlJssFLGg7+Dtr+E9'.
'59R75ZKtTCEf5s6a1cr+vD4tnSvwosNBdLdwOIL3b4VeQ8aV6He6R1cJXwLnxh6tT07ln/rj5GHy10Ui'.
'5JGG02iI7W7heLqIoe8PDTJ3rggMYblwQX0zoY49p2/admIWW8SFqYcXDe3Va7Q4K3hMX+9G/RRjxy1t'.
'FXhcWmHBviSMRT2yEV3EE4KryIm+N8YzsyNXwoIp9eeFwrvzRUx75P80GfR9yxUmk8FMVAAFfcc3OX32'.
'4LMebXtW/m3eqK5DN7ftpddn5TCnv7qHQlG2bduBq5rNQW0Xpb/qqFQqr969e3dm1KvTy4DFhZWrLpdI'.
'SoqgZYhkBpcS8cTQsmDQojrffh+V75S8+WbcQc9nMyMwZJpVe9TsvJLurnB5xSxsvKXy4GkWZ7EQnm5b'.
'Ug3bMnf11nGlbmVvZfX79uyYwR27kXPCJdVHq537fZc4ccGwhuS7ImCgt1EiIJrMoCe481CpgV5vAk6L'.
'9d2USkVuvPnZAyehGO4HPSibGBmWEdh1i8mg1oOZtLV0j1pZyvhCn7zn6bAyZDYSVVoccRB4pcpVbDF8'.
'6sbDTJYpa+H4bv7k3EsokgkY1Kq3a+2uIxywCB5Jc9ZvX7rgEjmHcV9UmSj8bKFBpfU+VLXfQlHMBYUA'.
'ygDUCNiN4VAUhHLfuHHjBHbJimOFHFbuyB6dpprN5igrm9ug3Lq9J9e7KZnfaZ1YL8YO6xYIRcExuiHb'.
'N0+X5TMqPnBnM5IqePNaJD3aW4NBXkUxhDB2+U3Ey83KGUrrfawOfQb27dlk8D4DZZSPndyxvxWfDPgM'.
'YMKHF48wrL/93XEhFnx6wQoL3hMHS4aWoVaMrkavNYK306PCbbNbUvs3T9aDnXr+knd+zELGHjd/4DfK'.
'+tvfHVQyf+Sd9te84wLwnUsAm8OD4AvnRllxovacv7KQzRfD0AkbUMYZvuad/y0RQUHngcP69+9Ub7dF'.
'JwezoETy4D4j+5LzqfCu6fFF8N9CHBvYhghy1pc5Ug5wgAMc4AAHOMAB/xDgLj94rPPVR6HJpx9eoqbv'.
'p9eYlIAvWyz/2fC/Emn/XwBWw5btKrf5vv5JsROrbFpBJkSnx+F5B/M44JNgfnL76us8uSZeqVNBvlIK'.
'UjUdEP1XBAto+JrZYU6pGjUk/YaMLIdfVs+aiuyNYdhP+X30NrEZS9dVsjCZrKNbViVmZmZi6z5rpvbb'.
'tr3ceM5guHvyJN7D7DV6YqkKVau7rpw2FmNkdIRizqqttb/rUDG8S/MBTlBQoPoLRMNBJZi1fGMVJ093'.
'l6y0lKwti35NseL62ROoBLjtund3r9ukdRmzxWAktIq30sr4mfezypQp49R/wqxKZove/Dn3G0wGUGoK'.
'IF9R+Nk4dh8w0qt2s6YV1AqpYs3MKTgJpfucdn5WEOPwncebSzGd/WNjsyFfpYPfTy3rHXb/bvKK/SdG'.
'qpM5ozlsFrA5LHCuzF02vsePi6CoM5lnb4aG8q5da2hUa4FZs4Z+zeXTo2+fO0Ov8gvccWhwK73O37Vs'.
'aRD7esETjfFk9xaNB1sRp3H7dcvuZgO6/nTNCVQ8uTQHUlRMubM51xnnOPPz0yAzPR5k8lxwK9NYvWPb'.
'hYYdF38f6SPxATdhCXARlAU2kw8Miqkxm0DBYIDACCnOhYSwSr0S9CYTHJ0f3OjFw4e4SlC39dzNYS/i'.
'k/ZqdQbwcHECkVG3JXDSSFwtaWjdtVeFPr/4H/B2EvFKebqD1gKqptXK4/SgHN5lUP7Fk/eydQ+jndk8'.
'DpjqlMmcOGfU6IzERFxRydh1/HLgNwZua7eyXsB3k8DFuNgZY7q33WjPDD8NG+a5ee7kXJZBCQYiQSKU'.
'4tc/t2iK60V0p65f3+HnTtVjMJnAZvPARBhFq5KCRi2H79qNwBWb8n23rofHZj6tGZuZANnSfAhZHoyR'.
'+2j7dxy89GCn8Ll0lMRdBIZKzpmd29TtZWvL+r1nAuKWPemt1xjg26UdE0YNb+kHdnM0X8I84hfhqUqF'.
'QgdPnyWDgpV9bqH/YFyhmGDtZLzfq2OPTVlCLocRl/Jb7xcP718L2La3fie+4Gb6k2cQc+EaHC8p/uV5'.
'aMg1KIqiG+3eLTp/+e6D6izKT+DqDGaDCfovmf/N/StXcL6I0/eX8W0XTx1+NiP1FURH3QMQloSxI5bg'.
'NGx+4K69k9zZGaM5fFfTvDlrfh4eOHOIUaPnrBo1ZYH1PRhdR5HNOPzwWriRmVqtUFMIOfJsEGurPVvQ'.
'b/QvVqIaZu85qQt9HQMPI+Ng6I9Nw4KmjRpJzqNEVdvRgjshILBbz+7dj3sS5jIYTfA8PuXg8PbN8Tna'.
'Y5cfBIkfxI9+fekJ6UwdLIre1YGcf0GOXHgbm8U2uz288Sy9hETI5wp5oFTrodq31dAuyZ2/cVe9kT99'.
'81SvVYKiIAOe57EfjOrbH+fOk6FIsnNOXAq+L8p50IiiTKCSS+HoC+b0swf24MxHNjkU68//Hv4q+X5N'.
'pU4DmYU57zBPjRo1xEuHr1PmxueBUW+CCJ/UtUHLArZa6YXPZ4+dH9iEcVR9h0WEAYPJAM+6pan5h0fh'.
'YvH8L2Ke01dfWvgWYEhlGgh5lADbNgzAIHaknXSwAU7lCK0Sx3xnQ5BWn18A8TfuAfObOuaxQStqWhmu'.
'ONUmebTzsEIoLpokLDRZoPngn3H+HWPCrFt3ThojX1wEo0EHIt/v5KMHTsJtBanWEW+bobDX628kwYyV'.
'6xpUbip4ItPJoECdDwVklF6ac6dfZkoiTlHjVJFp6fHLMTcfP6vyODqRvlV9cW9Ta6eri8GVtedqSCSf'.
'zajC53EhPV8Kxzaual77+2byrlDhxevzj0GtUoH7kHp/TFjgP8DaKX9SnYMnTq89snqLcKGzGEx6I2Q7'.
'8ZK79fyh/o4TZwc0KWXeZDRoQJabCi9UJU5MGz58vH3HHblyJZgdc6IrRVlAo5JDkkvHVYv9Ry6zSg7Y'.
'fOlcRHLO85pao44wTx4Ez979hnle3o1SPj75XBx7PwUUhUoos9Dvd6FIpGYwWW9UlMVsYhmNRo7FbKaN'.
'7KhnTx8d2LT6uJVe76iyj9k8rOBjjxm4FCYvX0MITxtfSihe3+rhre3i9HjnQdDl5BHOZUH1H5tjozTw'.
'4dkYTWTYK7Ux7LlIK5ODKpemE87Noai3hD2+TJC0AJcYPByu4B3mgA/PuLC23d6aeyT8oNveC69BRwg5'.
'vOqip7tHrR1nJaTSeh3zcuizkmHR8cDj8cHTSYiLItTwYZvCnJKReem3O6FVFAYLSDU6GNvix0Z8ATfl'.
'9fYHwBJzwcnTFcTOQmyEFj5gcx3ctCaubhsOmKVa0MnUIKlZAlflOP3Su+uufWdOt+HGX+qiVuQBJcvv'.
'3X346INn9u5EqW1cvC2oS+zhYV31OrovoESHVS8WDx95EOzUSh65LyTqMRgsJhDzRO/Q5MqW2+KEkFQQ'.
'isXg5C6GucP7oYkRAx+3wT69zbe4m3heIv21GxEQEZEBhVlKGDRhVjP4+NwigpZRqqSRIRICz80ZMq7e'.
'RkYQfORd7Nwbt0XSzGwwURSUHNoXJ6XfGGwisRuIJW4gEDgBi/Vp+/7q7at9xTM4pplXJ7nFFkaBt6Qk'.
'OB8v1WfzqF97kJ+fWxmHef/+fVxFw/jBr0qwm0QELkI+jeDI2fMbwUcGVY5G11qmVAGfYYEaJdxgc8DM'.
'kNUzJ9/nV3QFvpcYBK4iMKcacX8PSuNiJfuYeYtLFcSkk9EvAzPTAt5tK9+BIqmtH9a9R59U986/GiW+'.
'FqMyHRpz71xYETDIsGnNtGxXUE6OYrabsfyUsg05ak0aPhaXfLxxGBCMxJZjsdngLHIih7P9ay1VWld4'.
'KfYSgciVDzw+DzYEBeNCIb6VQT50fBA+Ol+8a2U/r7qNy1MiMYuMTBa8fiTaM2nhSVzo4vYeYVhrj9wI'.
'Wr7vFtoqMOPaCW+n2n5mBpcYjXo9BA2fgtztXgwxGbsmzI4jshI4LhKoNqSXcuimQFx2hiqLtlcIP4HZ'.
'TIGR2Bhmy0cdAHb7bT01XS/8dJTBYIKQLYIBZX59Ejk3pmFmWjIuTES8TV2HjCnXaG1D8/THU2+R7xWX'.
'D+81dmTnNnf5XBYhOgvuZ2uDevXqVa84Bgr67cTYc3dD/dydxFDayw1k14//DEXqOPe2U1R1vocQmOQ5'.
'hqh0cdCqo1ehaCvR+8Cr/MIcyxXwgCfmg/vEduF9/QfPJOcLqlZtIl4wsKRWcXF0YGH4GaZIxIMyVRvB'.
'd3VqQ52K3t7V3HUth7Qrv/ryibUHQ26dWX395vX28N5gZrNYICIS2olIHTcR/Xobzaluv/z0femWlYiE'.
'ZABLwID8w7E/LfhpvXzktF+RCTn2z6nVuJX3mqlH1MtG7MN+EBXTjs/ytnhEdVUaO+fok4jQZFpvWIi+'.
'tZAONVsooEjvcnlsaN7Vc9ei8UM3QZFxp7JydNk9gZvuKc6c87KQEcEgI8K9UwdgkAYWXr4GFo0GmORc'.
'xcnDHnQe2B8NXfTE0PCj9gTNIvxiAAZxk5jEuwCKARbCZAajgXgZehBV6jh/dLduuI3S9EtAgNclCMph'.
'EqbhsrjAI54InyMAPvnLJtIKV5oSTGm8EV8EMc8VLv1yGW0xNIyRK70CD/6+/czD5121BgMZwWbo0LA2'.
'+Lq7wNO4ZDSOaTxaNvhGe251wHBiN+GSFrS97G0jcZNWbRuOatD7suJJHA/tGbarGFya1gSL1gAF15+B'.
'mQwCFlGPWc099q4NmLHX+v4CeKt+xb+uXj/TKX7rfDaLSdpsodcj4ThnsTjkIK0hvUZZTEX0J7zDc/KC'.
'/Hp91TGpT0WII5dcx8ZrmUWyAdtcr2yHkWM7tcJ9b+yaDRr4jW87+7E0PJN+PpIEr6EsRYeZ2J0+naoW'.
'jJ/R5WfroMOlO+/buV+03gANKIF1NAmsI5OyPlRlJWJxK0KYVkbC1W9CeCvt8F7UtRq7e+3FJI4EHryN'.
'jr7JB2EFg/Xdtnt41oMJHxe3tmU+Zuv9hvd+w5Essh620WhbmaO14quFj8dBWNa2iuCtyqas9xisz9C8'.
'T6895y+uzvx93HQWmwuS7yaETRgxEY3lHCudGFZ8kJaux07uDJY9/80TTzHZHBi9+PL38NZ+sakce3rh'.
'eb0dbdhWHJ2sf9l2+GF/KOET8Z4vCRIi8VTW40vAYkesLwEjfH4wDeBdo/1rgbJ7zmdH2YoBpJUS3hrm'.
'n4RFQYcaZp3xJ4xDJAY5vARCDErGfQiPuFzzHA+KvZvN5cG+UAvGgVAK4mLBzw1iouersB5fBf9tK53+'.
'k4Hda6T/Dy3K5Nw0FqbQDITBQI7YEySVmxOVyQKTugC0KY9RZwG3Ro/0IQN+GUoMQlQpGBL40kH9l8HB'.
'PP88QG2A6g7NA1QnNtVpW/qHqgTVJqqWL50ucYADHOAABzjAAQ5wgAMc4AAHOMABDnDAPxsYuw8cqLtz'.
'507M5/Evid05Frv/9wDDuUwZ171X7mx5FPPconE3PXOv6ouz+MJ/xcv+J9Kg/S9ASFxChq8Tx8dgMkJK'.
'XjoUKKXgQglxTgsZR/1Xn/8+OCTOfwl8X7lilSsPovbishSNTgP5igKw6C24atGhqhzwUdBtXbVwtdFs'.
'Aq1RD0qtCmILVJ9c7fe14GCc/x4wlShbVoF7snSEcdR6DXA4XAfjOODzQE9UldagJYfurz/sI/ClxjHq'.
'S/6stZuqODm7ucXFRKfvX70Ulx7arzr78H0rNldy8fX2lOVmSVdOn2TbbfipvFUIrK3nrjUd36XtH1C0'.
'rIC/aMeBunu2rolOffVKbj3HOPX0zguziR/W59tvx8CXLQ57H7iNW3V2bd7xx7KUyWT6it2bCPRO0rmr'.
'tlQWubm4JsREZ+5dvTQVvmxpBH/KirWVvbx8PFMT47O2L12Q/Kn7UdKoiI1jMH4WqtgvuNu1Klcs4hN6'.
'xmZGR+OiNMPn3Pg5wDpwLWSZ2CiaWZgtB6PJAhw21i4s4juWM9/w5OXNxhvnzbSlyrMB5+jV+0HlcrOH'.
'iZgMYHG59AJ3k04HuH6Y4eEBsWLRnt6tv8caisWuHCzj5+d67szJLKFZzvOfs6H6lLmzDvmyChrIpNmQ'.
'n58Ban75e4O79Go3fvGKym17VHnJYfKBMntJydu0+A4Wo0ioYnpgnUkHFqbBV2soJA0XKgY0bOcFb1cT'.
'MtYdCe7p5Ol1jGHUs3gEV1yvi+uTmQwGuErEwBWKErYsWdzy2tnjGR/qvG+aN3dZ+uuaS95y3Xd0pk2K'.
'AqPOQO+3wkXs4OsGYYXZ00Z3+3HLhzpo2f6jbTs2rnHViVLTi7wQzMRboigz6DjO1IPYggUjO7XcjDg0'.
'/akX//6Vk7h60Nymb1/fkeP7ZkSkx8L916HQvELnM4uGD8PSNe9vwGP/di10axUQj+YQSqiUOlAptCBL'.
'KQRplhxYdb3iRv/SDguUIBMVKxA+h3HEIS+SFdlJBQzcOsz2Nl2aMbQr5ju0rN99O0SeqWCyCBM5uYhg'.
'0rhWb5I5Tlu9oVYngVO4KiaOSS6AlHrfXBw3+GfctqH1rVDBaVfgumMldToP53KlweLqAp1HjC4bGRaS'.
'btchguDQF7ubVHDrr5TngbQwC7J13AIXkLkrFPmQk5UIWdlJYNBpIUlVs03tTn7btIzEKq4CN/ASVwIB'.
'x50wDQuMeksOYVFg8+TeBZpMUGgV9NZhgbHS/Qnte3WHooXknLVnbyY/fRXlI+Rzwa9ShdzNcyeOSoiM'.
'xIX1jI2/Xw5wFgvbly/hBS5iIdwLezllQu/OuGvynWG9+fj5IeUiZfuNKh3wPJzgAa9wQ+DUsVj/ztCx'.
'35AqUzsN2evlJOK6lHSDfC5bU7dWeaxBp7DvnHOhz47XcTX11mmVYCCUGDhxcf8X9+8mdBs8osqSCb0O'.
'8dhsYLJY5ODQ24Z1GjmkWrxvd2/9YwfCOG7t+vyQkZCbBuGJr6Bt1Z5/YpxKlRo5LZ+yXM7VmsDZxxn2'.
'Pvp98sFNq1GSa718fV0mtQx4kHArFgSuQpC3MtQ5vGXlq+IGyadsHG5YRJrCpNAzkpPzgXLThxKmWQJF'.
'SXWjp4xsWSs+KQ+SkwvI6KeX3SLjsIaOnVq6j2eJV7JXkcysFxGwrzBrIWGahVCUMDguMzHxecd+3Rpn'.
'u7hm4O4IIWWBq/t2p1Ty8/O14sRMz0rWfF/Rrb9Wo4C83BRISnwOOanPhXeitUfDX9yCzIwEeocEjy+G'.
'nMz0EsciN1QpUOg103+a+3OvOh2+7VSzcY32NRpU7lK30ffhz17/piJSRqFTQKG2ECwmsZYwza9QpCZZ'.
'q8/cyL0RGubzNDYJJEJh/ORubXoTpnlpxTdmUs/2I2OjohbjLgoO6biWDeusX3fkDG4XfrPtZOvJSwMr'.
'Riv2pz6KgZQXCTD56PIhhGn22tp88eiBKz8NbVdPy2HRO0h82GzhixfxuJvSyfaM9b+d6lbX3dIbN+ep'.
'yWDZdPzuGsI02HHxwQf3XL4QlrE8OzUC0uPDIDkqBJILieckteRHx8Q8Bmt5pcScFChQFlq3G/1JWHAX'.
'j10sVyUWgCJfBUtOrZ1KmAar6eK655TczMzIwlr6mRyiSSiDGUpGeuB+NdfiBMxHGefC4+gHYDAzCgvV'.
'kJxWAHNH9cAtLrjXCFfl4wa7xMqNnfZmZsmA68I32jp9VNeusZqcXMiPSwBDterqUwd3465FXJCdZb0P'.
'RWvqwFHDWumVasDk7yyDHnZu2XMHrPt8SvmU834Qm/VKWpgJaamvCHM+h8u345bNGzNkxf5TSS29anaQ'.
'uZSoAbxynW5fOXU0ppKwse7ElOOdtSoVbvd9bcUzecj0uZIytZlT89V5kKvMobcNbxqzfjgUbfNRbjt/'.
'a/mz19HOMWlZkJ5fCLvn+k+34ppmxRPxzVo2+ZcNSr1JpSWqFreoNPCriVuFPKxE5fup2IdyY9Ih82US'.
'aJuVC4l7+SL8PVoVGI3G+ONPbg/VKTW06nKnQLg3+DqWVcJdDYzmdSsdMhKPSK9Rgkolh50rF1+0w6Pg'.
'xIHDQSrCUGpZFigLM6AwPVLXr8fgrnPG+aPao1UuboMRcPngLJL8iW227j1zyJSjAi1RS2qLxXzzzMnH'.
'VqbB56O9aSovrDiBw+cAm8sCXa6K4R8Q2B7e25eF8DHjmOdqYtfXECNLKlODd2UJSpl0KxFsoksf8Evv'.
'afM37irYsnIU5luX9xg6phwnI50vL5CBNDkNsprUx+2saBO8ry9N+fmZqXk8vowllbsQ4wx85IqKPmXL'.
'lsxKScFRKotLzjzm6qv2y8lJABaTA2mJSXSHxoSHxY3qPQrraeKWRmTYwqOjt9uklX3aFUGb3rWfqAz5'.
'INPKIF+dD9L7gtXWPVOYQIBKySuYmiOTQwZhmpb1/GRXLu1DhrLfBwVWvOVRkVELWdWrrilKVGCA5XuP'.
'jJ0zvP/y/cE3pymfZ0Lmq2RgE1V37Pz+nVC0N/v97BD6tfNnBPc++5B+Iot0jh/XGVXJYnwnz6wRm0xa'.
'MOhJ52rpYK9t9ysNzx/ezjOPrE8kpg7MZhOYVEnIcHnwdnsNSATEFuNwwWhE2+ydnEzs6mzv3kn6VFDl'.
'E8ZJyGOt6b3jPmazSI/MApPRBFzCMLFBjwAZh0nv72JAhVp1WpB7z8J79tgHGSdg255mBQVKwJ2T+USs'.
'qZhpKDWk8GfPQrFk0ihUX4il+udefZepX0eAOi8fcN/4hTMnbliZpjhjUp+q15+TyGSDTVriQsoU0Hfw'.
'qFbrl8zDUaAVO7lo8vOiQUe8BCaTB2wOx2BtgNYqvXLh7X4o+05GYB55cjUxXxPJlhL1lKvMBXeDX9jm'.
'Df62YgC6QRMnlolNSWdm5svoHaU1SvskXim+jQiWR3duXXTy8FijN5lBTQxeJ29fzD6xyVPHGiWNywF9'.
'oQb4Pk7w8mmoLTtGcYalOs+gT6NSVaU5PA6o8xUsZ0/PEvK8PAWmOeGZ1YAZLgxaejfLO5mQPT09mQZC'.
'C5NRQ/C1IPPQ3qT9NbhDgkUO3PVpr07Gjh3rU5gmhbyEAkiPyIbS/lUSwUcUKubyDNWgopVyFIMwHMds'.
'MrGJ5CJ8wzRP6tVxR3Ht+CDjVKhe68eEl3k040QToxics2yu5J8ICnb7iti5Bc2yX0ZB/utobAXxwPQf'.
'2sRHo5qtUt/zfPl6MJt4MSoy6ks1rIPJy3+zMgfExT4lI4FPDF0ejgDqvfd+0C3d+vvRXY9Sz5VIl6VB'.
'uiwdSgiraraO9cftuMnWTgW/hs2qbTh/F3JkSuCTTqRM+nz4iLt7+sDOdO96TUDAzyf2kgHMDAYmXxeq'.
'XqSXyXiUCHyREAQutKb9WJIqs5xhea17llAaO1hJmLZd1741T+zeHJ8iN4e6KRO/RaaR5yRD/WYtfcL+'.
'uB1ru7G//5Sa+Vl3aMZCYFXpRPrk2Ds14zILs+kYTp6iAMo7vR1PbVu3b5J8JhMyInNxxw14ezNDhg7o'.
'gfXq5Xa4vW/LWKy0+lNenQ+rKgo4LyPSCVczITw8DZp24Nt2AX4UVHl5kH0/FIxE2rCJC8vnmIu10mwg'.
'L5RmpYQ8AYqoRFlGFoga1Pd8Fy8mMUgFhAc/lRPhLZwKDm5zOH/X8Mjs15BBmMZZ4A5pmwvRrkEV+GYz'.
'm8lkZObLlcDj8YhdwKbd7k+00RQWlww5chXkqrRQsZQP7uDkFcRlAZu43gwRm4h57qeeQSllioysR9Fg'.
'0ZpBKZVDqQElsb4MI3BhYN+ZA+olG5TZIMtLg1H9fjxAGAdVMg48diVB6pXU14/prdI8iTds+HX9BCjy'.
'yt68Dyc4NQYtTXH7IjxE80hiQhKIscIDnpC01yxEoxdVXAF8HIrtuw8yjlIuz3j6NBk0GkwKYAbv0g2x'.
'SsfmT3WansNJ1CmVVfhCIbAFAvi2bmXP6IiID7r9HiVLeObGkgZx2MAgUicnLzvB/nf0mjAVCpPxebHK'.
'0aNHe2xIWnY9POsFLbLFPAm0kPdZeSR5U7iVUG8kQW5GWq5YgCoQ96MTT4LNw+xcHw1RxKVngZ5oCIGA'.
'DxU9XeQRhLAsb7GMm8p3YQqJDUmPk48nfGQD26cwPa9o3zuHCbGvX9H76Z8/fJg5V6n8Zuqgxo8KM6/z'.
'zenRJVZOb5XvVLlFliz6uk/u81N0wn7Per1MyzZfHijLz4+C93ZoYgCQTdrCJvRiMN4qq7T0tARMrsQT'.
'cIHL40J2irmpFc+vmpL4oFe1eeGM3zEBk4m4yzxiLEWEFmCmiU/WF82XCA6ziGphkJHHJMzQu0u3LvAR'.
'BmXkyRqw+TxgCQXAlYjg4vnjt+07l0s8BA6bRyTOZzEOm9nCkvQ04wkxPJkg5IlgQIU5T45s2oSxFDTQ'.
'aVW7Zuua0gMHDhRtmDcr3tPVBbjEHuCymJCQlY8VrD6YxqT7yJEeZrOZGKBccCHEr+zhjK6ygV235D0g'.
'0obBZ9Ee14Dxk2t/jHm0URlVWYQ2mP1C4uUCFy5cQM8J9YrJ29dXw/SsFSV0cgMhdrJQApqCTC+nci1i'.
'Bd8G7Fl7Vt3Nf9qWZpnJCQ+sbXrHDOAQZuQR5hLw+O907onTJ6J4Ii5wBMT+4bEg70YySjkn+Mqsph9k'.
'nJjw8Jzylb0pLo9BOg4gOjwdpixcOQDsyjEXB/NHDjjPdXelGBwy+giHa+486gNFa0KK7QxmyIOBTNIJ'.
'LDKCy7b6Hp6jmLOzpRjEM6B38lOfjtIvv3j4xomXx8ToEWDwr5FvO826ETNtdo1t2yz/rPFUao1+bc/j'.
'lzYNakewWZgxgwHRySmcWnXr4qq5Yrm0av0f+vK5HBCTQYHMc3H3JpTA6vCYe/OZhGlQeuBW3hZVvp8G'.
'bwv7vg9cZWhsRUyvhl5Vpd4NMXUY2hnG7XvXH2np9DAu+2pAXYteDsdjqk+ZsvjsD9Pm7qg9ftKSDnMn'.
'TViiUqmwxBAWjUGv7U8TUmwywLhsDvDZXBDb2fh3796VlmxeGYipCAw2UWKEnEFB167BRxZ6tenVC73W'.
'L665YmjT03sij0f0Nod0BCHsnYu6VVOXrcdERu8YHAOnzK40dPJhU8f+/VFv6vjTJy7HVCeYryP53kOY'.
'tCCwCxSTuGnExIm11SGPXFjIOETqnNPmI8HRU6JHUXZSPG0No6toMX98SmvzmUujA+8Ma64nriqPxQd3'.
'ng88XBoyDIpiKejW4qOYC8/uvIqp4DQyFZ7jPD2zvwdOJ7BYRZ3ee8KsI1B8zhjuw9jkpQKCKx7tG9Z+'.
'GfXsCWYCU29asya26uAm9/AZODz0l8P9mnfuXrU4Bty4++REQ44U2MSm4rk5weAl4ydBURzFzOAKKVuh'.
'Mkx3Mr65av3l3xbcu3923esbxxbFXzmyOPnioUUZN4J3hL58+Xxtq44dS77fsRzCNDwmh5Y6Hk5C+/41'.
'5voWDERXm8GxEG1A+uZAWLnde288gyLJYz+w2TtP395YO8FPFrBtb/Pi2vHJhJMrD9x4ciootBoajnQO'.
'GuK21mxYFqrV877O5XGUKbHSttd/fy6WuAgh5uXK8nJiG5H7BHv3nrwr27q9Dp2ThZxI6dS62+Yl8zAx'.
'Ij0MBk2dXaV2yItIqlDK4hCmcZ0w8MjQ0aPRykcDVr969erqZcs5XyzIjiiDuhpz5HF8GiZHRGf7b5g3'.
'4ybYjbbRswLKBLO3pOiNajLa0F7hw2CPhZfyM3OulypfMdvT19dDw5L++CDrdJekgld0fOIH/qC96yfM'.
'nEVuV85as2XAjYScPTizjIkklw3scPPnH1t3hbcr5/grDv3+ctulu5WFxJBu28AvbZP/4IFQFGi0ZZnw'.
'OLz293jVnVdORo0eKHexIVj3ou6tcycxLQmdSGrFziO9zFv+OE7hXJ+zEA4bH4x59fSRLXKLg8Vlw/IR'.
'KYrIC04CIumZDBNtKmCowIJ9Z1XXZpOZtjsZ5HuJ74Ymj5+42s/V1ZW1ZN+RhVcijkzmE6bhcXng617e'.
'nBOW0+PgxtXXrfQSBe29dif3ZGR9Ov8ReY6F2GQ4YHx/rGpiCtm5FoPZNe1ipADrg1Uc0iB77IT2mFol'.
'Hd5TiZ9iHORYt+V7T664eTJ1BM0EmEULkwGRF1qs379rV0W2dcmAbjqdDjs92zq6XZfu2D/T5fcLMw0F'.
'GOY3g7hObRBWrgT69AxQhj6mX1CuRRPYlxI54Xxw8H0oKpSoDA29dezF03O9wZp0iWYc8i6T2QgYWWXx'.
'nMB//CqxrWMnnFyUeO71jvK0mGZh0iVyEE8MmQgnOW2qrijpUpEt2NRpwIllwyaMtXa8qN4PrepVbdfj'.
'2uvkdL6B4OomFkHXJnXpbGFH7oSSDrSAk0gAzSv6HFk7ffx2K4PnwdsYEvaq15Y9p3cyTj7raFDr6Myr'.
'Ls1qAcddAqrXqaCOSaPp5tKxgWzujtkjC/OykfEwzGGb4GU7e3mVWeTfOtyUFSZGXGkao39EjF303Ohc'.
'ShTGcMwol0inc0HUYt7ZW0nXu+LgRsOYQydeYtEDBNssFriZ1g+b62xlHrf1e4J3aIPTetr6EDvRlngJ'.
'v7OILaT7gbVh+Yzxh6xM/ae0Kp8zyYmo4ktLzVi9eaBYWLY7ZWa5YlU+nkAdsmLqmD1KpQyDcXjY4iC2'.
'+zADg2+/cRNbN6tVvx9fo6uEubcsHLZS5ub0YNl0/xP5OTnoDqK3k2slIPYs2geY9hVdcxG8VakWq8TC'.
'hqBks00Qoqgtb70erzVZR4h9IifsWFSXNuM308qoNqmChr9X+fLlq6zdvHl0th6akMsI9cHixeO8unUx'.
'+PS2bduirMySbX33+/qTbaPVlMC1Pas4l+7KMzJLEG+GMnEgN5utuT1/9KBzUGTTZFvpZW+nsHaeObdW'.
'dmP+JHHZxsZobaUDblyLoXLZMkJnEaO8ABS1M6Jvuaql6YgWaRgOCg5IvvlZOqifPxbmZFnpZVMttsRW'.
'Umt7ldZrEMdyK3Ydn+qqFXdmWzhOhHEoNUf7OiT8+pETu7Y+sOKXA++5+1/CODZAgoutiNmIb8u2hYbn'.
'hzJYcaz3iK2fbZmrbNmybAEm+05gWt/BgbfVSe3xtVjvtVl/tkxi9gT7ENiyddki0PY4254jhi/IslXM'.
'83nWNtunLaHNNbs2v5MJq9fo0c7fl9GkqhJDnHhiNwjYHdVBo9Ekw9upGsSNziQ2zt+/eW2XqI1EPBCJ'.
'Qwzhmj3Sh/T7BRnHfvmELVuXLTOZHt56qzYcJda22rKcmaw0UX6qnV+ykMs+g5V9qrZPuTvYuWiIyu0a'.
'Yuv8D3WwxdoALXwe2LKI/VWwz0ZmXxD1s7J12l2rsx72tPpom9vULxma+3CvE66/4blXMGg0YSjBk+Fd'.
'iYT4sLdt2ZK2e9/6Cca4i5VQlf/xIiPIivPnZgazx7EA/tyfn2zn12yPeX9u6Evu+5esf/0Xwd+RwOhz'.
'acUwydOq4TobzCMNqmyULLYUd9R7z6Pz9zHlcZXoxJulf8jaPWou7p/60hR69u384rY61hz/Q4BVqukh'.
'zDrKJhLHqFPDhcNL0VnANMD2fcToNGhQmf27F2Xpc2OBU/HHrJGj5qJ3hwb250rnvwUcqd3+OeCydf/2'.
'c/rnu5uxiTeIs9zoTYorNgWuiw9gQEubGQ4mWQawXUpBtLHGipWzpgRDEdOgwf7/WvHVwTj/HEDJggHU'.
'CoG7D431FSg7cU1STzSA6bCEyM2stLjdOXrg2Lbbl4KTochmRGMYbZuvMR3+EjgY558F9GpCKPJ0xNbP'.
'9l6oLe+zzbv7Kzs5/jKiDvhnAhPe9ewQbIbsf5KT4QAHOMABDnCAAxzgAAc4wAEOcIADHOAABzjAAQ5w'.
'gAMc4AAHOMAB/yxwzJQ7wAH/fGC7urqK9h082KpMxTJjslR5LV+nx3AblPW71qr+D7hZFpfevJPB5J8M'.
'jtIODnDAPwtYaw+f+qFq9WrTPVzEzSV8lojLNINGpwapWg6xBcmQmp8OyblpUErig2kPfKBo4wouDP23'.
'rfP7EnDse3CAA/5BMHDaNH7FKpV7eLqIWjrzKBGXYQSjrfCCTgMyInhy5QXkyAedUo17b3HRMW6w+o8Z'.
'y/8xiDrAAf8LcHjtWm23Rt/MaFyxfOkO7To1ikmXxmFhMkyvgnn7VMTiQcGDVo9Oa7AlVPiPWpzucK8c'.
'4IB/FrxJtOFbvnwOi8M2Gs1GoMtMGfSg0RcJHr3JBNm6dwTOf4zQcVg6DnDAPxPMArHYZLFQFCb4R/cK'.
'iyJqjDoieHR00SMGg/kfI2jswSF0/rnAWhx02O/ArUdD5q5bh8HC9/d8OuB/ADBVutZq4Wj0GtASgaMn'.
'wofB+M9lhX+Ve8X57scfXfuPnd7ew9WzA9PCqsOimJUtRgudRp5BUcDCTIksJliYoDKa9REqo+bG4/u3'.
'TgUtXYAVNjA12JekaOC06dLFs8eICZ1KSCQtJUxGHTGHU5ljMrIo1AgGY1G5DfLZzOGAlqKSVQzmy0yl'.
'8uq5Y3svXT1+HANyhi985/vA3XjifJdvqpdf6sqjqqbJjLumDfKfFhPzQF/Gz080Z8nanyuUch8vZJv9'.
'LEYN02AwEBOZIZWZuEcvnTqz9sj2DZj6E6c96dqSh+7cW+/iqfqFycgFSfMGSSOm/zpuz5pArLlky4v3'.
'uVqOWaVKfd5PQ/r4+vqU8RFInPlJ8VGvNsyZmgafnmalsyRMWrq+ap3GjTrzeLymfC7Xj/SdD6bZwEyU'.
'mOXSZt8zmUy9xWyKNhhNT9NSEs4dXLE4JLqolqOtXX8FUOjyZ6/eWrN6nXodXTncZhIWtxafxfJiYrp7'.
'kxkoctjKqhhZDLUWLLFKyvwoNjHq/Nol8x/KU1PVfxEXpIcwYMueutVr1erjIeE0dRLwqvFYFL8oE4AF'.
'TBSDpoveTOnVenNMvsb0MDE+8eKc4X3vwdsiIJ/9fnStzFoVyDVKUGrVtPBBV+svAnegv3/J1p37/uwh'.
'dGoj5nDrcCxML0pvpjOimo1mMGLqW9KtOsoQZxDAo4y8tDMzB/W+BUXZMP7S9PzfKS75W89cGlCpVKWl'.
'oDCWUMg0IJNpQanUAaaIdS/nqjVYVM+wTCSbwS1jlFuqGVQGBqZhZdAHnVmVFkTuVd1TLwYfbP/b5rWY'.
'OdRQTCcxmv7Uy2OU/y/TfJQqf3ZikohBmJ/JZgOLCBWWSAhmXx9QcXk5aqMx10xROraFchJTVFlBTg6f'.
'QzQHm88HnkQEXIkY2E4SKBSIpdFyxY6V86dtjHj0yFae6FOE5f26fnfjH1p8F1jJnfcd26JlGQykvYQx'.
'UpXMxAtX7q7r3L7lhBICfVWLSQ961FSEebAEkUolA7VaAVqdEiwUE3ilmp1eM2flxLi4cOW28xensT1y'.
'5nHZFJPH4gGHiSlqOcBlcel0tZhZvqgmaVEZIpY1UROdSZJoRr2ZaEaDhj4wqbjBbKCZF/+azCbgcdxA'.
'lyEZP7l3z8PwdiC8yeA6ZfnamkTKrM4plLZSqZQsrBzI4xQdYpEInCQSo9lkTDeaTDJMis/ksDzIuPfV'.
'qlUsAZcDEiEfnIQCEAv4dBEWmdbwOioqZum0Ad0uwpcJTU5A0MGGfr4VlkiyVa1YMhVg1nosB4FKCyQC'.
'MLg5GRVmXYrRbFahguEx2J5CndmHU6Bkcnkc4JNreGJykL8WER/SzMbntx7cmx04aUwIfGbB4daDBrmP'.
'Hj0hoIoHb5grQye2EDqiBYJAkb4pNAtVSbnyF0TYKkR8nntpd2FtF0ohQJ7GRFqYwg/zOZoYXJBbuBaN'.
'kcqjLGYLmwmuRETxKbqevCUvLlvpP7pT67PW/rC06dvXZ4T/uGsaXXrNAqUM8pSFkFWYDSm56YCZiNtU'.
'7orlNQOgKCuwHD4yZe5du7ZoYeC6kSX04nn8Ar0Hn09oI+bR9cu0PJYlUyd/llKYEaLTqKVM0uMuLm5l'.
'XLSC9rLQdE9luhSUhWpQ5KlAWM6NKt2n2sn7T64uPL59Y6L1nV8kxP8OoSM6cuvRuiqePqNMGhOjUKqC'.
'7GwF5OTIQUcow3FXBweM7R9ENCImKFVZOxoZThi46+g4VbZgtKpAzWSxWKSDmMAhTCUUcsHTT3Lol84t'.
'5kJRKl9b9lnGlBWba7Vt2PC0U0xUJRXm487KpSs4YkOY3zaWn3r5dOWJ/UFPoSiNL95ny+ZqywzLb9Gp'.
'R5XJo8atKl2QV0Ho4gR8V2fgksHEIgPGyGJDWIE8eM6UyZOjnj+0WUBviDpvw/bKzZo331be06mFu4Bc'.
'TEadkfjZKEzURJho1DJQqqREIxHGNKjprLMoCHRqKf27SllArlHSQoAumGzN8y3xqgqJucKxfCdXdnaJ'.
'l2tMlIznLvSA0i41ga8tEZUWkXk/PvJ1ZEL069SXj0Jy7AbumwSxPqXKuS/bt2uxewl9I42RCDQidLRG'.
'LV0kWU+EHr7TjVut4NqeqzPP/bb/ERSloFZY28jZcubqMKXJsiYqIVmMVZvUOj1gv7gToVyzjM+L05tX'.
'rX8Z9jgV3mYxtlmHdNJU7NMZqzd3rduo8XQBiylyI0IdhQ8KK7oSoc6gCQuPCJjQp8se6zM+pDF5W44E'.
'D6hkkWwwRKVLlDlS0Ck0dKJ4nqcT6BuUjVi5bv6KmPBnaB2qi8PFycnNecys+Z1+qtFwigeDKRa6iIHn'.
'JAQ2EURYbCffaNLdj49ZPLZH++3WZ/wJFyx5MW7q7E21PTiDuZQeTEYitAk9jUSpmIiSS9QI4mZMmLY4'.
'PioixUpHjRUHdqd+Q/wm+g8L8jRnu9s/FduA2tVAFJCBWC4G0kdGAz5TBzqvBq9/7tgTa/LQBRSJ0PHq'.
'MXTAtZTc8JoKYu0oceaKKKtcWT5dP+dzhE7Vqk0kv65fs5YRLh0FCh0trNlkjKHAMVR2ylq1YcG8xzev'.
'Yek0JbwtQmmjI4eA6/KNx1ZGr3jUFiUdndee0I8j4kLNqd+HTvXv1lOr1RZC8cbB3y50mL+u39G0Q+uf'.
'LomJbaHVGollo4b0TBkkJuaB0EugOnN0xYywB3cjrMxdaO1cG2FoJu0+ZESjipU7nYx+mibG0iQiInBK'.
'V/QCuSF68ZbFM7F0R46VGOzfLt9ZVUWpGG/MzWUq0rOgID4JpEmpYCYDV9O327WABdM3GzQaWy54hVXA'.
'2ZuzDKvgwdTRzoE7D49q5+kx24mYD2jxcIiGZlorgsuImXzm8dOxc38ZcgLeLrxi7L92v1PVciWCnLjM'.
'Ek5s0jcWIxEiCpDLc0AqzaQLYhfmpxcJHY+WNwMmTd6kVSpV37Zo7TN84uh5quTb1YwaKd1xDPQYiDpk'.
'8SRAudS/GTh97qp+2/yDfo/eUK5hqeZQl9nx2Br/qQc1GqXM2h4VvF19aj9Nylq292ifqvU8V5oseUK0'.
'aDRE4Kn0KvovCh4OSwKMzBIXlk2ctIPQKN3aJ8iohmGzA8rUbtzseExKaoOsAhlkFkghM19KTHotNKxe'.
'WeerL9z426ZV1+z60Z453xS9tPapoELVqiUXbNq93tfDtbWTkAciYlVyiPCykMGmI25ltlKbtmfzll6/'.
'79sWCe/WHWRMCAis3qFxmwucyLTy0tQ8KEjKhvzEbNAS3nJrXlMdwk1acXr/TszdmmeHi+EDuAgr1/im'.
'9LJVmzbX4Iqb8omlwyMHi1sUVTARNyJepYpauW7ZgCu/H4sFO8unY8f+rhMDph+t7mppR5nNtGIxEkGB'.
'dQz1RABkgVfWwF6D/VXywiQrXWR2A492Bdv16ldrwojulzgFkS6UhTSRMluLXxAlRASO2rVOxsjB4+Ya'.
'jcZCa/9KrW1SWIWOZ/v+P1+LTg+tiYFkYl6CRqeBQiJ4zEQA/Fil25nFw4d/SOgw5q7d3rCej98lTXi2'.
'u15d5JJhkQYGUe4aP37IpJHdl0JRrv08u/HyPh1FgTuPTCw8kLpEky2nPRE6nGT1UCoPa5A+cWbX5lYa'.
'vJ8S/28VOoylu490aFHvu/N8isEwEfdJpdZDbo4C4hLIu0Vc8+kTy8aFP7z33EoQe2vlHcGFTPptq3ZV'.
'W7Qbcjo6TFrWyVkAFeoKz66YOnS1TqfDwYGrLalT1/7YXy4352ejQgmqnFwoSEiG3NfRtNARz5r0aO6S'.
'2Qv1Gk2KHQE/5R4h50lWHTgxprVYvFTEZrK4xC1jEXeAgbEmowmI1ocLMbEz54wbdsCuU2lriRyuK/Ye'.
'8W/fsNwMrSIbCvJTiXUXDzJpTpHl4ttQv+/AjbEPb1x7bm2DzvZOD5/S3m179KzN4/AkT/+4lfoq7Imt'.
'BrfC7h0ceJsuHQeWLbPkOzQcOXNmqVbdOx/RMxKaGS1EY1pwpkNHBI6aaEYFqIkmLS2qX3hj143Ft4JP'.
'PrUymU0BmKYGrqlSssY3V4h1UxZLJ6fnFUJydj4oiMCpWrYUNHBlbTi4eilWlcu0tsPm03+Mp7h8V1ev'.
'nScv7nOXCFtLBG8FDw4clVYPUo0+67egHQNP7t/xzNo+89zNu+u3KlfzMiMu06MgMQeyo9MgLy4T0G2S'.
'+JU1RVTUrTi0ec0lKy6F8Nay+BguvEo165dcs3bzoQom1ndYg5JLlAu6aVghxUBwyQBL1sI1i7vfOfd7'.
'tJUu1J4LV0d8V8EtSEBIbiYWoolYNyhwtMSS1RLB+Tibe2zW6FHroagAia1Wtz2/0YVPDl+9sccl63pP'.
'IIoAyyWh1EEXV6tSgoHJtcQxG60OnDzmMLxdVfxGqaB71Wlg72sJWWE1kW4mdJ2J8JETJafW6aBpuZ8+'.
'aOlMnxtQp029to90ySqunIxLtVQDJoOJWFUmEFX2tGhrsu8QZDKEYomaLxDqWWy22VYgl7iOTFWGspQx'.
'Q11TnSytmn4zlkWrSPyfaRU6UBQS4boIKLdRFdfOGvTzJmsbtJ8Yd18dSHauU73eEb1MyzARcw3rJGHs'.
'RirXQF6eEnx8uFeJwMESPDYN8KGCInhOE3rr6mty1IWiIhY823lrJxiJ2T+0RFZ2dwPpKL2SaO9CGahy'.
'ycAgrpVLix/gxvNHx4jAsQ0Km7vwKcCOVc4c0nvP2Qu3epSRFjQ0G4yAZSExZoBllRhqNXxXqnRAy87d'.
'w26fP/MS3goFGi+xq0eOljCiUpkPhYWZxNLLtZYUIgzN4OhI98it7ZfB26o2+flZaelHtm54aUd/s/W5'.
'NhPfViPDlta2uBgIZ/u5y8NdS1rW6U1xQhNharRwdKaiWQ4UOFj3xFNR/fzCoVOC3rNubFYKv1yteqte'.
'JSSVzVeoIFeqALR0UOCwieBtVLV84p3dG25b75Na2/ApTYZ46nVSae7TkD+WN2j6Q32lRuuCbhYWlENN'.
'r9brQanW+rTr028xETpDsb+rNWzIaVzJbxUrOsujMCUf8mIyID82k6YCl1gn3Lre8YcCJ9yyw+VTAseG'.
'iy7+dVjGzYf3FzrXbXpenC3lcongwVLf+HAjsUgFWoPPiIFjAojQ8QdbHBoYPiYd4TfKCGilGA3ERSWW'.
'o06rAB25h8n0thVYsXff3+dtA5PJsJiJW4a15elLrGW+UJBRZgtT4CrgW5+hKa5NWGrLReQERpOJri+E'.
'iwQ5TIyvaemYXnHQv39/1zG9hwTF/ZHE1ci1oMxVgTyLCEyFHlRkjOpCk4iGZbXiCjDeRBoh14FBZ6St'.
'GBaxgui4GavoLxb7E4j5RZY5bZ0z3jFV3Gv6QmFeLjIbx8rPDPgXCB3G1jNX++elFEpYFqz+xyKSmwKF'.
'Qgtp6VIwEsQSo18+sTK3Gj5S+tueSaFIUCjg3SIveJ/Ay2jur4iLZ1mIhjEQS0eRngmFxNIxYcyhXBnp'.
'y4tH0DS2xRm+JJs9LXgyjKbfnXLyG3DMZgabDAys32HGVaBKNUGMIWrfrlMXInTQjLZ314xsNseclRkJ'.
'eRmRROBk0aUbsIQDGipY6JhoBVsqY/sFXLYyXV8NQybNKtfi51bHC4yvGuXkymkXSm11p1R6Jf25tkfb'.
'grBDjxffPLc8DN61bt5YgAHbd9WOSs1o/CoxhWhPHWQToVMkcFjgKhGBM5eVkBwTmW3Xj18yu2fcFDDr'.
'WdDVkFeJOTnNMKjM5xKhQ9SjWmeAQkJbAZ9fq7//1PpHtqz7o1OvQTUY8bmN8l6lE7eZuKjJOXQAHWvq'.
'8r0lwJOwYqz8oYYvn2k0vHgREtWsRv2XuvjsBmyCB1bDJMgQHiKCWqUBfhn3Ru169a1+9eQxfL4qJibq'.
'TllJOb1Qn0+XrbWYDESSElcV43VaDThLSlSEt0tOivUY2g0Y4ALqnAoqWR4dt7GgiwVF9eoMRFkx3Cvr'.
'4l6HJ8JHPA6DSU9oJS+axSKWEgodJbkX1+qY3YslAaNZs2Y1MqJyGuUk54M8Uwl5SVKQZSpo44AtZEHZ'.
'Sb7H5kyZeBjexqGKGzMMeLtMg2X32R5XE4TSYwLjQTZl9i8pVsQUO7tUehWawuDQkhAHKEUGnQaSU/PB'.
'wmVAqXKUTYh8SWT7Q4V92Ia8/BLpT1+AmZiUBqkcVFk5xB830mVjTVo1UUSUrbDPl0wl28CokBWm5GZm'.
'G6jUdB7LGugzarWglRGtRlwOyaRxVaCoXBrucXmnrFl8bBhhoELg8QRES3Dp4tho6bBY/5LVCNxbD+/4'.
'P5I9DLyTcpBfqC4AKXm3VCMlf6XEglBBSZdK0N5p+LkNQ+ahdYOm//vWjQ0YPL7I7fmLBNHj6ESMcNLF'.
'HHlkQHIJDbBGMqGFrTiSCb58OQHSSJ9fWJD4PD6lGU7BMghNjMSlkROXJo8oDw8XZ+fvvH2xjK2AxWI4'.
'Z4cmiGQhMXTsh8cnWphHeJ3LBB66Z0KBTVjbZsW/CJc/zp4tHN1/Qq7sWQIdDMV/WFEV3SudSgtUGWeh'.
'h4dPWXItFhLXLJ826ZnTjj0Ta3qog0CWQr/SZNCDVkOEvEoGjMK8Rst27B4y95eRG6FI2b3jznt7e4t6'.
'9egxv/Dp7vqagjQwEHcXZ6mwoiZaCxyX0qZsTfldBzbOxoB+sa4zgkqngbSCTMAFgiaLiV4gqCWunoAr'.
'hA+wOqO0b9lqcbeSIPV5OrFw1GDSmIiQ5dABZA4ROpXLlkJ80TPAUIQSvl4J2pSo5Uv65assHfzvZUQ6'.
'MOl5bgboDRQ9PZ6XrwBXdz40aVO5pP21fxVU+fnmjMfPiLYh7g+ThSF1QjwhYKHzSj6eohIersy0pK9+'.
'PGUy6rV58ckWVegTtH0BA4d0wI2YlhwvT9AV5tnKz/1pgR5dCo8nosv10kKnyMKhp+//RmCsXLmykkcj'.
'96OBYQvq5ypzIEeVAyqDkiDEommCU+q9a0zJfXkgbOHa21PRdUMLxRaDKS6+hculzAIexyLgI95EUBKc'.
'0crhMIuChByeQPIX8bYolBrN45hE2nWjcJoflzSwiiralvHy0BbmpSN+TMpiYWBtcTYx+ZnExGdyiYtK'.
'BA4a7UycHrcUWwv8i3DRyFSqzKhUMKuJe4ThFZx3Jm2liGytWLOEyZVlRFxsPotuzi8jjlRv2DBkypRx'.
'6xjJV9vqpClESBFFpFYQy0XPYvEeTNm2bUH3PFal9RtmTzojlUoV5cuX549ftKyrQPpsWeLxEe6owOjl'.
'DMwituG7lAK3phNjN67evDgmfHastZ/Q/S7WSlBp1ZBekFnUYchX5FlYkJ7L4Ra5OsXwilan0+cm5UN2'.
'Qh4ZK1w6eM7msGkcmBwK8uI1deBtTdT/9yo0X1WWMT7y9V3gsSeHP01m4DhE7Y7MyiUmq0FnBoVU3FMo'.
'dN6n0cil1k78K4vujIwa1a+yJeLqxKmmXR8cHEzCvEwivaUxCbz+PXs1ePLkCXagTSh8ibXDcCvhW1ad'.
'lMLDxYPoy+JzUWggs7OrVIQ7ofeewQdiK1yuAIcM3bnoXmFVXxTEzA/4218BvJPXTk7eGrlhUdilJzwT'.
'ZaIHLK7XEfMlpAPZUMG9FjSwdDi5ecScvWaz2abB0Lp5xyp7vx/PH/4tslGfQWmRCSk1aIZmFa35YRPm'.
'xCNPqS7XunO3UjfPB+fA161eZzG43EpqrQ6EvKLKgbjmp6g8NhOq+3jk3Qjag1Pw5vAnjxMr12wfx0rK'.
'qkzTnsOkhT6wSXcS911TaKheo3Y9j8iXz3LhK5RZ0596iS1yXSmTSkcPPorDtE7/Mmkh5+lXumD3rHmJ'.
'8FZj09Zd1JMnCaP7D+tPPnv+PGJM86aNq48XJp33U2eEg8Uoh9z7m8uZzJaNY9vARizMzGBIQX55AijJ'.
'mCCGJPA9qwDDpZJSzikdcuX85fNPj92Jhx0j0dXFsWGL9X1wupme/8d1Pqwir8JmpWF5dRRAxfXrqdPH'.
'H3Zt0EeXFZvLRz7hEoHDRiGFpcgZFpBHGvzGzl/UdPuSAKxm/ZcX+30pfI3QsSzxH353wZbzD2JfZXyP'.
'J5ikE1lWpkWldOP0qxLTArdvWDlr6AgDrpZ7O2C/BgwbFszdMKl/n755R46VoBmFaEoGm0UzTG5ENFT/'.
'vv7gKlWqXIuNjUUf9VMxpPdBqLh1t79FKmXSwobDfrPIkC3gg0/7Zn/cmTPjGXzABGazeUSZmUjbOfQi'.
'MAZVVFD8b+hBxpg5AdXZ1TRHp/wxvrZMJwUzw1QU3KMFAxu4DC50qTwm99muhwEb7s5E6waFA1o39ksT'.
'6Gft2r/LT+4tOxCVH1mnlmu7ZVM69V0aeuuivO3PvVdVr1B2Z3xaBheVse1AZo9KyfD5v/auBS6qKo2f'.
'O+8Xz5FHCCoFaCaGYYYYmNRmaUatq7DaStv62HRL08xVfIT5SEUjxdTEtBTz0f7CjdyosBATAhXd5KXy'.
'EEUeo8CMA/Oeu+e7cw9zndAm0/3V7v30MAO/ueece873fed7/L87MaPGPI+VDnxdO8msuLu21JKN26NO'.
'1VwcCgpMDKcz3jMQADHev369A2y2lkv7zp89Cye9Ie9AdnvcmtgM/wcCN5sutTFuCCLBSywsogZ9SHLK'.
'jLFL582oQ04Ig7tzEcT97rGBxrKGaFAwjKJh+IhirJDAIf3MJ3XndhiNRqKomeDLn15dEBw/KuYN08XC'.
'P7VVfulp6fgYXT/qwJPJvQOQcsjUyzUaYf7JwoIfzp4outDa1NTuskaYDWqJAoN+DSwfGdGN0IebLyLc'.
'O/NVrw5AJLP3jNKRIKu1RwOF3r9/f2t8VtIehY9iKm1hD1IhYrNOAqQ/d00U5hOR8ZfXFnbseGf1V+w8'.
'fjHM2V26ndML5Klz08rpk5Kmj2iSyUTYvQArB4LKNHNzQJ9ln3906pysoimz54/Cv3oi59fX/xRJFm/5'.
'IDz22Wc92PnRlZVlLfmtlxP9k/7QCooBcy0+Q9lVxP+rd+yPSHs9daVare6FHOlsd80MyftZHy4U5Rc+'.
'DK6iQCZlUuZCmQxJvDxQ3+mTT8xe8eYafUcHCDP4vjcotI42DbLTdodjCyUBVjsDGgOLiUmP3j7JVhzc'.
'vbxMnVeWXZE5+FrXVTyonWE+sUCClY0UhflGofGe8w5sS1mVUlzw9XF8DZzSYOWQDFv3Hm/Ysj32rLw8'.
'f9fp7VFlzaeQ1qQfjP/uB3uyfGZKbnx478X9+wSbxYwFJcAKTcAcJG06HTrbdDVlwVtrktg9lLq5h2jM'.
'mAkBTV3m9Scrz/tA1koJDa+vEq9vWHCQ9V4Z2vH+ikVw0hKsTefiBbP2Bo/tv8J/QDASYveKyaLAKYbn'.
'Ym7VosBzplmp6VvGI0eWU+LuXJJefLl3ny6PjPbvKqRC5tQXMVgdiHOoB4WY2waLtyxbsiQfObNi1tR1'.
'mSHD7rV9U7f3xVmNhds9DVfrHVtPA97GgiSqXtYrbbbq5ppztb4qaePYZ54xz507V5qamqpYtmyZLDY2'.
'VsvuRyPbWjj3akRuKk1Qig6XF8sYdt2lkLnCbrxCIkMRalVPlwA7GjI2pb0ZmjKkVCLH7qwMrHcsm4yr'.
'SjFK6FpJgzjw3+o9mTsOZ8c/ndgfXwPuq9ty8/rad4Zv3/712YxF/7CtP/DZMOSQb7f0yS+JuQADqlds'.
'+zQ97+D5ZL3WQFGsEiC1aJAehdhI5LBQNDjG/5P682VbMtMWkvohktERRsfFeb84Z8nkuurO144c+iGo'.
'87oRPZ4YZa6syO5fcOhQK7tBYJWpN2buyrB/8unz1vYOAYMZgE5A0HFTJ0+4elTTmLJr47rjyIl47fG+'.
'40aPDpz0ZOI+/Qd74+1dBnzqCRnrBpg8KHaosVwte2/5qlW5LOMA2BCUDvc0ULyblbVcbKqcBxgMig0Q'.
'OtKiNJIGPGDI+WfxK4f27CQp5y43mEwwd3X6g1f8aw7kVu0IA6g9nMik3EGELQSJUIqejfhra+n2wjdL'.
'C74hsRtuZopBkkZER3vgk+zxTg/NG4UNBx9q62xiTk1vhT8aJknMXjvjtbeRE3gp/v1LM6MCHnxk2/Hy'.
'6gGAIyFZFkjvArJ45pMxR3a+s+6VI0eOQFT1VuUDwpycnFGnrhmzs/KO+UPKVYpPabBwlNhyfDgitO7E'.
'J7vXlBQeOcOZOzHxYY+VGRmbEiI9QrNqck/5QtwFGkAYoC6I8pAj8XNDPl+7dumcmoqyK+jWLqR41bbd'.
'T3tXde3S5Jb4OFxIyuGqKGVIMSayJuvz7SvPfF9Uzs6Fm4qXpG5479Fg+ZWDjd++5wv7CvoPzjuREJC5'.
'4FJDGYEKNw/8uxgfOHZkNhkZsKjZ2IUPIasjZYl/2Jk3FGOlQ2mEOqg/Ut7zoL5DEfZZbYt+TebShdXI'.
'GSh38GhiYvCo5MQviqu/HAjWoai7CZEUjz3IP/rUG39MmYEc30NOkgWEJL7BwX7L07am6/51KdmiMzL7'.
'6TghoWbOzhyOJKTQa3g/ozLa/3Br+6V9+3dkFv1QUqLjrKtoyeasIX37hI8zVV2fdOWrcwFGjZ6xFMNf'.
'fqRsxl+efJ4dvwu5YTH90kAvaDfV6KTJg+KeSE4rzrv4WLvmerfyQcipeBjGgca+Z/7GrIHjlZkMviYw'.
'xBcNHRlw+J/7MtLPHD9ex2EEKxlvQGR0v2nTXl6gKCodb7xQK0Ts4kHBH3TlGT+ii4p5JKeho2X/huWL'.
'S7o0GmBMasE7WyPD7wsfKzj9wyTt4bxga4e2G1kpUcpR+MRx2gLN5a0bMjIKsFvYzCobgjOijxUfe0Zg'.
'123UaVtDWppqqU59O1s3JnBU/bIFZMw/ZmOxkIiw1eQZiMQqP4tN5PFZUcGp17PWryTFnVwvTLzq0+yl'.
'e6ve/nuzrl4EfTL1VQJHoFgIcTPcfBUB6B6vUEf9FRtYpBgFYUd6UzvqMGqworB0d0yzi0uxYC5fZRC6'.
'3zzyg/WvzAdgG4DKdCyjMyjt5BmvDu/90PC0kqqaSF1nJ4spsbPKR4UmjIhqiAzw+uhEcXHuokWLoDjX'.
'lpCQoJo/f34sUnmNzz1ZOS7vZLkCLD5wuUFA7vFTo2Hhfc4WfrJna9FX/wKEOsScCKbK1R1m0Ly4ec9e'.
'vuapIeqIZeajVX2sOizEFmt3E3p7IOVjA2sNIcpdp06UfLFr/Vs10M/YSX/2fWzsM8PUyGOi4ZuKp7Sl'.
'5+UALiT8Je8XgGRPDarM3Jm+oby0GOKABGXtasnCPMCa8l349topvTpLUw2Xv1eJhBBKcLig8FHoG3ja'.
'zvAym1Fmy1tIJTjzwvA7/qyNfN6RkRbA3oplyPvB8bXHK01THx6dMKHd1PpnbedVWaOmDhnMembfRUJn'.
'nZ2Q7DvwLb7WzycEW5Fedg+Z77Ez+UUp29akNbL3AZaLMnnGrKEJMYnpHV/VD2aUD5E/VqEzHML+ILJI'.
'FBThZe41yj4+tNcTIaW79767uuTIl7XIiUX7KeDoHVE63M0B88znqQmTo0aOSZqu1VAj6yuaJR1X9d03'.
'0a2AaIc7Av56YIgP6jfATytT6fNydm/NOXms4BLLjFrkxBGYOQJKxoOcoXdUzKP3TnxpelKgrmucrfq8'.
'v6HqHGU3Gp3CRhGBo5yaDVLDffsiKrRPp8lHXnSq8vThj3Z9VGmz2XScBeTifpgi6oJjBU/IpfZUi9ms'.
'wAIpqtfohCKZXICHwo3GBhdtFgopGWI3EbsoYDh5QaAHqwW7jVKUFx0rTstat/rfyCXNGh0drXhhderG'.
'Rl3NaMycTEQawn40vlRil7ZTtMCGlQzNNEqA/yqww3vEokhvIBrChXbKRll88TuxXWCTI4Fd5Tgl5NoL'.
'39ak7klPz2OFn9QdAYlY5QOf9Rk3KSUmJi4+mVZ4xp2pb1TUNGmY6mM7feOQZI2BPLHyHhwajML8vK+0'.
'1tflHzr48ddVp082IifM37Wc42YkYpUPzMV30t9eHzE8cliSxzXbCGPFZZmp8SojwKh7nzn7SzsERKCS'.
'I3lEb5rq43vpEtXx9Z5tGfm11RUtLG9xBeVmGCRSU+Y1eea8hLjYAW9dL94cAaUvImzhCCRKxgIDKw6U'.
'h8XUyZRKCEgBLhxIjIKiWesfv9J2dv0oRknRjF4QMplPRVTyxctGxecKX1UMmEX4IBXQNtqIrFQnzby3'.
'41MM0qMON4gSCzwgmcvygl0kENcf/jB76Rf/2FfP4V0iLyokkainzV6Q8FBEzFT6ommItqIJ2UzWbski'.
'sklAjPAeSiZUoWpaGuapaaabc3e++3bu5Yu1JNWuc5FRt+K2d7LKXICcdU2AaQGloGRfZeyNCzljEhQu'.
'TJYbZHMNtN0sJksYQsL2L+eMJUVOdCQXmMdtJMDHHd+InAhT2y3Gk7NjED+Wusn8CJHxuGO5Ch1hDhl7'.
'H6R/bh/chlDPa0NxXrkAL5LVs3DW+kf4Es5cxJx7VbCv3H3krqPrnGzsvZnYMQhy163g6U3mIuWsjbtz'.
'IRgSCzs+d5+JkPQ4l+DgYPns1RvG+tJNK9rK9ve3XNdgN0SEFEEPWFpU8R+unPfqIc59WVzmK2SbJPbR'.
'2IAXXnhh6H29vUZYOyqjLlcVihBUqFOOraGZ5rBaFYOea5/24tznjEZjC9s36c+Vl1z5CyGnPBld7q+n'.
'dYT1A2VO5JNg0LjyyZUPsnZEPsmeclH0btPdfBIQd/FJ4yIaCZPYOI0wye2OR8bkvrqiKLko4dsdl9vn'.
'z1lDV6Vxq77v9N5w192debjOh+whWVui0Fzvjawnd11/6bN0bjYXMp+eBNOVv8j7n7pn8fqP9kyhLny6'.
'ubOhVAqmCuPGYhdH7h9mqxHHrd/05sIcdKPrfQPosod5EgtSOWHazNgxD/tlGGoLPChiEUHcDltN+ntG'.
'582ZOmMxcsQRe8Lu/Ahr5fL7rQ4j1+u48ilCN5fPOymjDN3NZyRzT5n/BpHxfk4ZxO3S3Xwm7a/tebd3'.
'rHzjtzCX1bsODJY25WW1N5xgkOUMoA6UAzShxG40dIE7Cu4ZqQZ3dx5MgP9qc4sQScLacIceDvcLAJNy'.
'ZOk75vScKVPT0c9zV26XT7g1ff914h9XyhNPTqKuNF40CBQ+LeBKOZqYbUJkuVYnHhNmWrhz546n0c//'.
'2hdJ2uas372UFPetoTq3LwSCoV9FeILpe33kmpenTJ2DHFkosKDcCsj+Vum3+6BVnni6OySbOH3WwCfj'.
'7t/XUfphOG01sE/+E7KPdXA0mX+EXXpfQrle4LevtqLyaHbmunMtLS2MOxQUFCSeOHN26L0DBw3xktAJ'.
'Ql3t04a677xsXWAcUUii7mezBI08vWXTto2nvztajxxWU/dzdND/sMJBiFc6PPHkSswzeHBT+ffuHfTS'.
'awuevy9ANFmovRBuai5HtNXMqaWiboRMsCC1bqECwKncG0kD7rdblMHl1Q36nPdWLf1Ge+0aCcaSp/Vx'.
'K+d/Ta71XVtgnnji6cdEHqRGMqMk08NkSIVCoSRqRHwvpVIhIilreJSJXqczlHybDyllEjOxImcWiGQv'.
'TciZ9frNfAf5nSJe6fDE008TN9tDMj3c7GhPWbyeMj8k+/N/pWR44oknnnjiiSeeeOKJJ5544oknnnji'.
'iSeeeOKJJ5544unXS/8BYWtPYz+7E7MAAAAASUVORK5CYII='.
'');
}
?>