After almost 3 days of troubleshooting I gotta ask for advice.
A have a small imageboard with 4 images and 4 'like' buttons. Earlier I made it so the number of clicks with each button stores in a .txt file. Now I basically need to make it so a person can press a certain button only once.
This is ip.txt. The number to the left is button ID, to the right is IP of the person that clicked that button.
click-001||127.0.0.1
click-002||
This is very simple. I need to make sure it stores ip when I click on my PC, then stores another IP when I click on my pad - and stops whatever I do next. Now for the last few days it's been doing anything except that!
My current code with isset. That sees the first IP but doesn't add the second:
$file2 = 'ip.txt'; // path to text file that stores counts
$fh2 = fopen($file2, 'r+');
$ip_addr = $_SERVER['REMOTE_ADDR'];
$lines2 = '';
while(!feof($fh2)) {
$line2 = trim(fgets($fh2));
if ($line2) {
$line2 = explode('||', $line2);
if(isset($line2[0], $line2[1])) {
$item2 = trim($line2[0]);
if(!empty($item2)) {
if($item2 == $id) {
if(empty($line2[1])) {
$lines2 .= "$item2||$ip_addr\r\n";
file_put_contents($file2, $lines2);
} else {
// this is where it always fails
if (!isset($ip_addr)) { $ip_all = $line2[1] . " " . $ip_addr;
$lines2 .= "$item2||$ip_all\r\n";
file_put_contents($file2, $lines2);
} else {
echo "lul";
}
}
}
}
}
}
}
fclose($fh2);
I also tried this with in_array function:
$ip_all = array($line2[1]);
if (!in_array($ip_addr, $ip_all)) {
array_push($ip_all, ',' , $ip_addr);
$ip_fin = implode($ip_all);
$lines2 .= "$item2||$ip_fin\r\n";
file_put_contents($file2, $lines2);
^ This one also sees the first IP and adds the second, but then fails to find whether the IP is already there and just keeps adding copies when I click.
This is brutal. What am I doing wrong and is there an easier way?
Use MySQL database to accomplish this.
Using a text file is super inefficient and can cause conflicts when multiple users liked at the same time!
Insert the IP to database everytime a user clicked the 'Like' button and then use a select query to determine if this IP has liked the picture before.
I do not recommend using just IP tho as some ISP gives dynamic IP that changes the IP (Public IP) address every few seconds.
Use cookies to store a unique cookie for a user (if they are not logged in) or just ask the user to login first before voting!
Information about MySQL Insert and Select are everywhere on Google.
Here's one : https://www.w3schools.com/sql/
best method: use a database or use XML (XML have very useful library)
text file method:
get file and edit...
$id = "...";
$ip="...";
$file2 = file('ip.txt');
$file2 = array_map(
function($current_line) use ($id,$ip) {
$current_line = explode('||', $current_line);
if($current_line[0] == $id){
$current_line[]=$ip;
}
return join("||",$current_line);
},$file2
);
file_put_contents('ip.txt', implode('\n', $file2));
Related
I have a PHP API front end running on a webserver. This specific PHP program is subject to distribution, thus it should be as portable as possible.
The feature I want to implement is an IP cooldown period, meaning that the same IP can only request the API a maximum of two times per second, meaning at least a 500ms delay.
The approach I had in mind is storing the IP in an MySQL database, along with the latest request timestamp. I get the IP by:
if (getenv('REMOTE_ADDR'))
$ipaddress = getenv('REMOTE_ADDR');
But some servers might not have a MySQL database or the user installling this has no access. Another issue is the cleanup of the database.
Is there a more portable way of temporarily storing the IPs (keeping IPv6 in mind)?
and
How can I provide an automatic cleanup of IPs that are older than 500ms, with the least possible performance impact?
Also: I have no interest at looking at stored IPs, it is just about the delay.
This is how I solved it for now, using a file.
Procedure
Get client IP and hash it (to prevent file readout).
Open IP file and scan each line
Compare the time of the current record to the current time
If difference is greater than set timeout goto 5., else 7.
If IP matches client, create updated record, else
drop record.
If IP matches client, provide failure message, else copy record.
Example code
<?php
$sIPHash = md5($_SERVER[REMOTE_ADDR]);
$iSecDelay = 10;
$sPath = "bucket.cache";
$bReqAllow = false;
$iWait = -1;
$sContent = "";
if ($nFileHandle = fopen($sPath, "c+")) {
flock($nFileHandle, LOCK_EX);
$iCurLine = 0;
while (($sCurLine = fgets($nFileHandle, 4096)) !== FALSE) {
$iCurLine++;
$bIsIPRec = strpos($sCurLine, $sIPHash);
$iLastReq = strtok($sCurLine, '|');
// this record expired anyway:
if ( (time() - $iLastReq) > $iSecDelay ) {
// is it also our IP?
if ($bIsIPRec !== FALSE) {
$sContent .= time()."|".$sIPHash.PHP_EOL;
$bReqAllow = true;
}
} else {
if ($bIsIPRec !== FALSE) $iWait = ($iSecDelay-(time()-$iLastReq));
$sContent .= $sCurLine.PHP_EOL;
}
}
}
if ($iWait == -1 && $bReqAllow == false) {
// no record yet, create one
$sContent .= time()."|".$sIPHash.PHP_EOL;
echo "Request from new user successful!";
} elseif ($bReqAllow == true) {
echo "Request from old user successful!";
} else {
echo "Request failed! Wait " . $iWait . " seconds!";
}
ftruncate($nFileHandle, 0);
rewind($nFileHandle);
fwrite($nFileHandle, $sContent);
flock($nFileHandle, LOCK_UN);
fclose($nFileHandle);
?>
Remarks
New users
If the IP hash doesn't match any record, a new record is created. Attention: Access might fail if you do not have rights to do that.
Memory
If you expect much traffic, switch to a database solution like this all together.
Redundant code
"But minxomat", you might say, "now each client loops through the whole file!". Yes, indeed, and that is how I want it for my solution. This way, every client is responsible for the cleanup of the whole file. Even so, the performance impact is held low, because if every client is cleaning, file size will be kept at the absolute minimum. Change this, if this way doesn't work for you.
Basically to keep it short I have an assignment for college where I have to make a few scripts.
In one of these I have to make a scripts that records when data is entered wrong in a log in form. So it will take the users IP address and place it into a text file followed by the time, date, error name (e.g. wrong username and password, wrong name, w.e.), error page and attempts made.
I have figured out:
how to get the users ip
how to get the time and date
how to record the attempts
Now, what I want to do: I want the script to search for the users IP address and if this IP address has already been recorded for a failed attempt, instead of making a new record I want it to just add 1 to the attempts made to that IP address, update the time and date. Only in the case that this IP address has not been recorded a new record should be created.
if IP ALREADY EXISTS{
FIND AMOUNT OF ATTEMPTS
attempts++;
}
else{
ADD IP;
ADD TIME;
ADD DATE;
ADD REASON;
attempt=1;
}
this is what i have so far:
<?php
$time = date("h:i:sa");
$date = date("d/m/y");
$error = "Incorrect Data Entered";
$attempts = 0;
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'];
}
$myFile = "errorLog.txt";
$file = fopen($myFile, 'a') or die("can't open file");
$searchfor = "$ip";
// the following line prevents the browser from parsing this as HTML.
header('Content-Type: text/plain');
// get the file contents, assuming the file to be readable (and exist)
$contents = file_get_contents($myFile);
// escape special characters in the query
$pattern = preg_quote($searchfor, '$ip');
// finalise the regular expression, matching the whole line
$pattern = "/^.*$pattern.*\$/m";
// search, and store all matching occurences in $matches
if(preg_match_all($pattern, $contents, $matches)){
echo "Found matches:\n";
echo implode("\n", $matches[0]);
}
else{
$attempts = 1;
$stringData = "$ip, $time, $date, $error, $attempts\n";
fwrite($file, $stringData);
fclose($file);
}
?>
If you simply append to the same text file over and over then if you really want to add up the number of attempts by each individual user, then your going to need to rewrite the full txt file as far as I'm aware.
If you want to make it easier for yourself then you can write your txt file in such a way that you can easily obtain the values you need out of it, such as the ip address.
You could write each line like:
IPADDRESS,ERROR CODE,ATTEMPTS /n
Then when you want to change that line you'll need to keep a record of which line it is. A loop will be required to parse your txt file.
So the way I would attempt it would be:
Store text from file into a variable.
Use the explode() function to create an array of records. ( Like a row in a database ). I would use it like $rows = explode("/n" , $txt);
Loop through the rows. I would probably use foreach( $rows as $row) for this.
Keep track of the row number. So create a variable for it and iterate it.
Use the explode() function again to create a list($ip, $err, $attempts) by doing: list($ip, $err, $attempts) = explode("," , $row);
Now use normal php if() function to check to see if the two ips match, if the error codes are the same.
If they do, you can do $attempts++. Now the tricky/essential part.
You will need to create a string up to but NOT including that row and place it into a variable call it something like $firstpart.
You will also need to do the same for the rows after the affect row. eg $lastpart
Now create a new string for your affected row. Easily done: $str = $ip . "," . $err . "," . $attempts;
Finally, write your txt file using the variables, in the order: $firstpart, $str, $lastpart.
Not sure if this will work, not sure how practical this is, but at the very least I hope it helps you to get some sort of brainwave for how to approach your problem.
I have a .flv file. This file needs to be hosted on our FTP server, and I need to make a php page that displays this video.
This video should be visible for only one pageload. for example:
http://somepage.com/?id=akudps2
with this URL it will load the video, one time, the next time this page is loaded the video is not visible anymore as the link expires.
there can be multiple valid links active simultaneously. So for example I could send the same video withdifferent URLs to multiple users.
MUST:
it should not be possible to get the real filename of the flv file from the page's source code. the viewer should not be able to download the file.
this should all be done without the use of a database as we dont have one on this database. it can use textfiles tough.
For your first segment in the question, you can create your own management via files.
Write a new line to a file every time you create a "key" for this, for example...
<?php
file_put_contents("sessions.txt", "sjhGtwtha 0", FILE_APPEND);
The '0' here for this instance, means "not used".
Then you can do something like this:
<?php
function isUsed($key) {
$keys = file_get_contents("sessions.txt");
foreach (explode("\n", str_ireplace("\r", "", $keys)) as $line) {
if (substr($line, 0, strlen($key)) == $key) {
return substr($line, strlen($key) + 1, 1) == '1';
}
}
return true; // ID is invalid, never been assigned at all
}
function updateFile($key) {
$keys = file_get_contents("sessions.txt");
foreach (explode("\n", str_ireplace("\r", "", $keys)) as &$line) {
if (substr($line, 0, strlen($key)) == $key)
$line = $key . ' 1';
}
file_put_contents("sessions.txt", implode("\n", $keys));
}
To decide whether it was used.
As for the second part, I'm not sure that's doable. You can try to obfuscate but eventually the browser needs to know what file is going to be played, so it will always at some point, be accessible via digging in the source.
I've setup a PHP IP Blacklist system and it works really well.
I now want it to be able to take a reason from the txt file.
ip_blacklist.txt
1.2.4.5 , No Spamming Allowed
1.2.4.5 , No Spamming Allowed
1.2.4.5 , No Spamming Allowed
Now in PHP it get's the IP to compare it to the users IP being used, that's perfect.
But if the IP matches the IP in the txt it will redirect you to a blacklist page.
I want it to display the reason for their blacklisting.
How do I get the reason matching the IP in the txt file using PHP and then linking it to
$reason?
Like the answers above, I agree that the best way to solve this would be store the user's IP in a database, but if you still need to read a file for it, this code should do the job:
<?php
//The file you will read
$file = fopen("ip_blacklist.txt", "r") or exit("Unable to open file!");
//Where we will store each ip as we read it
$ip = "";
$parts;
while(!feof($file))
{
//Split the line and save the parts
$parts = explode(" , ", fgets($file));
$ip = $parts[0];
$reason = $parts[1];
//And here you can compare it to the client's ip
//The first one is the ip read from the file
echo $ip."<br>";
//And this is how you would get the client's ip
echo $_SERVER['REMOTE_ADDR']."<br>";
}
//Close the file
fclose($file);
?>
Note that the way to get the client's IP I used is not the best by any means (since it can be spoofed really easily). For more information about that, read here How to get Client IP address in PHP?
-Edit-
And I just noticed you simply wanted to check the IP, compare it and get the reason. In that case, change the while to:
while(!feof($file))
{
//Split the line and save the parts
$parts = explode(" , ", fgets($file));
$ip = $parts[0];
if($_SERVER['REMOTE_ADDR'] == $ip)
echo $parts[1];
}
Or you could use explode:
$myTextFileLine = "1.2.4.5 , No Spamming Allowed";
$cutted = explode(",", $myTextFileLine);
echo "Ip blacklisted: ".$cutted[0].", reason: ".$cutted[1];
Edit: Edited to include preg_match method of getting result from file instead of db. This method would do the check if the user is in the blacklist as well as get the users reason.
You could simply store the users IP in your database along with a reason. Then when the check is run, if the user is in the blacklist, query the database for their ip, and return and display the reason.
$ip = $_SERVER['REMOTE_ADDR'];
$sql = 'SELECT reason FROM blacklist WHERE ip = "' . $ip . '"';
Then run that sql against your database. Ofcourse that is a rough idea and has no protection against sql injection, so I would advise in using some form of excaping and validating that $ip is in the correct format for an ip address before running the query.
The overall process would be:
Check if user is in blacklist by comparing ip to file.
User is in blacklist.
Get reason from database by the users ip.
Display reason.
If you are just looking to do it all by file, then it would be a better of getting the file contents, finding the ip and reason and displaying the reason.
This could be done using preg_match.
$file = file_get_contents('path/to/blacklist/file');
$ip = $_SERVER['REMOTE_ADDR'];
$pattern = '#' . $ip . '\s,\s.*#';
if(preg_match($pattern, $file_contents, $matches)) {
$match = $matches[0];
$explode = explode(',', $match);
$reason = $explode[1];
echo $reason;
}
Note this is untested, but I think it would work.
Hello there so I just setup this basic poll, I inspired myself from something I found out there, and it's just a basic ajax poll that waves the results in a text file.
Although I was wondering, since I do not want the user to simply mass-click to advantage / disadvantage the results, i thought about adding a new text file that could simply save the IP, one on each line, and then checks if it's already logged, if yes, display the results, if not, show the poll.
My lines of code to save the result are:
<?php
$vote = $_REQUEST['vote'];
$filename = "votes.txt";
$content = file($filename);
$array = explode("-", $content[0]);
$yes = $array[0];
$no = $array[1];
if ($vote == 0)
{
$yes = $yes + 1;
}
if ($vote == 1)
{
$no = $no + 1;
}
$insert = $yes."-".$no;
$fp = fopen($filename,"w");
fputs($fp,$insert);
fclose($fp);
?>
So I'd like to know how I could check out the IPs, in the same way it does basically.
And I'm not interested in database, even for security measures, I'm alright with what Ive got.
Thanks to any help!
To stop multiple votes, I'd set a cookie once a user has voted. If the user reloads the page with the voting form on it and has a cookie, you could show just the results, or a "You have already voted." message. Note that this will not stop craftier people from double-voting - all they would have to do is remove the saved cookie, and they could re-vote.
Keep in mind though that IPs can be shared so your idea of storing IPs might backfire - people on a shared external-facing IP won't be able to vote, as your system will have registered a previous vote from someone at the same IP address.
easiest way is to write data to file is
file_put_contents($filename, $data)
and to read data from file
file_get_contents($filename);
To get IP Address of the user
$_SERVER['REMOTE_ADDR']
See php manual for file_put_contents for more information and file_get_contents
Here is sample code
<?php
// File path
$file = 'votedips.txt';
// Get User's IP Address
$ip = $_SERVER['REMOTE_ADDR'];
// Get data from file (if it exists) or initialize to empty string
$votedIps = file_exists($file) ? file_get_contents($file) : '';
//
$ips = explode("\n", $votedIps);
if (array_search($ip, $ips)) {
// USER VOTED
} else {
$ips[] = $ip;
}
// Write data to file
$data = implode("\n", $ips);
file_put_contents($file, $data);
?>
You can use file_get_contents to save the file's content into a variable and then use the strpos function to check if the IP exists in that variable.
For example:
$ipfile=file_get_contents('ip.txt');
if (strpos($ipfile, $_SERVER['REMOTE_ADDR'])!==FALSE) // show the results
else // show the poll
Be careful with storing IPs in a text file, and then using file_get_contents() and similar functions for loading the data/parseing. As an absolute worst case, assuming that every possible IP address used your system to vote, you'd end up with a text file in the many many gigabytes in size, and you'd exceed PHP's memory_limit very quickly.