/*
** Copyright (c) 2002 D. Richard Hipp
**
** 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 library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to throttle output to misbehaving spiders.
*/
#include Your access to this website has been temporarily suspended because\n"
"you are using it excessively. You can retry your request later. This page is intended to capture spiders that\n"
"ignore the \"robots.txt\" file.\n"
"If you are not a spider, click on the \"I am human\" link\n"
"above. If you click on the \"I am a spider\" link, your access to this\n"
"server will be suspended for about an hour. \n"
"Remove older entries\n");
common_footer();
}
/*
** WEBPAGE: /captcha
**
** Generate the captcha page. This is basically a honeypot with a cookie
** for state. Once the client exceeds the throttle threshold, they risk
** getting locked out unless they (eventually) get this question correct
** or slow down on the hits.
*/
void captcha_page(void){
time_t now = time(NULL);
int q1, q2;
if( atoi(PD("a","0")) == (atoi(PD("q1","-1")) + atoi(PD("q2","-1"))) ){
/* User gave the right answer. Set cookie and continue on.
*/
captcha_set_cookie();
cgi_redirect(PD("nxp","index"));
return;
}
/* Note that we don't do _any_ credential checks in this page... However,
** some flags are needed for sane header generation. For example, we
** want a "Login" in the menu rather than "Logout" so isAnon should be
** set.
*/
g.isAnon = 1;
common_standard_menu(0, 0);
common_add_help_item("CvstracAdminAbuse");
common_header("Abbreviated Turing Test");
/* small numbers */
srand(now);
q1 = (rand()%5)+1;
q2 = (rand()%5)+1;
cgi_printf("In order to continue, you must show you're a human. Please answer\n"
"the following mathematical skill testing question (and ensure cookies\n"
"are enabled):\n"
" \n"
"\n"
"
\n"
"\n"
" \n");
az = db_query("SELECT ipaddr, lastaccess, load FROM access_load %s LIMIT %d",
zOrderBy, limit);
for(i=0; az[i]; i+=3){
struct tm *pTm;
time_t atime;
char zTime[200];
atime = atoi(az[i+1]);
pTm = localtime(&atime);
strftime(zTime, sizeof(zTime), "%Y-%m-%d %H:%M:%S", pTm);
cgi_printf("IP Address \n"
"Last Access \n"
"Load \n",az[i],zTime,az[i+2]);
}
cgi_printf(" %h \n"
" %h \n"
" %s
\n" "\n" "
\n" "\n"); common_footer(); } static int count_links(const char *zText){ int nLink = 0; if( zText!=0 ){ int i, j; for(i=0;zText[i];i++){ char c = zText[i]; if( (c=='h' || c=='f' || c=='m') && (j=is_url(&zText[i]))>0 ){ nLink ++; i += j; continue; } } } return nLink; } /* If someone blows the limit, tweak the current throttler counter ** for their IP. Each failure increases it by a defined fraction ** of the throttle limit, which mean they'll get locked out after ** triggering the failure too many times. Currently, that's ** twice. ** ** Hopefully this strikes a balance between stopping spammers and ** not annoying legitimate users too much... */ static void increase_load(){ const char *zAddr = getenv("REMOTE_ADDR"); const char *zLimit = db_config("throttle", 0); double rLimit; if( zLimit!=0 && (rLimit = atof(zLimit))>0.0 && zAddr!=0 ){ time_t now = time(0); char *zLastLoad = db_short_query("SELECT load FROM access_load " "WHERE ipaddr='%q'", zAddr); db_execute("REPLACE INTO access_load(ipaddr,load,lastaccess) " "VALUES('%q',%g,%d)", zAddr, (zLastLoad ? atof(zLastLoad) : 0) + rLimit/WIKI_EDIT_LOCKOUT, now); if(zLastLoad) free(zLastLoad); } /* we also need to clear any captcha cookie since having it ** bypasses the throttler. This is also handy since it's not ** IP specific, so users who are behind variable IP's are going ** to still get caught. */ captcha_clear_cookie(); } /* ** Apply any appropriate anti-spam heuristics to the provided wiki edit. ** ** Obviously, we're only going to apply this restriction to anonymous ** users. Currently. ** ** Returns NULL if the change is acceptable. Otherwise, it returns a string ** containing an explanation for the rejection. */ char *is_edit_allowed(const char *zOld, const char *zNew){ if( g.isAnon ){ const char *zKeys = db_config("keywords",""); int nMscore = atoi(db_config("keywords_max_score","0")); int nMax = atoi(db_config("max_links_per_edit","0")); /* ** Check for too many "bad words" in the new text. Checking the "diff" ** might be better? */ if( nMscore && zKeys[0] ) { db_add_functions(); if( db_exists("SELECT 1 WHERE search('%q','%q')>%d",zKeys,zNew,nMscore)){ increase_load(); return "Forbidden keywords!"; } } /* ** Check to see if the threshold of external links was exceeded. */ if( nMax ){ int nOld = count_links(zOld); int nNew = count_links(zNew); /* Note that someone could bypass this by replacing a whole bunch of ** links in an existing page. If that starts to happen it might be ** necessary to compare the list of links or something. ** ** Some keyword filtering would help a bit, too. */ if( nNew - nOld >= nMax ){ increase_load(); return "Too many external links for one edit!"; } } } return 0; }