Project

General

Profile

Feature #840 ยป mod_useronline.c

Module: online user tracking -- Wojciech Hlibowicki <wojtek - Anonymous, 2006-09-07 19:46

 
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "base.h"
#include "buffer.h"

#include "plugin.h"
#include "inet_ntop_cache.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

typedef struct {
size_t max_age;
size_t active;

size_t maxips;
size_t id;
int enable;
} plugin_config;

typedef struct {
unsigned int ipl;
int hits;
time_t last;
} user_data;

typedef struct {
PLUGIN_DATA;
plugin_config **config_storage;
plugin_config *conf;
user_data **ip_data;
} plugin_data;


INIT_FUNC(mod_useronline_init) {
plugin_data *p;
p = calloc(1, sizeof(*p));
return p;
}

FREE_FUNC(mod_useronline_free) {
plugin_data *p = p_d;
UNUSED(srv);

if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
free(p->ip_data[i]);
free(s);
}
free(p->config_storage);
free(p->ip_data);
}
free(p);
return HANDLER_GO_ON;
}

SETDEFAULTS_FUNC(mod_useronline_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
config_values_t cv[] = {
{ "useronline.maxage", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
{ "useronline.active", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ "useronline.maxips", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
{ "useronline.enable", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
if (!p) return HANDLER_ERROR;
/* 0 */
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
p->ip_data = calloc(1, srv->config_context->used * sizeof(user_data *));
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->max_age = 300;
s->active = 0;
s->maxips = 1024;
s->id = i;
s->enable = 0;
cv[0].destination = &(s->max_age);
cv[1].destination = &(s->active);
cv[2].destination = &(s->maxips);
cv[3].destination = &(s->enable);
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
return HANDLER_ERROR;
}

if (s->enable) {
if (s->active==0) s->active = s->max_age/3;
p->ip_data[i] = calloc(s->maxips, sizeof(user_data));
p->ip_data[i][0].hits=-1;
}
}
return HANDLER_GO_ON;
}

#define PATCH() \
p->conf = s;
static int mod_useronline_patch_connection(server *srv, connection *con, plugin_data *p) {
size_t i;
plugin_config *s = p->config_storage[0];

PATCH();
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
data_config *dc = (data_config *)srv->config_context->data[i];
s = p->config_storage[i];
/* condition didn't match */
if (!config_check_cond(srv, con, dc)) continue;
PATCH();
}
return 0;
}
#undef PATCH

static void mod_useronline_add_env_int(array *env, const char *key, int value) {
data_string *ds;
if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) {
ds = data_string_init();
}
buffer_copy_string(ds->key, key);
buffer_append_long(ds->value, value);
array_insert_unique(env, (data_unset *)ds);
}

URIHANDLER_FUNC(mod_useronline_uri_handler) {
plugin_data *p = p_d;
size_t j;
plugin_config *s;
user_data *ipd;
unsigned int ipl;
time_t time_threshold;
time_t active_threshold;
size_t user_count = 0;
size_t user_active = 0;
int free_slot = -1;
size_t last_item = 0;
int foundat = -1;
time_t current_time = srv->cur_ts;

mod_useronline_patch_connection(srv, con, p);
if (!p->conf->enable) return HANDLER_GO_ON;

s = p->conf;
ipd = p->ip_data[s->id];
ipl = inet_addr(inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
time_threshold = current_time - (s->max_age);
active_threshold= current_time - (s->active);
for (j=0; j<s->maxips; j++) {
if (ipd[j].ipl==0) {
/*empty entry, mark it if first free slot*/
if (free_slot==-1) free_slot=j;
if (ipd[j].hits==-1) {
/* end found */
j=s->maxips;
}
} else if (ipd[j].ipl==ipl) {
/*found a previous entry...*/
foundat=j;
ipd[j].hits++;
ipd[j].last=current_time;
} else if (ipd[j].last < time_threshold) {
/*expired ip, mark it if first free slot*/
ipd[j].ipl=0;
if (free_slot==-1) free_slot=j;
}
if (ipd[j].ipl>0) {
/*count user/ip*/
if (ipd[j].last >= active_threshold) user_active++;
user_count++;
last_item=j;
}
}
if (foundat==-1) {
/*not found, add new*/
if (free_slot!=-1) {
ipd[free_slot].ipl=ipl;
ipd[free_slot].last=current_time;
ipd[free_slot].hits=1;
foundat=free_slot;
if (free_slot>(int)last_item) last_item=free_slot;
user_active++;
user_count++;
}
}
/*reduce amount of elements to scan through (mark as end, hits=-1)*/
if (last_item+1 < s->maxips) {
ipd[last_item+1].ipl=0;
ipd[last_item+1].hits=-1;
}

/*set environment variables*/

/*users online currently*/
mod_useronline_add_env_int(con->environment, "users_online", user_count);
/*users active currently*/
mod_useronline_add_env_int(con->environment, "users_active", user_active);
/*hits made by current user*/
mod_useronline_add_env_int(con->environment, "user_hits", ipd[foundat].hits);
/*max age setting for current*/
mod_useronline_add_env_int(con->environment, "users_online_age", s->max_age);
/*max active age setting for current*/
mod_useronline_add_env_int(con->environment, "users_active_age", s->active);
/*max ips setting for current*/
mod_useronline_add_env_int(con->environment, "users_online_max_ips", s->maxips);
/*current id for current*/
mod_useronline_add_env_int(con->environment, "users_online_id", s->id);
return HANDLER_GO_ON;
}

int mod_useronline_plugin_init(plugin *p) {
p->version = LIGHTTPD_VERSION_ID;
p->name = buffer_init_string("useronline");
p->init = mod_useronline_init;
p->handle_uri_clean = mod_useronline_uri_handler;
p->set_defaults = mod_useronline_set_defaults;
p->cleanup = mod_useronline_free;
p->data = NULL;
return 0;
}
    (1-1/1)