How to log user actions with php and mysql? - php

I'm working on my CMS and I want it to log activities by users and other admins.
For example: when new user registers or admin makes a new news post -> update last activity.
I want to know what is the best and easiest way.

Create a table in your database to
log your user activity.
Define the various activity types
that can happen in your App.
Create a common function that logs
any activity to that table.
Call that function from anywhere
you perform log-worthy activities in
your app.
You can then write a reporting tool that gives your admins access to those logged activities, you can filter by user, time and activity types.
In my log-framework, I specially mark activities which could be seen as malicious actions and assign them different numeric threat-values. If the sum of a user's thread-value reaches a certain threshold I log-out the user.
Ideally if you write an Application, you write your infrastructure code like logging at the very beginning and then use it in all your business logic code later.
Edit for cleanup:
Over time you may collect lots of records in that table. Depending on your requirements you could do different things.
Delete any entries older than x days (maybe a year)
Delete any entries of certain types older than x days, but keep entries of other types for longer, or forever.
Move entries older than a certain threshold into an archive log table. This keeps your main table small but allows you to access older log data if you really have to. I have a checkbox Use archive on my review logs page.

Basic Answer
Instead of doing this yourself, from scratch, check out how some existing systems do it, and if their license allows, use their design and code (make sure you document what code you've used and add a copyright notice to your CMS somewhere).
Possibly Helpful Example
I'm not sure about PHP CMS's which do this, but I know Django's admin app does. Django is implemented in Python, but it should be fairly straightforward to port this code over to PHP. Even if the code isn't a straight port, the design could be ported.
The file which contains the logging is in the admin module in models.py.
Some key aspects:
The data model for the logging table:
class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.TextField(_('object id'), blank=True, null=True)
object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)
objects = LogEntryManager()
And the LogEntryManager, which saves the actual log entries:
class LogEntryManager(models.Manager):
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message)
e.save()

I use two tables for activities, one that gives each activity an id, and another one that just logs the user id, activity id, and a timestamp. I do this because an int takes up less space than a string, so why log the same strings over and over? The second one isn't really necessary, you just just as easily keep the action codes in a text file for your own reference, but the db just seems like a easier place to remember.
In the past I've used a function to handle the actual logging actions, but the next time I do it I'm going to be using the Observer pattern. It appears to be a lot more flexible, and I've already had to edit out logging function calls from older code I have that wasn't going to log anything. I much prefer reusing code with no editing required.

Its very simple to do with PHP/JAVA FUNCTION JQUERY and its AJAX data posting method...
Before posting the solution -- Lets read these two lines
Why and What we want to record ?
--- As we know only to record transaction with in the database --not all the clicks and checks -- but yes its possible with this solution....
Here is the solution step by step: -
1. create a DB Table -- to record these things
a) Page Name.
b) logged in user name
c) session details (To record all the sessions).
d) POST/GET data details (To record all the post/get data for the
page)
e) Record Created Date.
or any other thing that you want to record.
2. Create a Jquery function or PHP function -- which will be auto triggered with every page.
3. This function will collect all the session of that page,user logged in details and what data passed to that page.
Apart from this - you can also record -- from which page this new page is called -- Its pretty simple and best way to implement loggs recording features even in already running old software's :)
If you want all the Code i mentioned above to use -- Search it over the NET the mechanism i have defined just you need FUNCTION CODE -- AUTO execute function code -- simple

PHP AND MYSQL
Create a Table to save the logs
CREATE TABLE `test_loq` (
id int(11) PRIMARY KEY AUTO_INCREMENT,
page varchar(255) NOT NULL,
username varchar(255) NOT NULL,
log_time datetime DEFAULT CURRENT_TIMESTAMP,
log_action longtext NOT NULL,
log_name varchar(255) NOT NULL,
user_id int(11) NOT NULL,
ip int(11) NOT NULL
)
explain :
log_action is the kind of action made here you can write a lot of information about the action that has been made.
page is the page that the action was made of, the name of the php file
log_name is the name of the action that was done
username is the name of the user that hade made this action
user_id is the id of the user that made this action
ip is the ip adress of the user
2. Create a Class
class log
{
CONST ENVIRONMENT = 'developemnt';
private $id;
protected $log_action;
protected $username;
protected $page;
protected $ip;
protected $log_name;
private $user_id;
public function __construct(string $log_action, string $username, string $log_name)
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
if(!empty($_SESSION['id'])){
$id = $_SESSION['id'];
} else {
$id = 0;
}
$this->log_action = $log_action;
$this->username = $username;
$this->log_name = $log_name;
$this->user_id = $id;
$this->page = basename($_SERVER['PHP_SELF']);
$this->ip = $ip;
}
public function createAction()
{
global $conn;
if(!$conn) {
echo mysqli_error($conn); die;
}
$sql = "INSERT INTO test_log (`log_action`,`username`,`log_name`,`page`,`user_id`,`ip`) values ('".$this->log_action."','".$this->username."','".$this->log_name."','".$this->page."','".$this->user_id."','".$this->ip."')" ;
$sql_query = mysqli_query($conn,$sql);
if(!$sql_query){
echo mysqli_error($conn); die;
}
if(ENVIRONMENT == 'development'){
$_SESSION['msg'] = 'A new log was created ' . $this->log_name;
}
} }
explain:
ENVIRONMENT can be development or production , if it's in development it will show flash messages about the log that has been made
3.Log An Action!
example: log action for login attempts
Create a php file logincheck.php
<?php
session_start();
include("include/configurationadmin.php");
//include_once('../include/classes/config.inc.php');
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
$sql = mysqli_query($conn,"select * from ".$sufix."admin where username='".$username."'") ;
// HERE HOW TO LOG ACTION
$log = new log("Logging in attempt from $username" , $username ,'Login Attempt' );
$log->createAction();
//SIMPLE AND COOL RIGHT?
if(mysqli_num_rows($sql) > 0)
{
$rows = mysqli_fetch_assoc($sql);
if(md5($password) == $rows['password']) {
$_SESSION['id'] = $rows['id'];
$_SESSION['username'] = $rows['username'];
$_SESSION['usertype'] = $rows['type'];
mysqli_query($conn,"update ".$sufix."admin set lastlogin='".date('Y-m-d')."' where id = '".$rows['id']."' and username='".$rows['username']."'") ;
$domain = ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false;
setcookie('rrdssrdda', $rows['id'], time()+120, '/', $domain, false);
header("Location: http://localhost/test/admin-new/dashboard");
exit();
} else {
$_SESSION['message']="<div class='alert alert-danger' role='alert'>Invalid userid/password!</div>";
header("Location: http://localhost/test/admin-new/");
exit();
}
} else {
$_SESSION['message']="<div class='alert alert-danger' role='alert'>Invalid userid/password!</div>";
header("Location: http://localhost/test/admin-new/");
exit();
} ?>
Happy Coding!

Related

An user access to session of another user

Good evening, I tell you my problem. I have an application developed in PHP with Codeigniter running on a web hosting in Wiroos. Everything was working correctly, but as time passed, the application had more users who accessed simultaneously (obviously) and the following problem began to occur.
When user A logs in from location A, a session A is generated. If at the same time user B from location B accesses the application, user A's session is automatically loaded into its location B ... as it can Is this possible?
I show you the code of my application to see if you can help me
Login Controller
public function index($estadoLogin = 0){
if($this->session->userdata('estado_sesion'))
{
redirect(base_url()."panel");
}
$data['estadoLogin'] = $estadoLogin;
$data['title'] = "Iniciar SesiĆ³n - LandingApp";
$data['bodyClass'] = "external-page sb-l-c sb-r-c";
$this->load->view('templates/header',$data);
$this->load->view('login/loginbox', $data);
}//End method index
Login Method
public function ingresar(){
$correo = $this->security->xss_clean(strip_tags($this->input->post('correo')));
$pass = md5($this->security->xss_clean(strip_tags($this->input->post('password'))));
$Usuarios = new Usuario_Model();
$result = $Usuarios->login($correo, $pass);
if(count($result)>0){
foreach($result as $u){
$this->session->unset_userdata('id');
$this->session->unset_userdata('mail');
$this->session->unset_userdata('nombre');
$this->session->unset_userdata('activo');
$this->session->unset_userdata('logo_empresa');
$this->session->unset_userdata('nombre_empresa');
$this->session->unset_userdata('id_empresa');
$this->session->unset_userdata('nivel');
$this->session->unset_userdata('estado_sesion');
$this->session->set_userdata('id', $u->id);
$this->session->set_userdata('mail', $u->mail);
$this->session->set_userdata('nombre', $u->nombre);
$this->session->set_userdata('activo', $u->activo);
$this->session->set_userdata('nivel', $u->nivel);
$this->session->set_userdata('nombre_empresa', $u->nombre_empresa);
$this->session->set_userdata('id_empresa', $u->id_empresa);
$this->session->set_userdata('logo_empresa', $u->logo_empresa);
$this->session->set_userdata('avatar_user', $u->avatar_user);
$this->session->set_userdata('estado_sesion', TRUE);
redirect(base_url()."panel");
}//End foreach
}else{
$this->session->set_flashdata('mensaje', 'El usuario o password es incorrecto');
redirect(base_url()."login/index/1");
//$this->index(1);
}//End if
Panel Controller
public function index(){
$id = $this->session->userdata('id');
$id_empresa = $this->session->userdata('id_empresa');
$data_session['title'] = "Panel General";
$data_session['opcionMenu'] = "panel";
$data_session['bodyClass'] = "dashboard-page";
$data_session = $this->session_data_lib->set_data_session($data_session); //cargo las variables de sesion
if ($data_session['nivel']==1 || $data_session['nivel']==2){
$data_counters = $this->counters_lib->get_admin_counters(); //cargo las variables de contadores
}else{
$data_counters = $this->counters_lib->get_user_counters(); //cargo las variables de contadores
}
$this->load->view('templates/header', $data_session);
$this->load->view('templates/menu_top', $data_session);
$this->load->view('templates/menu_left', $data_counters);
if ($data_session['nivel']==0) {
$this->load->view('panel/panel_user',$data_counters);
}else{
$this->load->view('panel/panel_admin',$data_counters);
}
$this->load->view('templates/footer',$data_counters);
}//End method index
Session_Data_lib > set_data_session
public function set_data_session($data_session){
$data_lib_session['title'] = $data_session['title'];
$data_lib_session['opcionMenu'] = $data_session['opcionMenu'];
$data_lib_session['bodyClass'] = $data_session['bodyClass'];
$data_lib_session['nombre'] = $this->CI->session->userdata('nombre');
$data_lib_session['userid'] = $this->CI->session->userdata('id');
$data_lib_session['nivel'] = $this->CI->session->userdata('nivel');
$data_lib_session['avatar_user'] = $this->CI->session->userdata('avatar_user');
$data_lib_session['logo_empresa'] = $this->CI->session->userdata('logo_empresa');
$data_lib_session['nombre_empresa'] = $this->CI->session->userdata('nombre_empresa');
$data_lib_session['id_empresa'] = $this->CI->session->userdata('id_empresa');
$data_lib_session['arr_css'] = array("absolute_admin/assets/fonts/iconsweets/iconsweets.css");
$data_lib_session['lastSegs'] = $this->CI->panel_model->get10LastSeg($this->CI->session->userdata('id_empresa'));
return $data_lib_session;
}//End method set_data_session
I tried to migrate Codeigniter to version 3.0, and even make the following configuration in application / config / config.php
$config['sess_driver'] = 'database';
$config['sess_cookie_name'] = 'ci_sessions';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = 'ci_sessions';
$config['sess_match_ip'] = TRUE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
I do not know what else to try, I have the feeling that it can be a server configuration, but I still had no response from the support other than this:
Our servers have a micro-cache layer managed by nginx that may have
caused this behavior, now I deactivated it for your domain. Could you
please try it again?
Obviously the problem persists. I do not understand how it can be that two different users from different locations can access the session of the other at the time that both are consulting the server. I hope you can help me, thanks!
As I said in the comments you should update your passwords to use PHP's hash_password instead of md5
I can't say this is your issue, but you should verify that you do not have duplicate data in your users table. This code
public function ingresar(){
$correo = $this->security->xss_clean(strip_tags($this->input->post('correo')));
$pass = md5($this->security->xss_clean(strip_tags($this->input->post('password'))));
Specifically
$this->input->post('correo')
CI can return FALSE if the data is not set in the $_POST so if there is a problem you're are not checking if you are using FALSE for either one or both the password and the email. You should be checking like this, for example
public function ingresar(){
if(!$this->input->post('correo'))
//throw an error - stop execution, etc.
if(!$this->input->post('password'))
//throw an error - stop execution, etc.
/* ...continue on if both inputs are valid ... */
If you do not have unique fields set in your Database, you could have dozens of duplicate users with the same password and email. Both set to false
I can't tell this without knowing what constraints you have set on your DB fields for the user table. But, if you don't have the proper constraints then it may be possible to have multiple users with a password of false and an email of false ( at least according to your login code ) which would pass into your database lookup and pull multiple results.
The only way to know for sure is to look in PhpMyAdmin, at your user table, and see if you have duplicate passwords and emails in there.
Also in line with multiple users, you are not restricting login to 1 account only. Consider this part of your code.
public function ingresar(){
/* ..... */
if(count($result)>0){ //should expect only one user returned.
foreach($result as $u){
A loop here implies we are expecting one or more return records. This should throw an error if more then 1 user is returned. Emails should be unique and we should never have 2 or more users returned when looking up a user by his email. ( again without seeing the implementation of your User Model I can only guess ). But you should be explicitly checking like this:
public function ingresar(){
/* ..... */
if(count($result)==1){
//log user in
}else if(count($result)>1){
//this should never happen but it is, so you should pay attention here
}else{
//user not found or wrong password.
}
Something that could happen with the loop you have there, is that the session data will be overwritten until the last duplicate record. So everyone with a 'bad' account gets logged in as the last record pulled by the bad email data.
All in all I would say there are a lot of "holes" in your code that could allow this to happen under the right conditions. Most of this is "Guesses" based of what I can see an the loose constraints you have in place for login. The login is the gateway to your application and should be buttoned down much tighter then this.
PS. I used google translate, so I hope I read everything correctly.
Thanks for your replies. Problem was is micro-cache layer managed by NGINX. My hosting support disabled that and all works fine!

ACL implementation for databases

I'm trying to implement an ACL in my database. I have a database which looks like:
SYS_USERS { id, name, .. }
AUTH { au_id, au_name, .. }
USER_GROUPS { sys_users_id, auth_id } // bridge table
And say AUTH data looks like:
au_id au_name ...
1 admin ...
2 staff ...
... ... ...
My question is, how can I structure my query from php such that upon login, depending on your authentication level, you are presented different pages?
At the moment I have this, which seems a little off:
<?php
// code which verifies session variables etc here
$mysql_hostname = 'localhost';
$mysql_username = '...';
$mysql_password = '...';
$mysql_dbname = '...';
try {
/* Set up new DB object */
$db = new PDO("mysql:host=$mysql_hostname;dbname=$mysql_dbname", $mysql_username, $mysql_password);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* authenticate user access level */
// username = 'blah' for question clarity
$query = $db->prepare('SELECT au_name FROM auth WHERE au_id = (SELECT auth_id FROM user_groups WHERE sys_users_id = (SELECT id FROM sys_users WHERE username="blah"))');
// do something with results..
} catch(Exception $exception) {
$message = 'We are unable to process your request. Please try again later';
}
?>
So I guess my questions are:
Is the SELECT query adequate? Or should I use an INNER JOIN to achieve the same results? (Does it matter?)
Upon success of the query, how do I show the page depending on the level? For example, if it returned admin do I write a function such that
if ($result == 'admin') {
// show admin.php
} elseif ($result == 'staff') {
// show staff.php
} else { ... }
But this seems rather 'hard coded', i.e. if we were to extend the system for more AUTH roles, we would need to then add in more IF/ELSEIF statements to the above.
Anyone able to lead me in the right direction?
Thanks.
EDIT
So I was thinking of a new way to do this. I could add two more database tables:
PAGES { id, page_name .. }
AUTH_PAGES { au_id, pages_id, .. } // bridge between auth and pages
Which then in pages I could store page_name which would hold the authentication level required to view that page?
For example: a page called admin_page.php could only be accessed by administrators. Therefore this row in pages looks like:
id page_name
1 admin_page.php
2 members_page.php
and auth_pages:
au_id pages_id
1 1
1 2
Which is to say the auth row admin (au_name) has access to admin_page.php and members_page.php. Then all I would need to do in my PHP would be to cross reference the page name with the id from pages table with auth_pages table using echo basename($_SERVER['PHP_SELF']);.
Does that make any practical sense?
Since you mentioned its going to be simple so this is what I can suggest you.
At the time of login get the user id and then run a query with the id user as
select
a.au_name,a.au_id
from USER_GROUPS ag
inner join SYS_USERS su on su.id = ag.sys_users_id
inner join AUTH a on a.au_id = ag.auth_id
where ag.sys_users_id = {id of the user retrieved after the login validation}
Now Execute the above query and get the au_name and store it in a session variable as
$_SESSION['au_name'] = {auth name from the above query} ;
Create a function as below and execute it after the login.
get_page_access($au_id){
run a query to get all the pages for the auth id you got from previous query
store them in an array and finally to a session variable as
$_SESSION['page_access'] = $array ;
$array will hold all the pages you retrive
}
Now do the redirect based on the $_SESSION['au_name'] firstime after the login.
Now what if user hotlink an URL i.e. a non-admin user try to access a page. So for that create a file called check_access.php and add include it to all the pages other than the login page.
In this page you get the URL using PHP and get the filename from the URL, then check if that filename exists on the array $_SESSION['page_access'] and if yes user is allowed to view the page else show message.
Make sure you do session_start() before the include .
This will be fairly simple in nature

What query do I need to determine if a user has favourited another?

I apologise for the simpleton question but I am having a complete blank, hence why the wording of the title is vague.
I have built a simple PHP/MySQL user favouriting system that works fine, except for one part. Once the user has favourited another user, I cannot think for the life of me how to show the user that they have already favourited that user.
This is what I have so far:
if (($user_data['username'] == $profile_data['username']) === false) {
if () { ?>
// Favourite user
<?php } else { ?>
//See whether favourited or unfavourite user
<?php } } ?>
The table structure of favourites is simply two columns, favouritee being the profile favourited and favouriter being the current user favouriting. This table is joined to a main users table and the columns are populated by username strings.
EDIT
This is what I have got so far:
$username = $user_data['username'];
$favouritee = $profile_data['username'];
$check_fav = mysqli_query("SELECT `id` FROM `favourites` WHERE (`favouritee` = '$favouritee' AND `favouriter` = '$username')");
if (mysqli_num_rows($check_fav) == 1) {
// Favourite user
} else {
//See whether favourited or unfavourite user
}
(Posted on behalf of the OP):
Working code:
if (($user_data['username'] == $profile_data['username']) === false) {
$username = $user_data['username'];
$favouritee = $profile_data['username'];
$check_fav = mysqli_query("SELECT `id` FROM `favourites` WHERE (`favouritee` = '$favouritee' AND `favouriter` = '$username')");
if (mysqli_num_rows($check_fav) == 1) {
// Favourite user
} else {
// Unfavourited/check
}
}
To find whether a user has favourited another user, assume $myUsername as the logged-in user's username from the session, and assume $otherUsername coming from a profile page of another user (or perhaps a paged list of users).
SELECT 1 FROM favourite
WHERE favouriter = :favouriter AND favouritee = :favouritee
You can then inject parameters $myUsername into :favouriter and $otherUsername into :favouritee and if you get a row, you already have a favourite connection, and if you get zero rows, there is no favourite connection.
This is just the raw query, so of course you'll need to add PHP database code around this. If you're not familiar with this, take a look at the docs for PDO or MySQLi at php.net - both sections will give enough information to get you up and running.
That said, assuming usernames are stored in the user table, I'd be inclined to switch the two columns in the favourite table to integer foreign keys - it'll be faster and will save disk space. I'd call these from_user_id and to_user_id to make it clear what they are foreign keys of, and the direction of the favourite.

Determining Account Type from table and Registering Session

Basically what i am trying to do here is to read from the table in my database using the customers login details, then retrieve the record that matches this information. In this table there is a column called "AccountType", this differentiates the average user from a manager, if this column is 1, they are a average user. If this column is 2, they are a manager.
Now im having issues implementing this in my code, below is the snippet of my process script for the login:
<?php
***session_start()
$query = mysql_query("SELECT * FROM accounts WHERE username='$username' and password='$password'", $db) or die ("Query failed with error: ".mysql_error());
$count=#mysql_num_rows($query);
if(***$count == 1)
{
***$user_row = mysql_fetch_array($result)
$userid = $user_row["userid"];
$_SESSION['userid'] = $userid;
$customername = $user_row["customername"];
$_SESSION['customername'] = $customername;
$AccountType = $user_row["accounttype"];
if ($AccountType == 2)
{
$_SESSION['manager'] = $AccountType;
}
Depending on this, when my check login script which every page includes, it will display specific links on the navigation depending what there account type is, if they are user they will have access to normal links, but if they are a manager they have access to admin functions, below is the code snippet for this also:
***session_start();
if (***isset($_SESSION['userid']))
{
$employeeid = $_SESSION['userid'];
$firstname = $_SESSION['customername'];
if (***isset($_SESSION['manager']))
{
$User_Options .='Manager links go here';
}
else
{
$Links .='Normal Links go here';
}
}
Thats just a basic truncated version, but that gives the basis of what im trying to accomplish. I am guessing down to using the while loop its overwriting the session, which i understand, however there will only be one record for the information i am searching. It works to some extent, however even if the AccountType is 1, it displays the options for 2.
Can anyone assist me further in solving this issue? Thankyou!
Use something like this on the login form:
$_SESSION['manager'] = false;
if ($AccountType == 2) {
$_SESSION['manager'] = true;
}
then later:
if ($_SESSION['manager']) {
// display manager-only options
} else {
// display user-only options
}
// Display options for everyone here

Limt the amount a user downloads from a website, and redirect when limit is reached?

Sorry for the vague, title! I have a website with a lot of PDF files and limited monthly bandwith. What i would like to achieve (in PHP) is a way to limit each user ($_SESSION?) to a certain limit - say 50MB, and beyond that when they clicked to download another file they would be redirected to a webpage denying any further downloads (for the next 24 hours, say).
Is this possible? I'm not sure if my download "counter" can only count .pdf files (I dont want vistors to be blocked from browsing the site if they reach the limit). Any psuedo code would be greatly appreciated.
If you have all of your downloads go through a single php script:
<a href="download.php?file='filename.pdf'" />
You can do pretty much whatever you want. That php file can deliver all of your files (keeping them out of the webroot), write to your _SESSION, and it can perform your redirect. Enjoy.
If you already have a user system, I would recommend to store all information within the users profile.
So there's no problem if he deletes all his cookies and relogins!
And for guests, I would recommend captchas and session or IP based restrictions.
// Pseudo code
// download.php
function UserHasReachedLimit($file)
{
$info = $Database->QueryUserInfo('limit');
$max = $Database->GetLimitForFile($file);
if ( $info[$file] > $max )
return false;
else
return true;
}
if ( IsUser() )
{
if ( UserHasReachedLimit() )
error();
else
download();
}
else // guest
{
// session or IP based restrictions...
}
I'd probably stay away from sessions for this. Sessions are volatile and susceptible to various browser behavior. For example, in Firefox if a session is initialized, I can close Firefox, visit the same site, and session is still active. However in IE if I open up multiple tabs and visit the same site, each tabbed instance gets a new session id.
I'd recommend setting up an account system where a user has to log into your site. Then you can track their download amount at the account level, which will persist between multiple sessions.
I think you are trying to avoid forcing user to register in your site, while you are trying to track per visitor bandwidth with is unpractical with the common ways(cookies, ip ...). So, the best way(in my opinion, of course there are many improved solutions) is to make a simple registration form, say name, password and email, put an activation system per email to protect your site from of user, now each user logged in and tried to download a file, you process his request in the following steps:
1) user request for file name.pdf (check its availability and size(important)).
2) check user bandwidth:
$query = sql_query("SELECT Bandwidth, LastDownload FROM Users, Stats WHERE USER_ID=5");
$result = sql_fetch($query);
if ($result['Bandwidth'] < 50M)
showDownloadLink();
else if($result['LastDownload'] - currentTime() !=0)
echo "please wait to the next 24h";
Database should be like this:
Users:
ID_U int(key, auto increment), Name varchar(25), email varchar(255), password varchar(32), Bandwith float
Stats:
ID_S int(key, auto increment), LastDownload time, ID_U integer
Note:
Each time user download a file, you update Bandwidth row for the right user, so later you can check if particular user reach its limit or not. You have also to reset it after each 24H.
This is a generic solution and many thinks have to be checked, like the counter bandwidth must be reset every 24H.
Create a table to store count downloads
CREATE TABLE IF NOT EXISTS `downloaded` (
`ip` varchar(200) NOT NULL,
`count` int(11) NOT NULL DEFAULT '0',
`last_access` datetime DEFAULT NULL,
UNIQUE KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
<?php
/*
$limit => Number of Downloads Allowed
$period => In minutes
*/
function UserHasReachedLimit($limit, $period) {
$ip = addslashes($_SERVER['REMOTE_ADDR']);
$dl = false;
$sql = sprintf("SELECT UNIX_TIMESTAMP(last_access) last_time, count FROM downloaded WHERE ip = '%s' ORDER BY last_access DESC", $ip);
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) { // There is a registered IP already
$last_xs = mysql_result($res, 0, 'last_time');
$last_xs += $last_xs+$period * 60;
$count = mysql_result($res, 0, 'count'); // number of downloads by this ip
if ($count == $limit && $last_xs > time()) { // we check if downloads reached in this period
$dl = true;
} else {
$sql = sprintf("UPDATE downloaded SET count = CASE WHEN count >= %s THEN 0 ELSE count+1 END, last_access=now() WHERE ip ='%s'", $limit+1, $ip); // we just update download count + 1
mysql_query($sql);
}
} else { // There is not a registered IP and we create it
$sql = sprintf("INSERT INTO downloaded VALUES ('%s', '0', NOW());", $ip); mysql_query($sql);
}
return $dl;
}
/*
Usage
*/
$limit = 2;
$period = 2;
if(UserHasReachedLimit($limit, $period) == true) {
// User reached number of 2 downloads in 2 minutes
} else {
// Continue downloading
}
?>

Categories