File: sqlcounter.pike

package info (click to toggle)
libroxen-sqlcounter 1.4-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 44 kB
  • sloc: makefile: 36
file content (231 lines) | stat: -rw-r--r-- 6,782 bytes parent folder | download | duplicates (3)
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 &lt;accessed&gt; 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 &lt;sqlcounter&gt;. "
	    "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
//