Spaces:
No application file
No application file
| namespace Mautic\CoreBundle\Helper; | |
| use Doctrine\ORM\EntityManager; | |
| use Mautic\CoreBundle\Entity\IpAddress; | |
| use Mautic\CoreBundle\Entity\IpAddressRepository; | |
| use Mautic\CoreBundle\IpLookup\AbstractLookup; | |
| use Symfony\Component\HttpFoundation\RequestStack; | |
| class IpLookupHelper | |
| { | |
| /** | |
| * @var array | |
| */ | |
| protected $doNotTrackIps; | |
| /** | |
| * @var array | |
| */ | |
| protected $doNotTrackBots; | |
| /** | |
| * @var array | |
| */ | |
| protected $doNotTrackInternalIps; | |
| /** | |
| * @var array | |
| */ | |
| protected $trackPrivateIPRanges; | |
| /** | |
| * @var string | |
| */ | |
| private $realIp; | |
| private CoreParametersHelper $coreParametersHelper; | |
| public function __construct( | |
| protected RequestStack $requestStack, | |
| protected EntityManager $em, | |
| CoreParametersHelper $coreParametersHelper, | |
| protected ?AbstractLookup $ipLookup = null | |
| ) { | |
| $this->doNotTrackIps = $coreParametersHelper->get('do_not_track_ips'); | |
| $this->doNotTrackBots = $coreParametersHelper->get('do_not_track_bots'); | |
| $this->doNotTrackInternalIps = $coreParametersHelper->get('do_not_track_internal_ips'); | |
| $this->trackPrivateIPRanges = $coreParametersHelper->get('track_private_ip_ranges'); | |
| $this->coreParametersHelper = $coreParametersHelper; | |
| } | |
| /** | |
| * Guess the IP address from current session. | |
| * | |
| * @return string | |
| */ | |
| public function getIpAddressFromRequest() | |
| { | |
| $request = $this->requestStack->getCurrentRequest(); | |
| if (null !== $request) { | |
| $ipHolders = [ | |
| 'HTTP_CLIENT_IP', | |
| 'HTTP_X_FORWARDED_FOR', | |
| 'HTTP_X_FORWARDED', | |
| 'HTTP_X_CLUSTER_CLIENT_IP', | |
| 'HTTP_FORWARDED_FOR', | |
| 'HTTP_FORWARDED', | |
| 'REMOTE_ADDR', | |
| ]; | |
| foreach ($ipHolders as $key) { | |
| if ($request->server->get($key)) { | |
| $ip = trim($request->server->get($key)); | |
| if (str_contains($ip, ',')) { | |
| $ip = $this->getClientIpFromProxyList($ip); | |
| } | |
| // Validate IP | |
| if (null !== $ip && $this->ipIsValid($ip)) { | |
| return $ip; | |
| } | |
| } | |
| } | |
| } | |
| // if everything else fails | |
| return '127.0.0.1'; | |
| } | |
| /** | |
| * Get an IpAddress entity for current session or for passed in IP address. | |
| * | |
| * @param string $ip | |
| * | |
| * @return IpAddress | |
| */ | |
| public function getIpAddress($ip = null) | |
| { | |
| static $ipAddresses = []; | |
| $request = $this->requestStack->getCurrentRequest(); | |
| $isIpAnonymizationEnabled = (bool) $this->coreParametersHelper->get('anonymize_ip'); | |
| if (null === $ip) { | |
| $ip = $this->getIpAddressFromRequest(); | |
| } | |
| if (empty($ip) || !$this->ipIsValid($ip)) { | |
| // assume local as the ip is empty | |
| $ip = '127.0.0.1'; | |
| } | |
| $this->realIp = $ip; | |
| if ($isIpAnonymizationEnabled) { | |
| $ip = '*.*.*.*'; | |
| } | |
| if (empty($ipAddresses[$ip])) { | |
| $ipAddress = null; | |
| $saveIp = false; | |
| /** @var IpAddressRepository $repo */ | |
| $repo = $this->em->getRepository(IpAddress::class); | |
| $ipAddress = $repo->findOneByIpAddress($ip); | |
| $saveIp = (null === $ipAddress); | |
| if (null === $ipAddress) { | |
| $ipAddress = new IpAddress(); | |
| $ipAddress->setIpAddress($ip); | |
| } | |
| // Ensure the do not track list is inserted | |
| if (!is_array($this->doNotTrackIps)) { | |
| $this->doNotTrackIps = []; | |
| } | |
| if (!is_array($this->doNotTrackBots)) { | |
| $this->doNotTrackBots = []; | |
| } | |
| if (!is_array($this->doNotTrackInternalIps)) { | |
| $this->doNotTrackInternalIps = []; | |
| } | |
| $doNotTrack = array_merge($this->doNotTrackIps, $this->doNotTrackInternalIps); | |
| $ipAddress->setDoNotTrackList($doNotTrack); | |
| if ($ipAddress->isTrackable() && $request) { | |
| $userAgent = $request->headers->get('User-Agent', ''); | |
| foreach ($this->doNotTrackBots as $bot) { | |
| if (str_contains($userAgent, $bot)) { | |
| $doNotTrack[] = $ip; | |
| $ipAddress->setDoNotTrackList($doNotTrack); | |
| continue; | |
| } | |
| } | |
| } | |
| $details = $ipAddress->getIpDetails(); | |
| if ($ipAddress->isTrackable() && !$isIpAnonymizationEnabled && empty($details['city'])) { | |
| // Get the IP lookup service | |
| // Fetch the data | |
| if ($this->ipLookup) { | |
| $details = $this->getIpDetails($ip); | |
| $ipAddress->setIpDetails($details); | |
| // Save new details | |
| $saveIp = true; | |
| } | |
| } | |
| if ($saveIp) { | |
| $repo->saveEntity($ipAddress); | |
| } | |
| $ipAddresses[$ip] = $ipAddress; | |
| } | |
| return $ipAddresses[$ip]; | |
| } | |
| /** | |
| * @param string $ip | |
| * | |
| * @return array | |
| */ | |
| public function getIpDetails($ip) | |
| { | |
| if ($this->ipLookup) { | |
| return $this->ipLookup->setIpAddress($ip)->getDetails(); | |
| } | |
| return []; | |
| } | |
| /** | |
| * Validates if an IP address if valid. | |
| */ | |
| public function ipIsValid($ip): string|bool | |
| { | |
| $filterFlagNoPrivRange = $this->trackPrivateIPRanges ? 0 : FILTER_FLAG_NO_PRIV_RANGE; | |
| return filter_var( | |
| $ip, | |
| FILTER_VALIDATE_IP, | |
| FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | $filterFlagNoPrivRange | FILTER_FLAG_NO_RES_RANGE | |
| ); | |
| } | |
| protected function getClientIpFromProxyList($ip) | |
| { | |
| // Proxies are included | |
| $ips = explode(',', $ip); | |
| array_walk( | |
| $ips, | |
| function (&$val): void { | |
| $val = trim($val); | |
| } | |
| ); | |
| if ($this->doNotTrackInternalIps) { | |
| $ips = array_diff($ips, $this->doNotTrackInternalIps); | |
| } | |
| // https://en.wikipedia.org/wiki/X-Forwarded-For | |
| // X-Forwarded-For: client, proxy1, proxy2 | |
| foreach ($ips as $ip) { | |
| if ($this->ipIsValid($ip)) { | |
| return $ip; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getRealIp() | |
| { | |
| return $this->realIp; | |
| } | |
| } | |