├── .gitignore
├── LICENSE.md
├── README.md
├── composer.json
├── demo.php
└── src
└── HSPDev
└── HuaweiApi
├── CustomHttpClient.php
└── Router.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io
2 |
3 | ### Composer ###
4 | composer.phar
5 | vendor/
6 |
7 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
8 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
9 | # composer.lock
10 |
11 |
12 | ### OSX ###
13 | .DS_Store
14 | .AppleDouble
15 | .LSOverride
16 |
17 | # Icon must end with two \r
18 | Icon
19 |
20 |
21 | # Thumbnails
22 | ._*
23 |
24 | # Files that might appear in the root of a volume
25 | .DocumentRevisions-V100
26 | .fseventsd
27 | .Spotlight-V100
28 | .TemporaryItems
29 | .Trashes
30 | .VolumeIcon.icns
31 |
32 | # Directories potentially created on remote AFP share
33 | .AppleDB
34 | .AppleDesktop
35 | Network Trash Folder
36 | Temporary Items
37 | .apdisk
38 |
39 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) [2015] [Henrik Sylvester Pedersen]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Huawei E5180 API
2 |
3 | This project will let you interface with your Huawei E5180 Cube router easily.
4 | This router is deployed by 3 DK, as "home routers" for their 4G connections.
5 | You can get it for free if you order a 100GB or above package from 3.
6 | Link where I bought mine: [https://www.3.dk/mobiler-tablets/modems-routere/huawei/huawei-e5180-cube](https://www.3.dk/mobiler-tablets/modems-routere/huawei/huawei-e5180-cube)
7 |
8 | No advertisement or affiliation or anything, I wouldn't even really recommend the router. It got great speeds, but the WiFi is not the greatest,
9 | and I've seen it overload multiple times when you have lots of clients on your (W)LAN. The 4G link holds, but the LAN/WiFi/Router portion is really bad, so I personally use it with a D-link DIR-810l as router, with it's WAN port connected to the Huawei routers single LAN port, so it can focus on just delivering 4G Internet.
10 |
11 | ## Features
12 |
13 | - Send SMS through the mobile network
14 | - Receive SMS (you need to pool for this periodically)
15 | - Delete SMS (to avoid filling it up)
16 | - Query router status (lots of info)
17 | - Query traffic statistics (decent amount of info)
18 | - Get the current PLMN (which network you are on)
19 | - Get craddle status (hardware info, battery and such if yours have one)
20 | - Get WLAN clients
21 | - Check the device for notifications (Including SMS and updates)
22 | - Query LED status (the big blue one on top)
23 | - Turn the LED on or off.
24 |
25 | What could you use this for? Do you want a free inbound SMS gateway for your home? Now you got it, if you have this router. I know 3.dk currently charges me 0.20 DKK for outbound SMS, as of 19-06-2015, but be sure to check with your own provider before sending SMS. I could also easily imaging your provider being mad at you if you suddenly used this as a commercial gateway, sending and receiving thousands of SMS messages.
26 |
27 | If you like me, live in the country without good internet through wire, and want a 4G router like this, you could also make your own interface to keep your bandwidth bills in check.
28 |
29 | Also, the blue LED is quite fun to play with. You can tap the top, to turn it on and off, so I already have plans for letting me know if I have new emails by turning the LED blue.
30 |
31 | ## Documentation
32 |
33 | How do I use this library?
34 |
35 | Simply include the composer autoloader into your project after installation, as you are used to, and proceed to make a Router object. Set the address of your router and login. Now every other function listed here SHOULD work, at least on E5180. I can't talk about any other routers compliance.
36 |
37 | ```php
38 | require_once 'vendor/autoload.php';
39 |
40 | // The router class is the main entry point for interaction.
41 | $router = new HSPDev\HuaweiApi\Router;
42 |
43 | // If specified without http or https, assumes http://
44 | $router->setAddress('192.168.8.1');
45 |
46 | // Username and password.
47 | // Username is always admin as far as I can tell.
48 | $router->login('admin', 'your-password');
49 |
50 | var_dump($router->getLedStatus());
51 | ```
52 |
53 | This will get the current status off the blue LED on top, either true or false, for on/off. If this seems to work, try the following line instead:
54 |
55 | ```php
56 | var_dump($router->setLedOn(!$router->getLedStatus()));
57 | ```
58 |
59 | Every time you run the script now, it should turn the LED on or off, depending on it's current state.
60 |
61 | **Now let's try something else.**
62 |
63 | ```php
64 | var_dump($router->getNetwork());
65 | ```
66 |
67 | Which should return something like the following, which shows that I'm currently on the "3 DK"" network. You can look up PLMN lists to get the numeric codes.
68 |
69 | ```php
70 | object(SimpleXMLElement)#8 (5) {
71 | ["State"]=>
72 | string(1) "0"
73 | ["FullName"]=>
74 | string(4) "3 DK"
75 | ["ShortName"]=>
76 | string(4) "3 DK"
77 | ["Numeric"]=>
78 | string(5) "23806"
79 | ["Rat"]=>
80 | string(1) "2"
81 | }
82 | ```
83 |
84 | **What about some SMS?**
85 |
86 | ```php
87 | var_dump($router->getInbox());
88 | ```
89 |
90 | In my case it returned this, meaning I have no new messages.
91 |
92 | ```php
93 | object(SimpleXMLElement)#6 (2) {
94 | ["Count"]=>
95 | string(1) "0"
96 | ["Messages"]=>
97 | object(SimpleXMLElement)#4 (0) {
98 | }
99 | }
100 | ```
101 |
102 | That can't be true. Let's send some to our router. You can probably find the phone number for your router on your bills, sometimes in the web interface or maybe by simple logging into the web interface and sending yourself a message. After sending my router a SMS I got this result instead:
103 |
104 | ```php
105 | object(SimpleXMLElement)#6 (2) {
106 | ["Count"]=>
107 | string(1) "1"
108 | ["Messages"]=>
109 | object(SimpleXMLElement)#4 (1) {
110 | ["Message"]=>
111 | object(SimpleXMLElement)#8 (9) {
112 | ["Smstat"]=>
113 | string(1) "0"
114 | ["Index"]=>
115 | string(5) "40000"
116 | ["Phone"]=>
117 | string(11) "(my phone number)"
118 | ["Content"]=>
119 | string(3) "Lol"
120 | ["Date"]=>
121 | string(19) "2015-06-19 15:27:15"
122 | ["Sca"]=>
123 | object(SimpleXMLElement)#9 (0) {
124 | }
125 | ["SaveType"]=>
126 | string(1) "4"
127 | ["Priority"]=>
128 | string(1) "0"
129 | ["SmsType"]=>
130 | string(1) "1"
131 | }
132 | }
133 | }
134 | ```
135 |
136 | Have a look inside the Router.php class to find out what methods you can use, it's very well documented, but I will throw a list here anyway.
137 |
138 | - login($username, $password) username is almost always "admin". "password" is the one for the web interface.
139 | - getStatus() gives info about the routers status.
140 | - getTrafficStats() gives traffic info
141 | - getMonthStats() does the same for the current month (if you have setup limits)
142 | - getNetwork() gives info about the current network. Can't find the bars anywhere tho.
143 | - getCraddleStatus() lots of more info. I suspect you can get battery status here, if your device has one.
144 | - getSmsCount() DOES NOT RETURN AN integer, but also an XML object.
145 | - getWlanClients() gets a list of WlanClients, if they have IP 0.0.0.0 they are disconnected.
146 | - getNotifications() does what it says.
147 | - setLedOn(boolean $on) call with "true" to turn on, and "false" for off.
148 | - getLedStatus() true/false for LED status.
149 | - isLoggedIn() true/false to check if logged in.
150 | - getInbox($page = 1, $count = 20, $unreadPreferred = false) defaults are fine for most tinkering. page/count for pagination.
151 | - deleteSms($index) provide with SMS index for deleting. Returns true if not found also.
152 | - sendSms($receiver, $message) Pretty self explanatory. Might return true and not send anyway. There is an API to query for send status, but I didn't worry about it.
153 |
154 | I don't promise that these will work like advertised or at all, just have fun. It should get you started.
155 |
156 | ## Huawei Router API Error codes
157 |
158 | Sometimes if you are experimenting with the Huawei API and trying to talk with it, you will get a random error code back. This sucks, to say it politely, as there is absolutely no information on what is going wrong. Therefore, I've gotten hold of this list of error codes for the Huawei API, which I know is true for the E5180 and probably other devices too. So if you just googled "Huawei Router API Error" or something like that, congratulations, today is your lucky day.
159 | Please note that not all codes are in here, but most of them are.
160 |
161 | ```php
162 | ERROR_BUSY = 100004
163 | ERROR_CHECK_SIM_CARD_CAN_UNUSEABLE = 101004
164 | ERROR_CHECK_SIM_CARD_PIN_LOCK = 101002
165 | ERROR_CHECK_SIM_CARD_PUN_LOCK = 101003
166 | ERROR_COMPRESS_LOG_FILE_FAILED = 103102
167 | ERROR_CRADLE_CODING_FAILED = 118005
168 | ERROR_CRADLE_GET_CRURRENT_CONNECTED_USER_IP_FAILED = 118001
169 | ERROR_CRADLE_GET_CRURRENT_CONNECTED_USER_MAC_FAILED = 118002
170 | ERROR_CRADLE_GET_WAN_INFORMATION_FAILED = 118004
171 | ERROR_CRADLE_SET_MAC_FAILED = 118003
172 | ERROR_CRADLE_UPDATE_PROFILE_FAILED = 118006
173 | ERROR_DEFAULT = -1
174 | ERROR_DEVICE_AT_EXECUTE_FAILED = 103001
175 | ERROR_DEVICE_COMPRESS_LOG_FILE_FAILED = 103015
176 | ERROR_DEVICE_GET_API_VERSION_FAILED = 103006
177 | ERROR_DEVICE_GET_AUTORUN_VERSION_FAILED = 103005
178 | ERROR_DEVICE_GET_LOG_INFORMATON_LEVEL_FAILED = 103014
179 | ERROR_DEVICE_GET_PC_AISSST_INFORMATION_FAILED = 103012
180 | ERROR_DEVICE_GET_PRODUCT_INFORMATON_FAILED = 103007
181 | ERROR_DEVICE_NOT_SUPPORT_REMOTE_OPERATE = 103010
182 | ERROR_DEVICE_PIN_MODIFFY_FAILED = 103003
183 | ERROR_DEVICE_PIN_VALIDATE_FAILED = 103002
184 | ERROR_DEVICE_PUK_DEAD_LOCK = 103011
185 | ERROR_DEVICE_PUK_MODIFFY_FAILED = 103004
186 | ERROR_DEVICE_RESTORE_FILE_DECRYPT_FAILED = 103016
187 | ERROR_DEVICE_RESTORE_FILE_FAILED = 103018
188 | ERROR_DEVICE_RESTORE_FILE_VERSION_MATCH_FAILED = 103017
189 | ERROR_DEVICE_SET_LOG_INFORMATON_LEVEL_FAILED = 103013
190 | ERROR_DEVICE_SET_TIME_FAILED = 103101
191 | ERROR_DEVICE_SIM_CARD_BUSY = 103008
192 | ERROR_DEVICE_SIM_LOCK_INPUT_ERROR = 103009
193 | ERROR_DHCP_ERROR = 104001
194 | ERROR_DIALUP_ADD_PRORILE_ERROR = 107724
195 | ERROR_DIALUP_DIALUP_MANAGMENT_PARSE_ERROR = 107722
196 | ERROR_DIALUP_GET_AUTO_APN_MATCH_ERROR = 107728
197 | ERROR_DIALUP_GET_CONNECT_FILE_ERROR = 107720
198 | ERROR_DIALUP_GET_PRORILE_LIST_ERROR = 107727
199 | ERROR_DIALUP_MODIFY_PRORILE_ERROR = 107725
200 | ERROR_DIALUP_SET_AUTO_APN_MATCH_ERROR = 107729
201 | ERROR_DIALUP_SET_CONNECT_FILE_ERROR = 107721
202 | ERROR_DIALUP_SET_DEFAULT_PRORILE_ERROR = 107726
203 | ERROR_DISABLE_AUTO_PIN_FAILED = 101008
204 | ERROR_DISABLE_PIN_FAILED = 101006
205 | ERROR_ENABLE_AUTO_PIN_FAILED = 101009
206 | ERROR_ENABLE_PIN_FAILED = 101005
207 | ERROR_FIRST_SEND = 1
208 | ERROR_FORMAT_ERROR = 100005
209 | ERROR_GET_CONFIG_FILE_ERROR = 100008
210 | ERROR_GET_CONNECT_STATUS_FAILED = 102004
211 | ERROR_GET_NET_TYPE_FAILED = 102001
212 | ERROR_GET_ROAM_STATUS_FAILED = 102003
213 | ERROR_GET_SERVICE_STATUS_FAILED = 102002
214 | ERROR_LANGUAGE_GET_FAILED = 109001
215 | ERROR_LANGUAGE_SET_FAILED = 109002
216 | ERROR_LOGIN_ALREADY_LOGINED = 108003
217 | ERROR_LOGIN_MODIFY_PASSWORD_FAILED = 108004
218 | ERROR_LOGIN_NO_EXIST_USER = 108001
219 | ERROR_LOGIN_PASSWORD_ERROR = 108002
220 | ERROR_LOGIN_TOO_MANY_TIMES = 108007
221 | ERROR_LOGIN_TOO_MANY_USERS_LOGINED = 108005
222 | ERROR_LOGIN_USERNAME_OR_PASSWORD_ERROR = 108006
223 | ERROR_NET_CURRENT_NET_MODE_NOT_SUPPORT = 112007
224 | ERROR_NET_MEMORY_ALLOC_FAILED = 112009
225 | ERROR_NET_NET_CONNECTED_ORDER_NOT_MATCH = 112006
226 | ERROR_NET_REGISTER_NET_FAILED = 112005
227 | ERROR_NET_SIM_CARD_NOT_READY_STATUS = 112008
228 | ERROR_NOT_SUPPORT = 100002
229 | ERROR_NO_DEVICE = -2
230 | ERROR_NO_RIGHT = 100003
231 | ERROR_NO_SIM_CARD_OR_INVALID_SIM_CARD = 101001
232 | ERROR_ONLINE_UPDATE_ALREADY_BOOTED = 110002
233 | ERROR_ONLINE_UPDATE_CANCEL_DOWNLODING = 110007
234 | ERROR_ONLINE_UPDATE_CONNECT_ERROR = 110009
235 | ERROR_ONLINE_UPDATE_GET_DEVICE_INFORMATION_FAILED = 110003
236 | ERROR_ONLINE_UPDATE_GET_LOCAL_GROUP_COMMPONENT_INFORMATION_FAILED = 110004
237 | ERROR_ONLINE_UPDATE_INVALID_URL_LIST = 110021
238 | ERROR_ONLINE_UPDATE_LOW_BATTERY = 110024
239 | ERROR_ONLINE_UPDATE_NEED_RECONNECT_SERVER = 110006
240 | ERROR_ONLINE_UPDATE_NOT_BOOT = 110023
241 | ERROR_ONLINE_UPDATE_NOT_FIND_FILE_ON_SERVER = 110005
242 | ERROR_ONLINE_UPDATE_NOT_SUPPORT_URL_LIST = 110022
243 | ERROR_ONLINE_UPDATE_SAME_FILE_LIST = 110008
244 | ERROR_ONLINE_UPDATE_SERVER_NOT_ACCESSED = 110001
245 | ERROR_PARAMETER_ERROR = 100006
246 | ERROR_PB_CALL_SYSTEM_FUCNTION_ERROR = 115003
247 | ERROR_PB_LOCAL_TELEPHONE_FULL_ERROR = 115199
248 | ERROR_PB_NULL_ARGUMENT_OR_ILLEGAL_ARGUMENT = 115001
249 | ERROR_PB_OVERTIME = 115002
250 | ERROR_PB_READ_FILE_ERROR = 115005
251 | ERROR_PB_WRITE_FILE_ERROR = 115004
252 | ERROR_SAFE_ERROR = 106001
253 | ERROR_SAVE_CONFIG_FILE_ERROR = 100007
254 | ERROR_SD_DIRECTORY_EXIST = 114002
255 | ERROR_SD_FILE_EXIST = 114001
256 | ERROR_SD_FILE_IS_UPLOADING = 114007
257 | ERROR_SD_FILE_NAME_TOO_LONG = 114005
258 | ERROR_SD_FILE_OR_DIRECTORY_NOT_EXIST = 114004
259 | ERROR_SD_IS_OPERTED_BY_OTHER_USER = 114004
260 | ERROR_SD_NO_RIGHT = 114006
261 | ERROR_SET_NET_MODE_AND_BAND_FAILED = 112003
262 | ERROR_SET_NET_MODE_AND_BAND_WHEN_DAILUP_FAILED = 112001
263 | ERROR_SET_NET_SEARCH_MODE_FAILED = 112004
264 | ERROR_SET_NET_SEARCH_MODE_WHEN_DAILUP_FAILED = 112002
265 | ERROR_SMS_DELETE_SMS_FAILED = 113036
266 | ERROR_SMS_LOCAL_SPACE_NOT_ENOUGH = 113053
267 | ERROR_SMS_NULL_ARGUMENT_OR_ILLEGAL_ARGUMENT = 113017
268 | ERROR_SMS_OVERTIME = 113018
269 | ERROR_SMS_QUERY_SMS_INDEX_LIST_ERROR = 113020
270 | ERROR_SMS_SAVE_CONFIG_FILE_FAILED = 113047
271 | ERROR_SMS_SET_SMS_CENTER_NUMBER_FAILED = 113031
272 | ERROR_SMS_TELEPHONE_NUMBER_TOO_LONG = 113054
273 | ERROR_STK_CALL_SYSTEM_FUCNTION_ERROR = 116003
274 | ERROR_STK_NULL_ARGUMENT_OR_ILLEGAL_ARGUMENT = 116001
275 | ERROR_STK_OVERTIME = 116002
276 | ERROR_STK_READ_FILE_ERROR = 116005
277 | ERROR_STK_WRITE_FILE_ERROR = 116004
278 | ERROR_UNKNOWN = 100001
279 | ERROR_UNLOCK_PIN_FAILED = 101007
280 | ERROR_USSD_AT_SEND_FAILED = 111018
281 | ERROR_USSD_CODING_ERROR = 111017
282 | ERROR_USSD_EMPTY_COMMAND = 111016
283 | ERROR_USSD_ERROR = 111001
284 | ERROR_USSD_FUCNTION_RETURN_ERROR = 111012
285 | ERROR_USSD_IN_USSD_SESSION = 111013
286 | ERROR_USSD_NET_NOT_SUPPORT_USSD = 111022
287 | ERROR_USSD_NET_NO_RETURN = 111019
288 | ERROR_USSD_NET_OVERTIME = 111020
289 | ERROR_USSD_TOO_LONG_CONTENT = 111014
290 | ERROR_USSD_XML_SPECIAL_CHARACTER_TRANSFER_FAILED = 111021
291 | ERROR_WIFI_PBC_CONNECT_FAILED = 117003
292 | ERROR_WIFI_STATION_CONNECT_AP_PASSWORD_ERROR = 117001
293 | ERROR_WIFI_STATION_CONNECT_AP_WISPR_PASSWORD_ERROR = 117004
294 | ERROR_WIFI_WEB_PASSWORD_OR_DHCP_OVERTIME_ERROR = 117002
295 |
296 | // My own guess, don't trust this completely.
297 | // Unknown URLs are 100002
298 | ```
299 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hsp-dev/huawei-api",
3 | "type": "library",
4 | "description": "A barebones API to interact with the Huawei E5180 router",
5 | "homepage": "https://github.com/HSPDev/Huawei-E5180-API",
6 | "keywords": ["api","E5180"],
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Henrik Sylvester Pedersen",
11 | "email": "hsp@hsp.dk",
12 | "homepage": "http://blog.hsp.dk",
13 | "role": "Developer"
14 | }
15 | ],
16 | "autoload": {
17 | "psr-0" : {
18 | "HSPDev\\HuaweiApi" : "src"
19 | }
20 | },
21 | "require": {
22 | "php": ">=5.3.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/demo.php:
--------------------------------------------------------------------------------
1 | setAddress('192.168.8.1');
10 |
11 | //Username and password.
12 | //Username is always admin as far as I can tell.
13 | $router->login('admin', 'your-password');
14 |
15 | var_dump($router->getLedStatus());
16 |
--------------------------------------------------------------------------------
/src/HSPDev/HuaweiApi/CustomHttpClient.php:
--------------------------------------------------------------------------------
1 | manualCookieData = $cookie;
30 | $this->requestToken = $token;
31 | }
32 |
33 | /**
34 | * We need the current token to make the login hash.
35 | */
36 | public function getToken()
37 | {
38 | return $this->requestToken;
39 | }
40 |
41 | /**
42 | * Builds the Curl Object.
43 | */
44 | private function getCurlObj($url, $headerFields = [])
45 | {
46 | $ch = curl_init();
47 |
48 | //curl_setopt($ch, CURLOPT_VERBOSE, true); // DEBUGGING
49 |
50 | $header = [
51 | 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12',
52 | 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8;charset=UTF-8',
53 | 'Accept-Language: da-DK,da;q=0.8,en-US;q=0.6,en;q=0.4',
54 | 'Accept-Charset: utf-8;q=0.7,*;q=0.7',
55 | 'Keep-Alive: 115',
56 | 'Connection: keep-alive',
57 | //The router expects these two to be there, but empty, when not in use.
58 | 'Cookie: '.$this->manualCookieData,
59 | '__RequestVerificationToken: '.$this->requestToken,
60 | ];
61 | foreach ($headerFields as $h) {
62 | $header[] = $h;
63 | }
64 |
65 | curl_setopt($ch, CURLOPT_URL, $url);
66 |
67 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
68 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
69 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->responseTimeout);
70 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
71 | curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); //The router is fine with this, so no problem.
72 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
73 |
74 | //The router rotates tokens in the response headers randomly, so we will parse them all.
75 | curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, 'HandleHeaderLine']);
76 |
77 | return $ch;
78 | }
79 |
80 | //end function
81 |
82 | /**
83 | * Makes HTTP POST requests containing XML data to the router.
84 | */
85 | public function postXml($url, $xmlString)
86 | {
87 | //The API wants it like this.
88 | $ch = $this->getCurlObj($url, ['Content-Type: text/plain; charset=UTF-8', 'Cookie2: $Version=1']);
89 |
90 | curl_setopt($ch, CURLOPT_POST, 1);
91 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlString);
92 |
93 | $result = curl_exec($ch);
94 | curl_close($ch);
95 | if (!$result) {
96 | throw new \Exception('A network error occured with cURL.');
97 | }
98 |
99 | return $result;
100 | }
101 |
102 | /**
103 | * Handles the HTTP Response header lines from cURL requests,
104 | * so we can extract all those tokens.
105 | */
106 | public function HandleHeaderLine($curl, $header_line)
107 | {
108 |
109 | /*
110 | * Not the prettiest way to parse it out, but hey it works.
111 | * If adding more or changing, remember the trim() call
112 | * as the strings have nasty null bytes.
113 | */
114 | if (strpos($header_line, '__RequestVerificationTokenOne') === 0) {
115 | $token = trim(substr($header_line, strlen('__RequestVerificationTokenOne:')));
116 | $this->requestTokenOne = $token;
117 | } elseif (strpos($header_line, '__RequestVerificationTokenTwo') === 0) {
118 | $token = trim(substr($header_line, strlen('__RequestVerificationTokenTwo:')));
119 | $this->requestTokenTwo = $token;
120 | } elseif (strpos($header_line, '__RequestVerificationToken') === 0) {
121 | $token = trim(substr($header_line, strlen('__RequestVerificationToken:')));
122 | $this->requestToken = $token;
123 | } elseif (strpos($header_line, 'Set-Cookie:') === 0) {
124 | $cookie = trim(substr($header_line, strlen('Set-Cookie:')));
125 | $this->manualCookieData = $cookie;
126 | }
127 |
128 | return strlen($header_line);
129 | }
130 |
131 | /**
132 | * Performs a HTTP GET to the specified URL.
133 | */
134 | public function get($url)
135 | {
136 | $ch = $this->getCurlObj($url);
137 |
138 | $result = curl_exec($ch);
139 | curl_close($ch);
140 | if (!$result) {
141 | throw new \Exception('A network error occured with cURL.');
142 | }
143 |
144 | return $result;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/HSPDev/HuaweiApi/Router.php:
--------------------------------------------------------------------------------
1 | http = new CustomHttpClient();
28 | }
29 |
30 | /**
31 | * Sets the router address.
32 | */
33 | public function setAddress($address)
34 | {
35 | //Remove trailing slash if any.
36 | $address = rtrim($address, '/');
37 |
38 | //If not it starts with http, we assume HTTP and add it.
39 | if (strpos($address, 'http') !== 0) {
40 | $address = 'http://'.$address;
41 | }
42 |
43 | $this->routerAddress = $address;
44 | }
45 |
46 | /**
47 | * Most API responses are just simple XML, so to avoid repetition
48 | * this function will GET the route and return the object.
49 | *
50 | * @return SimpleXMLElement
51 | */
52 | public function generalizedGet($route)
53 | {
54 | //Makes sure we are ready for the next request.
55 | $this->prepare();
56 |
57 | $xml = $this->http->get($this->getUrl($route));
58 | $obj = new \SimpleXMLElement($xml);
59 |
60 | //Check for error message
61 | if (property_exists($obj, 'code')) {
62 | throw new \UnexpectedValueException('The API returned error code: '.$obj->code);
63 | }
64 |
65 | return $obj;
66 | }
67 |
68 | /**
69 | * Gets the current router status.
70 | *
71 | * @return SimpleXMLElement
72 | */
73 | public function getStatus()
74 | {
75 | return $this->generalizedGet('api/monitoring/status');
76 | }
77 |
78 | /**
79 | * Gets traffic statistics (numbers are in bytes).
80 | *
81 | * @return SimpleXMLElement
82 | */
83 | public function getTrafficStats()
84 | {
85 | return $this->generalizedGet('api/monitoring/traffic-statistics');
86 | }
87 |
88 | /**
89 | * Gets monthly statistics (numbers are in bytes)
90 | * This probably only works if you have setup a limit.
91 | *
92 | * @return SimpleXMLElement
93 | */
94 | public function getMonthStats()
95 | {
96 | return $this->generalizedGet('api/monitoring/month_statistics');
97 | }
98 |
99 | /**
100 | * Info about the current mobile network. (PLMN info).
101 | *
102 | * @return SimpleXMLElement
103 | */
104 | public function getNetwork()
105 | {
106 | return $this->generalizedGet('api/net/current-plmn');
107 | }
108 |
109 | /**
110 | * Gets the current craddle status.
111 | *
112 | * @return SimpleXMLElement
113 | */
114 | public function getCraddleStatus()
115 | {
116 | return $this->generalizedGet('api/cradle/status-info');
117 | }
118 |
119 | /**
120 | * Get current SMS count.
121 | *
122 | * @return SimpleXMLElement
123 | */
124 | public function getSmsCount()
125 | {
126 | return $this->generalizedGet('api/sms/sms-count');
127 | }
128 |
129 | /**
130 | * Get current WLAN Clients.
131 | *
132 | * @return SimpleXMLElement
133 | */
134 | public function getWlanClients()
135 | {
136 | return $this->generalizedGet('api/wlan/host-list');
137 | }
138 |
139 | /**
140 | * Get notifications on router.
141 | *
142 | * @return SimpleXMLElement
143 | */
144 | public function getNotifications()
145 | {
146 | return $this->generalizedGet('api/monitoring/check-notifications');
147 | }
148 |
149 | /**
150 | * Gets the SMS inbox.
151 | * Page parameter is NOT null indexed and starts at 1.
152 | * I don't know if there is an upper limit on $count. Your milage may vary.
153 | * unreadPrefered should give you unread messages first.
154 | *
155 | * @return bool
156 | */
157 | public function setLedOn($on = false)
158 | {
159 | //Makes sure we are ready for the next request.
160 | $this->prepare();
161 |
162 | $ledXml = ''.($on ? '1' : '0').'';
163 | $xml = $this->http->postXml($this->getUrl('api/led/circle-switch'), $ledXml);
164 | $obj = new \SimpleXMLElement($xml);
165 | //Simple check if login is OK.
166 | return (string) $obj == 'OK';
167 | }
168 |
169 | /**
170 | * Checks whatever we are logged in.
171 | *
172 | * @return bool
173 | */
174 | public function getLedStatus()
175 | {
176 | $obj = $this->generalizedGet('api/led/circle-switch');
177 | if (property_exists($obj, 'ledSwitch')) {
178 | if ($obj->ledSwitch == '1') {
179 | return true;
180 | }
181 | }
182 |
183 | return false;
184 | }
185 |
186 | /**
187 | * Checks whatever we are logged in.
188 | *
189 | * @return bool
190 | */
191 | public function isLoggedIn()
192 | {
193 | $obj = $this->generalizedGet('api/user/state-login');
194 | if (property_exists($obj, 'State')) {
195 | /*
196 | * Logged out seems to be -1
197 | * Logged in seems to be 0.
198 | * What the hell?
199 | */
200 | if ($obj->State == '0') {
201 | return true;
202 | }
203 | }
204 |
205 | return false;
206 | }
207 |
208 | /**
209 | * Gets the SMS inbox.
210 | * Page parameter is NOT null indexed and starts at 1.
211 | * I don't know if there is an upper limit on $count. Your milage may vary.
212 | * unreadPrefered should give you unread messages first.
213 | *
214 | * @return SimpleXMLElement
215 | */
216 | public function getInbox($page = 1, $count = 20, $unreadPreferred = false)
217 | {
218 | //Makes sure we are ready for the next request.
219 | $this->prepare();
220 |
221 | $inboxXml = '
222 | '.$page.'
223 | '.$count.'
224 | 1
225 | 0
226 | 0
227 | '.($unreadPreferred ? '1' : '0').'
228 |
229 | ';
230 | $xml = $this->http->postXml($this->getUrl('api/sms/sms-list'), $inboxXml);
231 | $obj = new \SimpleXMLElement($xml);
232 |
233 | return $obj;
234 | }
235 |
236 | /**
237 | * Deletes an SMS by ID, also called "Index".
238 | * The index on the Message object you get from getInbox
239 | * will contain an "Index" property with a value like "40000" and up.
240 | * Note: Will return true if the Index DOES NOT exist already.
241 | *
242 | * @return bool
243 | */
244 | public function deleteSms($index)
245 | {
246 | //Makes sure we are ready for the next request.
247 | $this->prepare();
248 |
249 | $deleteXml = '
250 | '.$index.'
251 |
252 | ';
253 | $xml = $this->http->postXml($this->getUrl('api/sms/delete-sms'), $deleteXml);
254 | $obj = new \SimpleXMLElement($xml);
255 | //Simple check if login is OK.
256 | return (string) $obj == 'OK';
257 | }
258 |
259 | /**
260 | * Sends SMS to specified receiver. I don't know if it works for foreign numbers,
261 | * but for local numbers you can just specifiy the number like you would normally
262 | * call it and it should work, here in Denmark "42952777" etc (mine).
263 | * Message parameter got the normal SMS restrictions you know and love.
264 | *
265 | * @return bool
266 | */
267 | public function sendSms($receiver, $message)
268 | {
269 | //Makes sure we are ready for the next request.
270 | $this->prepare();
271 |
272 | /*
273 | * Note how it wants the length of the content also.
274 | * It ALSO wants the current date/time wtf? Oh well..
275 | */
276 | $sendSmsXml = '
277 | -1
278 |
279 | '.$receiver.'
280 |
281 |
282 | '.$message.'
283 | '.strlen($message).'
284 | 1
285 | '.date('Y-m-d H:i:s').'
286 | 0
287 |
288 | ';
289 | $xml = $this->http->postXml($this->getUrl('api/sms/send-sms'), $sendSmsXml);
290 | $obj = new \SimpleXMLElement($xml);
291 | //Simple check if login is OK.
292 | return (string) $obj == 'OK';
293 | }
294 |
295 | /**
296 | * Not all methods may work if you don't login.
297 | * Please note that the router is pretty aggressive
298 | * at timing your session out.
299 | * Call something periodically or just relogin on error.
300 | *
301 | * @return bool
302 | */
303 | public function login($username, $password)
304 | {
305 | //Makes sure we are ready for the next request.
306 | $this->prepare();
307 |
308 | /*
309 | * Note how the router wants the password to be the following:
310 | * 1) Hashed by SHA256, then the raw output base64 encoded.
311 | * 2) The username is appended with the result of the above,
312 | * AND the current token. Yes, the password changes everytime
313 | * depending on what token we got. This really fucks with scrapers.
314 | * 3) The string from above (point 2) is then hashed by SHA256 again,
315 | * and the raw output is once again base64 encoded.
316 | *
317 | * This is how the router login process works. So the password being sent
318 | * changes everytime depending on the current user session/token.
319 | * Not bad actually.
320 | */
321 | $loginXml = '
322 | '.$username.'
323 | 4
324 | '.base64_encode(hash('sha256', $username.base64_encode(hash('sha256', $password, false)).$this->http->getToken(), false)).'
325 |
326 | ';
327 | $xml = $this->http->postXml($this->getUrl('api/user/login'), $loginXml);
328 | $obj = new \SimpleXMLElement($xml);
329 | //Simple check if login is OK.
330 | return (string) $obj == 'OK';
331 | }
332 |
333 | /**
334 | * Sets the data switch to enable or disable the mobile connection.
335 | *
336 | * @return bool
337 | */
338 | public function setDataSwitch($value)
339 | {
340 | if (is_int($value) === false) {
341 | throw new \Exception('Parameter can only be integer.');
342 | }
343 | if ($value !== 0 && $value !== 1) {
344 | throw new \Exception('Parameter can only be integer.');
345 | }
346 |
347 | //Makes sure we are ready for the next request.
348 | $this->prepare();
349 |
350 | $dataSwitchXml = ''.$value.'';
351 |
352 | $xml = $this->http->postXml($this->getUrl('api/dialup/mobile-dataswitch'), $dataSwitchXml);
353 | $obj = new \SimpleXMLElement($xml);
354 |
355 | //Simple check if login is OK.
356 | return (string) $obj == 'OK';
357 | }
358 |
359 | /**
360 | * Internal helper that lets us build the complete URL
361 | * to a given route in the API.
362 | *
363 | * @return string
364 | */
365 | private function getUrl($route)
366 | {
367 | return $this->routerAddress.'/'.$route;
368 | }
369 |
370 | /**
371 | * Makes sure that we are ready for API usage.
372 | */
373 | private function prepare()
374 | {
375 | //Check to see if we have session / token.
376 | if (strlen($this->sessionInfo) == 0 || strlen($this->tokenInfo) == 0) {
377 | //We don't have any. Grab some.
378 | $xml = $this->http->get($this->getUrl('api/webserver/SesTokInfo'));
379 | $obj = new \SimpleXMLElement($xml);
380 | if (!property_exists($obj, 'SesInfo') || !property_exists($obj, 'TokInfo')) {
381 | throw new \RuntimeException('Malformed XML returned. Missing SesInfo or TokInfo nodes.');
382 | }
383 | //Set it for future use.
384 | $this->http->setSecurity($obj->SesInfo, $obj->TokInfo);
385 | }
386 | }
387 | }
388 |
--------------------------------------------------------------------------------