1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace zembrowski\SMS; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class Orange |
7
|
|
|
* @package zembrowski\phpsms-poland |
8
|
|
|
*/ |
9
|
|
|
class Orange |
10
|
|
|
{ |
11
|
|
|
|
12
|
|
|
public $url = 'https://www.orange.pl'; // orange.pl URL |
13
|
|
|
private $user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4'; |
14
|
|
|
private $login_request_uri = '/zaloguj.phtml'; // login form request uri |
15
|
|
|
private $login_post_query_string = '?_DARGS=/ocp/gear/infoportal/portlets/login/login-box.jsp'; // login form POST query string |
16
|
|
|
private $send_request_uri = '/portal/map/map/message_box?mbox_view=newsms'; // request uri of form for sending new messages |
17
|
|
|
private $send_post_request_uri = '/portal/map/map/message_box?_DARGS=/gear/mapmessagebox/smsform.jsp'; // action target for POST request of the sending new messages form |
18
|
|
|
public $max_length = '640'; // max. length of one SMS message according to the sending new messages form |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Session placeholder during the whole execution |
22
|
|
|
* @var \Requests_Session |
23
|
|
|
*/ |
24
|
|
|
private $session; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Initialized DOM for response analyzing |
28
|
|
|
* @var \simple_html_dom |
29
|
|
|
*/ |
30
|
|
|
private $html; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Session data variable (not being cross-checked yet) |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
private $dynamic_session; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* True if user logged in successfully |
40
|
|
|
* @var boolean |
41
|
|
|
*/ |
42
|
|
|
private $logged_in = false; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Form submission token placeholder |
46
|
|
|
* @var string |
47
|
|
|
*/ |
48
|
|
|
private $token; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Instantiates the Requests handler with session support. |
52
|
|
|
*/ |
53
|
|
|
public function __construct() |
54
|
|
|
{ |
55
|
|
|
$session = new \Requests_Session($this->url); |
56
|
|
|
$session->useragent = $this->user_agent; |
57
|
|
|
$this->session = $session; |
58
|
|
|
$this->session->get($this->login_request_uri); |
59
|
|
|
|
60
|
|
|
$html = new \simple_html_dom(); |
61
|
|
|
$this->html = $html; |
62
|
|
|
|
63
|
|
|
$random = rand(1000000000, 2147483647); |
64
|
|
|
$this->dynamic_session = $random . $random; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Login at orange.pl |
69
|
|
|
* |
70
|
|
|
* You have to be register at orange.pl |
71
|
|
|
* Head to: https://www.orange.pl/rejestracja.phtml |
72
|
|
|
* |
73
|
|
|
* @param string $login - login or the number assosciated with the service you use |
74
|
|
|
* @param string $password - password (pol. "Hasło") |
75
|
|
|
*/ |
76
|
|
|
public function login($login, $password) |
77
|
|
|
{ |
78
|
|
|
// Referer header set only to act more like a browser |
79
|
|
|
$this->session->headers['Referer'] = $this->url . $this->login_request_uri; |
80
|
|
|
|
81
|
|
|
$this->session->data = array( |
82
|
|
|
'_dyncharset' => 'UTF-8', |
83
|
|
|
'_dynSessConf' => $this->dynamic_session, |
84
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.loginErrorURL' => $this->url . $this->login_request_uri, |
85
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.loginErrorURL' => '', |
86
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.loginSuccessURL' => $this->url . $this->send_request_uri, |
87
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.loginSuccessURL' => '', |
88
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.firstEnter' => 'true', |
89
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.firstEnter' => '', |
90
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.value.login' => $login, |
91
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.value.login' => '', |
92
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.value.password' => $password, |
93
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.value.password' => '', |
94
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.rememberMe' => '', |
95
|
|
|
'/tp/core/profile/login/ProfileLoginFormHandler.login' => 'Zaloguj się', |
96
|
|
|
'_D:/tp/core/profile/login/ProfileLoginFormHandler.login' => '', |
97
|
|
|
'_DARGS' => '/ocp/gear/infoportal/portlets/login/login-box.jsp' |
98
|
|
|
); |
99
|
|
|
|
100
|
|
|
$response = $this->session->post($this->login_request_uri . $this->login_post_query_string); |
101
|
|
|
|
102
|
|
|
// TODO: Proof, that user logged in (other than token) |
103
|
|
|
$this->logged_in = true; |
104
|
|
|
$this->token = $this->token($response->body); |
105
|
|
|
|
106
|
|
|
$result = array( |
107
|
|
|
'errors' => $this->checkErrors($response->body, 'div.login-box__error p', 'login'), 'remaining' => $this->remaining($response->body) |
108
|
|
|
); |
109
|
|
|
|
110
|
|
|
return $result; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Retrieves the token from the passed content |
115
|
|
|
* |
116
|
|
|
* @param string $content - content to be searched through |
117
|
|
|
* @return string - token |
118
|
|
|
*/ |
119
|
|
|
private function token($content) |
120
|
|
|
{ |
121
|
|
|
|
122
|
|
|
if ($content) { |
123
|
|
|
|
124
|
|
|
$element = $this->find($content, 'div#box-smsform form input[name=/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.token]', 0); |
125
|
|
|
|
126
|
|
|
if (count($element) > 0) { |
127
|
|
|
|
128
|
|
|
$value = $element->value; |
129
|
|
|
|
130
|
|
|
if (!empty($value)) { |
131
|
|
|
|
132
|
|
|
$result = $value; |
133
|
|
|
|
134
|
|
|
} else { |
135
|
|
|
|
136
|
|
|
$result = $this->getToken(); |
137
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
} else { |
141
|
|
|
|
142
|
|
|
$result = $this->getToken(); |
143
|
|
|
|
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
} else { |
147
|
|
|
|
148
|
|
|
$result = $this->getToken(); |
149
|
|
|
|
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $result; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Send a SMS through the webform at $this->send_post_request_uri |
157
|
|
|
* |
158
|
|
|
* @param string $recipient - addressable phone number of the recipient(s) |
159
|
|
|
* + 9 digits without leading zero for national mobile numbers |
160
|
|
|
* (e.g. 501234567) |
161
|
|
|
* + for landline and international numbers plus sign or two leading |
162
|
|
|
* zeros followed by international dialing code are allowed |
163
|
|
|
* (e.g. 004912345678901 or +4912345678901) |
164
|
|
|
* + integer values recommended (or strings with no special chars |
165
|
|
|
* except plus sign); spaces seem to get trimmed |
166
|
|
|
* + up to five recipients as a comma separeted string are allowed |
167
|
|
|
* for one request (e.g. 501234567,004912345678901) |
168
|
|
|
* @param string $text - content of the SMS |
169
|
|
|
* @param boolean $multiple - should be true for multiple send requests in |
170
|
|
|
* a session; in case of multiple send() |
171
|
|
|
* function invokes during one session a new |
172
|
|
|
* token for every request has to be retrieved |
173
|
|
|
* (default: false) |
174
|
|
|
*/ |
175
|
|
|
public function send($recipient, $text, $multiple = false) |
176
|
|
|
{ |
177
|
|
|
|
178
|
|
|
$this->checkLoggedIn(); |
179
|
|
|
|
180
|
|
|
if (strlen($text) <= 0 || strlen($text) > $this->max_length) { |
181
|
|
|
throw new \Exception('The message must be longer than 0 characters, but shorter than ' . $this->max_length . ' characters'); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$this->session->options['timeout'] = 30; |
185
|
|
|
|
186
|
|
|
// Referer header set only to act more like a browser |
187
|
|
|
$this->session->headers['Referer'] = $this->url . $this->send_request_uri; |
188
|
1 |
|
|
189
|
|
|
$this->session->data = array( |
190
|
1 |
|
'_dyncharset' => 'UTF-8', |
191
|
|
|
'_dynSessConf' => $this->dynamic_session, |
192
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.type' => 'sms', |
193
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.type' => '', |
194
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.errorURL' => '', |
195
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.errorURL' => '', |
196
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.successURL' => $this->send_request_uri, |
197
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.successURL' =>'', |
198
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.to' => $recipient, |
199
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.to' => '', |
200
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.body' => $text, |
201
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.body' => '', |
202
|
1 |
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.token' => $this->token, |
203
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.token' => '', |
204
|
|
|
'enabled' => false, |
205
|
1 |
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.create.x' => rand(0, 50), |
206
|
|
|
'/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.create.y' => rand(0, 25), |
207
|
|
|
'_D:/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.create' => '', |
208
|
|
|
'_DARGS' => '/gear/mapmessagebox/smsform.jsp' |
209
|
|
|
); |
210
|
|
|
|
211
|
|
|
$response = $this->session->post($this->send_post_request_uri); |
212
|
|
|
|
213
|
|
|
if ($multiple) $this->token = $this->token($response->body); |
214
|
|
|
|
215
|
|
|
$result = array( |
216
|
|
|
'status_code' => $response->status_code, |
217
|
|
|
'errors' => $this->checkErrors($response->body, 'div.box-error p', 'send'), |
218
|
|
|
'remaining' => $this->remaining($response->body) |
219
|
|
|
); |
220
|
|
|
|
221
|
|
|
return $result; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Find element in HTML Dom |
226
|
|
|
* |
227
|
|
|
* @param string $content - content to be searched through |
228
|
|
|
* @return object |
229
|
|
|
*/ |
230
|
|
|
private function find($content, $selector, $nth = null) |
231
|
|
|
{ |
232
|
|
|
$this->html->load($content); |
233
|
|
|
|
234
|
|
|
if (is_int($nth) || $nth === 0) { |
235
|
|
|
|
236
|
|
|
$result = $this->html->find($selector, $nth); |
237
|
|
|
|
238
|
|
|
} else { |
239
|
|
|
|
240
|
|
|
$result = $this->html->find($selector); |
241
|
|
|
|
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
return $result; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Checks the remaining SMS left this month from the response body |
249
|
|
|
* |
250
|
|
|
* @param string $content - content to be searched through |
251
|
|
|
* @return boolean|int|string - SMS remaining this month |
252
|
|
|
* false if no content |
253
|
|
|
* int if integer value present |
254
|
|
|
* string in other cases |
255
|
|
|
*/ |
256
|
|
|
private function remaining($content) |
257
|
|
|
{ |
258
|
|
|
|
259
|
|
|
$this->checkLoggedIn(); |
260
|
|
|
|
261
|
|
|
if ($content) { |
262
|
|
|
|
263
|
|
|
$elements = $this->find($content, '#syndication p.item span.value'); |
264
|
|
|
|
265
|
|
|
if (count($elements) > 0) { |
266
|
|
|
|
267
|
|
|
$pre_result = $this->checkRemaining($elements); |
268
|
|
|
|
269
|
|
|
if ($pre_result['found']) { |
270
|
|
|
|
271
|
|
|
$result = $pre_result; |
272
|
|
|
|
273
|
|
|
} else { |
274
|
|
|
|
275
|
|
|
$result = $this->getRemaining(); |
276
|
|
|
|
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
} else { |
280
|
|
|
|
281
|
|
|
$result = $this->getRemaining(); |
282
|
|
|
|
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
} else { |
286
|
|
|
|
287
|
|
|
$result = $this->getRemaining(); |
288
|
|
|
|
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
return $result; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Get the amount of remaining SMS this month through a request |
296
|
|
|
* |
297
|
|
|
* @return boolean|array - false if not logged in, no valuable content |
298
|
|
|
* otherwise array result of checkRemaining() function |
299
|
|
|
*/ |
300
|
|
View Code Duplication |
public function getRemaining() |
|
|
|
|
301
|
|
|
{ |
302
|
|
|
|
303
|
|
|
$this->checkLoggedIn(); |
304
|
|
|
|
305
|
|
|
$response = $this->session->get($this->send_request_uri); |
306
|
|
|
$elements = $this->find($response->body, '#syndication p.item span.value'); |
307
|
|
|
|
308
|
|
|
if (count($elements) > 0) { |
309
|
|
|
|
310
|
|
|
$result = $this->checkRemaining($elements); |
311
|
|
|
|
312
|
|
|
} else { |
313
|
|
|
|
314
|
|
|
$result = false; |
315
|
|
|
|
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
return $result; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* Get the token through a request |
323
|
|
|
* |
324
|
|
|
* @return string - token |
325
|
|
|
*/ |
326
|
|
View Code Duplication |
public function getToken() |
|
|
|
|
327
|
|
|
{ |
328
|
|
|
|
329
|
|
|
$this->checkLoggedIn(); |
330
|
|
|
|
331
|
|
|
$response = $this->session->get($this->send_request_uri); |
332
|
|
|
$element = $this->find($response->body, 'div#box-smsform form input[name=/amg/ptk/map/messagebox/formhandlers/MessageFormHandler.token]', 0); |
333
|
|
|
|
334
|
|
|
if (count($element) > 0) { |
335
|
|
|
|
336
|
|
|
$result = $this->token($response->body); |
337
|
|
|
$this->token = $result; |
338
|
|
|
|
339
|
|
|
} else { |
340
|
|
|
|
341
|
|
|
$result = false; |
342
|
|
|
|
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
|
346
|
|
|
return $result; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Check whether errors have been returned |
351
|
|
|
* |
352
|
|
|
* @param string $content - response body of a request |
353
|
|
|
* @return boolean - false if no element described by the selector exists |
354
|
|
|
*/ |
355
|
|
|
private function checkErrors($content, $selector, $function = null) |
356
|
|
|
{ |
357
|
|
|
$elements = $this->find($content, $selector); |
358
|
|
|
|
359
|
|
|
if (count($elements) > 0) { |
360
|
|
|
|
361
|
|
|
foreach ($elements as $key => $item) { |
362
|
|
|
|
363
|
|
|
$details = (!empty($function)) ? 'Function ' . $function . ' returned: ' : null; |
364
|
|
|
|
365
|
|
|
throw new \Exception($details . trim($item->plaintext)); |
366
|
|
|
|
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
} else { |
370
|
|
|
|
371
|
|
|
$result = false; |
372
|
|
|
|
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
return $result; |
|
|
|
|
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Checks whether user logged in |
380
|
|
|
*/ |
381
|
|
|
private function checkLoggedIn() { |
382
|
|
|
|
383
|
|
|
if (!$this->logged_in) { |
384
|
|
|
|
385
|
|
|
throw new \Exception('You are not logged in. Log in first.'); |
386
|
|
|
|
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Checks value for the remaining() and getRemaing() functions |
393
|
|
|
* |
394
|
|
|
* @param string $value - input value |
|
|
|
|
395
|
|
|
* @return array - information about the retrieved information |
396
|
|
|
* boolean 'found' - false if no valuable content (default: false) |
397
|
|
|
* int 'remaining' - remaining amount of SMS (default: 0) |
398
|
|
|
* array 'errors' - array with errors, key is the index of |
399
|
|
|
* the element with an error |
400
|
|
|
*/ |
401
|
|
|
private function checkRemaining($elements) |
402
|
|
|
{ |
403
|
|
|
|
404
|
|
|
$found = false; |
405
|
|
|
$count = 0; |
406
|
|
|
$errors = array(); |
407
|
|
|
|
408
|
|
|
foreach ($elements as $key => $item) { |
409
|
|
|
|
410
|
|
|
$value = $item->plaintext; |
411
|
|
|
|
412
|
|
|
if (!empty($value)) { |
413
|
|
|
|
414
|
|
|
$value_int = intval(trim($value)); |
415
|
|
|
|
416
|
|
|
if (is_int($value_int)) { |
417
|
|
|
|
418
|
|
|
$count += $value_int; |
419
|
|
|
$found = true; |
420
|
|
|
|
421
|
|
|
} else { |
422
|
|
|
|
423
|
|
|
$errors[$key] = 'No integer value found for key indexed ' . $key . '. Retrieved value: "' . $value . '"'; |
424
|
|
|
|
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
$result = array( |
432
|
|
|
'found' => $found, |
433
|
|
|
'count' => $count, |
434
|
|
|
'errors' => $errors |
435
|
|
|
); |
436
|
|
|
|
437
|
|
|
return $result; |
438
|
|
|
|
439
|
|
|
} |
440
|
|
|
} |
441
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.