Issues (3627)

app/bundles/CoreBundle/Helper/IpLookupHelper.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2016 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\CoreBundle\Helper;
13
14
use Doctrine\ORM\EntityManager;
15
use Mautic\CoreBundle\Entity\IpAddress;
16
use Mautic\CoreBundle\IpLookup\AbstractLookup;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
20
class IpLookupHelper
21
{
22
    /**
23
     * @var Request|null
24
     */
25
    protected $request;
26
27
    /**
28
     * @var EntityManager
29
     */
30
    protected $em;
31
32
    /**
33
     * @var AbstractLookup
34
     */
35
    protected $ipLookup;
36
37
    /**
38
     * @var array
39
     */
40
    protected $doNotTrackIps;
41
42
    /**
43
     * @var array
44
     */
45
    protected $doNotTrackBots;
46
47
    /**
48
     * @var array
49
     */
50
    protected $doNotTrackInternalIps;
51
52
    /**
53
     * @var array
54
     */
55
    protected $trackPrivateIPRanges;
56
57
    /**
58
     * @var string
59
     */
60
    private $realIp;
61
62
    /**
63
     * @var CoreParametersHelper
64
     */
65
    private $coreParametersHelper;
66
67
    public function __construct(
68
        RequestStack $requestStack,
69
        EntityManager $em,
70
        CoreParametersHelper $coreParametersHelper,
71
        AbstractLookup $ipLookup = null
72
    ) {
73
        $this->request               = $requestStack->getCurrentRequest();
74
        $this->em                    = $em;
75
        $this->ipLookup              = $ipLookup;
76
        $this->doNotTrackIps         = $coreParametersHelper->get('do_not_track_ips');
77
        $this->doNotTrackBots        = $coreParametersHelper->get('do_not_track_bots');
78
        $this->doNotTrackInternalIps = $coreParametersHelper->get('do_not_track_internal_ips');
79
        $this->trackPrivateIPRanges  = $coreParametersHelper->get('track_private_ip_ranges');
80
        $this->coreParametersHelper  = $coreParametersHelper;
81
    }
82
83
    /**
84
     * Guess the IP address from current session.
85
     *
86
     * @return string
87
     */
88
    public function getIpAddressFromRequest()
89
    {
90
        if (null !== $this->request) {
91
            $ipHolders = [
92
                'HTTP_CLIENT_IP',
93
                'HTTP_X_FORWARDED_FOR',
94
                'HTTP_X_FORWARDED',
95
                'HTTP_X_CLUSTER_CLIENT_IP',
96
                'HTTP_FORWARDED_FOR',
97
                'HTTP_FORWARDED',
98
                'REMOTE_ADDR',
99
            ];
100
101
            foreach ($ipHolders as $key) {
102
                if ($this->request->server->get($key)) {
103
                    $ip = trim($this->request->server->get($key));
104
105
                    if (false !== strpos($ip, ',')) {
106
                        $ip = $this->getClientIpFromProxyList($ip);
107
                    }
108
109
                    // Validate IP
110
                    if (null !== $ip && $this->ipIsValid($ip)) {
111
                        return $ip;
112
                    }
113
                }
114
            }
115
        }
116
117
        // if everything else fails
118
        return '127.0.0.1';
119
    }
120
121
    /**
122
     * Get an IpAddress entity for current session or for passed in IP address.
123
     *
124
     * @param string $ip
125
     *
126
     * @return IpAddress
127
     */
128
    public function getIpAddress($ip = null)
129
    {
130
        static $ipAddresses = [];
131
132
        if (null === $ip) {
133
            $ip = $this->getIpAddressFromRequest();
134
        }
135
136
        if (empty($ip) || !$this->ipIsValid($ip)) {
137
            //assume local as the ip is empty
138
            $ip = '127.0.0.1';
139
        }
140
141
        $this->realIp = $ip;
142
143
        if ($this->coreParametersHelper->get('anonymize_ip')) {
144
            $ip = preg_replace(['/\.\d*$/', '/[\da-f]*:[\da-f]*$/'], ['.***', '****:****'], $ip);
145
        }
146
147
        if (empty($ipAddresses[$ip])) {
148
            $repo      = $this->em->getRepository('MauticCoreBundle:IpAddress');
149
            $ipAddress = $repo->findOneByIpAddress($ip);
150
            $saveIp    = (null === $ipAddress);
151
152
            if (null === $ipAddress) {
153
                $ipAddress = new IpAddress();
154
                $ipAddress->setIpAddress($ip);
155
            }
156
157
            // Ensure the do not track list is inserted
158
            if (!is_array($this->doNotTrackIps)) {
159
                $this->doNotTrackIps = [];
160
            }
161
162
            if (!is_array($this->doNotTrackBots)) {
0 ignored issues
show
The condition is_array($this->doNotTrackBots) is always true.
Loading history...
163
                $this->doNotTrackBots = [];
164
            }
165
166
            if (!is_array($this->doNotTrackInternalIps)) {
167
                $this->doNotTrackInternalIps = [];
168
            }
169
170
            $doNotTrack = array_merge($this->doNotTrackIps, $this->doNotTrackInternalIps);
171
            if ('prod' === MAUTIC_ENV) {
172
                // Do not track internal IPs
173
                $doNotTrack = array_merge($doNotTrack, ['127.0.0.1', '::1']);
174
            }
175
176
            $ipAddress->setDoNotTrackList($doNotTrack);
177
178
            if ($ipAddress->isTrackable() && $this->request) {
179
                $userAgent = $this->request->headers->get('User-Agent');
180
                foreach ($this->doNotTrackBots as $bot) {
181
                    if (false !== strpos($userAgent, $bot)) {
182
                        $doNotTrack[] = $ip;
183
                        $ipAddress->setDoNotTrackList($doNotTrack);
184
                        continue;
185
                    }
186
                }
187
            }
188
189
            $details = $ipAddress->getIpDetails();
190
            if ($ipAddress->isTrackable() && empty($details['city']) && !$this->coreParametersHelper->get('anonymize_ip')) {
191
                // Get the IP lookup service
192
193
                // Fetch the data
194
                if ($this->ipLookup) {
195
                    $details = $this->getIpDetails($ip);
196
197
                    $ipAddress->setIpDetails($details);
198
199
                    // Save new details
200
                    $saveIp = true;
201
                }
202
            }
203
204
            if ($saveIp) {
205
                $repo->saveEntity($ipAddress);
206
            }
207
208
            $ipAddresses[$ip] = $ipAddress;
209
        }
210
211
        return $ipAddresses[$ip];
212
    }
213
214
    /**
215
     * @param string $ip
216
     *
217
     * @return array
218
     */
219
    public function getIpDetails($ip)
220
    {
221
        if ($this->ipLookup) {
222
            return $this->ipLookup->setIpAddress($ip)->getDetails();
223
        }
224
225
        return [];
226
    }
227
228
    /**
229
     * Validates if an IP address if valid.
230
     *
231
     * @param $ip
232
     *
233
     * @return mixed
234
     */
235
    public function ipIsValid($ip)
236
    {
237
        $filterFlagNoPrivRange = $this->trackPrivateIPRanges ? 0 : FILTER_FLAG_NO_PRIV_RANGE;
238
239
        return filter_var(
240
            $ip,
241
            FILTER_VALIDATE_IP,
242
            FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | $filterFlagNoPrivRange | FILTER_FLAG_NO_RES_RANGE
243
        );
244
    }
245
246
    /**
247
     * @param $ip
248
     */
249
    protected function getClientIpFromProxyList($ip)
250
    {
251
        // Proxies are included
252
        $ips = explode(',', $ip);
253
        array_walk(
254
            $ips,
255
            function (&$val) {
256
                $val = trim($val);
257
            }
258
        );
259
260
        if ($this->doNotTrackInternalIps) {
261
            $ips = array_diff($ips, $this->doNotTrackInternalIps);
262
        }
263
264
        // https://en.wikipedia.org/wiki/X-Forwarded-For
265
        // X-Forwarded-For: client, proxy1, proxy2
266
        foreach ($ips as $ip) {
267
            if ($this->ipIsValid($ip)) {
268
                return $ip;
269
            }
270
        }
271
272
        return null;
273
    }
274
275
    /**
276
     * @return string
277
     */
278
    public function getRealIp()
279
    {
280
        return $this->realIp;
281
    }
282
}
283