Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Repository/Ip.php
<?php

namespace XF\Repository;

use
XF\Mvc\Entity\Repository;

use function
count, intval, strlen;

class
Ip extends Repository
{
   
/**
     * Logs the specified IP. This will return null if the IP couldn't be logged for any reason.
     *
     * @param integer $userId
     * @param string $ip String IP representation; binary version also supported
     * @param string $contentType
     * @param int $contentId
     * @param string $action
     *
     * @return null|\XF\Entity\Ip If the insert fails (generally for an invalid IP address), null.
     */
   
public function logIp($userId, $ip, $contentType, $contentId, $action = '')
    {
       
$entity = $this->em->create('XF:Ip');
       
$entity->user_id = $userId;
       
$entity->ip = $ip;
       
$entity->content_type = $contentType;
       
$entity->content_id = $contentId;
       
$entity->action = $action;

        if (
$entity->save(false))
        {
            return
$entity;
        }
        else
        {
            return
null;
        }
    }

    public function
logCookieLoginIfNeeded($userId, $ip)
    {
       
$userId = intval($userId);
        if (!
$userId)
        {
            return
null;
        }

       
$binaryIp = \XF\Util\Ip::convertIpStringToBinary($ip);
        if (
$binaryIp === false)
        {
            return
null;
        }

       
// only log this if we haven't seen this IP in the last 6 hours
       
$cutOff = \XF::$time - 6*3600;
       
$recentLog = $this->db()->fetchOne("
            SELECT 1
            FROM xf_ip
            WHERE user_id = ?
                AND log_date > ?
                AND ip = ?
            LIMIT 1
        "
, [$userId, $cutOff, $binaryIp]);
        if (
$recentLog)
        {
            return
null;
        }

        return
$this->logIp($userId, $ip, 'user', $userId, 'cookie_login');
    }

    public function
pruneIps($cutOff = null)
    {
        if (
$cutOff === null)
        {
            if (!
$this->options()->ipLogCleanUp['enabled'])
            {
                return
0;
            }

           
$cutOff = \XF::$time - 86400 * $this->options()->ipLogCleanUp['delay'];
        }

        return
$this->db()->delete('xf_ip', 'log_date < ?', $cutOff);
    }

    public function
getLoggedIp($contentType, $contentId, $action, $userId = null)
    {
       
$params = [$contentType, $contentId, $action];
        if (
$userId !== null)
        {
           
$params[] = $userId;
        }

        return
$this->db()->fetchOne("
            SELECT ip
            FROM xf_ip
            WHERE content_type = ?
                AND content_id = ?
                AND action = ?
                "
. ($userId !== null ? 'AND user_id = ?' : '') . "
            ORDER BY log_date DESC
            LIMIT 1
        "
, $params);
    }

    public function
getSharedIpUsers($userId, $logDays)
    {
       
$db = $this->db();

        if (
$userId instanceof \XF\Entity\User)
        {
           
$userId = $userId->user_id;
        }

       
$cutOff = \XF::$time - $logDays * 86400;

       
// written this way due to mysql's ridiculous sub-query performance
       
$recentIps = $db->fetchAllColumn("
            SELECT DISTINCT ip
            FROM xf_ip
            WHERE user_id = ?
                AND log_date > ?
            LIMIT 500
        "
, [$userId, $cutOff]);
        if (!
$recentIps)
        {
            return [];
        }

       
$ipLogs = $db->fetchAll('
            SELECT user_id,
                ip,
                MIN(log_date) AS first_date,
                MAX(log_date) AS last_date,
                COUNT(*) AS total
            FROM xf_ip
            WHERE ip IN ('
. $db->quote($recentIps) . ')
                AND user_id <> ?
                AND user_id > 0
                AND log_date > ?
            GROUP BY user_id, ip
            LIMIT 1000
        '
, [$userId, $cutOff]);

       
$userIpLogs = [];
        foreach (
$ipLogs AS $ipLog)
        {
           
$userIpLogs[$ipLog['user_id']][$ipLog['ip']] = [
               
'ip' => $ipLog['ip'],
               
'first_date' => $ipLog['first_date'],
               
'last_date' => $ipLog['last_date'],
               
'total' => $ipLog['total']
            ];
        }

        if (!
$userIpLogs)
        {
            return [];
        }

       
$users = $this->em->findByIds('XF:User', array_keys($userIpLogs));
       
$output = [];

        foreach (
$users AS $user)
        {
           
$output[$user->user_id] = [
               
'user_id' => $user->user_id,
               
'user' => $user,
               
'ips' => $userIpLogs[$user->user_id]
            ];
        }

        return
$output;
    }

    public function
getUsersByIpRange($lowerBound, $upperBound)
    {
       
$ips = $this->db()->fetchAllKeyed("
            SELECT user_id,
                GROUP_CONCAT(DISTINCT ip ORDER BY ip SEPARATOR '  ') AS ips,
                MIN(log_date) AS first_date,
                MAX(log_date) AS last_date,
                COUNT(*) AS total
            FROM xf_ip
            WHERE ip >= ? AND ip <= ? AND LENGTH(ip) = ?
            GROUP BY user_id
        "
, 'user_id', [$lowerBound, $upperBound, strlen($lowerBound)]);
        if (!
$ips)
        {
            return [];
        }

       
$userIds = array_column($ips, 'user_id');
       
$userIds = array_unique($userIds);

       
$userFinder = $this->finder('XF:User')
            ->
where('user_id', $userIds)
            ->
order('username');

       
$users = $userFinder->fetch();
       
$output = [];
        foreach (
$users AS $user)
        {
           
$ipInfo = $ips[$user->user_id];

           
$matchIps = explode('  ', $ipInfo['ips']);

           
$output[$user->user_id] = [
               
'user_id' => $ipInfo['user_id'],
               
'ips' => $matchIps,
               
'ip_total' => count($matchIps),
               
'first_date' => $ipInfo['first_date'],
               
'last_date' => $ipInfo['last_date'],
               
'total' => $ipInfo['total'],
               
'user' => $user
           
];
        }

        return
$output;
    }

    public function
getUsersByIp($baseIp)
    {
       
$ip = \XF\Util\Ip::convertIpStringToBinary($baseIp);
        if (
$ip === false)
        {
           
$baseIp = preg_replace('/[^\x20-\x7F]/', '?', $baseIp);
            throw new \
InvalidArgumentException("Cannot convert IP '$baseIp' to binary");
        }

       
$ips = $this->db()->fetchAllKeyed("
            SELECT user_id,
                ip,
                MIN(log_date) AS first_date,
                MAX(log_date) AS last_date,
                COUNT(*) AS total
            FROM xf_ip
            WHERE ip = ?
            GROUP BY user_id
        "
, 'user_id', $ip);
        if (!
$ips)
        {
            return [];
        }

       
$userIds = array_column($ips, 'user_id');
       
$userIds = array_unique($userIds);

       
$userFinder = $this->finder('XF:User')
            ->
where('user_id', $userIds)
            ->
order('username');

       
$users = $userFinder->fetch();
       
$output = [];
        foreach (
$users AS $user)
        {
           
$ipInfo = $ips[$user->user_id];

           
$output[$user->user_id] = [
               
'user_id' => $ipInfo['user_id'],
               
'ips' => [$ipInfo['ip']],
               
'ip_total' => 1,
               
'first_date' => $ipInfo['first_date'],
               
'last_date' => $ipInfo['last_date'],
               
'total' => $ipInfo['total'],
               
'user' => $user
           
];
        }

        return
$output;
    }

    public function
getIpsByUser($userId, $limit = 150, $offset = 0)
    {
        if (
$userId instanceof \XF\Entity\User)
        {
           
$userId = $userId->user_id;
        }

        return
$this->db()->fetchAllKeyed($this->db()->limit("
            SELECT ip,
                MIN(log_date) AS first_date,
                MAX(log_date) AS last_date,
                COUNT(*) AS total
            FROM xf_ip
            WHERE user_id = ?
            GROUP BY ip
            ORDER BY last_date DESC
        "
, $limit, $offset), 'ip', $userId);
    }

}