Related
I will validate this URL with an email address inside.
These two domains are allowed:
https://www.example.com/secure/index.php?ID=john#example.com
https://www.example.com/secure/index.php?ID=john#example-test.com
All names before the # in the email address allowed.
When the user inserts another domain after the #, like this:
https://www.example.com/secure/index.php?ID=john#gmail.com
they will get an error. How can I do this?
Try this:
$email = $_GET['ID']; // remember to filter this!
$regex = '#\w+#(?<domain>\w+\-?\w+\.\w+)#';
preg_match($regex, $email, $matches);
$domain = $matches['domain'];
if ($domain !== 'example-test.com') {
// Unauthorised
}
See a working example here https://3v4l.org/SorhQ
See the regex and tweak if required here https://regex101.com/r/uDzOzm/1/
You can use the simple explode method to extract the domain name. see the code.
$parts = explode("#", "johndoe#domain.com");
$domain = $parts[1];
if(!in_array($domain, array('domain.com')))
{
//Redirect it wherever you want
}
You can do it:
if (isset($_GET['ID'])) {
$domain_name = substr(strrchr($_GET['ID'], "#"), 1);
if ($domain_name != "example-test.com"){
Forbidden....
}
}
I need to detect if a provided URL matches the one currently navigated to. Mind you the following are all valid, yet semantically equivalent URLs:
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
https://www.example.com/path/to/page/
https://www.example.com/path/to/page
http://www.example.com/path/to/page
//www.example.com/path/to/page
//www/path/to/page
../../../path/to/page
../../to/page
../page
./
The final function must return true if the given URL points back to the current page, or false if it does not. I do not have a list of expected URLs; this will be used for a client who just wants links to be disabled when they link to the current page. Note that I wish to ignore parameters, as these do not indicate the current page on this site. I got as far as using the following regex:
/^((https?:)?\/\/www(\.example\.com)\/path\/to\/page\/?(index.php)?(\?.+=.*(\&.+=.*)*)?)|(\.\/)$/i
where https?, www, \.example\.com, \/path\/to\/page, and index.php are dynamically detected with $_SERVER["PHP_SELF"] and made into regex form, but that doesn't match the relative URLs like ../../to/page.
EDIT: I got a bit farther with the regex: refiddle.com/gv8
now I'd just need PHP to dynamically create the regex for any given page.
First off, there is no way to predict the total list of valid URLs that will result in display of the current page, since you can't predict (or control) external links that might link back to the page. What if someone uses TinyURL or bit.ly? A regex will not cut the mustard.
If what you need is to insure that a link does not result in the same page, then you need to TEST it. Here's a basic concept:
Every page has a unique ID. Call it a serial number. It should be persistent. The serial number should be embedded somewhere predictable (though perhaps invisibly) within the page.
As the page is created, your PHP will need to walk through all the links for each page, visit each one, and determine whether the link resolves to a page with a serial number that matches the calling page's serial number.
If the serial number does not match, display the link as a link. Otherwise, display something else.
Obviously, this will be an arduous, resource-intensive process for page production. You really don't want to solve your problem this way.
With your "ultimate goal" comment in mind, I suspect your best approach is to be approximate. Here are some strategies...
First option is also the simplest. If you're building a content management system that USUALLY creates links in one format, just support that format. Wikipedia's approach works because a [[link]] is something THEY generate, so THEY know how it's formatted.
Second is more the direction you've gone with your question. The elements of a URL are "protocol", "host", "path" and "query string". You can break them out into a regex, and possibly get it right. You've already stated that you intend to ignore the query string. So ... start with '((https?:)?//(www\.)?example\.com)?' . $_SERVER['SCRIPT_NAME'] and add endings to suit. Other answers are already helping you with this.
Third option is quite a bit more complex, but gives you more fine-grained control over your test. As with the last option, you have the various URL elements. You can test for the validity of each without using a regex. For example:
$a = array(); // init array for valid URLs
// Step through each variation of our path...
foreach([$_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']] as $path) {
// Step through each variation of our host...
foreach ([$_SERVER['HTTP_HOST'], explode(".", $_SERVER['HTTP_HOST'])[0]] as $server) {
// Step through each variation of our protocol...
foreach (['https://','http://','//'] as $protocol) {
// Set the URL as a key.
$a[ $protocol . $server . $path ] = 1;
}
}
// Also for each path, step through directories and parents...
$apath=explode('/', $path); // turn the path into an array
unset($apath[0]); // strip the leading slash
for( $i = 1; $i <= count($apath); $i++ ) {
if (strlen($apath[$i])) {
$a[ str_repeat("../", 1+count($apath)-$i) . implode("/", $apath) ] = 1;
// add relative paths
}
unset($apath[$i]);
}
$a[ "./" . implode("/", $apath) ] = 1; // add current directory
}
Then simply test whether the link (minus its query string) is an index within the array. Or adjust to suit; I'm sure you get the idea.
I like this third solution the best.
A regex isn't actually necessary to strip off all the query parameters. You could use strok():
$url = strtok($url, '?');
And, to check the output for your URL array:
$url_list = <<<URL
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
...
./?parameter=value
./
URL;
$urls = explode("\n", $url_list);
foreach ($urls as $url) {
$url = strtok($url, '?'); // remove everything after ?
echo $url."\n";
}
As a function (could be improved):
function checkURLMatch($url, $url_array) {
$url = strtok($url, '?'); // remove everything after ?
if( in_array($url, $url_array)) {
// url exists array
return True;
} else {
// url not in array
return False;
}
}
See it live!
You can use this approach:
function checkURL($me, $s) {
$dir = dirname($me) . '/';
// you may need to refine this
$s = preg_filter(array('~^//~', '~/$~', '~\?.*$~', '~\.\./~'),
array('', '', '', $dir), $s);
// parse resulting URL
$url = parse_url($s);
var_dump($url);
// match parsed URL's path with self
return ($url['path'] === $me);
}
// your page's URL with stripped out .php
$me = str_replace('.php', '', $_SERVER['PHP_SELF']);
// assume this is the URL you are matching against
$s = '../page/';
// compare $me with $s
$ret = checkURL($me, $s);
var_dump($ret);
Live Demo: http://ideone.com/OZZM53
As I have been paid to work on this for the last couple days, I wasn't just sitting around waiting for an answer. I've come up with one that works in my test platform; what does everyone else think? It feels a little bloated, but also feels bulletproof.
Debug echoes left in in case you wanna echo out some stuffs.
global $debug;$debug = false; // toggle debug echoes and var_dumps
/**
* Returns a boolean indicating whether the given URL is the current one.
*
* #param $otherURL the other URL, as a string. Can be any URL, relative or canonical. Invalid URLs will not match.
*
* #return true iff the given URL points to the same place as the current one
*/
function isCurrentURL($otherURL)
{global $debug;
if($debug)echo"<!--\r\nisCurrentURL($otherURL)\r\n{\r\n";
if ($thisURL == $otherURL) // unlikely, but possible. Might as well check.
return true;
// BEGIN Parse other URL
$otherProtocol = parse_url($otherURL);
$otherHost = $otherProtocol["host"] or null; // if $otherProtocol["host"] is set and is not null, use it. Else, use null.
$otherDomain = explode(".", $otherHost) or $otherDomain;
$otherSubdomain = array_shift($otherDomain); // subdom only
$otherDomain = implode(".", $otherDomain); // domain only
$otherFilepath = $otherProtocol["path"] or null;
$otherProtocol = $otherProtocol["scheme"] or null;
// END Parse other URL
// BEGIN Get current URL
#if($debug){echo '$_SERVER == '; var_dump($_SERVER);}
$thisProtocol = $_SERVER["HTTP_X_FORWARDED_PROTO"]; // http or https
$thisHost = $_SERVER["HTTP_HOST"]; // subdom or subdom.domain.tld
$thisDomain = explode(".", $thisHost);
$thisSubdomain = array_shift($thisDomain); // subdom only
$thisDomain = implode(".", $thisDomain); // domain only
if ($thisDomain == "")
$thisDomain = $otherDomain;
$thisFilepath = $_SERVER["PHP_SELF"]; // /path/to/file.php
$thisURL = "$thisProtocol://$thisHost$thisFilepath";
// END Get current URL
if($debug)echo"Current URL is $thisURL ($thisProtocol, $thisSubdomain, $thisDomain, $thisFilepath).\r\n";
if($debug)echo"Other URL is $otherURL ($otherProtocol, $otherHost, $otherFilepath).\r\n";
$thisDomainRegexed = isset($thisDomain) && $thisDomain != null && $thisDomain != "" ? "(\." . str_replace(".","\.",$thisDomain) . ")?" : ""; // prepare domain for insertion into regex
// v this makes the last slash before index.php optional
$regex = "/^(($thisProtocol:)?\/\/$thisSubdomain$thisDomainRegexed)?" . preg_replace('/index\\\..+$/i','?(index\..+)?', str_replace(array(".", "/"), array("\.", "\/"), $thisFilepath)) . '$/i';
if($debug)echo "\r\nregex is $regex\r\nComparing regex against $otherURL";
if (preg_match($regex, $otherURL))
{
if($debug)echo"\r\n\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\tOther URL is NOT a fully-qualified URL in this subdomain. Checking if it is relative...";
if($otherURL == $thisFilepath) // somewhat likely
{
if($debug)echo"\r\n\t\tOhter URL and this filepath are an exact match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\t\tFilepath is not an exact match. Testing against regex...";
$regex = regexFilepath($thisFilepath);
if($debug)echo"\r\n\t\tNew Regex is $regex";
if($debug)echo"\r\n\t\tComparing regex against $otherFilepath...";
if (preg_match($regex, $otherFilepath))
{
if($debug)echo"\r\n\t\t\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
}
}
if($debug)echo"\r\nI tried my hardest, but couldn't match $otherURL to $thisURL. Returning false...\r\n}\r\n-->";
return false;
}
/**
* Uses the given filepath to create a regex that will match it in any of its relative representations.
*
* #param $path the filepath to be converted
*
* #return a regex that matches a all relative forms of the given filepath
*/
function regexFilepath($path)
{global $debug;
if($debug)echo"\r\nregexFilepath($path)\r\n{\r\n";
$filepathArray = explode("/", $path);
if (count($filepathArray) == 0)
throw new Exception("given parameter not a filepath: $path");
if ($filepathArray[0] == "") // this can happen if the path starts with a "/"
array_shift($filepathArray); // strip the first element off the array
$isIndex = preg_match("/^index\..+$/i", end($filepathArray));
$filename = array_pop($filepathArray);
if($debug){var_dump($filepathArray);}
$ret = '';
foreach($filepathArray as $i)
$ret = "(\.\.\/$ret$i\/)?"; // make a pseudo-recursive relative filepath
if($debug)echo "\r\n$ret";
$ret = preg_replace('/\)\?$/', '?)', $ret); // remove the last '?' and add one before the last '\/'
if($debug)echo "\r\n$ret";
$ret = '/^' . ($ret == '' ? '\.\/' : "((\.\/)|$ret)") . ($isIndex ? '(index\..+)?' : str_replace('.', '\.', $filename)) . '$/i'; // if this filepath leads to an index.php (etc.), then that filename is implied and irrelevant.
if($debug)echo'\r\n}\r\n';
}
This seems to match everything I need it to match, and not what I don't need it to.
I have a .txt file where I would like to find an EXACT match of a single email entered in a form.
The present directives (see below) I used, work for a standard form. But when I use it in conjunction with an AJAX call and jQuery, it confirms it exists by just finding the first occurrence.
For example:
If that person enters "bobby#" it says not found, good.
If someone enters their full Email address and it exists in the file, it says "found", very good.
Now, if someone enters just "bobby", it says "found", not good.
I used the following three examples below with the same results.
if ( !preg_match("/\b{$email}\b/i", $emails )) {
echo "Sorry, not found";
}
and...
if ( !preg_match( "/(?:^|\W){$email}(?:\W|$)/", $emails )) {
echo "Sorry, not found";
}
and...
if ( !preg_match('/^'.$email.'$/', $emails )) {
echo "Sorry, not found";
}
my AJAX
$.ajax({
type: "POST",
url: "email_if_exist.php",
data: "email="+ usr,
success: function(msg){
my text file
Bobby Brown bobby#somewhere.com
Guy Slim guy#somewhere.com
Slim Jim slim#somewhere.com
I thought of using a jQuery function to only accept a full email address, but with no success partly because I didn't know where to put it in the script.
I've spent a lot of time in searching for a solution to this and I am now asking for some help.
Cheers.
Because your text file contains "bobby" in it, any regex such as you are suggesting will always find "bobby". I would suggest checking for the presence of the # symbol BEFORE you run the regex, as any valid email will always have # in it. Try something like this:
if (strpos($email,'#')) {
if ( !preg_match("/\b{$email}\b/i", $emails )) {
echo "Sorry, not found";
}
}
EDIT: Looking at this 4 years later... I would make the regex match to the end of the line, using the m modifier to specify multiline so the $ matches newline or EOF. The PHP line would be:
if ( !preg_match("/\b{$email}$/im", $emails )) {
If you're just checking to see if the user exists, this should work:
$users = trim(preg_replace('/\s\s+/', ' ', $users));
$userArray = explode(' ', $users);
$exists = in_array($email, $userArray);
Where $users is referencing to the example file and $email is referencing to the queried e-mail.
This replaces all newlines (and double spaces) with spaces and then splits by spaces into an array, then, if the e-mail exists in the array, the user exists.
Hope I helped!
'/^'.$email.'$/' is quite close. Since you want the check being "true" only if the full email address is on the file you should include in the pattern the "limits" of the email: Whitespace before and end_of_the_line after if:
'/ '.$email.'$/'
(Yes, I've just changed ^ -start of line- for a whitespace)
If your text file filled with lines that every line ending with the email,
so you can regex with testing and match by your "email + end od line"
like that:
if( preg_match("/.+{$email}[\n|\r\n|\r]/", $textFileEmails) )
{
/// code
}
The code would validate first using php core functions whether the email is correct or not and then check for the occurrence.
$email = 'bobby#somewhere.com';
$found = false;
//PHP has a built-in function to validate an email
if(filter_var($email, FILTER_VALIDATE_EMAIL)){
//Grab lines from the file
$lines = file('myfile.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
//Grab words from the line
$words = explode(" ", $line);
//If email found within the words set the flag as true.
if(in_array($email, $words)) {
$found = true;
//If the aim is only to find the email, we can break out here.
break;
}
}
}
if(false === $found) {
echo 'Not found!';
} else {
echo 'Found you!';
}
If you file is formatted as your example first_name, last_name, email#address.tdl
it's really easy to break it up on load to search.
I don't know why you would use preg_match for this bit your if you were advised to use preg use it to verify the email address. You're better off using indexOf method in php (strpos) to search the file but the below method works for your fixed file format.
Object Orientated File Reader and searcher
class Search{
private $users = array();
public function __construct($password_file){
$file = file_get_contents($password_file);
$lines = explode("\n", $file);
$users = array();
foreach($lines as $line){
$users = expode(" ", $line);
}
foreach($users as $user){
$this->users[] = array("first_name" => $user[0], "last_name" => $user[1], "email" => $user[2])
}
}
public function searchByEmail($email){
foreach($this->users as $key => $user){
if($user['email'] == $email){
// return user array
return $user;
// or you could return user id
//return $key;
}
}
return false;
}
}
Then to use
$search = new Search($passwdFile);
$user = $search->searchByEmail($_POST['email']);
echo ($user)? "found":"Sorry, not found";
Using preg_match to validate email then check
If you want to use preg and your own file search system.
function validateEmail($email) {
$v = "/[a-zA-Z0-9_-.+]+#[a-zA-Z0-9-]+.[a-zA-Z]+/";
return (bool)preg_match($v, $email);
}
then use like
if(validateEmail($_POST['email'])){
echo (strpos($_POST['email'], $emails) !== false)? "found":"Sorry, not found";
}
In my signup form, I ask users to enter an email with the same domain name as they enter in the url field above.
Right now, I collect data this way:
URL : http://www.domain.com The domain.com part is what the user enters. The http://www is hard coded.
Email : info# domain.com The bold part is entered by the user. The # is hard coded.
The domain.com part in the url and domain.com part in the email should match. Right now, I can match the two fields since they are separate.
But I want to give up the above approach and make the user enter the entire domain name and email. When that's the case, what would be a good way to check if a user entered an email with the same domain he entered in the url field above.
I'm doing all this using php.
<?php
//extract domain from email
$email_domain_temp = explode("#", $_POST['email']);
$email_domain = $email_domain_temp[1];
//extract domain from url
$url_domain_temp = parse_url($_POST['url']);
$url_domain = strip_out_subdomain($url_domain_temp['host']);
//compare
if ($email_domain == $url_domain){
//match
}
function strip_out_subdomain($domain){
//do nothing if only 1 dot in $domain
if (substr_count($domain, ".") == 1){
return $domain;
}
$only_my_domain = preg_replace("/^(.*?)\.(.*)$/","$2",$domain);
return $only_my_domain;
}
So what this does is :
First, split the email string in 2 parts in an array. The second part is the domain.
Second, use the php built in function to parse the url, then extract the "host", while removing the (optionnal) subdomain.
Then compare.
you can do this by explode()
supp url = bla#gmail.com
$pieces = explode("#", $url);
$new = $pieces[1]; //which will be gmail.com
now again explode
$newpc= explode(".", $new );
$new1 = $newpc[0]; //which will be gmail
This is my version (tested, works):
<?php
$domain = 'www2.example.com'; // Set domain here
$email = 'info#example.com'; // Set email here
if(!preg_match('~^https?://.*$~i', $domain)) { // Does the URL start with http?
$domain = "http://$domain"; // No, prepend it with http://
}
if(filter_var($domain, FILTER_VALIDATE_URL)) { // Validate URL
$host = parse_url($domain, PHP_URL_HOST); // Parse the host, if it is an URL
if(substr_count($host, '.') > 1) { // Is there a subdomain?
$host = substr($host, -strrpos(strrev($host), '.')); // Get the host
}
if(strpos(strrev($email), strrev($host)) === 0) { // Does it match the end of the email?
echo 'Valid!'; // Valid
} else {
echo 'Does not match.'; // Invalid
}
} else {
echo 'Invalid domain!'; // Domain is invalid
}
?>
you could do:
$parsedUrl = parse_url($yourEnteredUrl);
$domainHost = str_replace("www.", "", $parsedUrl["host"]);
$emailDomain = array_pop(explode('#', $yourEnteredEmail));
if( $emailDomain == $domainHost ) {
//valid data
}
$email = 'myemail#example.com';
$site = 'http://example.com';
$emailDomain = ltrim( strstr($email, '#'), '#' );
// or automate it using array_map(). Syntax is correct only for >= PHP5.4
$cases = ['http://'.$emailDomain, 'https://'.$emailDomain, 'http://www.'.$emailDomain, 'https://www.'.$emailDomain];
$bSameDomain = in_array($site, $cases);
var_dump($bSameDomain);
Use regular expressions with positive lookbehinds(i.e only return the expression I'd like to match if it is preceded by a certain pattern, but don't include the lookbehind itself in the match), like so:
<?php
$url = preg_match("/(?<=http:\/\/www\.).*/",$_POST['url'],$url_match);
$email = preg_match("/(?<=#).*/",$_POST['email'],$email_match);
if ($url_match[0]==$email_match[0]) {
// Success Code
}
else {
// Failure Code
}
?>
Of course this is a bit oversimplified as you also need to account for https or www2 and the likes, but these require only minor changes to the RegExp, using the question mark as the "optional" operator
I am using this IP Validation Function that I came across while browsing, it has been working well until today i ran into a problem.
For some reason the function won't validate this IP as valid: 203.81.192.26
I'm not too great with regular expressions, so would appreciate any help on what could be wrong.
If you have another function, I would appreciate if you could post that for me.
The code for the function is below:
public static function validateIpAddress($ip_addr)
{
global $errors;
$preg = '#^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' .
'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$#';
if(preg_match($preg, $ip_addr))
{
//now all the intger values are separated
$parts = explode(".", $ip_addr);
//now we need to check each part can range from 0-255
foreach($parts as $ip_parts)
{
if(intval($ip_parts) > 255 || intval($ip_parts) < 0)
{
$errors[] = "ip address is not valid.";
return false;
}
return true;
}
return true;
} else {
$errors[] = "please double check the ip address.";
return false;
}
}
I prefer a simplistic approach described here. This should be considered valid for security purposes. Although make sure you get it from $_SERVER['REMOTE_ADDR'], any other http header can be spoofed.
function validateIpAddress($ip){
return long2ip(ip2long($ip)))==$ip;
}
There is already something built-in to do this : http://fr.php.net/manual/en/filter.examples.validation.php See example 2
<?php
if (filter_var($ip, FILTER_VALIDATE_IP)) {
// Valid
} else {
// Invalid
}
Have you tried using built-in functions to try and validate the address? For example, you can use ip2long and long2ip to convert the human-readable dotted IP address into the number it represents, then back. If the strings are identical, the IP is valid.
There's also the filter extension, which has an IP validation option. filter is included by default in PHP 5.2 and better.
Well, why are you doing both regex and int comparisons? You are "double" checking the address. Also, your second check is not valid, as it will always return true if the first octet is valid (you have a return true inside of the foreach loop).
You could do:
$parts = explode('.', $ip_addr);
if (count($parts) == 4) {
foreach ($parts as $part) {
if ($part > 255 || $part < 0) {
//error
}
}
return true;
} else {
return false;
}
But as others have suggested, ip2long/long2ip may suit your needs better...