diff --git a/ABUSE b/ABUSE new file mode 100644 index 0000000..bd11640 --- /dev/null +++ b/ABUSE @@ -0,0 +1,20 @@ +The following attempts to document and explain the anti-abuse system in +TightURL. Ideally abuse is kept out of the database to begin with, but in +many cases it must be dealt with afterward. + + 1. Rejection of bad bots thanks to Bad Behavior. + + 2. Rejection of invalid URLs. Since garbage in the database is abusive + to the operator of the TightURL service, we attempt to keep junk out. + + 3. Rejection of blacklisted file extensions. Much abuse involves getting + someone to run a Windows executable. A set of abused extensions is + provided, and can be updated by the site administrator. + + 4. Rejection of locally blacklisted sites. + + 5. Rejection of URI blacklisted sites. + + 6. Rejection of links to known redirector sites. + + 7. Re-checking accepted URLs for those who run the "killbot". diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..819a2e7 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,112 @@ +v 0.1.3.3a +October 17, 2008 +---------------- + Bugfix: TightURL was failing to screen out forbidden extensions such as .exe + All users should upgrade to restore this important functionality. + +Feature: It is possible, if not necessarily advisable, to activate Policy Ban + URLs. ex: http://example.com/?ban=abc + This will mark the TightURL http://example.com/abc as banned for + violating your site's policy/Terms of Service. This feature is + activated by setting $pbi in tighturl.config.inc.php and is intended + only to allow administrators a fast and easy way to disable URLs in + the database. This feature will be secured in a following release + against misuse. + +v 0.1.3.3 +September 13, 2008 +------------------ + Change: A modification has been made to the URL resolver routine to increase + PHP version compatibility. + Change: Having discovered a new class of Internet abuse, a Pay-To-Click (PTC) + blacklist has been added. + Change: Missing blacklist handling is more robust in case any are compltely + removed. + Change: Upgraded to Bad Behavior 2.0.23, which still generates one warning + corrected in the version distributed with TightURL. + +v 0.1.3.2 +July 26, 2008 +--------------- + Bugfix: Some incorrect information in UPGRADING that referred to some work + in progress on the upcoming major release has been removed and + replaced with the correct information. + Change: A modification has been made to the URI blacklist lookups to try and + work around DNS setups that deliberately falsify returned information + when the true response is NXDOMAIN. + +v 0.1.3.1 +July 24, 2008 +--------------- + Bugfix: Some late night last minute packaging errors resulted in an error + in the default local blacklist file. This has been corrected. + Bugfix: Another slip-up due to the late hour, the installer script creates + a database which begins issuing redirection URLs in the ten-thousands + instead of zero. This has been corrected. + +v 0.1.3 +July 24, 2008 +--------------- +Feature: Optional full Bad Behavior integration into TightURL front-end! + http://www.bad-behavior.ioerror.us/ +Feature: Option to require submitted URLs to exist. +Feature: Resolves all submitted HTTP URLs to the end up to 10 redirects. + Bugfix: Strip tags embedded within tags in tighturl.tmpl . +Feature: New templates for blacklisted URLs, URLs that violate site policy, + URLs disabled due to complaints, Greylisted URLs, and previews. +Feature: tighturl-install.php preliminary web-based installer and + upgrade script. Presently allows for either the initial creation of + the tables TightURL uses, or upgrades an existing 0.1.2 or earlier + database to the current schema. +Feature: tighturl-killbot shell script partly developed to cut off service to + abused URLs. An attempt will be made to piggyback maintenance tasks + on top of service requests. +Feature: TightURL now updates the fields status, lasthit, hits, adddate, addip. + These fields were added as necessary for the anti-abuse + system. There is no intent to add an ability to view this data + for its own sake, however it may later be displayed as part the + anti-abuse information in previews. +Feature: Updatable urlpatterns, two-level domains pattern, redirector patterns. + rejecting URIs of other known redirectors. +Feature: Blacklist domains to refuse to create TightURLs for. +Feature: TightURL does not accept links to itself; TightURL allows an domain + alias to be configured for this purpose. Both the main site and the + alias will be recognized with or without a preceeding "www." . +Feature: Attempt to stop use of TightURL as an obfuscation service by + linking to other redirectors. +Feature: TightURL no longer accepts URLs it can't shorten. + Change: TightURL now returns 301 redirects instead of 302 +Feature: Blacklist Windows executables and other file extensions. + Change: TightURL no longer contains any OS-specific code, thus Windows + support should be transparent now. Reports about TightURL working or + not under Windows, BSD, or OS X would be appreciated. + Change: Replaced chkdnsrr with gethostbyname, which should work on + non-Unix operating systems without a replacement function. + Bugfix: URIBL check was slow and worse did not work. Now works and not slow. + BugFix: TightURL now accepts the nasty URLs Yahoo uses. + Bugfix: Was truncating URLs longer than 256 characters. + New limit on URLs is 2083 characters + Bugfix: Inconsistency between config.inc.php and tighturl.sql + (closes SF bug 1161870) + Bugfix: Fix PHP warnings for defined constants. (closes SF bug 1161874) + +v 0.1.2 +December 3, 2004 +---------------- + Bugfix: @ was not considered valid in URLs and should be. + Bugfix: Local text about running an experimental service removed from main template. + Bugfix: Main template link to TightURL changed from /sourcecode to /project. +Feature: Reserved IDs, and Regexes moved into tighturl.config.inc.php +Feature: sample config file and sample templates moved to ./local directory +Feature: Supports validation of IPv6 URLs. (not supported for URIBL lookups) + +v 0.1.1 +December 2, 2004 +---------------- + Bugfix: Bad tighturl.sql file fixed + Bugfix: Empty database issue fixed. + +v 0.1 +December 2, 2004 +---------------- +Initial Alpha release. diff --git a/DOCUMENTATION b/DOCUMENTATION new file mode 100644 index 0000000..eba6f6b --- /dev/null +++ b/DOCUMENTATION @@ -0,0 +1,10 @@ +Submitted URLs that are accepted are assigned a sequential Base-36 ID code. + + http://example.com/aa (aa is the Base-36 ID) + +This allows for assigning the shortest ID possible. + +Duplicate accepted URLs will have the existing ID returned, in preference +to always assigning the shortest URL possible. + + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..17fdb98 --- /dev/null +++ b/INSTALL @@ -0,0 +1,416 @@ + TightURL + Installation + version 0.1.3.2 + + Installing TightURL + ------------------- + + 1. Untar the file: tar zxvf tighturl-0.1.3.2.tar.gz or + unzip the file: unzip tighturl-0.1.3.2.zip + + 2. Move the files in the directory to where you want to run + your installation of TightURL. + + 3. Rename tighturl.php to index.php if desired. (highly desirable!) + + 4. A. New installations should move the contents of the ./local directory + into the main TightURL directory. + B. Upgrades should review the contents of ./local and adjust existing + config file and templates as seen fit. Things probably won't work + correctly unless you modify the tighturl.config.inc in ./local with + your desired settings and use that. + + 5. New installations should create a MySQL database and user if + necessary. If you don't know how, (we were all beginners once) it + goes something like this: + mysql -u root -p + Enter password: ********** + mysql> create database tighturl; + mysql> quit + + If you also need to add a MySQL user (you should never use "root"), + then before you quit MySQL, you should enter a command like this: + + mysql> grant all on dbname.* to dbuser@localhost identified by 'pass'; + + substituting the name of your database for "dbname", the name of the + MySQL user you want to create for "dbuser", and the desired password + in place of "pass". + + 6. Edit the config settings in tighturl.config.inc.php as needed. + New installs will need to change at least the MySQL database settings. + See 'Configuration Settings' below. + + 7. Run the script: tighturl-install.php + When this is done, you will need to remove or rename this script + before your TightURL installation will work. + + 8. If you can set a custom 404 page for your Web site/directory, you + can set it to tighturl/index.php and get shorter URLs. To take + advantage of this, set $FOFMethod=TRUE in tighturl.config.inc.php. + + 9. Change the $svcname in tighturl.config.inc.php if desired. + + 10. Edit the master site HTML template tighturl.tmpl if desired. + See 'TightURL Templates' below. + + 11. Edit the HTML sub-templates if desired. These are: + A. tighturl.main.tmpl : HTML shown for your TightURL homepage + B. tighturl.save.tmpl : HTML shown when a URL is saved + C. tighturl.help.tmpl : HTML shown for Help info (may not exist yet) + D. tighturl.faq.tmpl : HTML shown for FAQ (may not exist yet) + D. tighturl.blacklist.tmpl : HTML shown for blacklisted URL submissions + D. tighturl.policy.tmpl : HTML shown for URL submissions that violate + your local policy. + + See 'TightURL Templates' below for more information. + + Configuration Settings + ---------------------- + + These are the configuration (variables) settings that can and should + be changed in tighturl.config.inc.php : + + MySQL: + $dbhost : DNS name of your MySQL server + $dbuser : MySQL user name + $dbpass : password for MySQL user name + $dbname : database containing TightURL's table + $dbtable : name of TightURL's table + + Operating modes: + $FOFMethod : Use 404-Method or mod_rewrite to compress TightURLs + 0 (FALSE) = full URL path (not compressed) TightURLs + 1 = 404-Method compressed TightURLs + 2 = mod_rewrite compressed TightURLs + + URIBL variables: + $uribl : Array of URIBLs to check + $uriblurl : Array of Web sites corresponding to URIBLs checked + + Text strings and style variables: + $svcname : One-word name for your TightURL service + $verbtext : One-word name for the act of tightening URLs + $pasttext : Past-tense name for a tightended URL + $tagline : "tag line" for your TightURL service + $headcolor : Color for the TightURL <H1> HTML header + $tablecolor : Color for the table the URL input field appears in + $copystart : 4-digit year of start of your copyright on your content. + defaults to current year if not given. + + NEVER CHANGE SETTINGS IN tighturl.php OR YOU WILL LOSE YOUR CHANGES + any time you upgrade to a newer version of TightURL. Always put your + configuration settings in tighturl.config.inc.php where they will + survive upgrades. Values in tighturl.config.inc.php override the + defaults (garbage) in tinyurl.php . + + Reserved URLs + ------------- + + Because we use Base-36 numbers as TightURL IDs, as time goes on, + certain desirable URLs will be issued that we might want to keep + for ourselves. + + For instance, the letters 'faq' are well-understood around the Internet + to mean "Frequently Answered Questions". We might want to use the URL + http://example.com/faq to point to our FAQ page, rather than issue it + as a regular TightURL to a random visitor. Reserved URLs allow you to + specify Base-36 IDs that should never be issued as TightURLs. + + There are two kinds of Reserved URLs. External (implicit) and Internal + (explicit) Reserved URLs. + + External Reserved URLs: + + Any URL which exists in your Web space will be served up by + your Web server, and not TightURL. For this reason, any URL + reasonably within your TightURL ID space that you want to use + with your TightURL site itself should be reserved so that you + don't issue a TightURL to anyone that won't actually work. + + For example, if you have a directory named: /www/example.com/faq + your Web server will attempt to serve index.html from within + that directory, and TightURL will never even see the request. + This kind of reservation is implicit, and TightURL must be + made aware of it. + + Internal Reserved URLs: + + Any URL which does not exist in your Web space but is Reserved + within TightURL will cause TightURL to look for a correspondingly + named TightURL Template, and serve that. If the template does + not exist, a 404 error is generated. + + For example, if you have reserved the ID 'faq', and TightURL + sees a request for this reserved ID, it will attempt to open a + template named tighturl.faq.tmpl and return that to the browser. + + It is VERY IMPORTANT that you decide what your Reserved URLs will be + before your TightURL service is put into operation, or else you run + the risk of wanting to reserve a URL that's already been issued as + a TightURL to someone. For example, by the time you've issued your + 19826th TightURL, you will have lost the opportunity to use 'faq' as + a Reserved URL, because decimal (Base-10) number '19826' is number + 'faq' in sexigesimal (Base-36). + + Reserving URLs: + + To reserve a URL, add it to the configuration array $ReservedURL . + The following URLs are reserved by default: + + faq, help, code, source, docs, cvs, arch, url, + about, admin, setup, svn + + There is little point in reserving URLs larger than 5 charcters long. + The first 6 character ID is '100000' sexigesimal, which would be far + past your 60 millionth TightURL issued. Since each additional Reserved + URL adds a little more processing time, you don't want to add URLs + that will never be issued anyway. + + TightURL presently uses the ID "api" with the template that describes + how to use the TightURL API. It would be the 13878th TightURL in the + system, which can easily be achieved on a moderately-used corporate- + wide TightURL installation. + + TightURL Templates + ------------------ + TightURL supports a simple variable-substitution templating system with + one level of template nesting. + + The outer template is *always* the main site template, tighturl.tmpl . + This template controls your site design and is *always* used to generate + output pages to the browser. + + The inner template is *always* one of the following sub-templates + (or no sub-template): + tighturl.main.tmpl : HTML shown for your TightURL homepage + tighturl.save.tmpl : HTML shown when a URL is saved + tighturl.help.tmpl : HTML shown for Help info (may not exist yet) + (none) : No inner template. + + Please note that many screens are generated using only the main site + (outer) template. All error screens are generated this way. + + The variables supported by TightURL Templates are: + + $HTML : In the innermost or only template, replaced by a HTML + message from TightURL. In an outer template, it is + replaced by contents of the inner (sub-)template. + You should never remove this variable from a template. + $PARM : Parameter when 404-Method not used. + $URL : Replaced by browser submitted URL. + $URLLEN : Replaced by lenghth of the submitted URL. + $TIGHTURL : Replaced by TightURL generated by the submission. + $TIGHTURLLEN : Replaced by length of the generated TightURL. + $DIFF : Replaced by difference in length between URLs. + $INPUT : Replaced by submitted URL for use when there has + been an error detected in the input. + $SVCNAME : Replaced by name chosen for your TightURL service. + $HEADCOLOR : Replaced by color value chosen for <H1> HTML tags. + $TABLECOLOR : Replaced by color value chosen for the table. + $TAGLINE : Replaced by "tag line" for your TightURL service. + $CPASTTEXT : Replaced by capitalized past tense description of + tightened URLs. + $PASTTEXT : Same as $CPASTTEXT, but first letter not capitalized. + $CVERBTEXT : Replaced by capitalized verb description of your + TightURL service. + $VERBTEXT : Same as $CVERBTEXT, but first letter not capitalized. + $COPYRIGHT : Replaced by generated copyright string created from + $copystart config value and current year. + $URIBLS : Replaced by HTML showing URIBLs being checked. + $HOST : Replaced by hostname of Web site. + $SELF : Replaced by the URL path to the TightURL script. + $BBSTATS : Replaced by Bad Behavior stats when BB2 is enabled. + $TIGHTURLVER : Replaced by the TightURL running version number. + + TightURL Operating Modes + ------------------------ + + TightURL supports three distinct "operating modes". These are known as + "basic", "mod_rewrite", and "404 Method". You want to avoid changing your + operating mode later, as changes to or from basic mode will cause all your + URLs to change. Changes between mod_rewrite and the 404 Method do not + result in changes to your URLs, but your operating mode is something you + should choose carefully and then stick with. + + Using basic mode + ---------------- + + This operating mode produces URLs that follow this pattern: + http://example.com/?i=id or http://example.com/yoursite/?i=id + + No changes to httpd.conf or .htaccess are required for basic mode, but + this produces slightly longer and more inconvenient to type URLs. If you + can use mod_rewrite or the 404 Method, it is highly recommended that you + do so. + + + Using mod_rewrite with Apache + ----------------------------- + + PLEASE NOTE: There was a longstanding bug in Apache mod_rewrite that + probably makes TightURL or anything like it unable to work except with + what are still very recent versions of Apache. + https://issues.apache.org/bugzilla/show_bug.cgi?id=34602 + + Users are encouraged to use "404 Mode", and especially in the event + their Apache version has the bug shown above. + + + This operating mode produces URLs that follow this pattern: + http://example.com/id or http://example.com/yoursite/id + + TightURL can be used with mod_rewrite by adding the following lines to + httpd.conf or if you can't modify httpd.conf, you can use .htaccess : + + <IfModule mod_rewrite.c> + RewriteEngine On + RewriteRule ^view/([a-z0-9-]+)/?$ index.php?v=$1 [NC,L] + RewriteRule ^([a-z0-9-]+)/?$ index.php?i=$1 [NC,L] + </IfModule> + + If you change the parameters (form variables) from "i" for the TightURL ID + to look up, and "v" for the TightURL ID to preview, then you'll need to + adjust these lines accordingly. + + Be aware that http.conf is read once when the Web server is started or + reloaded, but .htaccess must be read and parsed for *every request*, so + you always want to use http.conf over .htaccess whenever possible. + + + Using "The 404-Method" with Apache + ---------------------------------- + + This operating mode produces URLs that follow this pattern: + http://example.com/id or http://example.com/yoursite/id + + The "404-Method" refers to instructing your Web server to direct all + requests that generate a "Not found" error (which is an HTTP error code + 404) to the TightURL script. By doing this, TightURL will run for URLs in + your TightURL site that don't exist, such as would be the case for + http://example.com/tighturl.php/xxyy + + The xxyy is a TightURL ID number, not an actual Web page in your site. By + sending non-existent page requests to the TightURL script, you can look up + those IDs and issue an HTTP Redirect without the need for mod_rewrite. + + To use the 404-Method, set $FOFMethod=1 in tighturl.config.inc.php. + + Using .htaccess + --------------- + + Then put this in your .htaccess file: + + ErrorDocument 404 /index.php + + This change should take effect immediately after saving .htaccess . + If it doesn't work, make sure you have AllowOverride FileInfo on in the + directory you're running TightURL from. + + Using httpd.conf + ---------------- + + Alternatively to using .htaccess (and for improved performance), put the + following line in your httpd.conf in the appropriate Directory section + (and VirtualHost if applicable): + + ErrorDocument 404 /index.php + + You will need to reload your Apache configuration for this change to take + effect. + + See the Apache documentation for more information. + http://httpd.apache.org/docs/mod/core.html#errordocument + http://httpd.apache.org/docs/mod/core.html#allowoverride + + + AntiAbuse: Generally if your web site is connected to the Internet and + and the TightURL library can make outbound network connections, you should + run with the antiabuse system turned on. When the antiabuse system is + turned off, virtually everything will be accepted into your database that + can be shortened. + + NetChecks: Generally if your web site is connected to the Internet and + and the TightURL library can make outbound network connections, you should + run with the netchecks system turned on. If your site has no connectivity + or ability to make outbound network connections, you should turn the + netchecks off. When netchecks are off, TightURL will not be able to resolve + URLs through redirections to their final destination, check for new versions + of TightURL, or check submitted URLs against URI blacklists. + + + Adding more URIBLs + ------------------ + + In the event other URIBLs beside SURBL become available, and you wish + to check those also, edit $uribl and $uriblurl in + tighturl.config.inc.php . + + To add a new URIBL named uribl.example.com whose Web site is located + at http://www.example.com + + Change this: + + $uribl = array("multi.surbl.org"); + $uriblurl = array("www.surbl.org"); + + To this: + + $uribl = array("multi.surbl.org","uribl.example.com"); + $uriblurl = array("www.surbl.org","www.example.com"); + + You can add as many URIBLs as you want this way. + + Squeezing Your Base URL Down to the Smallest Possible Size + ---------------------------------------------------------- + The "base URL" is the fixed portion of your TightURL URLs. This + would be everything up to the TightURL ID at the end of the URL. + The length of the TightURL ID will start out at 1 character and may + get up to 6 characters after tens of millions of TightURLs have been + created in your system. (Most TightURL installations are not + expected to get into the tens of thousands, let alone tens of + millions) Because the ID is at most 6 characters, the most + important thing you can do to get the shortest URL possible is to + "squeeze" as many characters out of the base portion of the URL + as possible. + + The following URL is "unfortunately long", but should still allow + you to create tens of millions of TightURLs that can be used without + wrapping in most e-mail programs. It is 72 characters long: + + http://www.a-really-quite-long-name.com/~example01/tighturl.php?i=abcdef + + This URL is not only unfortunately long, it is unnecessarily long + as well. The following optimizations can be attempted: + + 1. Drop the "www." + 2. Rename tighturl.php to something shorter. + 3. Rename tighturl.php to index.php and put it in a 1-character subdir. + + If optimizations 1 and 3 are successfully applied, we get a 58 + character URL (14 characters shorter): + + http://a-really-quite-long-name.com/~example01/t/?i=abcdef + + A more optimistic domain name (39 characters): + + http://example.com/~example/t/?i=abcdef + + Ideal scenario with own domain (29 characters): + + http://tighturl.com/?i=abcdef + + If you are able to use Mod_Rewrite or the 404-Method as shown + above, you can also drop the "?i=" portion of the URL, saving + another 3 characters. + + Mod_Rewrite or 404-Method scenario (26 characters): + + http://tighturl.com/abcdef + + This also works with the "unfortunately long" URL, bringing + it down to 56 characters: + + http://a-really-quite-long-name.com/~example01/t/abcdef + +******************************************************************************** diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14db8fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..56e201a --- /dev/null +++ b/NEWS @@ -0,0 +1,15 @@ +NEWS +==== + +This is a quick release of the version in use at http://tighturl.com +due to some user requests and the need to spend more time working on +the next major release. It contains an all new installer, bugfixes, +and some important new functionality backported from the development +version. + +There's likely to be some unused templates. + +**** First time TightURL users who installed 0.1.3 should delete and +then recreate their database, then install this version and re-run +the installer script to avoid generating needlessly large URLs. +Everyone else should follow the normal instructions. diff --git a/README b/README new file mode 100644 index 0000000..6f43b19 --- /dev/null +++ b/README @@ -0,0 +1,155 @@ + + TightURL + Version 0.1.3.2 + + What is it? + ----------- + + TightURL is a LAMP-based (Linux Apache MySQL PHP) blind redirection + service similar to TinyURL, designed for personal or company use by + those who prefer not to use external services, or cannot use them for + legal or logistical reasons. It may run correctly on a WAMP server + (Windows Apache MySQL PHP), but it is unknown if it does or not. + + The major consideration in the design was the ability to customize + the application without altering program code. All browser output + except fatal error messages is produced by HTML-based templates. + + A fair amount of customization is possible simply by setting a few + configuration variables in the configuration file or editing the + included HTML template files. Under no circumstances does the program + script file ever have to be edited, unless you're a developer. + + The primary reason for a blind redirection service is to avoid + e-mailing URLs to people that are broken when received, because the + recipient's e-mail program is unable to pick up the rest of the URL + after a line-wrap. It is called a blind redirection service because + you can't tell where you'll end up just by looking at the link. + + For the purposes of TightURL, we want to generate the shortest + redirection URL possible, thus we use a blind redirection URL, and + encourage the use of mod_rewrite or the "404-page Method" to achieve + the shortest possible URL. + + Can I use it without getting a special domain for it? + ----------------------------------------------------- + Yes. The requirement for TightURL to be considered useful is the + ability is to create a URL that won't wrap in a plain text e-mail + message. That means the upper limit is about 72 characters of URL. + A worst case scenario looks like this: + + http://www.a-really-quite-long-name.com/~example01/tighturl.php?i=abcdef + + Even this example can actually be made 10 characters shorter in most + cases without needing to ask the webmaster for special settings: + + http://www.a-really-quite-long-name.com/~example01/t/?i=abcdef + + See INSTALL for more details on how to "squeeze" your base URL down + to the smallest possible size. + + Why was it written? + ------------------- + + This software was written for the oldest reason in the Free Software + book; to scratch an itch. The author had been looking for some + Free Software to run his own URL shortening service. Finding no Free + Software that fit the bill, and wanting a first PHP project to work + on, TightURL was born. Issuing an HTTP Redirect is not exactly + rocket science, what the URL shortening service is really providing + is convenience and processing resources. + + The software itself can be boiled down to a very few lines if it is + hard-wired to its configuration values and output screens. But + since the author had been looking for some cookie-cutter software and + couldn't find any, he decided to write this so that hopefully no one + else will have to. It can be cosmetically tweaked from the config + file very easily, and in more depth by editing the HTML templates. + + + Additional Disclaimer + --------------------- + + Original author's code sucks. He is a rank amateur, evertything a + professional developer despises. (I wouldn't run code written by me + if I were you.) + + Contributing (code) to TightURL + ------------------------------- + + Patches welcome. Critical advice welcomed if gently given. + Access to CVS or Arch possible, please contact maintainer. + ron-a-t-vnetworx.net + + The Latest Version + ------------------ + + Details about the latest stable and development versions are + available at the TightURL Web site at http://tighturl.com/ . + + Documentation + ------------- + + The documentation available as of the date of this release is the + contents of this README file. Additional documentation is available + at runtime in the browser for using TightURL. + + Requirements + ------------ + + PHP - Should work with any version >= 4.3.0 + MySQL - Should work with versions greater than >= 3.x + + Features + -------- + + * Can create very short URLs using mod_rewrite or the "404 Method" + * ID size kept very short by using Base-36 IDs + * The same URL entered more than once always returns the same ID + * All output is generated by templates. + * Site design is controlled by a single template. + + Known Limitations + ----------------- + + TightURL probably works under Windows with Apache and MySQL, but we + have no way of knowing for sure until someone reports they've tested it + and it works. One possible problem would be if TightURL can't find + nslookup on the PATH. The Windows version uses nslookup as a kluge for + missing functionality in PHP for Windows. + + Coming Features + --------------- + + Please see the file called TODO. + + Installation + ------------ + + Please see the file called INSTALL for detailed installation + instructions. + + Anti-spam Features + ------------------ + + A problem with blind redirection services is they can be abused by + spammers to conceal the domains their spam is trying to get people to visit. + The TightURL Library attempts to screen out other known redirection + services, Windows executables, and matches against your local blacklist. + + To keep spammers and phishers from abusing TightURL, it checks the URI + blacklists at multi.surbl.org and black.uribl.com . TightURL will refuse to + add URLs that appear on these URI BLs. You can add additional URI BLs. + + Licensing + --------- + + Please see the file called LICENSE. + + Acknowledgments + ---------------- + + The TightURL developer(s) acknowledge the following copyrighted works + that make up portions of the TightURL software: + + Bad Behavior diff --git a/TODO b/TODO new file mode 100644 index 0000000..d2a221f --- /dev/null +++ b/TODO @@ -0,0 +1,8 @@ +Bugs: +===== +All known bugs in the released versions have been fixed. + +API: +==== +The API has not been implemented, and may not be as it seems to be little +more than an abuse vector. diff --git a/UPGRADING b/UPGRADING new file mode 100644 index 0000000..2fa89a5 --- /dev/null +++ b/UPGRADING @@ -0,0 +1,25 @@ +Upgrading TightURL +================== + +Please note that those using nightly builds of TightURL may end up with cruft +in their databases. Upgrades are designed to work with released versions. + +There have been many additions to the configuration file. You may wish to +review the sample tighturl.config. You should also compare your templates +against the ones in this version of TightURL if you've customized your +templates. + +Current users of versions 0.1.3.1 +--------------------------------- +Your database is current. No need to run tighturl-install.php . + +Current users of version 0.1.3 +------------------------------ +Users upgrading from the (broken) 0.1.3 release should delete their empty +database, recreate it, and run tighturl-install.php again. + +Current users of versions prior to 0.1.3 +---------------------------------------- +Users upgrading from versions prior to 0.1.3 should run the installer +script, tighturl-install.php in order to upgrade their database to the latest +version. diff --git a/bad-behavior/COPYING b/bad-behavior/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/bad-behavior/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/bad-behavior/README.txt b/bad-behavior/README.txt new file mode 100644 index 0000000..c7dbc42 --- /dev/null +++ b/bad-behavior/README.txt @@ -0,0 +1,102 @@ +=== Bad Behavior === +Tags: comment,trackback,referrer,spam,robot,antispam +Contributors: error, MarkJaquith, Firas, skeltoac +Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=error%40ioerror%2eus&item_name=Bad%20Behavior%20%28From%20WordPress%20Page%29&no_shipping=1&cn=Comments%20about%20Bad%20Behavior&tax=0¤cy_code=USD&bn=PP%2dDonationsBF&charset=UTF%2d8 +Requires at least: 1.2 +Tested up to: 2.7 +Stable tag: 2.0.23 + +Welcome to a whole new way of keeping your blog, forum, guestbook, wiki or +content management system free of link spam. Bad Behavior is a PHP-based +solution for blocking link spam and the robots which deliver it. + +Bad Behavior complements other link spam solutions by acting as a gatekeeper, +preventing spammers from ever delivering their junk, and in many cases, from +ever reading your site in the first place. This keeps your site's load down, +makes your site logs cleaner, and can help prevent denial of service +conditions caused by spammers. + +Bad Behavior also transcends other link spam solutions by working in a +completely different, unique way. Instead of merely looking at the content of +potential spam, Bad Behavior analyzes the delivery method as well as the +software the spammer is using. In this way, Bad Behavior can stop spam attacks +even when nobody has ever seen the particular spam before. + +Bad Behavior is designed to work alongside existing spam prevention services +to increase their effectiveness and efficiency. Whenever possible, you should +run it in combination with a more traditional spam prevention service. + +Bad Behavior works on, or can be adapted to, virtually any PHP-based Web +software package. Bad Behavior is available natively for WordPress, MediaWiki, +Drupal, ExpressionEngine, and LifeType, and people have successfully made it +work with Movable Type, phpBB, and many other packages. + +Installing and configuring Bad Behavior on most platforms is simple and takes +only a few minutes. In most cases, no configuration at all is needed. Simply +turn it on and stop worrying about spam! + +The core of Bad Behavior is free software released under the GNU General +Public License. (On some non-free platforms, special license terms exist for +Bad Behavior's platform connector.) + +== Installation == + +*Warning*: If you are upgrading from a 1.x.x version of Bad Behavior, +you must remove it from your system entirely, and delete all of its +database tables, before installing Bad Behavior 2.0.x. You do not need +to remove a 2.0.x version of Bad Behavior before upgrading to this +release. + +Bad Behavior has been designed to install on each host software in the +manner most appropriate to each platform. It's usually sufficient to +follow the generic instructions for installing any plugin or extension +for your host software. + +On MediaWiki, it is necessary to add a second line to LocalSettings.php +when installing the extension. Your LocalSettings.php should include +the following: + +` include_once( 'includes/DatabaseFunctions.php' ); + include( './extensions/Bad-Behavior/bad-behavior-mediawiki.php' ); + +For complete documentation and installation instructions, please visit +http://www.bad-behavior.ioerror.us/ + +== Release Notes == + += Bad Behavior 2.0 Known Issues = + +* Bad Behavior may be unable to protect cached pages on MediaWiki. + +* When upgrading from version 2.0.19 or prior on MediaWiki and WordPress, +you must remove the old version of Bad Behavior from your system manually +before manually installing the new version. Other platforms are not +affected by this issue. + +* The basic functionality of Bad Behavior on WordPress requires version 1.2 +or later. The management page for WordPress, which allows browsing Bad +Behavior's logs, requires version 2.1 or later. Users of older versions should +use phpMyAdmin to browse Bad Behavior's logs, or upgrade WordPress. + +* On WordPress when using WordPress Advanced Cache (WP-Cache) or WP-Super +Cache, Bad Behavior requires a patch to WP-Cache or WP-Super Cache in order to +protect Cached pages. Bad Behavior cannot protect Super Cached pages. + + Edit the wp-content/plugins/wp-cache/wp-cache-phase1.php or +wp-content/plugins/wp-super-cache/wp-cache-phase1.php file and find the +following two lines at around line 34 (line 56 in WP-Super Cache): + +` if (! ($meta = unserialize(@file_get_contents($meta_pathname))) ) + return;` + + Immediately after this, insert the following line: + +` require_once( ABSPATH . 'wp-content/plugins/Bad-Behavior/bad-behavior-generic.php');` + + Then visit your site. Everything should work normally, but spammers will +not be able to access your cached pages either. + +* When using Bad Behavior in conjunction with Spam Karma 2, you may see PHP +warnings when Spam Karma 2 displays its internally generated CAPTCHA. This +is a design problem in Spam Karma 2. Contact the author of Spam Karma 2 for +a fix. diff --git a/bad-behavior/bad-behavior-generic.php b/bad-behavior/bad-behavior-generic.php new file mode 100644 index 0000000..7558c5a --- /dev/null +++ b/bad-behavior/bad-behavior-generic.php @@ -0,0 +1,144 @@ +<?php +/* +Bad Behavior - detects and blocks unwanted Web accesses +Copyright (C) 2005-2006 Michael Hampton + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +As a special exemption, you may link this program with any of the +programs listed below, regardless of the license terms of those +programs, and distribute the resulting program, without including the +source code for such programs: ExpressionEngine + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Please report any problems to badbots AT ioerror DOT us +*/ + +############################################################################### +############################################################################### + +define('BB2_CWD', dirname(__FILE__)); + +// Settings you can adjust for Bad Behavior. +// Most of these are unused in non-database mode. +$bb2_settings_defaults = array( + 'log_table' => 'bad_behavior', + 'display_stats' => true, + 'strict' => false, + 'verbose' => false, + 'logging' => true, + 'httpbl_key' => '', + 'httpbl_threat' => '25', + 'httpbl_maxage' => '30', +); + +// Bad Behavior callback functions. + +// Return current time in the format preferred by your database. +function bb2_db_date() { + return gmdate('Y-m-d H:i:s'); // Example is MySQL format +} + +// Return affected rows from most recent query. +function bb2_db_affected_rows() { + return false; +} + +// Escape a string for database usage +function bb2_db_escape($string) { + // return mysql_real_escape_string($string); + return $string; // No-op when database not in use. +} + +// Return the number of rows in a particular query. +function bb2_db_num_rows($result) { + if ($result !== FALSE) + return count($result); + return 0; +} + +// Run a query and return the results, if any. +// Should return FALSE if an error occurred. +// Bad Behavior will use the return value here in other callbacks. +function bb2_db_query($query) { + return FALSE; +} + +// Return all rows in a particular query. +// Should contain an array of all rows generated by calling mysql_fetch_assoc() +// or equivalent and appending the result of each call to an array. +function bb2_db_rows($result) { + return $result; +} + +// Return emergency contact email address. +function bb2_email() { + // return "example@example.com"; // You need to change this. + return "badbots@ioerror.us"; // You need to change this. +} + +// retrieve settings from database +// Settings are hard-coded for non-database use +function bb2_read_settings() { + global $bb2_settings_defaults; + return $bb2_settings_defaults; +} + +// write settings to database +function bb2_write_settings($settings) { + return false; +} + +// installation +function bb2_install() { + return false; +} + +// Screener +// Insert this into the <head> section of your HTML through a template call +// or whatever is appropriate. This is optional we'll fall back to cookies +// if you don't use it. +function bb2_insert_head() { + global $bb2_javascript; + echo $bb2_javascript; +} + +// Display stats? This is optional. +function bb2_insert_stats($force = false) { + $settings = bb2_read_settings(); + + if ($force || $settings['display_stats']) { + $blocked = bb2_db_query("SELECT COUNT(*) FROM " . $settings['log_table'] . " WHERE `key` NOT LIKE '00000000'"); + if ($blocked !== FALSE) { + echo sprintf('<p><a href="http://www.bad-behavior.ioerror.us/">%1$s</a> %2$s <strong>%3$s</strong> %4$s</p>', __('Bad Behavior'), __('has blocked'), $blocked[0]["COUNT(*)"], __('access attempts in the last 7 days.')); + } + } +} + +// Return the top-level relative path of wherever we are (for cookies) +// You should provide in $url the top-level URL for your site. +function bb2_relative_path() { + //$url = parse_url(get_bloginfo('url')); + //return $url['path'] . '/'; + return '/'; +} + +// Calls inward to Bad Behavor itself. +require_once(BB2_CWD . "/bad-behavior/version.inc.php"); +require_once(BB2_CWD . "/bad-behavior/core.inc.php"); +bb2_install(); // FIXME: see above + +bb2_start(bb2_read_settings()); + +?> diff --git a/bad-behavior/bad-behavior-lifetype.php b/bad-behavior/bad-behavior-lifetype.php new file mode 100644 index 0000000..3ca270f --- /dev/null +++ b/bad-behavior/bad-behavior-lifetype.php @@ -0,0 +1,172 @@ +<?php + /* + http://blog.markplace.net + + Bad Behavior - LifeType Plugin + Copyright (C) 2006 Mark Wu http://blog.markplace.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + // This file is the entry point for Bad Behavior in LifeType. + + if (!defined('PLOG_CLASS_PATH')) die('No cheating!'); + + // Timer start + $bb2_mtime = explode(" ", microtime()); + $bb2_timer_start = $bb2_mtime[1] + $bb2_mtime[0]; + + define('BB2_CWD', PLOG_CLASS_PATH . "plugins/badbehavior/" ); + define('BB2_EMERGENCY_EMAIL', "admin@yourblog.com" ); + define('BB2_DEFAULT_LOG_TABLE', "bad_behavior" ); + + // Bad Behavior callback functions. + + // Return current time in the format preferred by your database. + function bb2_db_date() { + return gmdate('Y-m-d H:i:s'); + } + + // Return affected rows from most recent query. + function bb2_db_affected_rows() { + lt_include( PLOG_CLASS_PATH."class/database/db.class.php" ); + $db =& Db::getDb(); + + return $db->Affected_Rows(); + } + + // Escape a string for database usage + function bb2_db_escape($string) { + lt_include( PLOG_CLASS_PATH."class/database/db.class.php" ); + + return Db::qstr($string); + } + + // Return the number of rows in a particular query. + function bb2_db_num_rows($result) { + return $result->RecordCount(); + } + + // Run a query and return the results, if any. + // Should return FALSE if an error occurred. + function bb2_db_query($query) { + lt_include( PLOG_CLASS_PATH."class/database/db.class.php" ); + $db =& Db::getDb(); + + $result = $db->Execute( $query ); + + if (!$result) + return FALSE; + + return $result; + } + + // Return all rows in a particular query. + // Should contain an array of all rows generated by calling mysql_fetch_assoc() + // or equivalent and appending the result of each call to an array. + function bb2_db_rows($result) { + $rows = array(); + while( $row = $result->FetchRow()) { + $rows[] = $row; + } + + return $rows; + } + + // Return emergency contact email address. + function bb2_email() { + return BB2_EMERGENCY_EMAIL; + } + + // retrieve settings from lifetype config + function bb2_read_settings() { + lt_include( PLOG_CLASS_PATH."class/database/db.class.php" ); + lt_include( PLOG_CLASS_PATH."class/config/config.class.php" ); + $config =& Config::getConfig(); + $prefix = Db::getPrefix(); + $logTable = $config->getValue( 'bb2_log_table', BB2_DEFAULT_LOG_TABLE ); + $displayStats = $config->getValue( 'bb2_display_stats', true ); + $strict = $config->getValue( 'bb2_strict', false ); + $verbose = $config->getValue( 'bb2_verbose', false ); + $isInstalled = $config->getValue( 'bb2_installed', false ); + $logging = $config->getValue( 'bb2_logging', true ); + $httpbl_key = $config->getValue( 'bb2_httpbl_key', '' ); + $httpbl_threat = $config->getValue( 'bb2_httpbl_threat', '25' ); + $httpbl_maxage = $config->getValue( 'bb2_httpbl_maxage', '30' ); + + return array('log_table' => $prefix . $logTable, + 'display_stats' => $displayStats, + 'strict' => $strict, + 'verbose' => $verbose, + 'logging' => $logging, + 'httpbl_key' => $httpbl_key, + 'httpbl_threat' => $httpbl_threat, + 'httpbl_maxage' => $httpbl_maxage, + 'is_installed' => $isInstalled ); + } + + // write settings to lifetype config + function bb2_write_settings($settings) { + lt_include( PLOG_CLASS_PATH."class/config/config.class.php" ); + $config =& Config::getConfig(); + $config->setValue( 'bb2_log_table', BB2_DEFAULT_LOG_TABLE ); + $config->setValue( 'bb2_display_stats', $settings['display_stats'] ); + $config->setValue( 'bb2_strict', $settings['strict'] ); + $config->setValue( 'bb2_verbose', $settings['verbose'] ); + $config->setValue( 'bb2_httpbl_key', $settings['httpbl_key'] ); + $config->setValue( 'bb2_httpbl_threat', $settings['httpbl_threat'] ); + $config->setValue( 'bb2_httpbl_maxage', $settings['httpbl_maxage'] ); + $config->setValue( 'bb2_installed', $settings['is_installed'] ); + $config->save(); + } + + // installation + function bb2_install() { + $settings = bb2_read_settings(); + if( $settings['is_installed'] == false && $settings['logging'] ) + { + bb2_db_query(bb2_table_structure($settings['log_table'])); + $settings['is_installed'] = true; + bb2_write_settings( $settings ); + } + } + + // Return the top-level relative path of wherever we are (for cookies) + function bb2_relative_path() { + lt_include( PLOG_CLASS_PATH."class/config/config.class.php" ); + $config =& Config::getConfig(); + + $url = parse_url( $config->getValue( 'base_url' ) ); + if( empty($url['path']) ) + return '/'; + else { + if( substr( $url['path'], -1, 1 ) == '/' ) + return $url['path']; + else + return $url['path'] . '/'; + } + } + + // Load Bad Behavior Core + lt_include(BB2_CWD . "bad-behavior/core.inc.php"); + bb2_install(); + $settings = bb2_read_settings(); + bb2_start($settings); + + // Time Stop + $bb2_mtime = explode(" ", microtime()); + $bb2_timer_stop = $bb2_mtime[1] + $bb2_mtime[0]; + $bb2_timer_total = $bb2_timer_stop - $bb2_timer_start; +?> diff --git a/bad-behavior/bad-behavior-mediawiki.php b/bad-behavior/bad-behavior-mediawiki.php new file mode 100644 index 0000000..c47d58b --- /dev/null +++ b/bad-behavior/bad-behavior-mediawiki.php @@ -0,0 +1,160 @@ +<?php +/* +http://www.bad-behavior.ioerror.us/ + +Bad Behavior - detects and blocks unwanted Web accesses +Copyright (C) 2005 Michael Hampton + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +// This file is the entry point for Bad Behavior. + +if (!defined('MEDIAWIKI')) die(); + +// Settings you can adjust for Bad Behavior. +$bb2_settings_defaults = array( + 'log_table' => $wgDBprefix . 'bad_behavior', + 'display_stats' => true, + 'strict' => false, + 'verbose' => false, + 'logging' => true, + 'httpbl_key' => '', + 'httpbl_threat' => '25', + 'httpbl_maxage' => '30', +); + +define('BB2_CWD', dirname(__FILE__)); + +// Bad Behavior callback functions. + +// Return current time in the format preferred by your database. +function bb2_db_date() { + return gmdate('Y-m-d H:i:s'); +} + +// Return affected rows from most recent query. +function bb2_db_affected_rows($result) { + return wfAffectedRows($result); +} + +// Escape a string for database usage +function bb2_db_escape($string) { + // FIXME SECURITY: Get a straight answer from somebody on how MW escapes stuff + return addslashes($string); +} + +// Return the number of rows in a particular query. +function bb2_db_num_rows($result) { + return wfNumRows($result); +} + +// Run a query and return the results, if any. +// Should return FALSE if an error occurred. +function bb2_db_query($query) { + $bb2_last_query = wfQuery($query, DB_WRITE); + return $bb2_last_query; +} + +// Return all rows in a particular query. +// Should contain an array of all rows generated by calling mysql_fetch_assoc() +// or equivalent and appending the result of each call to an array. +function bb2_db_rows($result) { + $rows = array(); + while ($row = wfFetchRow($result)) { + $rows[] = $row; + } + return $rows; +} + +// Return emergency contact email address. +function bb2_email() { + global $wgEmergencyContact; + return $wgEmergencyContact; +} + +// This Bad Behavior-related function is a stub. You can help MediaWiki by expanding it. +// retrieve settings from database +function bb2_read_settings() { + global $bb2_settings_defaults; + return $bb2_settings_defaults; +} + +// This Bad Behavior-related function is a stub. You can help MediaWiki by expanding it. +// write settings to database +function bb2_write_settings($settings) { + return; +} + +// In some configurations automatic table creation may fail with the message +// You must update your load-balancing configuration. +// You can create the table manually (see query in +// bad-behavior/database.inc.php) and add this line to your LocalSettings.php: +// +// define('BB2_NO_CREATE', true); + +// installation +function bb2_install() { + $settings = bb2_read_settings(); + if (defined('BB2_NO_CREATE')) return; + if (!$settings['logging']) return; + bb2_db_query(bb2_table_structure($settings['log_table'])); +} + +// Return the top-level relative path of wherever we are (for cookies) +function bb2_relative_path() { + // TODO: This might not be the best way, but it seems to work + global $wgScript; + return dirname($wgScript) . "/"; +} + +// Cute timer display +function bb2_mediawiki_timer(&$parser, &$text) { + global $bb2_timer_total; + $text = "<!-- Bad Behavior " . BB2_VERSION . " run time: " . number_format(1000 * $bb2_timer_total, 3) . " ms -->" . $text; + return true; +} + +function bb2_mediawiki_entry() { + global $bb2_timer_total; + + $bb2_mtime = explode(" ", microtime()); + $bb2_timer_start = $bb2_mtime[1] + $bb2_mtime[0]; + + if (php_sapi_name() != 'cli') { + require_once(BB2_CWD . "/bad-behavior/core.inc.php"); + bb2_install(); // FIXME: see above + $settings = bb2_read_settings(); + bb2_start($settings); + } + + $bb2_mtime = explode(" ", microtime()); + $bb2_timer_stop = $bb2_mtime[1] + $bb2_mtime[0]; + $bb2_timer_total = $bb2_timer_stop - $bb2_timer_start; +} + +require_once(BB2_CWD . "/bad-behavior/version.inc.php"); +$wgExtensionCredits['other'][] = array( + 'name' => 'Bad Behavior', + 'version' => BB2_VERSION, + 'author' => 'Michael Hampton', + 'description' => 'Detects and blocks unwanted Web accesses', + 'url' => 'http://www.bad-behavior.ioerror.us/' +); + +#$wgHooks['ParserAfterTidy'][] = 'bb2_mediawiki_timer'; +$wgExtensionFunctions[] = 'bb2_mediawiki_entry'; + +?> diff --git a/bad-behavior/bad-behavior-tighturl.php b/bad-behavior/bad-behavior-tighturl.php new file mode 100644 index 0000000..21bd034 --- /dev/null +++ b/bad-behavior/bad-behavior-tighturl.php @@ -0,0 +1,166 @@ +<?php +/* +Bad Behavior for TightURL - detects and blocks unwanted Web accesses +Ron Guerin <ron@vnetworx.net> + +I wrote almost none of this, but if you're using this file, you should contact +me (Ron Guerin) and not Michael Hampton, the author of Bad Behavior if you +have questions about this file or the installation of Bad Behavior that you +got with TightURL (which has been modified so as to not throw some unneeded +warnings). + +Based on bad-behavior-generic.php +Copyright (C) 2005-2006 Michael Hampton badbots AT ioerror DOT us + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +############################################################################### +############################################################################### + +define('BB2_CWD', dirname(__FILE__)); + +// Settings you can adjust for Bad Behavior. +// Change these by changing the corresponding TightURL settings in +// tighturl.config.inc.php instead of editing this file. + +$bb2_settings_defaults = array( 'log_table' => $dbtable . '_bb2', + 'display_stats' => $BBstats, + 'strict' => $BBstrict, + 'verbose' => $BBverbose, + 'logging' => $BBLogging +); + +// Bad Behavior callback functions. + +// Return current time in the format preferred by your database. +function bb2_db_date() { + return gmdate('Y-m-d H:i:s'); // Example is MySQL format +} + +// Return affected rows from most recent query. +function bb2_db_affected_rows() { + return mysql_affected_rows(); +} + +// Escape a string for database usage +function bb2_db_escape($string) { + return mysql_real_escape_string($string); +} + +// Return the number of rows in a particular query. +function bb2_db_num_rows($result) { + if ($result !== FALSE) return mysql_num_rows($result); + return 0; +} + +// Run a query and return the results, if any. +// Should return FALSE if an error occurred. +// Bad Behavior will use the return value here in other callbacks. +function bb2_db_query($query) { + return mysql_query($query); +} + +// Return all rows of a particular query in an array. + +function bb2_db_rows($result) { + $rows = array(); + while ($row = mysql_fetch_assoc($result)) { + $rows[] = $row; + } + return $rows; +} + +// Return emergency contact email address. +function bb2_email() { + return $SiteAdminEmail; +} + +// read settings from TightURL +function bb2_read_settings() { + global $bb2_settings_defaults; + $settings = $bb2_settings_defaults; + return $settings; +} + +// write settings to TightURL +function bb2_write_settings($settings) { + return false; +} + +// installation +function bb2_install() { + return bb2_db_query("CREATE TABLE IF NOT EXISTS `".$settings['log_table']."` ( + `id` INT(11) NOT NULL auto_increment, + `ip` TEXT NOT NULL, + `date` DATETIME NOT NULL default '0000-00-00 00:00:00', + `request_method` TEXT NOT NULL, + `request_uri` TEXT NOT NULL, + `server_protocol` TEXT NOT NULL, + `http_headers` TEXT NOT NULL, + `user_agent` TEXT NOT NULL, + `request_entity` TEXT NOT NULL, + `key` TEXT NOT NULL, + INDEX (`ip`(15)), + INDEX (`user_agent`(10)), + PRIMARY KEY (`id`))"); +} + +// Screener +// Insert this into the <head> section of your HTML through a template call +// or whatever is appropriate. This is optional we'll fall back to cookies +// if you don't use it. +function bb2_insert_head() { + global $bb2_javascript; + return $bb2_javascript; +} + +// Display stats +function bb2_insert_stats($force = false) { + $settings = bb2_read_settings(); + + if ($force || $settings['display_stats']) { + $blocked = bb2_db_query("SELECT COUNT(*) FROM " . $settings['log_table'] . " WHERE `key` NOT LIKE '00000000'"); + if ($blocked !== FALSE) { + if (! isset($blocked[0]["COUNT(*)"])) { + $count = 0; + } + else { + $count = $blocked[0]["COUNT(*)"]; + } + $ret = '<br /><a href="http://www.bad-behavior.ioerror.us/">Bad Behavior</a> has blocked ' . $count . ' access attempt(s) in the last 7 days.'; + } + else { + $ret = ""; + } + } + return($ret); +} + +// Return the top-level relative path of wherever we are (for cookies) +// You should provide in $url the top-level URL for your site. +function bb2_relative_path() { + global $self; + return $self; +} + +// Calls inward to Bad Behavior itself. +require_once(BB2_CWD . "/bad-behavior/version.inc.php"); +require_once(BB2_CWD . "/bad-behavior/core.inc.php"); + +bb2_start(bb2_read_settings()); + +?> diff --git a/bad-behavior/bad-behavior-wordpress-admin.php b/bad-behavior/bad-behavior-wordpress-admin.php new file mode 100644 index 0000000..2ab543c --- /dev/null +++ b/bad-behavior/bad-behavior-wordpress-admin.php @@ -0,0 +1,287 @@ +<?php if (!defined('BB2_CORE')) die('I said no cheating!'); + +require_once("bad-behavior/responses.inc.php"); + +function bb2_admin_pages() { + global $wp_db_version; + + if (function_exists('current_user_can')) { + // The new 2.x way + if (current_user_can('manage_options')) { + $bb2_is_admin = true; + } + } else { + // The old 1.x way + global $user_ID; + if (user_can_edit_user($user_ID, 0)) { + $bb2_is_admin = true; + } + } + + if ($bb2_is_admin) { + add_options_page(__("Bad Behavior"), __("Bad Behavior"), 8, 'bb2_options', 'bb2_options'); + if ($wp_db_version >= 4772) { // Version 2.1 or later + add_management_page(__("Bad Behavior"), __("Bad Behavior"), 8, 'bb2_manage', 'bb2_manage'); + } + @session_start(); + } +} + +function bb2_clean_log_link($uri) { + foreach (array("paged", "ip", "key", "blocked", "request_method", "user_agent") as $arg) { + $uri = remove_query_arg($arg, $uri); + } + return $uri; +} + +function bb2_httpbl_lookup($ip) { + $engines = array( + 2 => "Bloglines", + 5 => "Googlebot", + 8 => "msnbot", + 9 => "Yahoo! Slurp", + ); + $settings = bb2_read_settings(); + $httpbl_key = $settings['httpbl_key']; + if (!$httpbl_key) return false; + + $r = $_SESSION['httpbl'][$ip]; + $d = ""; + if (!$r) { // Lookup + $find = implode('.', array_reverse(explode('.', $ip))); + $result = gethostbynamel("${httpbl_key}.${find}.dnsbl.httpbl.org."); + if (!empty($result)) { + $r = $result[0]; + $_SESSION['httpbl'][$ip] = $r; + } + } + if ($r) { // Interpret + $ip = explode('.', $r); + if ($ip[0] == 127) { + if ($ip[3] == 0) { + if ($engines[$ip[2]]) { + $d .= $engines[$ip[2]]; + } else { + $d .= "Search engine ${ip[2]}<br/>\n"; + } + } + if ($ip[3] & 1) { + $d .= "Suspicious<br/>\n"; + } + if ($ip[3] & 2) { + $d .= "Harvester<br/>\n"; + } + if ($ip[3] & 4) { + $d .= "Comment Spammer<br/>\n"; + } + if ($ip[3] & 7) { + $d .= "Threat level ${ip[2]}<br/>\n"; + } + if ($ip[3] > 0) { + $d .= "Age ${ip[1]} days<br/>\n"; + } + } + } + return $d; +} + +function bb2_manage() { + global $wpdb; + + $request_uri = $_SERVER["REQUEST_URI"]; + $settings = bb2_read_settings(); + $rows_per_page = 100; + $where = ""; + + // Get query variables desired by the user with input validation + $paged = 0 + $_GET['paged']; if (!$paged) $paged = 1; + if ($_GET['key']) $where .= "AND `key` = '" . $wpdb->escape($_GET['key']) . "' "; + if ($_GET['blocked']) $where .= "AND `key` != '00000000' "; + if ($_GET['ip']) $where .= "AND `ip` = '" . $wpdb->escape($_GET['ip']) . "' "; + if ($_GET['user_agent']) $where .= "AND `user_agent` = '" . $wpdb->escape($_GET['user_agent']) . "' "; + if ($_GET['request_method']) $where .= "AND `request_method` = '" . $wpdb->escape($_GET['request_method']) . "' "; + + // Query the DB based on variables selected + $r = bb2_db_query("SELECT COUNT(*) FROM `" . $settings['log_table']); + $results = bb2_db_rows($r); + $totalcount = $results[0]["COUNT(*)"]; + $r = bb2_db_query("SELECT COUNT(*) FROM `" . $settings['log_table'] . "` WHERE 1=1 " . $where); + $results = bb2_db_rows($r); + $count = $results[0]["COUNT(*)"]; + $pages = ceil($count / 100); + $r = bb2_db_query("SELECT * FROM `" . $settings['log_table'] . "` WHERE 1=1 " . $where . "ORDER BY `date` DESC LIMIT " . ($paged - 1) * $rows_per_page . "," . $rows_per_page); + $results = bb2_db_rows($r); + + // Display rows to the user +?> +<div class="wrap"> +<h2><?php _e("Bad Behavior"); ?></h2> +<form method="post" action="<?php echo $request_uri; ?>"> + <p>For more information please visit the <a href="http://www.bad-behavior.ioerror.us/">Bad Behavior</a> homepage.</p> + <p>If you find Bad Behavior valuable, please consider making a <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=error%40ioerror%2eus&item_name=Bad%20Behavior%20<?php echo BB2_VERSION; ?>%20%28From%20Admin%29&no_shipping=1&cn=Comments%20about%20Bad%20Behavior&tax=0¤cy_code=USD&bn=PP%2dDonationsBF&charset=UTF%2d8">financial contribution</a> to further development of Bad Behavior.</p> + +<div class="tablenav"> +<?php + $page_links = paginate_links(array('base' => add_query_arg("paged", "%#%"), 'format' => '', 'total' => $pages, 'current' => $paged)); + if ($page_links) echo "<div class=\"tablenav-pages\">$page_links</div>\n"; +?> +<div class="alignleft"> +<?php if ($count < $totalcount): ?> +Displaying <strong><?php echo $count; ?></strong> of <strong><?php echo $totalcount; ?></strong> records filtered by:<br/> +<?php if ($_GET['key']) echo "Status [<a href=\"" . remove_query_arg(array("paged", "key"), $request_uri) . "\">X</a>] "; ?> +<?php if ($_GET['blocked']) echo "Blocked [<a href=\"" . remove_query_arg(array("paged", "blocked"), $request_uri) . "\">X</a>] "; ?> +<?php if ($_GET['ip']) echo "IP [<a href=\"" . remove_query_arg(array("paged", "ip"), $request_uri) . "\">X</a>] "; ?> +<?php if ($_GET['user_agent']) echo "User Agent [<a href=\"" . remove_query_arg(array("paged", "user_agent"), $request_uri) . "\">X</a>] "; ?> +<?php if ($_GET['request_method']) echo "GET/POST [<a href=\"" . remove_query_arg(array("paged", "request_method"), $request_uri) . "\">X</a>] "; ?> +<?php else: ?> +Displaying all <strong><?php echo $totalcount; ?></strong> records<br/> +<?php endif; ?> +<?php if (!$_GET['key'] && !$_GET['blocked']) { ?><a href="<?php echo add_query_arg(array("blocked" => "true", "paged" => false), $request_uri); ?>">Show Blocked</a><?php } ?> +</div> +</div> + +<table class="widefat"> + <thead> + <tr> + <th scope="col" class="check-column"><input type="checkbox" onclick="checkAll(document.getElementById('request-filter'));" /></th> + <th scope="col"><?php _e("IP/Date/Status"); ?></th> + <th scope="col"><?php _e("Headers"); ?></th> + <th scope="col"><?php _e("Entity"); ?></th> + </tr> + </thead> + <tbody> +<?php + $alternate = 0; + if ($results) foreach ($results as $result) { + $key = bb2_get_response($result["key"]); + $alternate++; + if ($alternate % 2) { + echo "<tr id=\"request-" . $result["id"] . "\" valign=\"top\">\n"; + } else { + echo "<tr id=\"request-" . $result["id"] . "\" class=\"alternate\" valign=\"top\">\n"; + } + echo "<th scope=\"row\" class=\"check-column\"><input type=\"checkbox\" name=\"submit[]\" value=\"" . $result["id"] . "\" /></th>\n"; + $httpbl = bb2_httpbl_lookup($result["ip"]); + echo "<td><a href=\"" . add_query_arg("ip", $result["ip"], remove_query_arg("paged", $request_uri)) . "\">" . $result["ip"] . "</a><br/><br/>\n" . $result["date"] . "<br/><br/><a href=\"" . add_query_arg("key", $result["key"], remove_query_arg(array("paged", "blocked"), $request_uri)) . "\">" . $key["log"] . "</a>\n"; + if ($httpbl) echo "<br/><br/>http:BL:<br/>$httpbl\n"; + echo "</td>\n"; + $headers = str_replace("\n", "<br/>\n", htmlspecialchars($result['http_headers'])); + if (@strpos($headers, $result['user_agent']) !== FALSE) $headers = substr_replace($headers, "<a href=\"" . add_query_arg("user_agent", rawurlencode($result["user_agent"]), remove_query_arg("paged", $request_uri)) . "\">" . $result['user_agent'] . "</a>", strpos($headers, $result['user_agent']), strlen($result['user_agent'])); + if (strpos($headers, $result['request_method']) !== FALSE) $headers = substr_replace($headers, "<a href=\"" . add_query_arg("request_method", rawurlencode($result["request_method"]), remove_query_arg("paged", $request_uri)) . "\">" . $result['request_method'] . "</a>", strpos($headers, $result['request_method']), strlen($result['request_method'])); + echo "<td>$headers</td>\n"; + echo "<td>" . str_replace("\n", "<br/>\n", htmlspecialchars($result["request_entity"])) . "</td>\n"; + echo "</tr>\n"; + } +?> + </tbody> +</table> +<div class="tablenav"> +<?php + $page_links = paginate_links(array('base' => add_query_arg("paged", "%#%"), 'format' => '', 'total' => $pages, 'current' => $paged)); + if ($page_links) echo "<div class=\"tablenav-pages\">$page_links</div>\n"; +?> +<div class="alignleft"> +</div> +</div> +</form> +</div> +<?php +} + +function bb2_options() +{ + $settings = bb2_read_settings(); + + if ($_POST) { + if ($_POST['display_stats']) { + $settings['display_stats'] = true; + } else { + $settings['display_stats'] = false; + } + if ($_POST['strict']) { + $settings['strict'] = true; + } else { + $settings['strict'] = false; + } + if ($_POST['verbose']) { + $settings['verbose'] = true; + } else { + $settings['verbose'] = false; + } + if ($_POST['logging']) { + if ($_POST['logging'] == 'verbose') { + $settings['verbose'] = true; + $settings['logging'] = true; + } else if ($_POST['logging'] == 'normal') { + $settings['verbose'] = false; + $settings['logging'] = true; + } else { + $settings['verbose'] = false; + $settings['logging'] = false; + } + } else { + $settings['verbose'] = false; + $settings['logging'] = false; + } + if ($_POST['httpbl_key']) { + $settings['httpbl_key'] = $_POST['httpbl_key']; + } else { + $settings['httpbl_key'] = ''; + } + if ($_POST['httpbl_threat']) { + $settings['httpbl_threat'] = $_POST['httpbl_threat']; + } else { + $settings['httpbl_threat'] = '25'; + } + if ($_POST['httpbl_maxage']) { + $settings['httpbl_maxage'] = $_POST['httpbl_maxage']; + } else { + $settings['httpbl_maxage'] = '30'; + } + bb2_write_settings($settings); +?> + <div id="message" class="updated fade"><p><strong><?php _e('Options saved.') ?></strong></p></div> +<?php + } +?> + <div class="wrap"> + <h2><?php _e("Bad Behavior"); ?></h2> + <form method="post" action="<?php echo $_SERVER['REQUEST_URI']; ?>"> + <p>For more information please visit the <a href="http://www.bad-behavior.ioerror.us/">Bad Behavior</a> homepage.</p> + <p>If you find Bad Behavior valuable, please consider making a <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=error%40ioerror%2eus&item_name=Bad%20Behavior%20<?php echo BB2_VERSION; ?>%20%28From%20Admin%29&no_shipping=1&cn=Comments%20about%20Bad%20Behavior&tax=0¤cy_code=USD&bn=PP%2dDonationsBF&charset=UTF%2d8">financial contribution</a> to further development of Bad Behavior.</p> + + <h3><?php _e('Statistics'); ?></h3> + <?php bb2_insert_stats(true); ?> + <table class="form-table"> + <tr><td><label><input type="checkbox" name="display_stats" value="true" <?php if ($settings['display_stats']) { ?>checked="checked" <?php } ?>/> <?php _e('Display statistics in blog footer'); ?></label></td></tr> + </table> + + <h3><?php _e('Logging'); ?></h3> + <table class="form-table"> + <tr><td><label><input type="radio" name="logging" value="verbose" <?php if ($settings['verbose'] && $settings['logging']) { ?>checked="checked" <?php } ?>/> <?php _e('Verbose HTTP request logging'); ?></label></td></tr> + <tr><td><label><input type="radio" name="logging" value="normal" <?php if ($settings['logging'] && !$settings['verbose']) { ?>checked="checked" <?php } ?>/> <?php _e('Normal HTTP request logging (recommended)'); ?></label></td></tr> + <tr><td><label><input type="radio" name="logging" value="false" <?php if (!$settings['logging']) { ?>checked="checked" <?php } ?>/> <?php _e('Do not log HTTP requests (not recommended)'); ?></label></td></tr> + </table> + + <h3><?php _e('Strict Mode'); ?></h3> + <table class="form-table"> + <tr><td><label><input type="checkbox" name="strict" value="true" <?php if ($settings['strict']) { ?>checked="checked" <?php } ?>/> <?php _e('Strict checking (blocks more spam but may block some people)'); ?></label></td></tr> + </table> + + <h3><?php _e('http:BL'); ?></h3> + <p>To use Bad Behavior's http:BL features you must have an <a href="http://www.projecthoneypot.org/httpbl_configure.php?rf=24694">http:BL Access Key</a>.</p> + <table class="form-table"> + <tr><td><label><input type="text" size="12" maxlength="12" name="httpbl_key" value="<?php echo $settings['httpbl_key']; ?>" /> http:BL Access Key</label></td></tr> + <tr><td><label><input type="text" size="3" maxlength="3" name="httpbl_threat" value="<?php echo $settings['httpbl_threat']; ?>" /> Minimum Threat Level (25 is recommended)</label></td></tr> + <tr><td><label><input type="text" size="3" maxlength="3" name="httpbl_maxage" value="<?php echo $settings['httpbl_maxage']; ?>" /> Maximum Age of Data (30 is recommended)</label></td></tr> + </table> + + <p class="submit"><input class="button" type="submit" name="submit" value="<?php _e('Update »'); ?>" /></p> + </form> + </div> +<?php +} + +add_action('admin_menu', 'bb2_admin_pages'); + +?> diff --git a/bad-behavior/bad-behavior-wordpress.php b/bad-behavior/bad-behavior-wordpress.php new file mode 100644 index 0000000..98532a6 --- /dev/null +++ b/bad-behavior/bad-behavior-wordpress.php @@ -0,0 +1,173 @@ +<?php +/* +Plugin Name: Bad Behavior +Version: 2.0.23 +Description: Deny automated spambots access to your PHP-based Web site. +Plugin URI: http://www.bad-behavior.ioerror.us/ +Author: Michael Hampton +Author URI: http://www.homelandstupidity.us/ +License: GPL + +Bad Behavior - detects and blocks unwanted Web accesses +Copyright (C) 2005 Michael Hampton + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +As a special exemption, you may link this program with any of the +programs listed below, regardless of the license terms of those +programs, and distribute the resulting program, without including the +source code for such programs: ExpressionEngine + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Please report any problems to badbots AT ioerror DOT us +*/ + +############################################################################### +############################################################################### + +if (!defined('ABSPATH')) die("No cheating!"); + +$bb2_mtime = explode(" ", microtime()); +$bb2_timer_start = $bb2_mtime[1] + $bb2_mtime[0]; + +define('BB2_CWD', dirname(__FILE__)); + +// Bad Behavior callback functions. + +// Return current time in the format preferred by your database. +function bb2_db_date() { + return get_gmt_from_date(current_time('mysql')); +} + +// Return affected rows from most recent query. +function bb2_db_affected_rows() { + global $wpdb; + + return $wpdb->rows_affected; +} + +// Escape a string for database usage +function bb2_db_escape($string) { + global $wpdb; + + return $wpdb->escape($string); +} + +// Return the number of rows in a particular query. +function bb2_db_num_rows($result) { + if ($result !== FALSE) + return count($result); + return 0; +} + +// Run a query and return the results, if any. +// Should return FALSE if an error occurred. +// Bad Behavior will use the return value here in other callbacks. +function bb2_db_query($query) { + global $wpdb; + + $wpdb->hide_errors(); + $result = $wpdb->get_results($query, ARRAY_A); + $wpdb->show_errors(); + if (mysql_error()) { + return FALSE; + } + return $result; +} + +// Return all rows in a particular query. +// Should contain an array of all rows generated by calling mysql_fetch_assoc() +// or equivalent and appending the result of each call to an array. +// For WP this is pretty much a no-op. +function bb2_db_rows($result) { + return $result; +} + +// Return emergency contact email address. +function bb2_email() { + return get_bloginfo('admin_email'); +} + +// retrieve settings from database +function bb2_read_settings() { + global $wpdb; + + // Add in default settings when they aren't yet present in WP + $settings = get_settings('bad_behavior_settings'); + if (!$settings) $settings = array(); + return array_merge(array('log_table' => $wpdb->prefix . 'bad_behavior', 'display_stats' => true, 'strict' => false, 'verbose' => false, 'logging' => true, 'httpbl_key' => '', 'httpbl_threat' => '25', 'httpbl_maxage' => '30',), $settings); +} + +// write settings to database +function bb2_write_settings($settings) { + update_option('bad_behavior_settings', $settings); +} + +// installation +function bb2_install() { + $settings = bb2_read_settings(); + if (!$settings['logging']) return; + bb2_db_query(bb2_table_structure($settings['log_table'])); +} + +// Cute timer display; screener +function bb2_insert_head() { + global $bb2_timer_total; + global $bb2_javascript; + echo "\n<!-- Bad Behavior " . BB2_VERSION . " run time: " . number_format(1000 * $bb2_timer_total, 3) . " ms -->\n"; + echo $bb2_javascript; +} + +// Display stats? +function bb2_insert_stats($force = false) { + $settings = bb2_read_settings(); + + if ($force || $settings['display_stats']) { + $blocked = bb2_db_query("SELECT COUNT(*) FROM " . $settings['log_table'] . " WHERE `key` NOT LIKE '00000000'"); + if ($blocked !== FALSE) { + echo sprintf('<p><a href="http://www.bad-behavior.ioerror.us/">%1$s</a> %2$s <strong>%3$s</strong> %4$s</p>', __('Bad Behavior'), __('has blocked'), $blocked[0]["COUNT(*)"], __('access attempts in the last 7 days.')); + } + } +} + +// Return the top-level relative path of wherever we are (for cookies) +function bb2_relative_path() { + $url = parse_url(get_bloginfo('url')); + return $url['path'] . '/'; +} + +// FIXME: some sort of hack to run install on 1.5 (and older?) blogs +// FIXME: figure out what's wrong on 2.0 that this doesn't work +// register_activation_hook(__FILE__, 'bb2_install'); +//add_action('activate_bb2/bad-behavior-wordpress.php', 'bb2_install'); +add_action('wp_head', 'bb2_insert_head'); +add_action('wp_footer', 'bb2_insert_stats'); + +// Calls inward to Bad Behavor itself. +require_once(BB2_CWD . "/bad-behavior/version.inc.php"); +require_once(BB2_CWD . "/bad-behavior/core.inc.php"); +bb2_install(); // FIXME: see above + +if (is_admin() || strstr($_SERVER['PHP_SELF'], 'wp-admin/')) { // 1.5 kludge + #wp_enqueue_script("admin-forms"); + require_once(BB2_CWD . "/bad-behavior-wordpress-admin.php"); +} + +bb2_start(bb2_read_settings()); + +$bb2_mtime = explode(" ", microtime()); +$bb2_timer_stop = $bb2_mtime[1] + $bb2_mtime[0]; +$bb2_timer_total = $bb2_timer_stop - $bb2_timer_start; + +?> diff --git a/bad-behavior/bad-behavior/banned.inc.php b/bad-behavior/bad-behavior/banned.inc.php new file mode 100644 index 0000000..3caa497 --- /dev/null +++ b/bad-behavior/bad-behavior/banned.inc.php @@ -0,0 +1,49 @@ +<?php if (!defined('BB2_CORE')) die('I said no cheating!'); + +// Functions called when a request has been denied +// This part can be gawd-awful slow, doesn't matter :) + +require_once(BB2_CORE . "/responses.inc.php"); + +function bb2_display_denial($settings, $key, $previous_key = false) +{ + if (!$previous_key) $previous_key = $key; + if ($key == "e87553e1") { + // FIXME: lookup the real key + } + // Create support key + $ip = explode(".", $_SERVER['REMOTE_ADDR']); + $ip_hex = ""; + foreach ($ip as $octet) { + $ip_hex .= str_pad(dechex($octet), 2, 0, STR_PAD_LEFT); + } + $support_key = implode("-", str_split("$ip_hex$key", 4)); + + // Get response data + $response = bb2_get_response($previous_key); + header("HTTP/1.1 " . $response['response'] . " Bad Behavior"); + header("Status: " . $response['response'] . " Bad Behavior"); +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>HTTP Error <?php echo $response['response']; ?> + + +

Error

+

We're sorry, but we could not fulfill your request for + on this server.

+

+

Your technical support key is:

+

You can use this key to fix this problem yourself.

+

If you are unable to fix the problem yourself, please contact "> and be sure to provide the technical support key shown above.

+ diff --git a/bad-behavior/bad-behavior/blackhole.inc.php b/bad-behavior/bad-behavior/blackhole.inc.php new file mode 100644 index 0000000..08564fb --- /dev/null +++ b/bad-behavior/bad-behavior/blackhole.inc.php @@ -0,0 +1,51 @@ + array("127.0.0.4"), // CBL is problematic + "dnsbl.sorbs.net" => array("127.0.0.10",), // Dynamic IPs only + "list.dsbl.org" => array(), + "dnsbl.ioerror.us" => array(), + ); + + // Check the blackhole lists + $ip = $package['ip']; + $find = implode('.', array_reverse(explode('.', $ip))); + foreach ($bb2_blackhole_lists as $dnsbl) { + $result = gethostbynamel($find . "." . $dnsbl . "."); + if (!empty($result)) { + // Got a match and it isn't on the exception list + $result = @array_diff($result, $bb2_blackhole_exceptions[$dnsbl]); + if (!empty($result)) { + return '136673cd'; + } + } + } + return false; +} + +function bb2_httpbl($settings, $package) { + if (!@$settings['httpbl_key']) return false; + + $find = implode('.', array_reverse(explode('.', $package['ip']))); + $result = gethostbynamel($settings['httpbl_key'].".${find}.dnsbl.httpbl.org."); + if (!empty($result)) { + $ip = explode('.', $result[0]); + if ($ip[0] == 127 && ($ip[3] & 7) && $ip[2] >= $settings['httpbl_threat'] && $ip[1] >= $settings['httpbl_maxage']) { + return '2b021b1f'; + } + } + return false; +} +?> diff --git a/bad-behavior/bad-behavior/blackhole.inc.php~ b/bad-behavior/bad-behavior/blackhole.inc.php~ new file mode 100644 index 0000000..cbc0826 --- /dev/null +++ b/bad-behavior/bad-behavior/blackhole.inc.php~ @@ -0,0 +1,51 @@ + array("127.0.0.4"), // CBL is problematic + "dnsbl.sorbs.net" => array("127.0.0.10",), // Dynamic IPs only + "list.dsbl.org" => array(), + "dnsbl.ioerror.us" => array(), + ); + + // Check the blackhole lists + $ip = $package['ip']; + $find = implode('.', array_reverse(explode('.', $ip))); + foreach ($bb2_blackhole_lists as $dnsbl) { + $result = gethostbynamel($find . "." . $dnsbl . "."); + if (!empty($result)) { + // Got a match and it isn't on the exception list + $result = @array_diff($result, $bb2_blackhole_exceptions[$dnsbl]); + if (!empty($result)) { + return '136673cd'; + } + } + } + return false; +} + +function bb2_httpbl($settings, $package) { + if (!$settings['httpbl_key']) return false; + + $find = implode('.', array_reverse(explode('.', $package['ip']))); + $result = gethostbynamel($settings['httpbl_key'].".${find}.dnsbl.httpbl.org."); + if (!empty($result)) { + $ip = explode('.', $result[0]); + if ($ip[0] == 127 && ($ip[3] & 7) && $ip[2] >= $settings['httpbl_threat'] && $ip[1] >= $settings['httpbl_maxage']) { + return '2b021b1f'; + } + } + return false; +} +?> diff --git a/bad-behavior/bad-behavior/blacklist.inc.php b/bad-behavior/bad-behavior/blacklist.inc.php new file mode 100644 index 0000000..e77719f --- /dev/null +++ b/bad-behavior/bad-behavior/blacklist.inc.php @@ -0,0 +1,121 @@ + diff --git a/bad-behavior/bad-behavior/common_tests.inc.php b/bad-behavior/bad-behavior/common_tests.inc.php new file mode 100644 index 0000000..4ffc1ab --- /dev/null +++ b/bad-behavior/bad-behavior/common_tests.inc.php @@ -0,0 +1,137 @@ + diff --git a/bad-behavior/bad-behavior/core.inc.php b/bad-behavior/bad-behavior/core.inc.php new file mode 100644 index 0000000..641106b --- /dev/null +++ b/bad-behavior/bad-behavior/core.inc.php @@ -0,0 +1,208 @@ + $v) { + $headers .= bb2_db_escape("$h: $v\n"); + } + $request_entity = ""; + if (!strcasecmp($request_method, "POST")) { + foreach ($package['request_entity'] as $h => $v) { + $request_entity .= bb2_db_escape("$h: $v\n"); + } + } + return "INSERT INTO `" . bb2_db_escape($settings['log_table']) . "` + (`ip`, `date`, `request_method`, `request_uri`, `server_protocol`, `http_headers`, `user_agent`, `request_entity`, `key`) VALUES + ('$ip', '$date', '$request_method', '$request_uri', '$server_protocol', '$headers', '$user_agent', '$request_entity', '$key')"; +} + +// Kill 'em all! +function bb2_banned($settings, $package, $key, $previous_key=false) +{ + // Some spambots hit too hard. Slow them down a bit. + sleep(2); + + require_once(BB2_CORE . "/banned.inc.php"); + bb2_display_denial($settings, $key, $previous_key); + bb2_log_denial($settings, $package, $key, $previous_key); + if (is_callable('bb2_banned_callback')) { + bb2_banned_callback($settings, $package, $key); + } + // Penalize the spammers some more + require_once(BB2_CORE . "/housekeeping.inc.php"); + bb2_housekeeping($settings, $package); + die(); +} + +function bb2_approved($settings, $package) +{ + // Dirk wanted this + if (is_callable('bb2_approved_callback')) { + bb2_approved_callback($settings, $package); + } + + // Decide what to log on approved requests. + if (($settings['verbose'] && $settings['logging']) || empty($package['user_agent'])) { + bb2_db_query(bb2_insert($settings, $package, "00000000")); + } +} + +// Check the results of a particular test; see below for usage +// Returns FALSE if test passed (yes this is backwards) +function bb2_test($settings, $package, $result) +{ + if ($result !== FALSE) + { + bb2_banned($settings, $package, $result); + return TRUE; + } + return FALSE; +} + + +// Let God sort 'em out! +function bb2_start($settings) +{ + // Gather up all the information we need, first of all. + $headers = bb2_load_headers(); + // Postprocess the headers to mixed-case + // FIXME: get the world to stop using PHP as CGI + $headers_mixed = array(); + foreach ($headers as $h => $v) { + $headers_mixed[uc_all($h)] = $v; + } + + // We use these frequently. Keep a copy close at hand. + $ip = $_SERVER['REMOTE_ADDR']; + $request_method = $_SERVER['REQUEST_METHOD']; + $request_uri = $_SERVER['REQUEST_URI']; + $server_protocol = $_SERVER['SERVER_PROTOCOL']; + @$user_agent = $_SERVER['HTTP_USER_AGENT']; + + // Reconstruct the HTTP entity, if present. + $request_entity = array(); + if (!strcasecmp($request_method, "POST") || !strcasecmp($request_method, "PUT")) { + foreach ($_POST as $h => $v) { + $request_entity[$h] = $v; + } + } + + $package = array('ip' => $ip, 'headers' => $headers, 'headers_mixed' => $headers_mixed, 'request_method' => $request_method, 'request_uri' => $request_uri, 'server_protocol' => $server_protocol, 'request_entity' => $request_entity, 'user_agent' => $user_agent, 'is_browser' => false); + + // Please proceed to the security checkpoint and have your + // identification and boarding pass ready. + + // First check the whitelist + require_once(BB2_CORE . "/whitelist.inc.php"); + if (!bb2_whitelist($package)) { + // Now check the blacklist + require_once(BB2_CORE . "/blacklist.inc.php"); + bb2_test($settings, $package, bb2_blacklist($package)); + + // Check the http:BL + require_once(BB2_CORE . "/blackhole.inc.php"); + bb2_test($settings, $package, bb2_httpbl($settings, $package)); + + // Check for common stuff + require_once(BB2_CORE . "/common_tests.inc.php"); + bb2_test($settings, $package, bb2_protocol($settings, $package)); + bb2_test($settings, $package, bb2_cookies($settings, $package)); + bb2_test($settings, $package, bb2_misc_headers($settings, $package)); + + // Specific checks + @$ua = $headers_mixed['User-Agent']; + // MSIE checks + if (stripos($ua, "MSIE") !== FALSE) { + $package['is_browser'] = true; + if (stripos($ua, "Opera") !== FALSE) { + require_once(BB2_CORE . "/opera.inc.php"); + bb2_test($settings, $package, bb2_opera($package)); + } else { + require_once(BB2_CORE . "/msie.inc.php"); + bb2_test($settings, $package, bb2_msie($package)); + } + } elseif (stripos($ua, "Konqueror") !== FALSE) { + $package['is_browser'] = true; + require_once(BB2_CORE . "/konqueror.inc.php"); + bb2_test($settings, $package, bb2_konqueror($package)); + } elseif (stripos($ua, "Opera") !== FALSE) { + $package['is_browser'] = true; + require_once(BB2_CORE . "/opera.inc.php"); + bb2_test($settings, $package, bb2_opera($package)); + } elseif (stripos($ua, "Safari") !== FALSE) { + $package['is_browser'] = true; + require_once(BB2_CORE . "/safari.inc.php"); + bb2_test($settings, $package, bb2_safari($package)); + } elseif (stripos($ua, "Lynx") !== FALSE) { + $package['is_browser'] = true; + require_once(BB2_CORE . "/lynx.inc.php"); + bb2_test($settings, $package, bb2_lynx($package)); + } elseif (stripos($ua, "MovableType") !== FALSE) { + require_once(BB2_CORE . "/movabletype.inc.php"); + bb2_test($settings, $package, bb2_movabletype($package)); + } elseif (stripos($ua, "msnbot") !== FALSE || stripos($ua, "MS Search") !== FALSE) { + require_once(BB2_CORE . "/msnbot.inc.php"); + bb2_test($settings, $package, bb2_msnbot($package)); + } elseif (stripos($ua, "Googlebot") !== FALSE || stripos($ua, "Mediapartners-Google") !== FALSE) { + require_once(BB2_CORE . "/google.inc.php"); + bb2_test($settings, $package, bb2_google($package)); + } elseif (stripos($ua, "Mozilla") !== FALSE && stripos($ua, "Mozilla") == 0) { + $package['is_browser'] = true; + require_once(BB2_CORE . "/mozilla.inc.php"); + bb2_test($settings, $package, bb2_mozilla($package)); + } + + // More intensive screening applies to POST requests + if (!strcasecmp('POST', $package['request_method'])) { + require_once(BB2_CORE . "/post.inc.php"); + bb2_test($settings, $package, bb2_post($settings, $package)); + } + } + + // Last chance screening. + require_once(BB2_CORE . "/screener.inc.php"); + bb2_screener($settings, $package); + + // And that's about it. + bb2_approved($settings, $package); + return true; +} +?> diff --git a/bad-behavior/bad-behavior/functions.inc.php b/bad-behavior/bad-behavior/functions.inc.php new file mode 100644 index 0000000..22e8882 --- /dev/null +++ b/bad-behavior/bad-behavior/functions.inc.php @@ -0,0 +1,70 @@ +$word) { + $temp[$key] = ucfirst(strtolower($word)); + } + return join ('', $temp); +} + +// Determine if an IP address resides in a CIDR netblock or netblocks. +function match_cidr($addr, $cidr) { + $output = false; + + if (is_array($cidr)) { + foreach ($cidr as $cidrlet) { + if (match_cidr($addr, $cidrlet)) { + $output = true; + } + } + } else { + @list($ip, $mask) = explode('/', $cidr); + if (!$mask) $mask = 32; + $mask = pow(2,32) - pow(2, (32 - $mask)); + $output = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask)); + } + return $output; +} + +// Obtain all the HTTP headers. +// NB: on PHP-CGI we have to fake it out a bit, since we can't get the REAL +// headers. Run PHP as Apache 2.0 module if possible for best results. +function bb2_load_headers() { + if (!is_callable('getallheaders')) { + $headers = array(); + foreach ($_SERVER as $h => $v) + if (ereg('HTTP_(.+)', $h, $hp)) + $headers[str_replace("_", "-", uc_all($hp[1]))] = $v; + } else { + $headers = getallheaders(); + } + return $headers; +} + +?> diff --git a/bad-behavior/bad-behavior/google.inc.php b/bad-behavior/bad-behavior/google.inc.php new file mode 100644 index 0000000..956bdb4 --- /dev/null +++ b/bad-behavior/bad-behavior/google.inc.php @@ -0,0 +1,13 @@ + diff --git a/bad-behavior/bad-behavior/housekeeping.inc.php b/bad-behavior/bad-behavior/housekeeping.inc.php new file mode 100644 index 0000000..b837ee0 --- /dev/null +++ b/bad-behavior/bad-behavior/housekeeping.inc.php @@ -0,0 +1,16 @@ + diff --git a/bad-behavior/bad-behavior/index.html b/bad-behavior/bad-behavior/index.html new file mode 100644 index 0000000..e69de29 diff --git a/bad-behavior/bad-behavior/konqueror.inc.php b/bad-behavior/bad-behavior/konqueror.inc.php new file mode 100644 index 0000000..3a84dba --- /dev/null +++ b/bad-behavior/bad-behavior/konqueror.inc.php @@ -0,0 +1,17 @@ + diff --git a/bad-behavior/bad-behavior/lynx.inc.php b/bad-behavior/bad-behavior/lynx.inc.php new file mode 100644 index 0000000..a59ba10 --- /dev/null +++ b/bad-behavior/bad-behavior/lynx.inc.php @@ -0,0 +1,13 @@ + diff --git a/bad-behavior/bad-behavior/movabletype.inc.php b/bad-behavior/bad-behavior/movabletype.inc.php new file mode 100644 index 0000000..b15fe9c --- /dev/null +++ b/bad-behavior/bad-behavior/movabletype.inc.php @@ -0,0 +1,14 @@ + diff --git a/bad-behavior/bad-behavior/mozilla.inc.php b/bad-behavior/bad-behavior/mozilla.inc.php new file mode 100644 index 0000000..a1cac82 --- /dev/null +++ b/bad-behavior/bad-behavior/mozilla.inc.php @@ -0,0 +1,19 @@ + diff --git a/bad-behavior/bad-behavior/msie.inc.php b/bad-behavior/bad-behavior/msie.inc.php new file mode 100644 index 0000000..8709d6b --- /dev/null +++ b/bad-behavior/bad-behavior/msie.inc.php @@ -0,0 +1,25 @@ + diff --git a/bad-behavior/bad-behavior/msnbot.inc.php b/bad-behavior/bad-behavior/msnbot.inc.php new file mode 100644 index 0000000..0341da1 --- /dev/null +++ b/bad-behavior/bad-behavior/msnbot.inc.php @@ -0,0 +1,13 @@ + diff --git a/bad-behavior/bad-behavior/opera.inc.php b/bad-behavior/bad-behavior/opera.inc.php new file mode 100644 index 0000000..e29a8c7 --- /dev/null +++ b/bad-behavior/bad-behavior/opera.inc.php @@ -0,0 +1,13 @@ + diff --git a/bad-behavior/bad-behavior/post.inc.php b/bad-behavior/bad-behavior/post.inc.php new file mode 100644 index 0000000..ecbfc7e --- /dev/null +++ b/bad-behavior/bad-behavior/post.inc.php @@ -0,0 +1,80 @@ + $value) { + $pos = strpos($key, " document.write"); + if ($pos !== FALSE) { + return "dfd9b1ad"; + } + } + + // If Referer exists, it should refer to a page on our site + if (array_key_exists('Referer', $package['headers_mixed']) && stripos($package['headers_mixed']['Referer'], $package['headers_mixed']['Host']) === FALSE) { + return "cd361abb"; + } + + // Screen by cookie/JavaScript form add + if (isset($_COOKIE[BB2_COOKIE])) { + $screener1 = explode(" ", $_COOKIE[BB2_COOKIE]); + } else { + $screener1 = array(0); + } + if (isset($_POST[BB2_COOKIE])) { + $screener2 = explode(" ", $_POST[BB2_COOKIE]); + } else { + $screener2 = array(0); + } + $screener = max($screener1[0], $screener2[0]); + + if ($screener > 0) { + // Posting too fast? 5 sec + // FIXME: even 5 sec is too intrusive + // if ($screener + 5 > time()) + // return "408d7e72"; + // Posting too slow? 48 hr + if ($screener + 172800 < time()) + return "b40c8ddc"; + + // Screen by IP address + $ip = ip2long($package['ip']); + $ip_screener = ip2long($screener[1]); +// FIXME: This is b0rked, but why? +// if ($ip && $ip_screener && abs($ip_screener - $ip) > 256) +// return "c1fa729b"; + + if (!empty($package['headers_mixed']['X-Forwarded-For'])) { + $ip = $package['headers_mixed']['X-Forwarded-For']; + } + // Screen for user agent changes + // User connected previously with blank user agent +// $q = bb2_db_query("SELECT `ip` FROM " . $settings['log_table'] . " WHERE (`ip` = '" . $package['ip'] . "' OR `ip` = '" . $screener[1] . "') AND `user_agent` != '" . $package['user_agent'] . "' AND `date` > DATE_SUB('" . bb2_db_date() . "', INTERVAL 5 MINUTE)"); + // Damnit, too many ways for this to fail :( +// if ($q !== FALSE && $q != NULL && bb2_db_num_rows($q) > 0) +// return "799165c2"; + } + + return false; +} + +?> diff --git a/bad-behavior/bad-behavior/responses.inc.php b/bad-behavior/bad-behavior/responses.inc.php new file mode 100644 index 0000000..56fdf95 --- /dev/null +++ b/bad-behavior/bad-behavior/responses.inc.php @@ -0,0 +1,48 @@ + array('response' => 200, 'explanation' => '', 'log' => 'Permitted'), + '136673cd' => array('response' => 403, 'explanation' => 'Your Internet Protocol address is listed on a blacklist of addresses involved in malicious or illegal activity. See the listing below for more details on specific blacklists and removal procedures.', 'log' => 'IP address found on external blacklist'), + '17566707' => array('response' => 403, 'explanation' => 'An invalid request was received from your browser. This may be caused by a malfunctioning proxy server or browser privacy software.', 'log' => 'Required header \'Accept\' missing'), + '17f4e8c8' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'User-Agent was found on blacklist'), + '21f11d3f' => array('response' => 403, 'explanation' => 'An invalid request was received. You claimed to be a mobile Web device, but you do not actually appear to be a mobile Web device.', 'log' => 'User-Agent claimed to be AvantGo, claim appears false'), + '2b021b1f' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'IP address found on http:BL blacklist'), + '2b90f772' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. If you are using the Opera browser, then Opera must appear in your user agent.', 'log' => 'Connection: TE present, not supported by MSIE'), + '35ea7ffa' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Check your browser\'s language and locale settings.', 'log' => 'Invalid language specified'), + '408d7e72' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'POST comes too quickly after GET'), + '41feed15' => array('response' => 400, 'explanation' => 'An invalid request was received. This may be caused by a malfunctioning proxy server. Bypass the proxy server and connect directly, or contact your proxy server administrator.', 'log' => 'Header \'Pragma\' without \'Cache-Control\' prohibited for HTTP/1.1 requests'), + '45b35e30' => array('response' => 403, 'explanation' => 'An invalid request was received from your browser. This may be caused by a malfunctioning proxy server or browser privacy software.', 'log' => 'Header \'Referer\' is corrupt'), + '57796684' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'Prohibited header \'X-Aaaaaaaaaa\' or \'X-Aaaaaaaaaaaa\' present'), + '582ec5e4' => array('response' => 400, 'explanation' => 'An invalid request was received. If you are using a proxy server, bypass the proxy server or contact your proxy server administrator. This may also be caused by a bug in the Opera web browser.', 'log' => '"Header \'TE\' present but TE not specified in \'Connection\' header'), + '69920ee5' => array('response' => 403, 'explanation' => 'An invalid request was received from your browser. This may be caused by a malfunctioning proxy server or browser privacy software.', 'log' => 'Header \'Referer\' present but blank'), + '6c502ff1' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'Bot not fully compliant with RFC 2965'), + '799165c2' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'Rotating user-agents detected'), + '7a06532b' => array('response' => 400, 'explanation' => 'An invalid request was received from your browser. This may be caused by a malfunctioning proxy server or browser privacy software.', 'log' => 'Required header \'Accept-Encoding\' missing'), + '7ad04a8a' => array('response' => 400, 'explanation' => 'The automated program you are using is not permitted to access this server. Please use a different program or a standard Web browser.', 'log' => 'Prohibited header \'Range\' present'), + '7d12528e' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'Prohibited header \'Range\' or \'Content-Range\' in POST request'), + '939a6fbb' => array('response' => 403, 'explanation' => 'The proxy server you are using is not permitted to access this server. Please bypass the proxy server, or contact your proxy server administrator.', 'log' => 'Banned proxy server in use'), + '9c9e4979' => array('response' => 403, 'explanation' => 'The proxy server you are using is not permitted to access this server. Please bypass the proxy server, or contact your proxy server administrator.', 'log' => 'Prohibited header \'via\' present'), + 'a0105122' => array('response' => 417, 'explanation' => 'Expectation failed. Please retry your request.', 'log' => 'Header \'Expect\' prohibited; resend without Expect'), + 'a1084bad' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'User-Agent claimed to be MSIE, with invalid Windows version'), + 'a52f0448' => array('response' => 400, 'explanation' => 'An invalid request was received. This may be caused by a malfunctioning proxy server or browser privacy software. If you are using a proxy server, bypass the proxy server or contact your proxy server administrator.', 'log' => 'Header \'Connection\' contains invalid values'), + 'b40c8ddc' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, close your browser, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'POST more than two days after GET'), + 'b7830251' => array('response' => 400, 'explanation' => 'Your proxy server sent an invalid request. Please contact the proxy server administrator to have this problem fixed.', 'log' => 'Prohibited header \'Proxy-Connection\' present'), + 'b9cc1d86' => array('response' => 403, 'explanation' => 'The proxy server you are using is not permitted to access this server. Please bypass the proxy server, or contact your proxy server administrator.', 'log' => 'Prohibited header \'X-Aaaaaaaaaa\' or \'X-Aaaaaaaaaaaa\' present'), + 'c1fa729b' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'Use of rotating proxy servers detected'), + 'cd361abb' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Data may not be posted from offsite forms.', 'log' => 'Referer did not point to a form on this site'), + 'd60b87c7' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, please remove any viruses or spyware from your computer.', 'log' => 'Trackback received via proxy server'), + 'dfd9b1ad' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'Request contained a malicious JavaScript or SQL injection attack'), + 'e4de0453' => array('response' => 403, 'explanation' => 'An invalid request was received. You claimed to be a major search engine, but you do not appear to actually be a major search engine.', 'log' => 'User-Agent claimed to be msnbot, claim appears to be false'), + 'e87553e1' => array('response' => 403, 'explanation' => 'You do not have permission to access this server.', 'log' => 'I know you and I don\'t like you, dirty spammer.'), + 'f0dcb3fd' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. Before trying again, run anti-virus and anti-spyware software and remove any viruses and spyware from your computer.', 'log' => 'Web browser attempted to send a trackback'), + 'f1182195' => array('response' => 403, 'explanation' => 'An invalid request was received. You claimed to be a major search engine, but you do not appear to actually be a major search engine.', 'log' => 'User-Agent claimed to be Googlebot, claim appears to be false.'), + 'f9f2b8b9' => array('response' => 403, 'explanation' => 'You do not have permission to access this server. This may be caused by a malfunctioning proxy server or browser privacy software.', 'log' => 'A User-Agent is required but none was provided.'), + ); + + if (array_key_exists($key, $bb2_responses)) return $bb2_responses[$key]; + return array('00000000'); +} +?> diff --git a/bad-behavior/bad-behavior/safari.inc.php b/bad-behavior/bad-behavior/safari.inc.php new file mode 100644 index 0000000..523bdd5 --- /dev/null +++ b/bad-behavior/bad-behavior/safari.inc.php @@ -0,0 +1,13 @@ + diff --git a/bad-behavior/bad-behavior/screener.inc.php b/bad-behavior/bad-behavior/screener.inc.php new file mode 100644 index 0000000..55da198 --- /dev/null +++ b/bad-behavior/bad-behavior/screener.inc.php @@ -0,0 +1,63 @@ + + + "; +} + +function bb2_screener($settings, $package) +{ + $cookie_name = BB2_COOKIE; + + // Set up a simple cookie + $screener = array(time(), $package['ip']); + if (isset($package['headers_mixed']['X-Forwarded-For'])) { + array_push($screener, $package['headers_mixed']['X-Forwarded-For']); + } + if (isset($package['headers_mixed']['Client-Ip'])) { + array_push($screener, $package['headers_mixed']['Client-Ip']); + } + + $cookie_value = implode(" ", $screener); + + bb2_screener_cookie($settings, $package, BB2_COOKIE, $cookie_value); + bb2_screener_javascript($settings, $package, BB2_COOKIE, $cookie_value); +} +?> diff --git a/bad-behavior/bad-behavior/trackback.inc.php b/bad-behavior/bad-behavior/trackback.inc.php new file mode 100644 index 0000000..bd47e51 --- /dev/null +++ b/bad-behavior/bad-behavior/trackback.inc.php @@ -0,0 +1,18 @@ + diff --git a/bad-behavior/bad-behavior/version.inc.php b/bad-behavior/bad-behavior/version.inc.php new file mode 100644 index 0000000..b10353f --- /dev/null +++ b/bad-behavior/bad-behavior/version.inc.php @@ -0,0 +1,3 @@ + diff --git a/bad-behavior/bad-behavior/whitelist.inc.php b/bad-behavior/bad-behavior/whitelist.inc.php new file mode 100644 index 0000000..ebd9200 --- /dev/null +++ b/bad-behavior/bad-behavior/whitelist.inc.php @@ -0,0 +1,58 @@ + diff --git a/bad-behavior/index.html b/bad-behavior/index.html new file mode 100644 index 0000000..e69de29 diff --git a/gettext.php b/gettext.php new file mode 100644 index 0000000..de462f5 --- /dev/null +++ b/gettext.php @@ -0,0 +1,401 @@ +. + Copyright (c) 2005 Nico Kaiser + + This file is part of PHP-gettext. + + PHP-gettext is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PHP-gettext is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PHP-gettext; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/** + * Provides a simple gettext replacement that works independently from + * the system's gettext abilities. + * It can read MO files and use them for translating strings. + * The files are passed to gettext_reader as a Stream (see streams.php) + * + * This version has the ability to cache all strings and translations to + * speed up the string lookup. + * While the cache is enabled by default, it can be switched off with the + * second parameter in the constructor (e.g. whenusing very large MO files + * that you don't want to keep in memory) + */ +class gettext_reader { + //public: + var $error = 0; // public variable that holds error code (0 if no error) + + //private: + var $BYTEORDER = 0; // 0: low endian, 1: big endian + var $STREAM = NULL; + var $short_circuit = false; + var $enable_cache = false; + var $originals = NULL; // offset of original table + var $translations = NULL; // offset of translation table + var $pluralheader = NULL; // cache header field for plural forms + var $select_string_function = NULL; // cache function, which chooses plural forms + var $total = 0; // total string count + var $table_originals = NULL; // table for original strings (offsets) + var $table_translations = NULL; // table for translated strings (offsets) + var $cache_translations = NULL; // original -> translation mapping + + + /* Methods */ + + + /** + * Reads a 32bit Integer from the Stream + * + * @access private + * @return Integer from the Stream + */ + function readint() { + if ($this->BYTEORDER == 0) { + // low endian + $low_end = unpack('V', $this->STREAM->read(4)); + return array_shift($low_end); + } else { + // big endian + $big_end = unpack('N', $this->STREAM->read(4)); + return array_shift($big_end); + } + } + + /** + * Reads an array of Integers from the Stream + * + * @param int count How many elements should be read + * @return Array of Integers + */ + function readintarray($count) { + if ($this->BYTEORDER == 0) { + // low endian + return unpack('V'.$count, $this->STREAM->read(4 * $count)); + } else { + // big endian + return unpack('N'.$count, $this->STREAM->read(4 * $count)); + } + } + + /** + * Constructor + * + * @param object Reader the StreamReader object + * @param boolean enable_cache Enable or disable caching of strings (default on) + */ + function gettext_reader($Reader, $enable_cache = true) { + // If there isn't a StreamReader, turn on short circuit mode. + if (! $Reader || isset($Reader->error) ) { + $this->short_circuit = true; + return; + } + + // Caching can be turned off + $this->enable_cache = $enable_cache; + + // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 + $MAGIC1 = (int) - 1794895138; + // $MAGIC2 = (int)0xde120495; //bug + $MAGIC2 = (int) - 569244523; + // 64-bit fix + $MAGIC3 = (int) 2500072158; + + $this->STREAM = $Reader; + $magic = $this->readint(); + if ($magic == $MAGIC1 || $magic == $MAGIC3) { // to make sure it works for 64-bit platforms + $this->BYTEORDER = 0; + } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) { + $this->BYTEORDER = 1; + } else { + $this->error = 1; // not MO file + return false; + } + + // FIXME: Do we care about revision? We should. + $revision = $this->readint(); + + $this->total = $this->readint(); + $this->originals = $this->readint(); + $this->translations = $this->readint(); + } + + /** + * Loads the translation tables from the MO file into the cache + * If caching is enabled, also loads all strings into a cache + * to speed up translation lookups + * + * @access private + */ + function load_tables() { + if (is_array($this->cache_translations) && + is_array($this->table_originals) && + is_array($this->table_translations)) + return; + + /* get original and translations tables */ + $this->STREAM->seekto($this->originals); + $this->table_originals = $this->readintarray($this->total * 2); + $this->STREAM->seekto($this->translations); + $this->table_translations = $this->readintarray($this->total * 2); + + if ($this->enable_cache) { + $this->cache_translations = array (); + /* read all strings in the cache */ + for ($i = 0; $i < $this->total; $i++) { + $this->STREAM->seekto($this->table_originals[$i * 2 + 2]); + $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); + $this->STREAM->seekto($this->table_translations[$i * 2 + 2]); + $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); + $this->cache_translations[$original] = $translation; + } + } + } + + /** + * Returns a string from the "originals" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + function get_original_string($num) { + $length = $this->table_originals[$num * 2 + 1]; + $offset = $this->table_originals[$num * 2 + 2]; + if (! $length) + return ''; + $this->STREAM->seekto($offset); + $data = $this->STREAM->read($length); + return (string)$data; + } + + /** + * Returns a string from the "translations" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + function get_translation_string($num) { + $length = $this->table_translations[$num * 2 + 1]; + $offset = $this->table_translations[$num * 2 + 2]; + if (! $length) + return ''; + $this->STREAM->seekto($offset); + $data = $this->STREAM->read($length); + return (string)$data; + } + + /** + * Binary search for string + * + * @access private + * @param string string + * @param int start (internally used in recursive function) + * @param int end (internally used in recursive function) + * @return int string number (offset in originals table) + */ + function find_string($string, $start = -1, $end = -1) { + if (($start == -1) or ($end == -1)) { + // find_string is called with only one parameter, set start end end + $start = 0; + $end = $this->total; + } + if (abs($start - $end) <= 1) { + // We're done, now we either found the string, or it doesn't exist + $txt = $this->get_original_string($start); + if ($string == $txt) + return $start; + else + return -1; + } else if ($start > $end) { + // start > end -> turn around and start over + return $this->find_string($string, $end, $start); + } else { + // Divide table in two parts + $half = (int)(($start + $end) / 2); + $cmp = strcmp($string, $this->get_original_string($half)); + if ($cmp == 0) + // string is exactly in the middle => return it + return $half; + else if ($cmp < 0) + // The string is in the upper half + return $this->find_string($string, $start, $half); + else + // The string is in the lower half + return $this->find_string($string, $half, $end); + } + } + + /** + * Translates a string + * + * @access public + * @param string string to be translated + * @return string translated string (or original, if not found) + */ + function translate($string) { + if ($this->short_circuit) + return $string; + $this->load_tables(); + + if ($this->enable_cache) { + // Caching enabled, get translated string from cache + if (array_key_exists($string, $this->cache_translations)) + return $this->cache_translations[$string]; + else + return $string; + } else { + // Caching not enabled, try to find string + $num = $this->find_string($string); + if ($num == -1) + return $string; + else + return $this->get_translation_string($num); + } + } + + /** + * Get possible plural forms from MO header + * + * @access private + * @return string plural form header + */ + function get_plural_forms() { + // lets assume message number 0 is header + // this is true, right? + $this->load_tables(); + + // cache header field for plural forms + if (! is_string($this->pluralheader)) { + if ($this->enable_cache) { + $header = $this->cache_translations[""]; + } else { + $header = $this->get_translation_string(0); + } + $header .= "\n"; //make sure our regex matches + if (eregi("plural-forms: ([^\n]*)\n", $header, $regs)) + $expr = $regs[1]; + else + $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; + + // add parentheses + // important since PHP's ternary evaluates from left to right + $expr.= ';'; + $res= ''; + $p= 0; + for ($i= 0; $i < strlen($expr); $i++) { + $ch= $expr[$i]; + switch ($ch) { + case '?': + $res.= ' ? ('; + $p++; + break; + case ':': + $res.= ') : ('; + break; + case ';': + $res.= str_repeat( ')', $p) . ';'; + $p= 0; + break; + default: + $res.= $ch; + } + } + $this->pluralheader = $res; + } + + return $this->pluralheader; + } + + /** + * Detects which plural form to take + * + * @access private + * @param n count + * @return int array index of the right plural form + */ + function select_string($n) { + if (is_null($this->select_string_function)) { + $string = $this->get_plural_forms(); + if (preg_match("/nplurals\s*=\s*(\d+)\s*\;\s*plural\s*=\s*(.*?)\;+/", $string, $matches)) { + $nplurals = $matches[1]; + $expression = $matches[2]; + $expression = str_replace("n", '$n', $expression); + } else { + $nplurals = 2; + $expression = ' $n == 1 ? 0 : 1 '; + } + $func_body = " + \$plural = ($expression); + return (\$plural <= $nplurals)? \$plural : \$plural - 1;"; + $this->select_string_function = create_function('$n', $func_body); + } + return call_user_func($this->select_string_function, $n); + } + + /** + * Plural version of gettext + * + * @access public + * @param string single + * @param string plural + * @param string number + * @return translated plural form + */ + function ngettext($single, $plural, $number) { + if ($this->short_circuit) { + if ($number != 1) + return $plural; + else + return $single; + } + + // find out the appropriate form + $select = $this->select_string($number); + + // this should contains all strings separated by NULLs + $key = $single.chr(0).$plural; + + + if ($this->enable_cache) { + if (! array_key_exists($key, $this->cache_translations)) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->cache_translations[$key]; + $list = explode(chr(0), $result); + return $list[$select]; + } + } else { + $num = $this->find_string($key); + if ($num == -1) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->get_translation_string($num); + $list = explode(chr(0), $result); + return $list[$select]; + } + } + } + +} + +?> diff --git a/local/tighturl.api.tmpl b/local/tighturl.api.tmpl new file mode 100644 index 0000000..c2adebb --- /dev/null +++ b/local/tighturl.api.tmpl @@ -0,0 +1 @@ +

This is the TightURL API documentation. Please stand by.

diff --git a/local/tighturl.config.inc.php b/local/tighturl.config.inc.php new file mode 100644 index 0000000..9edabd7 --- /dev/null +++ b/local/tighturl.config.inc.php @@ -0,0 +1,65 @@ + + */ + +/** + * MySQL variables + */ +$dbhost = "localhost"; +$dbuser = "dbuser"; +$dbpass = "dbpass"; +$dbname = "tighturl"; +$dbtable = "tighturl"; + +/** + * Site + */ +$sitenames = array("example.com","www.example.com"); + +/** + * Operating modes + */ +$FOFMethod = FALSE; //FALSE=Use full URL path or mod_rewrite, TRUE=Use 404 Method to compress URLs +$antiabuse = TRUE; //0=do not run network checks or other anti-abuse measures on submitted URIs, 1=run tests + //Must be set to something other than true to deactivate +$netchecks = TRUE; //0=do not run network checks, 1=run network tests as part of anti-abuse system +$mustexist = TRUE; //0=do not check URLs to see if they exist, 1=check URLs if netchecks are on. +$BB2=TRUE; //0=do not use Bad Behavior, 1=Protect your site with Bad Behavior + +/** + * URIBL variables + */ +$uribl = array("multi.surbl.org", "black.uribl.com"); +$uriblurl = array("www.surbl.org", "www.uribl.com"); + +/** Text strings and style variables + * + */ +$svcname = "URLSquisher"; +$verbtext = "Squish"; +$pasttext = "Squished"; +$tagline = "Squish long URLs to make short ones"; +$headcolor = "#006600"; +$tablecolor = "#00CC99"; +$copystart = date("Y"); +$copyrightholder = "SquishURL Enterprises"; +$complain = true; +$validschemes = "(http|https|ftp|sftp)"; +$forbid = "\.(cmd|bat|exe|scr|pif|vbs|js|pif|msi|cdr)"; + +// Validate IP addresses + +$validipv4pattern = ":\/\/(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\." + . "(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])" + . "\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\/*"; + +// Reserved URLs +$ReservedURL = array("x", "rest", "xmlrpc", "soap", "xml", "atom", "rss", "blog", + "faq", "help", "about", "api", "code", "source", "docs", + "cvs", "arch", "url", "admin", "setup", "svn", "project", "abuse", "exploited"); +?> diff --git a/local/tighturl.faq.tmpl b/local/tighturl.faq.tmpl new file mode 100644 index 0000000..bf7dbac --- /dev/null +++ b/local/tighturl.faq.tmpl @@ -0,0 +1 @@ +

This is the TightURL FAQ. Please stand by.

diff --git a/local/tighturl.main.tmpl b/local/tighturl.main.tmpl new file mode 100644 index 0000000..00d41f1 --- /dev/null +++ b/local/tighturl.main.tmpl @@ -0,0 +1,12 @@ + Long URLs that don't fit on one line often aren't clickable after you + e-mail them. They can also be tedious to type out on + Web-enabled cellphones. $SVCNAME squeezes very long URLs + like this:
+
+     http://www.cspan.org/watch/index.asp?Cat=TV&Code=CS&ShowVidDays=30&ShowVidDesc=&ArchiveDays=30
+
+ Down to a $PASTTEXT, compact URL + like this:
+
+     http://$HOST$SELF$PARMda42
+
diff --git a/local/tighturl.save.tmpl b/local/tighturl.save.tmpl new file mode 100644 index 0000000..8c206a8 --- /dev/null +++ b/local/tighturl.save.tmpl @@ -0,0 +1,16 @@ + Your $PASTTEXT URL is: $TIGHTURL (opens in new window)
+
+ That's $TIGHTURLLEN characters, which is $DIFF shorter than the $URLLEN characters of: $URL. +
+ +
+ + diff --git a/local/tighturl.setup.tmpl b/local/tighturl.setup.tmpl new file mode 100644 index 0000000..181d3f8 --- /dev/null +++ b/local/tighturl.setup.tmpl @@ -0,0 +1,25 @@ +$__HTML :
+$__PARM :
+$__URLLEN :
+$__URL :
+$__INPUT :
+$__TIGHTURLVER :
+$__TIGHTURLLEN :
+$__TIGHTURL :
+$__DIFF :
+$__SVCNAME :
+$__HEADCOLOR :
+$__TABLECOLOR :
+$__TAGLINE :
+$__CPASTTEXT :
+$__PASTTEXT :
+$__VERBTEXT :
+$__CVERBTEXT :
+$__COPYRIGHTHOLDER :
+$__COPYRIGHT :
+$__URIBLS :
+$__HOST :
+$__SELF :
+$__BBSTATS :
+$____VARIABLENAME : Allows you to print $VARIABLENAME literally. :
+ diff --git a/local/tighturl.tmpl b/local/tighturl.tmpl new file mode 100644 index 0000000..0ece406 --- /dev/null +++ b/local/tighturl.tmpl @@ -0,0 +1,38 @@ + + + $SVCNAME + + + $SVCNAME :: $TAGLINE +

+ $HTML +

+

+

+ + + + + + + +
Enter a URL to $VERBTEXT (it must exist):
+ +
+
+
+ $SVCNAME Bookmarklet:  Drag the link + $SVCNAME into your browser + toolbar.  Then you can create a $SVCNAME URL for any page open in your browser window + simply by clicking on the bookmarklet.

+
+

+ ©$COPYRIGHT $COPYRIGHTHOLDER.
$SVCNAME uses $URIBLS to + protect against redirector abuse by spammers.

+ $SVCNAME is powered by TightURL $TIGHTURLVER, + which is Free Software.$BBSTATS

+
+

+ + diff --git a/streams.php b/streams.php new file mode 100644 index 0000000..830997d --- /dev/null +++ b/streams.php @@ -0,0 +1,192 @@ +. + + This file is part of PHP-gettext. + + PHP-gettext is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PHP-gettext is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PHP-gettext; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + + +// Simple class to wrap file streams, string streams, etc. +// seek is essential, and it should be byte stream +class StreamReader { + // should return a string [FIXME: perhaps return array of bytes?] + function read($bytes) { + return false; + } + + // should return new position + function seekto($position) { + return false; + } + + // returns current position + function currentpos() { + return false; + } + + // returns length of entire stream (limit for seekto()s) + function length() { + return false; + } +} + +class StringReader { + var $_pos; + var $_str; + + function StringReader($str='') { + $this->_str = $str; + $this->_pos = 0; + // If string functions are overloaded, we need to use the mb versions + $this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr'); + } + + function _substr($string, $start, $length) { + if ($this->is_overloaded) { + return mb_substr($string,$start,$length,'ascii'); + } else { + return substr($string,$start,$length); + } + } + + function _strlen($string) { + if ($this->is_overloaded) { + return mb_strlen($string,'ascii'); + } else { + return strlen($string); + } + } + + function read($bytes) { + $data = $this->_substr($this->_str, $this->_pos, $bytes); + $this->_pos += $bytes; + if ($this->_strlen($this->_str)<$this->_pos) + $this->_pos = $this->_strlen($this->_str); + + return $data; + } + + function seekto($pos) { + $this->_pos = $pos; + if ($this->_strlen($this->_str)<$this->_pos) + $this->_pos = $this->_strlen($this->_str); + return $this->_pos; + } + + function currentpos() { + return $this->_pos; + } + + function length() { + return $this->_strlen($this->_str); + } +} + + +class FileReader { + var $_pos; + var $_fd; + var $_length; + + function FileReader($filename) { + if (file_exists($filename)) { + + $this->_length=filesize($filename); + $this->_pos = 0; + $this->_fd = fopen($filename,'rb'); + if (!$this->_fd) { + $this->error = 3; // Cannot read file, probably permissions + return false; + } + } else { + $this->error = 2; // File doesn't exist + return false; + } + } + + function read($bytes) { + if ($bytes) { + fseek($this->_fd, $this->_pos); + + // PHP 5.1.1 does not read more than 8192 bytes in one fread() + // the discussions at PHP Bugs suggest it's the intended behaviour + while ($bytes > 0) { + $chunk = fread($this->_fd, $bytes); + $data .= $chunk; + $bytes -= strlen($chunk); + } + $this->_pos = ftell($this->_fd); + + return $data; + } else return ''; + } + + function seekto($pos) { + fseek($this->_fd, $pos); + $this->_pos = ftell($this->_fd); + return $this->_pos; + } + + function currentpos() { + return $this->_pos; + } + + function length() { + return $this->_length; + } + + function close() { + fclose($this->_fd); + } + +} + +// Preloads entire file in memory first, then creates a StringReader +// over it (it assumes knowledge of StringReader internals) +class CachedFileReader extends StringReader { + function CachedFileReader($filename) { + parent::StringReader(); + + if (file_exists($filename)) { + + $length=filesize($filename); + $fd = fopen($filename,'rb'); + + if (!$fd) { + $this->error = 3; // Cannot read file, probably permissions + return false; + } + $this->_str = fread($fd, $length); + fclose($fd); + + } else { + $this->error = 2; // File doesn't exist + return false; + } + } +} + + +?> diff --git a/tighturl-base10 b/tighturl-base10 new file mode 100755 index 0000000..6432e04 --- /dev/null +++ b/tighturl-base10 @@ -0,0 +1,5 @@ +#!/usr/bin/php + diff --git a/tighturl-base10.php b/tighturl-base10.php new file mode 100644 index 0000000..74f70bc --- /dev/null +++ b/tighturl-base10.php @@ -0,0 +1,22 @@ + + +TightURL ID converter + + +TightURLID(Base-36): ".$_POST[id]."
DBID(decimal): ".base_convert($_POST[id], 36, 10)."

\n"; +} +else +{ +?> +
+ id: + +
+ + + diff --git a/tighturl-base36 b/tighturl-base36 new file mode 100755 index 0000000..c8b2c3d --- /dev/null +++ b/tighturl-base36 @@ -0,0 +1,5 @@ +#!/usr/bin/php + diff --git a/tighturl-base36.php b/tighturl-base36.php new file mode 100644 index 0000000..8e4a176 --- /dev/null +++ b/tighturl-base36.php @@ -0,0 +1,22 @@ + + +Convert base 10 to TightURL ID + + +Base-10(decimal): ".$_POST[decimal]."
TightURL ID(base-36): ".base_convert($_POST[decimal], 10, 36)."

\n"; +} +else +{ +?> +
+ id: + +
+ + + diff --git a/tighturl-install.php b/tighturl-install.php new file mode 100644 index 0000000..f1468c9 --- /dev/null +++ b/tighturl-install.php @@ -0,0 +1,133 @@ + + + + + TightURL Installer + + + +

Finished.

+ +

Your TightURL database has been installed or upgraded as appropriate.

+ +

You must delete (or at least rename) tighturl-install.php in order to run TightURL.

+ + + + + + + + + + TightURL Install/Upgrade + + +

Welcome to the TightURL Installer and Upgrader

+ +

This script will either set up a newly created TightURL database, or upgrade an existing TightURL database +to work with the version of TightURL you just installed. This process will happen automatically once you click +the "install/upgrade now!" button below.

+ +

Before starting, please make sure you've completed the following steps below:
+

    +
  1. BACK UP your TightURL database!!!!
  2. +
  3. Make sure tighturl.config.inc.php has the proper settings for + $dbhost, $dbname, $dbtable, + $dbuser, and $dbpass.
  4. +
+ +
+ + + + +
+

+ +

+
+
+

+ +


+ + +\n \n TightURL Installer/Upgrader\n \n \n"; + echo " " . $errmsg . "
\n"; + echo " \n"; + die; +} + +?> diff --git a/tighturl-killbot.php b/tighturl-killbot.php new file mode 100755 index 0000000..904bab7 --- /dev/null +++ b/tighturl-killbot.php @@ -0,0 +1,27 @@ +#!/usr/bin/php += ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")"); + } + + if (file_exists("tighturl.tltpattern.inc.php")) include("tighturl.tltpattern.inc.php"); + if (file_exists("tighturl.urlpattern.inc.php")) include("tighturl.urlpattern.inc.php"); + if (file_exists("tighturl.ptcpattern.inc.php")) include("tighturl.ptcpattern.inc.php"); + if (file_exists("tighturl.blippattern.inc.php")) include("tighturl.blippattern.inc.php"); + (include("tighturl.lib.inc.php")) or die("Error: cannot load tighturl.lib.inc.php"); + if (file_exists("tighturl.config.inc.php")) include("tighturl.config.inc.php"); + + // Connect to MySQL, open database. + $conn = @mysql_connect($dbhost, $dbuser, $dbpass) or die("Error: Cannot connect to database."); + $db = mysql_select_db($dbname, $conn) or die("Error: Cannot select database. ". mysql_error()); + + // Query records with status normal, and added within last 10 days or records with status normal and more than 7 hits and hit within the last 7 days + TightURL_KillBot("SELECT * FROM $dbtable where status=0 && (DATE_SUB(CURDATE(), INTERVAL 10 DAY) <= adddate || (hits > 7 && DATE_SUB(CURDATE(), INTERVAL 7 DAY) <= lasthit)) ORDER BY id;"); + TightURL_NewReport("SELECT * FROM $dbtable where (status=0) && (checkcount < 2) ORDER BY id;"); + exit; + +?> diff --git a/tighturl.blacklist.tmpl b/tighturl.blacklist.tmpl new file mode 100644 index 0000000..69f2931 --- /dev/null +++ b/tighturl.blacklist.tmpl @@ -0,0 +1,5 @@ + The $SVCNAME redirection URL you have requested has been disabled due to + its appearance on one or more of the URI blacklists this site has used to + detect abuse, including spamming and phishing abuse.

+ + Most likely it was listed on one of the following URI Blacklists: $URIBLS . diff --git a/tighturl.blippattern.inc.php b/tighturl.blippattern.inc.php new file mode 100644 index 0000000..851216d --- /dev/null +++ b/tighturl.blippattern.inc.php @@ -0,0 +1,4 @@ + diff --git a/tighturl.blpattern.inc.php b/tighturl.blpattern.inc.php new file mode 100644 index 0000000..b113546 --- /dev/null +++ b/tighturl.blpattern.inc.php @@ -0,0 +1,19 @@ + diff --git a/tighturl.complaints.tmpl b/tighturl.complaints.tmpl new file mode 100644 index 0000000..6a4e5b3 --- /dev/null +++ b/tighturl.complaints.tmpl @@ -0,0 +1,2 @@ + The $SVCNAME redirection URL you have requested has been disabled due to + excessive abuse complaints. diff --git a/tighturl.lib.inc.php b/tighturl.lib.inc.php new file mode 100644 index 0000000..18f6f91 --- /dev/null +++ b/tighturl.lib.inc.php @@ -0,0 +1,287 @@ += ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")"); + } + + +$validipv4pattern = "/^(http|https|ftp|sftp):\/\/(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\."; +$validipv4pattern .= "(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])"; +$validipv4pattern .= "\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\/*/"; + +// ****** !All overridable configuration variables must go above this line! ****** + +function TightURL_version_check() { + global $VUCycle; + + // check http://tighturl.com/version.php and/or http://tighturl.sf.net/version.php + // parse out if there's a security release + // notify admin of this tighturl installation once if there's a new version, + // once a week, if there's a security fix. + // Possibly display "newer version available" on templates. + // Possibly disable vulnerable versions after a certain amount of time. + // ie, only show "Site down for emergency maintenance. If you are the administrator, click here." + // validate against mysql database password, show vulnerability screen. + + if ($VUCycle < 24) $VUCycle = 24; // someone's trying to get banned. +} + +/** + * Checks accepted URLs against URI blacklists and marks listed URLs as abused. + */ +function TightURL_KillBot($query='') { + + global $dbtable; + + if ($query == "") $query="SELECT * FROM $dbtable;"; + + // Query records with status normal, and added within last 2 weeks or records with status normal and more than 7 hits and hit within the last 2 weeks + // "SELECT * FROM $dbtable where status=0 && (DATE_SUB(CURDATE(), INTERVAL 14 DAY) <= adddate || (hits > 7 && DATE_SUB(CURDATE(), INTERVAL 14 DAY) <= lasthit)) ORDER BY id;" + $set = mysql_query($query); + $rows = @mysql_num_rows($set) or $rows = 0; + + for ($i = 0; $i <$rows; $i++) { + + $row = mysql_fetch_array($set); + $id = $row["id"]; + $url = $row["url"]; + $status = $row["status"]; + $hits = $row["hits"]; + + $hit = URI_on_URIBL($url); + if ($hit) { + // Change this to disable instead of delete, delete at some future point. + $req ="update $dbtable set status=3 where id='$id';"; + $res = mysql_query($req); + } +// elseif ($hits > 4000) { +// // Change this to disable instead of delete, delete at some future point. +// $req ="update $dbtable set status=5 where id='$id';"; +// $res = mysql_query($req); +// } + else { + $req = "update $dbtable set lastcheck=NOW(), checkcount=checkcount+1 where id='$id';"; + $res = mysql_query($req); + } + } +} + +function TightURL_NewReport($query='') { + + global $dbtable; + + if ($query == "") $query="SELECT * FROM $dbtable;"; + + $set = mysql_query($query); + $rows = @mysql_num_rows($set) or $rows = 0; + + if ($rows > 0) { + $MP = "/usr/sbin/sendmail -t ron@vnetworx.net"; + $fd = popen($MP, "w"); + fputs($fd, "To: ron@vnetworx.net\n"); + fputs($fd, "From: killbot@tighturl.com \n"); + fputs($fd, "Subject: TightURL additions report\n"); + fputs($fd, "\n"); + + for ($i = 0; $i <$rows; $i++) { + $row = mysql_fetch_array($set); + $id = $row["id"]; + $url = $row["url"]; + fputs($fd, "Block: http://tighturl.com/?p=" . base_convert($id, 10, 36) . " URL: $url\n\n"); + } + pclose($fd); + } +} + +/** + * Checks to see if a given URI is on a URI blacklist. + * Currently this means SURBL (http://www.surbl.org) and URIBL (http://www.uribl.com) + * + * Returns TRUE if the domain is listed on any configured URIBLs. + * + * A companion URI extractor must be written for the below issues + * Must be changed to do full resolution of redirections on URI, simulating a browser + * Must be changed to do IPv6 lookups + * Must be changed to check multiple URIs + * Must be changed to optionally check HTML entity encoded versions of URIs + * Must be changed to handle URIBL's inclusion of some third-level domains. + * + */ +function URI_on_URIBL($uri) { + +// This code does not yet properly implement a correct and efficient querying +// of URI BL data. + + global $uribl, $uribluri, $validurlpattern, $validipv4pattern, $tltlds, $validschemes; + + $uribls = ""; + + if ($uri) { + // Test for IPv4 address, reverse the quads if found + if (preg_match($validipv4pattern,$uri,$matches)) { + $domain=$matches[5] . "." . $matches[4] . "." . $matches[3] . "." . $matches[2]; + } + else { + // strip out second-level domain name, *unless* on exception list, + // in which case, strip out third level also and test that instead. + preg_match("/^".$validschemes.$validurlpattern."$/", $uri, $matches); + //preg_match($validurlpattern, $uri, $matches); + $domain = $matches[4]; + if (preg_match("/".$tltlds."$/", $domain, $matches)) {$levels = 2;} else {$levels = 1;} + //if (preg_match($tltlds, $domain, $matches)) {$levels = 2;} else {$levels = 1;} + + // klugey stripping routine to reduce domain to base domain name + // expect regex wojuld be better +// (.*\..*){2} matches vnetworx.co.uk but not vnetworx.com +// .*(\.co\.uk) matches[1] .co.uk + + $ss = countSubstrs($domain, "."); + while ($ss > $levels) { + $chop = strpos($domain, "."); + $domain = substr($domain, $chop + 1); + $ss = countSubstrs($domain, "."); + } + } + + // Query URI blacklists to see if domain/IP appears as target in known spam + // or something involved in a malware/phishing attack. + for ($i=0; $i 0) $uribls .= ", "; + $uribls .= $uribl[$i]; + } + } + if ($uribls) return $uribls; else return FALSE; // change to return an array of indexes into the URIBL array + } +} + +/** + * Checks to see if a given URL is a Reserved URL. + * + * Returns TRUE if the ID is listed as a Reserved URL. + */ +function on_Reserve($decimal) { + global $ReservedURL; + + $res=FALSE; + + if ($decimal) { + $sexatrigesimal = base_convert($decimal, 10, 36); + for ($i=0; $i $v2[0]) $ret = 1; + elseif ($v1[0] < $v2[0]) $ret = -1; + else { + // Major version numbers are equal + if ($v1[1] > $v2[1]) $ret = 1; + elseif ($v1[1] < $v2[1]) $ret = -1; + else { + // Minor version numbers are equal + if ($v1[2] > $v2[2]) $ret = 1; + elseif ($v1[2] < $v2[2]) $ret = -1; + else $ret = 0; + } + } + return $ret; + } +} + +/** + * Add in file_get_contents for PHP < 4.3.0 + */ +if (! function_exists('file_get_contents')) { + function file_get_contents($filename, $incpath = false, $resource_context = null) + { + if (false === $fh = fopen($filename, 'rb', $incpath)) { + trigger_error('file_get_contents() failed to open stream: No such file or directory', E_USER_WARNING); + return false; + } + clearstatcache(); + if ($fsize = @filesize($filename)) $data = fread($fh, $fsize); + else { + $data = ''; + while (!feof($fh)) $data .= fread($fh, 8192); + } + fclose($fh); + return $data; + } +} + +?> diff --git a/tighturl.php b/tighturl.php new file mode 100644 index 0000000..f2fd10d --- /dev/null +++ b/tighturl.php @@ -0,0 +1,757 @@ + + * portions Copyright (c) 2002,2003 Free Software Foundation + * + * This file implements a blind redirection service named TightURL. + * TightURL is Free Software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TightURL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * If you are not able to view the LICENSE, which should + * always be possible within a valid and working TightURL release, + * please write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * to get a copy of the GNU General Public License or to report a + * possible license violation. + * + * @package TightURL + * @author Ron Guerin + * @license http://www.fsf.org/licenses/gpl.html GNU Public License + * @copyright Copyright © 2004-2009 Ron Guerin + * @filesource + * @link http://tighturl.com TightURL + * @version 0.1.4 + * + */ + +define("VERSION", "0.1.4"); +define("REQUIRED_PHP_VERSION", "4.3.0"); + +// System defaults, DO NOT EDIT THIS FILE +// Edit tighturl.config.inc.php instead! + +global $copyright, $conn, $db, $os, $svcname; + +$dbhost = "localhost"; +$dbuser = "dbuser"; +$dbpass = "dbpass"; +$dbname = "tighturl"; +$dbtable = "urls"; +$FOFMethod=FALSE; //0=Full URL path or mod_rewrite, 1=404-Method compressed URLs +$os=""; + +// URIBL variables +$uribl = array("multi.surbl.org", "black.uribl.com"); +$uriblurl = array("www.surbl.org", "www.uribl.com"); + +// Bad Behavior variables +$BB2 = true; +$BBstats = true; +$BBstrict = false; +$BBverbose = true; +$BBLogging = true; +$bb2_settings_defaults = ""; + +// Require submitted URLs to exist? +$mustexist = true; + +// Text strings and style variables +$svcname = "URLSquisher"; +$verbtext = "Squish"; +$pasttext = "Squished"; +$tagline = "Squish long URLs to make short ones"; +$headcolor = "#006600"; +$tablecolor = "#00CC99"; +$copystart = date("Y"); +$copyrightholder = "SquishURL Enterprises"; + +// Reserved URLs +$ReservedURL = array("x", "rest", "xmlrpc", "soap", "xml", "atom", "rss", "blog", + "faq", "help", "about", "api", "code", "source", "docs", + "git", "cvs", "arch", "url", "admin", "setup", "svn", "project", + "abuse", "cgi-sys", "exploited"); + +// You REALLY don't want to edit below here unless you know what you're doing. + +// ************************************************************************* + + if (version_compare(phpversion(), REQUIRED_PHP_VERSION)<0) { + die_HTML($svcname, "Error: TightURL ".VERSION." needs PHP >= ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")"); + } + + if (file_exists("tighturl-install.php")) die_HTML($svcname, "Error: You must remove tighturl-install.php before using $svcname."); + + $os=strpos(strtolower(PHP_OS), "win")===false?"nix":"win"; + + $validurlpattern = "\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)" + . "*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])" + . "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)" + . "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)" + . "\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])" + . "|((([0-9A-F]{1,4}(((:[0-9A-F]{1,4}){5}::[0-9A-F]{1,4})|((:[0-9A-F]{1,4}){4}" + . "::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,1})|((:[0-9A-F]{1,4}){3}::[0-9A-F]{1,4}" + . "(:[0-9A-F]{1,4}){0,2})|((:[0-9A-F]{1,4}){2}::[0-9A-F]{1,4}(:[0-9A-F]{1,4})" + . "{0,3})|(:[0-9A-F]{1,4}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,4})|(::[0-9A-F]{1,4}" + . "(:[0-9A-F]{1,4}){0,5})|(:[0-9A-F]{1,4}){7}))|(::[0-9A-F]{1,4}(:[0-9A-F]{1,4}" + . "){0,6}))|::)|((([0-9A-F]{1,4}(((:[0-9A-F]{1,4}){3}::([0-9A-F]{1,4}){1})" + . "|((:[0-9A-F]{1,4}){2}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,1})|((:[0-9A-F]{1,4})" + . "{1}::[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,2})|(::[0-9A-F]{1,4}(:[0-9A-F]{1,4}" + . "){0,3})|((:[0-9A-F]{1,4}){0,5})))|([:]{2}[0-9A-F]{1,4}(:[0-9A-F]{1,4}){0,4}))" + . ":|::)((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{0,2})\.){3}(25[0-5]|2[0-4][0-9]|" + . "[0-1]?[0-9]{0,2})" + . "|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org" + . "|mobi|biz|arpa|info|name|pro|aero|coop|museum" + . "|[a-zA-Z]{2}))(\:[0-9]+)*(\/.($|[a-zA-Z0-9\.\:\,\?\'\(\)\\\*\+&%\$;|#\=~_\-\s@]*))*\/*"; + + $validipv4pattern = ":\/\/(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\." + . "(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])" + . "\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\/*"; + + $forbid = "\.(cmd|bat|exe|scr|pif|vbs|js|pif|msi|cdr)"; + +// ****** !All overridable configuration variables must go above this line! ****** + + if (! isset($antiabuse)) $antiabuse = true; + if (! isset($netchecks)) $netchecks = true; + if (! isset($mustexist)) $mustexist = true; + + // Status: 0=Ok, 1=Warn, 2=Black, 3=Policy, 4=Complaints + + if (file_exists("tighturl.urlpattern.inc.php")) include("tighturl.urlpattern.inc.php"); + if (file_exists("tighturl.tltpattern.inc.php")) include("tighturl.tltpattern.inc.php"); + if (file_exists("tighturl.redirpattern.inc.php")) include("tighturl.redirpattern.inc.php"); + if (file_exists("tighturl.ptcpattern.inc.php")) include("tighturl.ptcpattern.inc.php"); + if (file_exists("tighturl.blpattern.inc.php")) include("tighturl.blpattern.inc.php"); + if (file_exists("tighturl.config.inc.php")) include("tighturl.config.inc.php"); + + // Figure out our copyright string + $thisyear = date("Y"); + $copyright = $copystart; + if ($copystart != $thisyear) $copyright .= "-" . $thisyear; + $uribls = ""; + for ($i=0; $i 0) $uribls .= ", "; + $uribls .= "" . $uribl[$i] . ""; + } + + if (! $FOFMethod) $parm = "?i="; // We need the parameter tag + + // Figure out correct self + if (strncmp($_SERVER['PHP_SELF'], $_SERVER['REQUEST_URI'], strlen($_SERVER['PHP_SELF'])) != 0) { + if (preg_match("|(.*)/.*$|",$_SERVER['PHP_SELF'],$matches)) $self = $matches[1]; + if (! preg_match("|.*/$|", $self)) $self .= "/"; + } + else { + $self = $_SERVER['PHP_SELF']; // We need the script name + if (! preg_match("|.*/$|", $self)) $self .= "/"; + } + + // Connect to MySQL, open database. + $conn = @mysql_connect($dbhost, $dbuser, $dbpass) or die_HTML($svcname, "Error: Cannot connect to database."); + $db = mysql_select_db($dbname, $conn) or die_HTML($svcname, "Error: Cannot select database. ". mysql_error()); + + // When in doubt, turn Bad Behavior on, set it to FALSE in the config to turn it off. + if (! isset($BB2)) $BB2 = true; + + // If user has not turned off Bad Behavior in the config, use BB2 (highly recommended) TODO: warn user if $BB2 but not BB2 + if ($BB2 && file_exists("bad-behavior/bad-behavior-tighturl.php")) require_once("bad-behavior/bad-behavior-tighturl.php"); + else $BB2 = FALSE; + + // Figure out what kind of request this is and service it. + + // This is klugey. Clean up later. + // also I think data should be sanitized immediately + if ((isset($_REQUEST['save']) && $_REQUEST['save'] == 'y') + && (isset($_REQUEST['url']) && ! empty($_REQUEST['url']) && trim($_REQUEST['url']) != "" + && (preg_match("/^.*url=(.*)$/", $_SERVER['QUERY_STRING'], $matches) != 0))) { + $url = trim($matches[1]); + if (preg_match("/^(.*)&tighturlaction.*$/", $matches[1], $matches)) $url = $matches[1]; + save_URL(urldecode($url)); + } + elseif (isset($_REQUEST['i']) && !empty($_REQUEST['i'])) { + lookup_ID($_REQUEST['i']); + } + elseif (isset($pbi) && !empty($pbi) && isset($_REQUEST[$pbi]) && !empty($_REQUEST[$pbi])) { + PolicyBan_ID($_REQUEST[$pbi]); + } + elseif ($FOFMethod && preg_match("/^\/+([a-zA-Z0-9]+)\/*(.*)\/*$/", $_SERVER['REQUEST_URI'], $matches)) { + lookup_ID($matches[1]); + } + elseif ($FOFMethod && $_SERVER['REQUEST_URI'] != "/") { + display_HTML("", "", "Error: Couldn't find a valid " . $svcname . " URI."); + } + else { + display_HTML("", "main"); + } + exit; + +// ************************************************************************* + + +/** + * sanitize a string for SQL input + */ +function sanitize_sql_string($string) { + return(mysql_real_escape_string($string)); +} + +/** + * Counts the number of times a substring is contained in a given string. + */ +function countSubstrs($haystack, $needle) { + return (($p = strpos($haystack, $needle)) === false) ? 0 : (1 + countSubstrs(substr($haystack, $p+1), $needle)); +} + +/** + * Checks to see if a given URI is on a URI blacklist. + * Currently this means SURBL (http://www.surbl.org) and URIBL (http://www.uribl.com) + * + * Returns TRUE if the domain is listed on any configured URIBLs, returns FALSE if + * anything goes wrong or the anti-abuse system is turned off. + * + * A companion URI extractor must be written for the below issues + * Must be changed to do full resolution of redirections on URI, simulating a browser + * Must be changed to do IPv6 lookups + * Must be changed to check multiple URIs (maybe a wrapper instead) + * Must be changed to optionally check HTML entity encoded versions of URIs + * Must be changed to handle URIBL's inclusion of some third-level domains. + * + */ +function URI_on_URIBL($uri) { + +// This code does not yet properly implement a correct and efficient querying +// of URI BL data. + + global $uribl, $uribluri, $validschemes, $validurlpattern, $validipv4pattern, + $antiabuse, $netchecks, $tltlds; + + // Everything gets a pass if antiabuse or network tests are off. + if ((! $antiabuse) || (! $netchecks)) return(false); + + $uribls = ""; + + if ($uri) { + // Test for IPv4 address, reverse the quads if found + if (preg_match("/^".$validschemes.$validipv4pattern."/", $uri, $matches)) { + $domain=$matches[5] . "." . $matches[4] . "." . $matches[3] . "." . $matches[2]; + } + else { + // strip out second-level domain name, *unless* on exception list, + // in which case, strip out third level also and test that instead. + // FIX: when testing uribl.com lists, also test additional level. First hit wins. + + preg_match("/^".$validschemes.$validurlpattern."$/", $uri, $matches); + $domain = $matches[4]; + if (preg_match("/".$tltlds."$/", $domain, $matches)) {$levels = 2;} else {$levels = 1;} + + // klugey stripping routine to reduce domain to base domain name + // expect regex wojuld be better + + $ss = countSubstrs($domain, "."); + while ($ss > $levels) { + $chop = strpos($domain, "."); + $domain = substr($domain, $chop + 1); + $ss = countSubstrs($domain, "."); + } + } + + // Query URI blacklists to see if domain/IP appears as target in known spam + // or something involved in a malware/phishing attack. + for ($i=0; $i", $recexists)) { + if ($i > 0) $uribls .= ", "; + $uribls .= $uribl[$i]; + } + } + return ($uribls); // change to return an array of indexes into the URIBL array + } +} + +/** + * Checks to see if a given URL is a Reserved URL. + * + * Returns TRUE if the ID is listed as a Reserved URL. + */ +function on_Reserve($decimal) { + global $ReservedURL; + + $res=FALSE; + + if ($decimal) { + $sexatrigesimal = base_convert($decimal, 10, 36); + for ($i=0; $ii", $url)) { + display_HTML("", "", "Error: ".$svcname." is not a URL obfuscation service, and does not accept redirection links.", $url, "", $url); + } + elseif (isset($ptc) && preg_match("/.*".$ptc.".*/i", $url)) { + display_HTML("", "", "Error: ".$svcname." does not accept PTC (Pay To Click) links due to spamming abuse.", $url, "", $url); + } + else { + $safeurl = sanitize_sql_string($url); + $result = mysql_query("SELECT MAX(id) FROM $dbtable") or display_HTML("", "", "Error: $svcname system error.", $url, "", $url); + $lastid = mysql_result($result, 0) + 1; + $guesssexatrigesimal = base_convert($lastid, 10, 36); + $guessurl = "http://" . $_SERVER['HTTP_HOST'] . $self; + if (! $FOFMethod) $guessurl .= "?i="; // We need the parameter tag + $guessurl .= $guesssexatrigesimal; // Append the Base-36 ID to the URL + if ( strlen($guessurl) >= strlen($url) ) { + display_HTML("", "", "Fail: That URL cannot be shortened by $svcname. Sorry!", $url, "", $url); + } + else { + if ($antiabuse && $netchecks) {$lists = URI_on_URIBL($url);} else {$lists = false;} + if (! $lists) { + $rows=0; $srows=0; $testurl=$safeurl; + if (preg_match("/\/$/", $testurl)) $testurl = rtrim($testurl,"/"); + $req = "SELECT * FROM $dbtable WHERE url = '$testurl/';"; + $res = mysql_query($req); + $srows = @mysql_num_rows($res) or $srows = 0; + if ($srows == 0) { + $req = "SELECT * FROM $dbtable WHERE url = '$testurl';"; + $res = mysql_query($req); + $rows = @mysql_num_rows($res) or $rows = 0; + } + if ($rows == 0 && $srows == 0) { + do { + $req ="INSERT INTO $dbtable (id, url, adddate, addip) "; + $req .= "VALUES ('', '$safeurl', NOW(), '$remote');"; + if (mysql_query($req)) { + $decimal = mysql_insert_id(); + } + else { + die_HTML($svcname, "Error: Database failure."); + } + $reserved_id = on_Reserve($decimal); + if ($reserved_id) { + // Delete this record so it doesn't override the reserved ID. (?) + $req = "DELETE FROM $dbtable WHERE id = '$decimal';"; + $res = mysql_query($req) or die_HTML($svcname, "Error: Database failure."); + } + } while ($reserved_id); + } + else { + // Return existing ID for this duplicate request + $decimal = mysql_result($res, 0, "id"); + } + $sexatrigesimal = base_convert($decimal, 10, 36); + $address = "http://" . $_SERVER['HTTP_HOST'] . $self; + if (! $FOFMethod) $address .= "?i="; // We need the parameter tag + $address .= $sexatrigesimal; // Append the Base-36 ID to the URL + display_HTML("", "save", "", $url, $address); + } + else { + display_HTML("HTTP/1.0 403 Forbidden", "", "Error: Submitted URL (" . $url . ") is listed in " . $lists . ". You may not create a " . $svcname . " link for it."); + } + } + } +} + +/** + * + */ +function PolicyBan_ID($sexatrigesimal) { + global $dbtable, $svcname; + + // First, convert unsafe user input sexatrigesimal to decimal, which will be safe. + $decimal = base_convert ($sexatrigesimal, 36, 10); + + $req = "SELECT * FROM $dbtable WHERE id = '$decimal';"; + $res = mysql_query($req) or die_HTML($svcname, "Error: Query failed"); + + $rows = mysql_num_rows($res); + if (($rows != 0) && (mysql_result($res, 0, "url") != "")) { + $req ="update $dbtable set status='4' where id='$decimal';"; + $res = mysql_query($req); + die_HTML($svcname, "ID: " . $sexatrigesimal . " banned for policy violation.", $code="HTTP/1.0 200 OK"); + } +} + +/** + * Looks up given ID in the database and redirects, displays template, or + * displays error page. Expects the ID to be a sexatrigesimal (Base-36) number, + * which is the format used by TightURLs. + * + * We convert the ID to decimal before looking it up in the database, as the + * ID field is a MySQL autoincrement decimal value. + */ +function lookup_ID($sexatrigesimal) { + global $dbtable, $svcname; + + // First, convert unsafe user input sexatrigesimal to decimal, which will be safe. + $decimal = base_convert ($sexatrigesimal, 36, 10); + + $req = "SELECT * FROM $dbtable WHERE id = '$decimal';"; + $res = mysql_query($req) or die_HTML($svcname, "Error: Query failed"); + + $rows = mysql_num_rows($res); + if (($rows != 0) && (mysql_result($res, 0, "url") != "")) { + // Change this logic to display a templated page instead? + switch (mysql_result($res, 0, "status")) { + case 5: + display_HTML("HTTP/1.0 403 Forbidden", "complaints"); + return; + break; + case 4: + display_HTML("HTTP/1.0 403 Forbidden", "policy"); + return; + break; + case 3: + display_HTML("HTTP/1.0 403 Forbidden", "blacklist"); + return; + break; + default: + $url = stripslashes(mysql_result($res, 0, "url")); + break; + } + $req ="update $dbtable set lasthit=NOW(), hits=hits+1 where id='$decimal';"; + $res = mysql_query($req); + header("HTTP/1.0 301 Moved Permanently"); + header("Location: $url"); + } + elseif (! on_Reserve($decimal)) { // Not found, Not on reserve + display_HTML("HTTP/1.0 404 Not Found", "", "Error: That " . $svcname . " ID is not in our database."); + } + else { // It's a(n implied) Reserved URL + // Is this a template or an API? + $sexatrigesimal = strtolower($sexatrigesimal); + switch ($sexatrigesimal) { + case "rest": + api_REST(); + break; + case "xmlrpc": + api_XMLRPC(); + break; + case "soap": + api_SOAP(); + break; + default: + display_HTML("", $sexatrigesimal); + } + } +} + +function api_REST() { + die_HTML($svcname, "Error: REST API not implemented yet.", "HTTP/1.0 501 Not Implemented"); +} + +function api_XMLRPC() { + die_HTML($svcname, "Error: XML-RPC API not implemented yet.", "HTTP/1.0 501 Not Implemented"); +} + +function api_SOAP() { + die_HTML($svcname, "Error: SOAP API not implemented yet.", "HTTP/1.0 501 Not Implemented"); +} + +/** + * Display HTML page using template and template variables. + * + * Reads in the main system template file (tighturl.tmpl) into $html . + * + * $code + * HTTP 1.0 status code and message. + * + * $template + * Checks for the existence of a subtemplate named tighturl.$template.tmpl + * and replaces template variable $HTML in the main template tighturl.tmpl + * with the contents of tighturl.$template.tmpl if any. + * + * Then any remaining $HTML from the only or inner template is replaced by $content, + * along with $url, $tighturl, and $input. A variety of other replacements are + * made using various global variables. + * + * $content + * HTML content to be replace template variable $HTML + * + * $url + * URL submitted to TightURL + * + * $tighturl + * TightURL generated for $url + * + * $input + * When submitted URL does not validate it is passed back as $input + * + * Template variables are words in all capital letters that start with a + * $ symbol, such as $TEMPLATEVARIABLE. TightURL now supports at least + * 20 template variables. At runtime, these template variables are replaced + * by program variables. + * - $HTML : HTML passed into the function as $input by the program or an inner template + * - $PARM : Parameter tag when not using 404-Method + * - $URL : URL submitted to TightURL + * - $URLLEN : Length of the submitted URL + * - $TIGHTURL : TightURL generated for the submitted URL + * - $TIGHTURLLEN : Length of generated TightURL + * - $DIFF : Difference in length between submitted and TightURLs + * - $INPUT : Bad input URL being passed back to output form + * - $SVCNAME : Name of the TightURL service + * - $HEADCOLOR : Color of the H1 Header tag + * - $TABLECOLOR : Color of the table containing URL input field + * - $TAGLINE : Tagline of the TightURL service + * - $CPASTTEXT : Capitalized past-tense word for tightening URLs + * - $PASTTEXT : Non-Capitalized past-tense word for tightening URLs + * - $CVERBTEXT : Capitalized action word for tightening URLs + * - $VERBTEXT : Non-Capitalized action word for tightening URLs + * - $COPYRIGHT : Copyright duration string generated from $copystart global variable, + * will be current 4-digit year if $copystart not defined. + * - $COPYRIGHTHOLDER : Name of copyright holder + * - $URLBLS : HTML string of URIBLs TightURL is checking + * - $HOST : Hostname TightURL is running on + * - $SELF : Name TightURL is invoked as + */ +function display_HTML ($code, $template, $content="", $url="", $tighturl="", $input="") { + global $svcname, $verbtext, $pasttext, $tagline, $uribls, $parm, + $headcolor, $tablecolor, $copyright, $copyrightholder, $self, $BB2; + +// $url = htmlspecialchars($url); + if ($code="") $code = "HTTP/1.0 200 OK"; + if (preg_match("/\/$/", $template)) $template = rtrim($template,"/"); + if (file_exists("tighturl.tmpl")) { + $html = file_get_contents("tighturl.tmpl"); + if (($template != "") && file_exists("tighturl." . $template . ".tmpl")) { + $template = file_get_contents("tighturl." . $template . ".tmpl"); + $html = preg_replace("/\\\$HTML/", $template, $html); + } + elseif ($template != "") { + die_HTML($svcname, "Error: Template file tighturl." . $template . ".tmpl cannot be found."); + } + if (substr($content, 0, 6) == "Error:") { + $content = preg_replace("/Error:/", "Error:", $content).""; + } + if ($content) $content .= "
\n"; + // Always replace longer similar tokens before shorter ones. Things won't work the + // way you expect if you replace $URL first, and then replace $URLMORELETTERS. + $html = preg_replace("/\\\$HTML/", $content, $html); + $html = preg_replace("/\\\$PARM/", $parm, $html); + $html = preg_replace("/\\\$URLLEN/", strlen($url), $html); + $html = preg_replace("/\\\$URL/", htmlspecialchars(strip_tags($url), ENT_QUOTES), $html); + $html = preg_replace("/\\\$INPUT/", $input, $html); + $html = preg_replace("/\\\$TIGHTURLVER/", VERSION, $html); + $html = preg_replace("/\\\$TIGHTURLLEN/", strlen($tighturl), $html); + $html = preg_replace("/\\\$TIGHTURL/", $tighturl, $html); + $html = preg_replace("/\\\$DIFF/", strlen($url)-strlen($tighturl), $html); + $html = preg_replace("/\\\$SVCNAME/", $svcname, $html); + $html = preg_replace("/\\\$HEADCOLOR/", $headcolor, $html); + $html = preg_replace("/\\\$TABLECOLOR/", $tablecolor, $html); + $html = preg_replace("/\\\$TAGLINE/", $tagline, $html); + $html = preg_replace("/\\\$CPASTTEXT/", $pasttext, $html); + $html = preg_replace("/\\\$PASTTEXT/", strtolower($pasttext), $html); + $html = preg_replace("/\\\$VERBTEXT/", strtolower($verbtext), $html); + $html = preg_replace("/\\\$CVERBTEXT/", $verbtext, $html); + $html = preg_replace("/\\\$COPYRIGHTHOLDER/", $copyrightholder, $html); + $html = preg_replace("/\\\$COPYRIGHT/", $copyright, $html); + $html = preg_replace("/\\\$URIBLS/", $uribls, $html); + $html = preg_replace("/\\\$HOST/", $_SERVER['HTTP_HOST'], $html); + $html = preg_replace("/\\\$SELF/", $self, $html); + $html = preg_replace("/\\\$__/", "$", $html); // Template Variables shown as text instead of substituted + if (preg_match("|(.*)|is", $html, $matches)) + $html = preg_replace("|(.*)|is", "" . strip_tags($matches[1]) . "", $html); + if ($BB2) { + $html = preg_replace("/\\\$BBSTATS/", bb2_insert_stats(), $html); + $bb2code = bb2_insert_head(); + if (preg_match("|(.*)|is", $html, $matches)) + $html = preg_replace("|(.*)|is", "\n" . $bb2code . $matches[1] . "", $html); + } + else { + $html = preg_replace("/\\\$BBSTATS/", "", $html); + } + header($code); + echo $html; + } + else { + die_HTML($svcname, "Error: Error: TightURL Redirection service (" . $svcname . ") site template not found."); + } +} + +/** + * Die in an HTML-friendly way, without the benefit of a template. + * Use display_HTML to "die" using the TightURL site template. + */ +function die_HTML($svcname, $errmsg, $code="HTTP/1.0 500 Internal Server Error") { + + header($code); + echo "\n \n " . $svcname . "\n \n \n"; + echo " " . $errmsg . "
\n"; + echo " \n"; + die; +} + +/** + * Verifies the existence and accessibility of a resource in a given URL. + * + * Returns FALSE if the resource does not exist or cannot be accessed using + * supplied authentication information, else returns the resolved and verified + * URL. Given URL is returned as resolved to itself if $netchecks are off. + * + * Will recurse through redirection chains up to 12 times by default. This + * value is preferably selectable by the user in a configuration screen + * somewhere, but probably should not be lower than 12, as attempts are made + * to detect HTML and JavaScript redirects in addition to HTTP redirects, and + * a dozen redirects to find the end is quite possible. + * + * Unfortunately Google's GFE server erroneously returns 404 errors when + * they should be returning something like a 405, making it impossible to + * use HTTP HEAD to verify the existence of resources front-ended by GFE. + * Additionally Amazon throws a 405 attempting to HEAD some of their resources + * so this function does not attempt to use HEAD at all. + * + * BUG: Presently only does HTTP + * + */ +//function Resolve_URL ($url, &$resolvedchain=array(""), $chainlen=12) { +function Resolve_URL ($url, &$resolvedchain, $chainlen=12) { +// $resolvedchain = array($url); + + if (! isset($resolvedchain)) $resolvedchain=array(""); + + global $netchecks; + + if (! $netchecks) { + // If network checks off, accept submitted URL as resolved. + return($url); + } + + $parsed = parse_url($url); + + $pre = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : ''; + $pre .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : ''; + $pre .= isset($parsed['host']) ? $parsed['host'] : ''; + $pre .= isset($parsed['port']) ? ':'.$parsed['port'] : ''; + if(isset($parsed['path'])) + $post = (substr($parsed['path'], 0, 1) == '/') ? $parsed['path'] : ('/'.$parsed['path']); + else + $post = "/"; + $post .= isset($parsed['query']) ? '?'.$parsed['query'] : ''; + $post .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : ''; + + $resolved = false; + + // Change this to support all protocols TightURL supports, not just HTTP + if (! isset($parsed['port']) || $parsed['port'] == 0) $parsed['port'] = 80; +//if($connection = @fsockopen ($parsed['host'], $parsed['port'], $errno, $errstr, 5)) { + $ip = gethostbyname($parsed['host']); // This is supposed to avoid unnecessary DNS lookups + if($connection = @fsockopen ($ip, $parsed['port'], $errno, $errstr, 5)) { + stream_set_timeout($connection, 5); + // HTTP send Connection: Close so we don't have to wait + // Google's GFE handling of HEAD is broken, and Amazon returns 405 on HEAD so had to use GET + fwrite($connection, "GET ".$post." HTTP/1.0\r\nHost: ".$parsed['host']."\r\nConnection: Close\r\n\r\n"); + while (!feof($connection)) { + $line_read=fgets($connection); + if ($line_read == "") break; //blank line is header delimiter, if you see it you're done here + //Fix: change this and start parsing the body for HTML-based redirections. + + if (preg_match("/HTTP\/\S* +(\S*) /", $line_read, $matches)) { // Look for certain HTTP status codes + switch ($matches[1]) { + case 200: // Ok, we have a final destination (as far as HTTP is concerned) + case 201: // Created, we have a final destination + case 202: // Accepted, we have a final destination + case 203: // Non-authoritative reply, we have a final destination + case 204: // No content, we have a final destination + case 205: // Reset content, we have a final destination + case 206: // Partial content, we have a final destination + case 207: // Multi-status, we have a final destination + case 304: // Not Modified (this is ok) + case 401: // Authorization required (this is ok) + case 402: // Payment required (this is ok) + case 403: // Forbidden (but also ok) + case 405: // Method not allowed (but also ok) + case 406: // Not acceptable (acceptable here unless someone tells us otherwise) + case 409: // Conflict (acceptable unless someone tells us otherwise) + case 421: // Too many connections (fail ok) + case 426: // Use TLS (fail ok) + case 500: // Internal server error (fail ok) + case 502: // Bad gateway (fail ok) + case 503: // Service unavailable (fail ok) + case 504: // Gateway timeout (fail ok) + case 505: // HTTP version not supported (fail ok) + case 509: // Bandwidth exceeded pseudo code (fail ok) + $resolved = $url; + $resolvedchain[] = $url; + break 2; + case 300: + case 301: + case 302: + case 307: + break; + case 404: // Not found + case 408: // Request timeout (this URL will never work again) + case 410: // Gone (and not coming back) + break 2; + default: + $resolved = $url; + $resolvedchain[] = $url; + break 2; + } + } + + // If this is a redirect (300, 301, 302, 307), follow it if the chain isn't too long + if (preg_match("/Location: (.*)\r\n/", $line_read, $matches)) { + fclose($connection); + $connection = false; + $resolvedchain[] = $url; + if ($chainlen > 0 ) $resolved = Resolve_URL($matches[1], $resolvedchain, $chainlen - 1); + break; + } + + } + // Parse body here? + if ($connection) fclose($connection); + } + return($resolved); +} +?> diff --git a/tighturl.policy.tmpl b/tighturl.policy.tmpl new file mode 100644 index 0000000..3182d33 --- /dev/null +++ b/tighturl.policy.tmpl @@ -0,0 +1,2 @@ + The $SVCNAME redirection URL you have requested has been disabled due to + violations of $SVCNAME policy/Terms of Service. diff --git a/tighturl.ptcpattern.inc.php b/tighturl.ptcpattern.inc.php new file mode 100644 index 0000000..43c0893 --- /dev/null +++ b/tighturl.ptcpattern.inc.php @@ -0,0 +1,163 @@ + diff --git a/tighturl.redirpattern.inc.php b/tighturl.redirpattern.inc.php new file mode 100644 index 0000000..aabd1d0 --- /dev/null +++ b/tighturl.redirpattern.inc.php @@ -0,0 +1,118 @@ + diff --git a/tighturl.tltpattern.inc.php b/tighturl.tltpattern.inc.php new file mode 100644 index 0000000..5b32549 --- /dev/null +++ b/tighturl.tltpattern.inc.php @@ -0,0 +1,255 @@ + diff --git a/tighturl.urlpattern.inc.php b/tighturl.urlpattern.inc.php new file mode 100644 index 0000000..f833560 --- /dev/null +++ b/tighturl.urlpattern.inc.php @@ -0,0 +1,24 @@ +