1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
/*
* SQLCounter (C)Copyright 2000, David Hedbor and Xavier Beaudouin
*
* This Program is GPL.
*
*/
//#define SQLCOUNTERDEBUG
string cvs_version = "$Id: sqlcounter.pike,v 1.4 2000/11/29 16:36:46 kiwi Exp $";
int thread_safe=1;
#include <module.h>
#include <string.h>
#include <simulate.h>
inherit "module";
inherit "roxenlib";
#ifdef SQLCOUNTERDEBUG
#define DEBUGLOG(X) perror("SQLCounter: "+X+"\n");
#else
#define DEBUGLOG(X)
#endif
string tagname="sqlcounter"; // The name of the tag
int usecount=0; // Usage count of the module
int db_accesses=0; // DB accesses
int last_db_access=0; // Last time the database was accessed
string starttime = ctime(time()); // Date of start of the module
object db=0; // The database
array register_module()
{
return ({ MODULE_PARSER|MODULE_PROVIDER,
"SQL Counter",
"An <accessed> replacement that stores hits in a SQL "
"database. It has several advantages to the standard Roxen "
"tag. Multi-server safety being the main one "
"(ie if you have 5 Roxen's for the same "
"website, this module can be used in them all to get the total "
"number of hits). The tag is called <sqlcounter>. "
"It can also generate graphical counters using the "
"graphical counter module. "
"It requires a correctly setup SQL Module.",
0, 1
});
} // register_module
mapping query_tag_callers()
{
return ([
tagname : tag_sqlcounter,
]);
}
/*
* DB management functions
*/
// This gets called only by call_outs, so we can avoid storing call_out_ids
// Also, I believe storing in a local variable the last time of an access
// to the database is more efficient than removing and reseting call_outs.
// This leaves a degree of uncertainty on when the DB will be effectively
// closed, but it's below the values of the module variable "timer" for sure.
void close_db()
{
if (!QUERY(closedb))
return;
if ( (time(1)-last_db_access) > QUERY(timer) )
{
db=0;
DEBUGLOG("Closing the database");
return;
}
call_out(close_db,QUERY(timer));
}
void open_db()
{
mixed err;
last_db_access = time(1);
db_accesses++; // Count DB accesses here, since this is called before
// each accesses.
if(objectp(db)) // already opened ?
return;
err=catch{
db=Sql.sql(QUERY(sqlserver));
};
if (err) {
perror("SQLCounter: Couldn't open counter database !\n");
if (db)
perror("SQLCounter: Database interface replies: "+db->error()+"\n");
else
perror("SQLCounter: Unknown reason\n");
perror("SQLCounter: Check the values in the configuration interface, and "
"that the user\n\trunning the server has adequate permissions "
"to the server\n");
db=0;
return;
}
DEBUGLOG("Database successfully opened");
if(QUERY(closedb))
call_out(close_db,QUERY(timer));
}
/*
* End of DB management functions
*/
constant cargs = ({"bgcolor","fgcolor","trans","rotate",
"style","len","size"});
constant aargs=({"add","addreal","case","cheat","database","factor","file","lang",
"per","prec","reset","since","type"});
#define CURL(x,y) "1/n/n/0/6/5/0/"+x+"/"+y+".gif"
string tag_sqlcounter(string tag, mapping m, object id)
{
string me;
// object db = id->conf->call_provider("sql", "sql_object", m->host);
open_db();
if(!db)
return " sqlaccess: Failed to connect to database. ";
usecount++; // Do some accounting...
// Safify the file name
string cnt, f = db->quote(id->not_query);
me = db->quote(id->conf->query("MyWorldLocation"));
array res =
db->query(sprintf("SELECT count FROM counter WHERE server='%s' "
"AND file='%s'", me, f));
if(sizeof(res))
cnt = res[0]->count;
// Reset counter.. :)
if(m->reset) {
db->query(sprintf("UPDATE counter SET count=0,"
"total_count=total_count+%d,"
"reset=NOW() "
"WHERE server='%s' AND file='%s'", m->add,me,f));
}
if(!id->misc->_sql_counter_added || m->add) {
id->misc->_sql_counter_added = 1;
m->add = (int)m->add || 1;
if(!cnt) {
db->query(sprintf("REPLACE INTO counter (file,server,count,total_count,created,reset) VALUES ('%s',"
"'%s',%d,%d,NOW(),NOW())",
f, me, 1+m->add, 1+m->add ));
cnt = "1";
} else {
db->query(sprintf("UPDATE counter SET count=count+%d,"
"total_count=total_count+%d "
"WHERE server='%s' AND file='%s'", m->add,
m->add, me, f));
}
}
if(m->cheat) {
cnt = (string)(Gmp.mpz(cnt)+(int)m->cheat);
}
if(m->style)
{
string url = id->conf->call_provider("counter", "query_internal_location");
if(url) {
m->src = url + CURL(m->style, cnt);
foreach(cargs+aargs, string tmp)
m_delete(m, tmp);
return make_tag("img",m);
}
}
return cnt;
}
// Module Create
void create()
{
defvar( "tagname", "sqlcounter", "Tag name", TYPE_STRING,
"RXML name of the tag");
defvar( "sqlserver", "mysql://localhost/counter", "SQL server",
TYPE_STRING,
"This is the host running the SQL server with the Counter database "
"where is stored all the counter informations.<br>"
"Specify an \"SQL-URL\":<ul>"
"<pre>[<i>sqlserver</i>://][[<i>user</i>][:<i>password</i>]@]"
"[<i>host</i>[:<i>port</i>]]/<i>database</i></pre></ul></ul>"
"Valid values for \"sqlserver\" depend on which "
"sql-servers your pike has support for, but the following "
"might exist: msql, mysql, odbc, oracle, postgres." );
defvar( "closedb", 1, "Close the database if not used", TYPE_FLAG,
"Setting this will save one filedescriptor without a small "
"performance loss." );
defvar( "timer", 60, "Database close timer", TYPE_INT,
"The timer after which the database is closed",0,
lambda() { return !QUERY(closedb);} );
}
// Status of the module (usefull to see the usage of the system !)
string status()
{
return "Called <b>"+ usecount +"</b> times since " + starttime;
}
// Start the program and set up some things...
void start(int num, object conf)
{
module_dependencies( conf, ({ "counter" }));
tagname = query("tagname");
usecount = 0;
return;
}
// $Log: sqlcounter.pike,v $
// Revision 1.4 2000/11/29 16:36:46 kiwi
// Corrected Roxen 2.1 woes
//
// Revision 1.3 2000/07/17 17:31:59 kiwi
// Ajout des fonctions de connection/deconnection automatique sur la
// base SQL.
//
// Revision 1.2 2000/06/05 21:05:17 kiwi
// Modification du compteur :
//
// - Changement de nom,
// - ajout fonction reset
// - ajout de stats
//
// Revision 1.1 2000/06/05 18:36:13 kiwi
// Original version by David Hedbor :)
//
// First checking of basic graphical sql-counter
//
|