Completed
Push — master ( 026b2f...fbea6a )
by Taosikai
11:48
created

CredentialResolver::wait()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 0
dl 0
loc 41
rs 9.264
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the slince/smartqq package.
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slince\SmartQQ;
13
14
use GuzzleHttp\Cookie\CookieJar;
15
use Slince\SmartQQ\Exception\RuntimeException;
16
17
class CredentialResolver
18
{
19
20
    /**
21
     * @var Client
22
     */
23
    protected $client;
24
25
    /**
26
     * @var CookieJar
27
     */
28
    protected $cookies;
29
30
    /**
31
     * 获取ptwebqq的地址
32
     *
33
     * @var string
34
     */
35
    protected $certificationUrl;
36
37
    public function __construct(Client $client)
38
    {
39
        $this->client = $client;
40
    }
41
42
    /**
43
     * 获取授权凭据.
44
     *
45
     * @param callable $qrCallback
46
     * @return $this
47
     */
48
    public function resolve($qrCallback)
49
    {
50
        // 重置cookie
51
        $this->cookies = new CookieJar();
52
        //获取二维码资源
53
        $response = $this->sendRequest(new Request\GetQrCodeRequest());
54
        $qrCallback((string)$response->getBody());
55
56
        return $this;
57
    }
58
59
    /**
60
     * 等待授权验证.
61
     *
62
     * @return Credential
63
     */
64
    public function wait()
65
    {
66
        //查找"qrsig"参数
67
        $qrSign = $this->findQRSign();
68
        //计算ptqrtoken
69
        $ptQrToken = Utils::hash33($qrSign);
70
71
        //验证状态
72
        while (true) {
73
            //查看二维码状态
74
            $status = $this->getQrCodeStatus($ptQrToken);
75
76
            // 认证成功
77
            if (Request\VerifyQrCodeRequest::STATUS_CERTIFICATION === $status) {
78
                //授权成功跳出状态检查
79
                break;
80
            } elseif (Request\VerifyQrCodeRequest::STATUS_EXPIRED == $status) {
81
                //查找"qrsig"参数
82
                $qrSign = $this->findQRSign();
83
                //计算ptqrtoken
84
                $ptQrToken = Utils::hash33($qrSign);
85
            }
86
            //暂停1秒
87
            usleep(1000000);
88
        }
89
90
        $ptWebQQ = $this->getPtWebQQ();
91
        $vfWebQQ = $this->getVfWebQQ($ptWebQQ);
92
        list($uin, $pSessionId) = $this->getUinAndPSessionId($ptWebQQ);
93
94
        $credential = new Credential(
95
            $ptWebQQ,
96
            $vfWebQQ,
97
            $pSessionId,
98
            $uin,
99
            Client::$clientId, //smartqq保留字段,固定值
0 ignored issues
show
Bug introduced by
The property clientId cannot be accessed from this context as it is declared private in class Slince\SmartQQ\Client.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
100
            $this->cookies
101
        );
102
103
        return $credential;
104
    }
105
106
    /**
107
     * 从cookie中查找 "qrsig" 参数
108
     *
109
     * @return string
110
     */
111
    protected function findQRSign()
112
    {
113
        foreach ($this->getCookies() as $cookie) {
114
            if (0 === strcasecmp($cookie->getName(), 'qrsig')) {
115
                return $cookie->getValue();
116
            }
117
        }
118
        throw new RuntimeException('Can not find parameter [qrsig]');
119
    }
120
121
    /**
122
     * 验证二维码状态
123
     *
124
     * @param int $ptQrToken qr token
125
     *
126
     * @return int
127
     */
128
    protected function getQrCodeStatus($ptQrToken)
129
    {
130
        $request = new Request\VerifyQrCodeRequest($ptQrToken);
131
        $response = $this->sendRequest($request);
132
        if (false !== strpos($response->getBody(), '未失效')) {
133
            $status = Request\VerifyQrCodeRequest::STATUS_UNEXPIRED;
134
        } elseif (false !== strpos($response->getBody(), '已失效')) {
135
            $status = Request\VerifyQrCodeRequest::STATUS_EXPIRED;
136
        } elseif (false !== strpos($response->getBody(), '认证中')) {
137
            $status = Request\VerifyQrCodeRequest::STATUS_ACCREDITATION;
138
        } else {
139
            $status = Request\VerifyQrCodeRequest::STATUS_CERTIFICATION;
140
            //找出认证url
141
            if (preg_match("#'(http.+)'#U", strval($response->getBody()), $matches)) {
142
                $this->certificationUrl = trim($matches[1]);
143
            } else {
144
                throw new RuntimeException('Can not find certification url');
145
            }
146
        }
147
148
        return $status;
149
    }
150
151
    /**
152
     * 获取ptwebqq的参数值
153
     *
154
     * @return string
155
     */
156
    protected function getPtWebQQ()
157
    {
158
        $request = new Request\GetPtWebQQRequest();
159
        $request->setUri($this->certificationUrl);
160
        $this->sendRequest($request);
161
        foreach ($this->getCookies() as $cookie) {
162
            if (0 === strcasecmp($cookie->getName(), 'ptwebqq')) {
163
                return $cookie->getValue();
164
            }
165
        }
166
        throw new RuntimeException('Can not find parameter [ptwebqq]');
167
    }
168
169
    /**
170
     * @param string $ptWebQQ
171
     *
172
     * @return string
173
     */
174
    protected function getVfWebQQ($ptWebQQ)
175
    {
176
        $request = new Request\GetVfWebQQRequest($ptWebQQ);
177
        $response = $this->sendRequest($request);
178
179
        return Request\GetVfWebQQRequest::parseResponse($response);
180
    }
181
182
    /**
183
     * 获取pessionid和uin.
184
     *
185
     * @param string $ptWebQQ
186
     *
187
     * @return array
188
     */
189
    protected function getUinAndPSessionId($ptWebQQ)
190
    {
191
        $request = new Request\GetUinAndPsessionidRequest([
192
            'ptwebqq' => $ptWebQQ,
193
            'clientid' => Client::$clientId,
0 ignored issues
show
Bug introduced by
The property clientId cannot be accessed from this context as it is declared private in class Slince\SmartQQ\Client.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
194
            'psessionid' => '',
195
            'status' => 'online',
196
        ]);
197
        $response = $this->sendRequest($request);
198
199
        return Request\GetUinAndPsessionidRequest::parseResponse($response);
200
    }
201
202
    protected function sendRequest(Request\RequestInterface $request)
203
    {
204
        return $this->client->sendRequest($request, [
205
            'cookies' => $this->cookies //使用当前cookies
206
        ]);
207
    }
208
209
    protected function getCookies()
210
    {
211
        return $this->cookies;
212
    }
213
}