Completed
Pull Request — master (#98)
by Sergey
02:49
created

Request::addDefaultCsrfInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
namespace seregazhuk\PinterestBot\Api;
4
5
use seregazhuk\PinterestBot\Helpers\UrlHelper;
6
use seregazhuk\PinterestBot\Helpers\FileHelper;
7
use seregazhuk\PinterestBot\Helpers\CsrfHelper;
8
use seregazhuk\PinterestBot\Contracts\HttpInterface;
9
use seregazhuk\PinterestBot\Exceptions\AuthException;
10
use seregazhuk\PinterestBot\Contracts\RequestInterface;
11
12
/**
13
 * Class Request.
14
 *
15
 * @property resource $ch
16
 * @property bool     $loggedIn
17
 * @property string   $userAgent
18
 * @property string   $csrfToken
19
 * @property string   $cookieJar
20
 */
21
class Request implements RequestInterface
22
{
23
    const INTEREST_ENTITY_ID = 'interest_id';
24
    const BOARD_ENTITY_ID = 'board_id';
25
    const COOKIE_NAME = 'pinterest_cookie';
26
    const PINNER_ENTITY_ID = 'user_id';
27
28
    protected $userAgent = 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0';
29
    /**
30
     * @var HttpInterface
31
     */
32
    protected $http;
33
    protected $loggedIn;
34
    protected $cookieJar;
35
    protected $options;
36
37
    /**
38
     *
39
     * @var string
40
     */
41
    protected $filePathToUpload;
42
43
    public $csrfToken = '';
44
45
    /**
46
     * Common headers needed for every query.
47
     *
48
     * @var array
49
     */
50
    protected $requestHeaders = [
51
        'Accept: application/json, text/javascript, */*; q=0.01',
52
        'Accept-Language: en-US,en;q=0.5',
53
        'DNT: 1',
54
        'Host: nl.pinterest.com',
55
        'X-Pinterest-AppState: active',
56
        'X-NEW-APP: 1',
57
        'X-APP-VERSION: 04cf8cc',
58
        'X-Requested-With: XMLHttpRequest',
59
    ];
60
61
    /**
62
     * @var string
63
     */
64
    protected $postFileData;
65
66
    /**
67
     * @param HttpInterface $http
68
     */
69
    public function __construct(HttpInterface $http)
70
    {
71
        $this->http = $http;
72
        $this->cookieJar = tempnam(sys_get_temp_dir(), self::COOKIE_NAME);
73
    }
74
75
    /**
76
     * Executes api call for follow or unfollow user.
77
     *
78
     * @param int    $entityId
79
     * @param string $entityName
80
     * @param string $url
81
     *
82
     * @return array
83
     */
84
    public function followMethodCall($entityId, $entityName, $url)
85
    {
86
        $dataJson = [
87
            'options' => [
88
                $entityName => $entityId,
89
            ],
90
            'context' => [],
91
        ];
92
93
        if ($entityName == self::INTEREST_ENTITY_ID) {
94
            $dataJson['options']['interest_list'] = 'favorited';
95
        }
96
97
        $post = ['data' => json_encode($dataJson, JSON_FORCE_OBJECT)];
98
        $postString = UrlHelper::buildRequestString($post);
99
100
        return $this->exec($url, $postString);
101
    }
102
103
    public function upload($pathToFile, $url)
104
    {
105
        $this->filePathToUpload = $pathToFile;
106
107
        return $this->exec($url);
108
    }
109
110
    /**
111
     * Executes request to Pinterest API.
112
     *
113
     * @param string $resourceUrl
114
     * @param string $postString
115
     *
116
     * @return array
117
     */
118
    public function exec($resourceUrl, $postString = '')
119
    {
120
        $url = UrlHelper::buildApiUrl($resourceUrl);
121
        $this->makeHttpOptions($postString);
122
        $res = $this->http->execute($url, $this->options);
123
124
        $this->filePathToUpload = null;
125
        return json_decode($res, true);
126
    }
127
128
    /**
129
     * Adds necessary curl options for query.
130
     *
131
     * @param string $postString POST query string
132
     *
133
     * @return $this
134
     */
135
    protected function makeHttpOptions($postString = '')
136
    {
137
        $this->setDefaultHttpOptions();
138
139
        if ($this->csrfToken == CsrfHelper::DEFAULT_TOKEN) {
140
            $this->options = $this->addDefaultCsrfInfo($this->options);
141
        }
142
143
        if (!empty($postString) || $this->filePathToUpload) {
144
            $this->options[CURLOPT_POST] = true;
145
            $this->options[CURLOPT_POSTFIELDS] = $this->filePathToUpload ? $this->postFileData : $postString;
146
        }
147
148
        return $this;
149
    }
150
151
    /**
152
     * @return array
153
     */
154
    protected function setDefaultHttpOptions()
155
    {
156
        $this->options = [
157
            CURLOPT_USERAGENT      => $this->userAgent,
158
            CURLOPT_RETURNTRANSFER => true,
159
            CURLOPT_SSL_VERIFYPEER => false,
160
            CURLOPT_FOLLOWLOCATION => true,
161
            CURLOPT_ENCODING       => 'gzip,deflate',
162
            CURLOPT_HTTPHEADER     => $this->getDefaultHttpHeaders(),
163
            CURLOPT_REFERER        => UrlHelper::URL_BASE,
164
            CURLOPT_COOKIEFILE     => $this->cookieJar,
165
            CURLOPT_COOKIEJAR      => $this->cookieJar,
166
        ];
167
    }
168
169
    /**
170
     * @param array $options
171
     *
172
     * @return mixed
173
     */
174
    protected function addDefaultCsrfInfo($options)
175
    {
176
        $options[CURLOPT_REFERER] = UrlHelper::URL_BASE;
177
        $options[CURLOPT_HTTPHEADER][] = CsrfHelper::getDefaultCookie();
178
179
        return $options;
180
    }
181
    
182
    /**
183
     * Clear token information.
184
     *
185
     * @return $this
186
     */
187
    public function clearToken()
188
    {
189
        $this->csrfToken = CsrfHelper::DEFAULT_TOKEN;
190
191
        return $this;
192
    }
193
194
    /**
195
     * Mark api as logged.
196
     * @return $this
197
     * @throws AuthException
198
     */
199
    public function setLoggedIn()
200
    {
201
        $this->csrfToken = CsrfHelper::getTokenFromFile($this->cookieJar);
202
        if (empty($this->csrfToken)) {
203
            throw new AuthException('Cannot parse token from cookies.');
204
        }
205
        $this->loggedIn = true;
206
        return $this;
207
    }
208
209
    /**
210
     * Get log status.
211
     *
212
     * @return bool
213
     */
214
    public function isLoggedIn()
215
    {
216
        return $this->loggedIn;
217
    }
218
219
    /**
220
     * @param $userAgent
221
     * @return $this
222
     */
223
    public function setUserAgent($userAgent)
224
    {
225
        if ($userAgent !== null) {
226
            $this->userAgent = $userAgent;
227
        }
228
229
        return $this;
230
    }
231
232
    /**
233
     * Create request string.
234
     *
235
     * @param array  $data
236
     * @param string $sourceUrl
237
     * @param array  $bookmarks
238
     *
239
     * @return string
240
     */
241
    public static function createQuery(array $data = [], $sourceUrl = '/', $bookmarks = [])
242
    {
243
        $request = self::createRequestData($data, $sourceUrl, $bookmarks);
244
245
        return UrlHelper::buildRequestString($request);
246
    }
247
248
    /**
249
     * @param array|object $data
250
     * @param string|null  $sourceUrl
251
     * @param array        $bookmarks
252
     *
253
     * @return array
254
     */
255
    public static function createRequestData(array $data = [], $sourceUrl = '/', $bookmarks = [])
256
    {
257
        if (empty($data)) {
258
            $data = self::createEmptyRequestData();
259
        }
260
261
        if (!empty($bookmarks)) {
262
            $data['options']['bookmarks'] = $bookmarks;
263
        }
264
265
        $data['context'] = new \stdClass();
266
267
        return [
268
            'source_url' => $sourceUrl,
269
            'data'       => json_encode($data),
270
        ];
271
    }
272
273
    /**
274
     * @return array
275
     */
276
    protected static function createEmptyRequestData()
277
    {
278
        return ['options' => []];
279
    }
280
281
    /**
282
     * @return array
283
     */
284
    protected function getDefaultHttpHeaders()
285
    {
286
        return array_merge(
287
            $this->requestHeaders, $this->getContentTypeHeader(), ['X-CSRFToken: ' . $this->csrfToken]
288
        );
289
    }
290
291
    /**
292
     * If we are uploading file, we should build boundary form data. Otherwise
293
     * it is simple urlencoded form.
294
     *
295
     * @return array
296
     */
297
    protected function getContentTypeHeader()
298
    {
299
        if ($this->filePathToUpload) {
300
            $delimiter = '-------------' . uniqid();
301
            $this->buildFilePostData($delimiter);
302
303
            return [
304
                'Content-Type: multipart/form-data; boundary=' . $delimiter,
305
                'Content-Length: ' . strlen($this->postFileData)
306
            ];
307
        }
308
309
        return ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'];
310
    }
311
312
    protected function buildFilePostData($delimiter)
313
    {
314
        $data = "--$delimiter\r\n";
315
        $data .= 'Content-Disposition: form-data; name="img"; filename="' . basename($this->filePathToUpload) . '"' . "\r\n";
316
        $data .= 'Content-Type: ' . FileHelper::getMimeType($this->filePathToUpload) . "\r\n\r\n";
317
        $data .= file_get_contents($this->filePathToUpload) . "\r\n";
318
        $data .= "--$delimiter--\r\n";
319
320
        $this->postFileData = $data;
321
322
        return $this;
323
    }
324
}
325