php and mysql session destroyed before update - php

so im trying to have a table that shows which users are online on my site I have everything working besides the part where if someone closes the site without logging out first they stay on the table as logged in. i'll try to eplain my situation the best I can.
in my database I have a users table in that table is online when you login your online status is set to 1, when you click logout online is set to 0. here is the code im trying to use so that if someone is inactive for 10minutes they will be logged out ( online set to 0 )
<?php
session_start();
$timeout = 10; // Set timeout minutes
$logout_redirect_url = "logout.php"; // Set logout URL
$timeout = $timeout * 60; // Converts minutes to seconds
if (isset($_SESSION['last_activity'])) {
$elapsed_time = time() - $_SESSION['last_activity'];
if ($elapsed_time >= $timeout) {
mysql_query("UPDATE users SET online = 0 WHERE username = '".$_SESSION['username']."'")
or die(mysql_error());
session_destroy();
header("Location: $logout_redirect_url");
}
}
$_SESSION['last_activity'] = time();
?>
This works fine it logs them out if they wait the 10minutes, but if they just close the browser it keeps online = 1 which displays them on the list.
I believe the problem is that when they close the browser that destroys the session before it gets the chance to update the users online and set it to 0
is there anyway around this? help would be very much appreciated thanks :)
btw I know not to use mysql anymore and everything, I will try to switch to something else once I learn more and get everything working first.

The underlying problem is that when a user closes their browser, there's no reliable way to first communicate with your server. [There are things you could try, but in my opinion that's a rabbit hole you shouldn't go down] All you really "know" is how recently the user visited your site logged-in. (in your case $_SESSION['last_activity']).
At a basic level, you will want to start persisting that information in your database, and then have a process kicked off occasionally to cleanup stale sessions.
e.g.
//On page load with a logged-in user
mysql_query("UPDATE `users`
SET `last_activity` = '" . time() . "'
WHERE `username` = '" . mysql_real_escape_string($_SESSION['username']) . "'");
note that assumes you've added an INT column to your table named last_activity
and then in a process that executes, say, every 15 minutes:
mysql_query("UPDATE `users`
SET `online` = 0
WHERE `last_activity` < '" . strtotime('-30 minutes') . "'");
Unix servers have something called cron especially for period tasks/scripts, so that's what I would recommend for kicking off your cleanup script.

Related

Users not going offline

So basically I have this script for my server that keeps all the users up to date on my panel for my .net licensing system, but for some reason some of the users don't go offline when they are most definitely not connected to the server.
Basically every time the user checks in( every minute ) it will update their account with the time they checked in. Then I have the following script to check if the user's check in time is 2 minutes behind the time it is now, so then it will set them offline as they haven't checked in, in 2 minutes.
My problem is, it is working for most users, but like 9% of them don't go offline when checking and there seems to be no errors, unless i'm wrong, can anyone help me out in fixing this simple problem?
PHP SCRIPT:
$SQLGetUsers = $odb -> query("SELECT * FROM Account");
while ($getInfo = $SQLGetUsers -> fetch(PDO::FETCH_ASSOC))
{
$id = $getInfo['id'];
$lastlogon = $getInfo['lastlogon'];
$time = time();
$timeouttime = ($time - 120);
if ($lastlogon <= $timeouttime)
{
$odb->exec("UPDATE Account SET `online` = '0' WHERE `id` = '$id'");
}
$odb->exec("OPTIMIZE TABLE Account");
}
I do know this is a bad script, but it's the only way I personally know of executing this specific command. Thanks to anyone who can help!

Validate user's rights

I'm writing an hour registration system for my projectteam at school. Everything is pretty much working, but I can't seem to get the validation of user rights to work.
Validation is done in the acctype field within the user table. If 0 (guest), you can only view the list of hours, if 1 (specialist) you can add your own hours and if 2 (project-manager), you can review the hours users have submitted.
At first I was only using the $account query but instead of selecting them all I selected acctype only.
Does anyone have any idea what am I doing wrong?
$cookie = $_COOKIE['user'];
$account = mysqli_query($conn, "SELECT * FROM user WHERE user = '" . $cookie . "'");
$acctype = mysqli_fetch_assoc($account->acctype);
if(isset($cookie) && $acctype >= 1) {
} else {
}
Jonathan
I believe there's a few things wrong here:
You're reading the cookie before checking if it's set. That's a mistake. You should see if there's a cookie, and THEN read it in. You also don't need to assign it a separate variable.
Note: As I said in my comment, user data should be in a session, not a cookie.
I don't know what your DB schema looks like, but your query is SELECT * FROM user, meaning that if you have an ID, a user name, an access level, and some other things, you're going to get ALL that into the var $acctype, which obviously isn't an integer.
I think the fix is to execute your query, get your results, and then compare the row(s) returned and only check the acctype part:
if ($row['acctype'] >= 1){
}
Documentation: http://us1.php.net/mysqli_fetch_assoc

How do you write a program to automatically modify existing SQL database every x minutes?

I am a PHP/SQL novice user....Finishing off my first PHP website. The question is similar to the initial question found at:
http://forums.phpfreaks.com/topic/266235-modifying-database-after-a-set-time-limit/
but I did not completely understand the answer. Similar to that user, I have an entire column (called status) of a database (called challenge) that can take 3 values for status - 'inactive', 'pending', or 'active'. In the normal flow of website operations, User A will typically click a button (event 1) which creates a new row (with a unique *challenge_id*) in the database and triggers a status of 'pending' in that row. User B can change the status with other clicked buttons, which can set the status to 'inactive' or 'active'.
One undesirable scenario is where USER B does nothing (i.e., no event trigger). In this case, User A is unfortunately stuck, waiting for the status to change from 'pending' to either 'active' or 'inactive' before he/she can click and trigger the next event 1. This situation could occur for example if User B gets tired of the site and does not use it anymore, leaving 'pending' requests unanswered.
Clearly, I can manually alter the SQL, changing any 'pending' status to 'inactive' after a certain time limit. This would be fine at the beginning, but if the site ever became popular, this would take more time. Is there any way to write a non-PHP program to account for this 'no event trigger' scenario where all 'pending' status SQL entries are automatically altered after a certain time limit? Or can PHP do this? I tried writing a php script that would sweep the database every time any user logged in (note: *challenge_id* is created by an event triggered on a different PHP page):
<?php session_start();
if ((($_SESSION['role']) != SHA1('user')) && (($_SESSION['status']) != SHA1('active')))
{
header( 'Location: index.php' ) ;
session_destroy();
} else
include 'connect.php';
$_SESSION['login_id'];
$universaltime = time();
$sqlt = mysql_fetch_assoc(mysql_query("SELECT challenge.challengetime,
challenge.status FROM challenge"); //Selects an array of all values for challengetime
//and status for all users I presume
while ((($universaltime - $sqlt['challengetime']) > 1000) &&
($sqlt['status'] == 'pending'))
{
$sqlt1 = mysqli_query("UPDATE challenge SET $sqlt['status'] ='inactive'");
//Also tried with if instead of while
}
?>
I'm sure my code can be improved...any help would be appreciated greatly! Or do I need to use something other than PHP?
First of all your UPDATE statement is wrong. Assuming that challengetime is of int data type holding unix time values your UPDATE statements should look something like this
UPDATE challenge
SET status = 'inactive'
WHERE status = 'pending'
AND 1000 < UNIX_TIMESTAMP() - challengetime;
It can be and should be run on its own. You don't need to select anything prior to calling it.
Therefore you can change this part
$universaltime = time();
$sqlt = mysql_fetch_assoc(mysql_query("SELECT challenge.challengetime,
challenge.status FROM challenge"); //Selects an array of all values for challengetime
//and status for all users I presume
while ((($universaltime - $sqlt['challengetime']) > 1000) &&
($sqlt['status'] == 'pending'))
{
$sqlt1 = mysqli_query("UPDATE challenge SET $sqlt['status'] ='inactive'");
//Also tried with if instead of while
}
with just
$sql = "UPDATE challenge SET status = 'inactive' WHERE status = 'pending' AND 1000 < UNIX_TIMESTAMP() - challengetime";
$result = mysql_query($sql);
if (!$result) {
die('Invalid query: ' . mysql_error()); //TODO better error handling
}
Now to make it execute periodically on it own you don't necessarily need php. You can:
First option Use MySQL event.
To execute this statement every day at 11pm
CREATE EVENT change_status
ON SCHEDULE EVERY 1 DAY
STARTS CURDATE() + INTERVAL 23 HOUR
DO
UPDATE challenge
SET status = 'inactive'
WHERE status = 'pending'
AND 1000 < UNIX_TIMESTAMP() - challengetime;
Use SHOW PROCESSLIST to check if event scheduler is enabled. If it's ON you should see a process "Daemon" by user "event_scheduler".
Use SET GLOBAL event_scheduler = ON;to enable the scheduler if it's currently not enabled.
More on configuring event scheduler here
Second option Use crontab to invoke CLI mysql
/usr/local/mysql/bin/mysql -uuser -ppassword -e " UPDATE challenge SET status = 'inactive' WHERE status = 'pending' AND 1000 < UNIX_TIMESTAMP() - challengetime"
If your web & PHP server is running on Linux (which is often the case) and if the delay x is more than a few minutes (i.e. x>=5 minutes) then you could use crontab(1) and add a crontab(5) entry. Remember to use absolute paths there. That entry would run (periodically) some script (which you could code in PHP, but also in some other scripting language like Python or OCaml) which would update the MySQL database.

Ending a session/clearing cookies

Ok, so I've got some legacy code from another company that I have to maintain/repair. One of the features of this code is that there are several sites tied to this one main site, and whenever you navigate from the other sites to this site, a logo from the other site is displayed on the main site (basically this is a service for multiple banks to use, and each bank wants its own branding).
The problem is is that once you navigate from, say, site1 to main site, if you then navigate from site2 to main site, you'll still get site1's branding.
Is there a way to clear a session's cookies? So far they've got code like this, which should THEORETICALLY work:
else
{
setcookie("cuid","",time() - 31536000); //DELETES COOKIE
if( $cuid_demo!="samplecu" && $cuid!="samplecu" )
setcookie("cuid",$cuid,time()+2592000); //2592000 = 1 month
$link=dbconnect();
if(!$link)
die("error: Could not connect to database - ".dberror() );
select_db($link) or die("error: ".dberror() );
if (isset($admin_id))
{
$cuid = $admin_id;
$id = $admin_id;
}
$query="UPDATE cusucceed SET visits=visits+1 WHERE id = '$cuid'";
$result=dbquery($link, $query)or die("Database Server Error 2: ".dberror());
include("index_main.php");
} //END IF/ELSE TO CHECK FOR PREVIOUS VISIT WITH THE PAST MONTH
}
Before it does nothing. Shouldn't setcookie("cuid", "", time() - 31536000); delete the cookie, as the comments say?
You need to set correct domain for cookie when you reset it.
setcookie('name', NULL, -86400, 'cookie_path', 'subdomain.site1.com');

Tracking unique visitors only?

Currently I have a file called "hits.php" and on any page I want to track page hits I just use <?php include("hits.php"); ?>
How can I track unique visitors only though? My hits are false since it can be refreshed by the same person and hits go up.
Here's my source:
<?php
$hits = file_get_contents("./client/hits.txt");
$hits = $hits + 1;
$handle = fopen("./client/hits.txt", "w");
fwrite($handle, $hits);
fclose($handle);
print $hits;
?>
I don't really know how I could do cookie checking... is there a way to check IP's? Or what can I do?
Thanks StackO.
The simplest method would be cookie checking.
A better way would be to create an SQL database and assign the IP address as the primary key. Then whenever a user visits, you would insert that IP into the database.
Create a function included on all pages that checks for $_SESSION['logged'] which you can assign whatever 'flag' you want.
If $_SESSION['logged'] returns 'false' then insert their IP address into the MySQL database.
Set $_SESSION['logged'] to 'true' so you don't waste resources logging the IP multiple times.
Note: When creating the MySQL table, assign the IP address' field as the key.
<?php
session_start();
if (!$_SESSION['status']) {
$connection = mysql_connect("localhost", "user", "password");
mysql_select_db("ip_log", $connection);
$ip = $_SERVER['REMOTE_ADDR'];
mysql_query("INSERT INTO `database`.`table` (IP) VALUES ('$ip')");
mysql_close($connection);
$_SESSION['status'] = true;
}
?>
There isn't a perfect solution, but the first two methods (IP address and/or cookies) are the most reliable, and a combination might be even better.
Rather than reinventing the wheel I used an off the shelf solution. For commercial reasons I avoided Google Analytics (I don't want Google to know my web stats - their best interests are not mine). They're probably fine for non-commercial websites, or if you don't use Google for advertising. There are also dozens of alternatives. Eg I use Getclicky.com
At a basic level, you can get the client's IP address by using the PHP $_SERVER['REMOTE_ADDR'] property
Consider setting a cookie or using a session, though this can be defeated by deletion of a cookie or cookie rejection. See the PHP setcookie docs for more info.
There are other methods for browser fingerprinting - check out all the different data you could conceivably use at https://coveryourtracks.eff.org/
How about google analytics if you cant. you could do a database or create another file with the IPs in it, but it could get complicated with a flat file like that.
I found the solution of very poor quality and just a quick and dirty way of doing it.
I too was developing something similar and formulated a quick method which works without redundancy.
I needed a counter for every time someone accessed another user's profile.
Pseudo:
Create a table with viewer's name and viewee's name (daily_views table).
Check to see if exists the viewer's name with the viewee's name (on the same row).
If they do not exist, update user counter +1 (in users table).
Else do nothing.
Reset entire table values null every 24/12 hours via cron job.
This will deny the same person accessing the same user profile to add 1 to the
counter on refresh for the whole day (or 12 hours) whereas the above solution
by Glenn Nelson would indeed add 1 to the counter, but deny adding to every
user's counter at the same time.
Not only this, but if you were to logoff and log back in to the website, then
it would simply re-add to the counter in which some cases trolls and haxorz
wannabe's will exploit this (as the session is destroyed and started again).
Here are my sample tables:
users
{
user_id INT(8) auto increment, user_name varchar(32), user_counter INT(12)
};
daily_views
{
view_id INT(8) auto increment, viewer_name VARCHAR(32), viewee_name VARCHAR(32)
};
Here is sample code I've written:
<?php
session_start();
$uname = $_SESSION['username'];
$vieweepage = $_GET['name']; //gets the name of the persons page/profile via previous page/form
$connect = mysql_connect("localhost","user","password") or die("Couldn't connect; check your mysql_connect() settings");
$database = mysql_select_db("database") or die("Could not locate database!");
$query = mysql_query("SELECT user_counter from users");
$query = mysql_fetch_row($query);
$counter = $query[0];
$viewcheck = mysql_query("SELECT viewer_name from daily_views WHERE viewee_name='$vieweepage'");
$viewrow = mysql_num_rows($viewcheck);
$newcounter = $counter + 1;
if($viewrow == 0)
{
$update = mysql_query("UPDATE users SET user_counter='$newcounter' WHERE user_name='$vieweepage'");
$insert = mysql_query("INSERT into daily_views (viewer_name, viewee_name) VALUES ('$uname', '$vieweepage')");
}
?>
currently i am using remote address and session ID for visitor.i think its valid visitor because a single user can visit no of times in a days and counter not depends on refresh its only depends on new session.
You could save a timestamp to localStoage in javascript. LocalStoage isn't removed by the browser, so you should be save to check against that. I know that it isn't serverside checking, but it may be helpful anyway.

Categories