]>
Commit | Line | Data |
---|---|---|
6c74a64c MT |
1 | #!/bin/bash |
2 | ############################################################################### | |
3 | # # | |
4 | # IPFire.org - A linux based firewall # | |
5 | # Copyright (C) 2012 IPFire Network Development Team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
22 | # Exit codes from the chat(8) command: | |
23 | CHAT_OK=0 | |
24 | CHAT_INVALID=1 | |
25 | CHAT_ERROR=2 | |
26 | CHAT_TIMEOUT=3 | |
27 | ||
28 | function modem_chat() { | |
29 | local answer="OK" | |
30 | local device | |
31 | local timeout=2 | |
32 | local quiet="false" | |
33 | ||
34 | while [ $# -gt 0 ]; do | |
35 | case "${1}" in | |
36 | --timeout=*) | |
37 | timeout=$(cli_get_val ${1}) | |
38 | ;; | |
39 | --answer=*) | |
40 | answer=$(cli_get_val ${1}) | |
41 | ;; | |
42 | --quiet) | |
43 | quiet="true" | |
44 | ;; | |
45 | *) | |
46 | device=${1} | |
47 | shift; break | |
48 | ;; | |
49 | esac | |
50 | shift | |
51 | done | |
52 | ||
53 | assert serial_exists ${device} | |
54 | assert serial_is_unlocked ${device} | |
55 | assert isset answer | |
56 | assert isset timeout | |
57 | ||
58 | local command=$@ | |
59 | ||
60 | log DEBUG "Sending command to ${device}: ${command}" | |
61 | ||
62 | ( | |
63 | # This cannot be run with -x. | |
64 | set +x 2>/dev/null | |
65 | ||
66 | chat -V -s -t ${timeout} "" "${command}" "${answer}" \ | |
67 | < ${device} > ${device} || exit $? | |
68 | print # Print line feed. | |
69 | ) 2>&1 | __modem_chat_process_output "${answer}" ${quiet} | |
70 | ||
71 | local ret=${PIPESTATUS[0]} | |
72 | ||
73 | # Return the exit code of the chat command. | |
74 | case "${ret}" in | |
75 | ${CHAT_OK}) | |
76 | return ${EXIT_OK} | |
77 | ;; | |
78 | ||
79 | # When the timeout condition hit, the expected string has not | |
80 | # been received in the given amount of time. | |
81 | ${CHAT_TIMEOUT}|${CHAT_ERROR}) | |
82 | return ${EXIT_ERROR} | |
83 | ;; | |
84 | ||
85 | ${CHAT_INVALID}) | |
86 | return ${EXIT_CONF_ERROR} | |
87 | ;; | |
88 | esac | |
89 | ||
90 | log WARNING "Received unknown exit code from chat(8): ${ret}" | |
91 | return ${EXIT_ERROR} | |
92 | } | |
93 | ||
94 | function __modem_chat_process_output() { | |
95 | local answer=${1} | |
96 | local quiet=${2} | |
97 | ||
98 | if enabled quiet; then | |
99 | # Just throw everything away. | |
100 | cat >/dev/null | |
101 | return ${EXIT_OK} | |
102 | fi | |
103 | ||
104 | local counter=0 | |
105 | ||
106 | local line | |
daa5599a | 107 | while read -r line; do |
6c74a64c MT |
108 | # Also skip empty lines. |
109 | [ -n "${line}" ] || continue | |
110 | ||
111 | # Ignore all volatile messages. | |
112 | [ "${line:0:1}" = "^" ] && continue | |
113 | ||
daa5599a | 114 | log DEBUG "Output[${counter}]: ${line}" |
6c74a64c MT |
115 | counter=$(( ${counter} + 1 )) |
116 | ||
117 | # Skip the first line, because that's out command. | |
118 | [ ${counter} -eq 1 ] && continue | |
119 | ||
120 | # Filter out the expected answer. | |
121 | [ "${line}" = "${answer}" ] && continue | |
122 | ||
123 | # Print the rest. | |
124 | print "${line}" | |
125 | done | |
126 | } | |
127 | ||
95416c1e MT |
128 | function modem_initialize() { |
129 | local device="${1}" | |
130 | assert isset device | |
3255c894 MT |
131 | shift |
132 | ||
133 | # Sleep for $sleep seconds, to give | |
134 | # the modem a moment to initialize itself. | |
135 | local sleep=1 | |
136 | ||
137 | while [ $# -gt 0 ]; do | |
138 | case "${1}" in | |
139 | --sleep=*) | |
140 | sleep="$(cli_get_val "${1}")" | |
141 | ;; | |
142 | *) | |
143 | warning "Unrecognized argument: ${1}" | |
144 | ;; | |
145 | esac | |
146 | shift | |
147 | done | |
148 | assert isinteger sleep | |
95416c1e MT |
149 | |
150 | log INFO "Initializing modem ${device}" | |
151 | ||
152 | # Reset the modem. | |
153 | modem_chat "${device}" "${AT_INITIALIZE}" | |
3255c894 MT |
154 | |
155 | # Wait... | |
156 | if [ ${sleep} -gt 0 ]; then | |
157 | sleep ${sleep} | |
158 | fi | |
95416c1e MT |
159 | } |
160 | ||
6c74a64c MT |
161 | # Exit codes of the sim_status function. |
162 | EXIT_SIM_READY=0 | |
163 | EXIT_SIM_PIN=1 | |
164 | EXIT_SIM_PUK=2 | |
165 | EXIT_SIM_UNKNOWN=3 | |
166 | ||
167 | function modem_sim_status() { | |
168 | local device=${1} | |
169 | assert isset device | |
170 | ||
171 | local output | |
172 | output=$(modem_chat ${device} "AT+CPIN?") | |
173 | assert_check_retval $? | |
174 | ||
175 | # Strip leading +CPIN: from the output. | |
176 | output=${output#*: } | |
177 | ||
178 | case "${output}" in | |
179 | "READY") | |
180 | log DEBUG "${device}'s SIM is unlocked or doesn't need a PIN." | |
181 | return ${EXIT_SIM_READY} | |
182 | ;; | |
183 | "SIM PIN") | |
184 | log DEBUG "${device}'s SIM is waiting for a PIN." | |
185 | return ${EXIT_SIM_PIN} | |
186 | ;; | |
187 | "SIM PUK") | |
188 | log DEBUG "${device}'s SIM is PUK locked." | |
189 | return ${EXIT_SIM_PUK} | |
190 | ;; | |
191 | esac | |
192 | ||
193 | log WARNING "${device}: Invalid output of the AT+CPIN? command." | |
194 | return ${EXIT_SIM_UNKNOWN} | |
195 | } | |
196 | ||
197 | function modem_sim_unlocked() { | |
198 | local device=${1} | |
199 | assert isset device | |
200 | ||
201 | modem_sim_status "${device}" | |
202 | local ret=$? | |
203 | ||
204 | [ ${ret} -eq ${EXIT_SIM_READY} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE} | |
205 | } | |
206 | ||
207 | function modem_sim_locked() { | |
208 | modem_sim_unlocked $@ && return ${EXIT_FALSE} || return ${EXIT_TRUE} | |
209 | } | |
210 | ||
211 | function modem_sim_unlock() { | |
212 | local device=${1} | |
213 | assert isset device | |
214 | ||
215 | local pin=${2} | |
216 | assert isset pin | |
217 | ||
218 | local command="AT+CPIN=${pin}" | |
219 | ||
220 | local new_pin=${3} | |
221 | if isset new_pin; then | |
222 | command="${command},${new_pin}" | |
223 | fi | |
224 | ||
225 | modem_chat --timeout=2 --quiet "${device}" "${command}" | |
226 | ||
227 | local ret=$? | |
228 | case "${ret}" in | |
229 | ${EXIT_OK}) | |
230 | log INFO "Successfully unlocked SIM card on ${device}." | |
231 | ;; | |
232 | *) | |
233 | log ERROR "Could not unlock SIM card on ${device}." | |
234 | ret=${EXIT_ERROR} | |
235 | ;; | |
236 | esac | |
237 | ||
238 | return ${ret} | |
239 | } | |
240 | ||
95416c1e MT |
241 | function modem_sim_auto_unlock() { |
242 | local device="${1}" | |
243 | assert isset device | |
244 | ||
245 | local pin="${2}" | |
246 | assert isset pin | |
247 | ||
248 | # Get the current state the SIM card is in. | |
249 | modem_sim_status "${device}" &>/dev/null | |
250 | local sim_status_code=$? | |
251 | ||
252 | case "${sim_status_code}" in | |
253 | ${EXIT_SIM_READY}) | |
254 | # Everything's fine. The SIM card is | |
255 | # already unlocked. | |
256 | return ${EXIT_OK} | |
257 | ;; | |
258 | ${EXIT_SIM_PIN}) | |
259 | # Try to unlock the device. | |
260 | if modem_sim_unlock ${device} ${pin}; then | |
261 | return ${EXIT_OK} | |
262 | else | |
263 | return ${EXIT_ERROR} | |
264 | fi | |
265 | ;; | |
266 | ${EXIT_SIM_PUK}) | |
267 | log ERROR "SIM card is PUK locked. Please unlock manually." | |
268 | return ${EXIT_ERROR} | |
269 | ;; | |
270 | esac | |
271 | ||
272 | return ${EXIT_ERROR} | |
273 | } | |
274 | ||
6c74a64c MT |
275 | # Returns the vendor of the modem. |
276 | # For example: "huawei" | |
277 | function modem_get_manufacturer() { | |
278 | local device=${1} | |
279 | assert isset device | |
280 | ||
281 | local output | |
282 | output=$(modem_chat ${device} "AT+GMI") | |
283 | assert_check_retval $? | |
284 | ||
285 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
286 | output=${output//\"/} | |
287 | ||
288 | print "${output}" | |
289 | } | |
290 | ||
291 | function modem_get_model() { | |
292 | local device=${1} | |
293 | assert isset device | |
294 | ||
295 | local output | |
296 | output=$(modem_chat ${device} "AT+GMM") | |
297 | assert_check_retval $? | |
298 | ||
299 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
300 | output=${output//\"/} | |
301 | ||
302 | print "${output}" | |
303 | } | |
304 | ||
305 | function modem_get_software_version() { | |
306 | local device=${1} | |
307 | assert isset device | |
308 | ||
309 | local output | |
310 | output=$(modem_chat ${device} "AT+GMR") | |
311 | assert_check_retval $? | |
312 | ||
313 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
314 | output=${output//\"/} | |
315 | ||
316 | print "${output}" | |
317 | } | |
318 | ||
319 | function modem_get_sim_imsi() { | |
320 | local device=${1} | |
321 | assert isset device | |
322 | ||
323 | # Make sure the SIM card is unlocked for this operation. | |
324 | assert modem_sim_unlocked ${device} | |
325 | ||
326 | modem_chat ${device} "AT+CIMI" | |
327 | } | |
328 | ||
329 | function modem_get_device_imei() { | |
330 | local device=${1} | |
331 | assert isset device | |
332 | ||
333 | local output | |
334 | output=$(modem_chat --timeout=1 ${device} "AT+CGSN") || assert_check_retval $? | |
335 | local ret=$? | |
336 | ||
337 | if [ ${ret} -eq ${EXIT_OK} ]; then | |
338 | print "${output}" | |
339 | fi | |
340 | ||
341 | return ${ret} | |
342 | } | |
343 | ||
344 | function modem_is_mobile() { | |
345 | local device=${1} | |
346 | assert isset device | |
347 | ||
348 | # Check if the device can return it's IMEI. | |
349 | # If not, it's probably a serial 56k modem or something | |
350 | # in that category. | |
351 | ||
352 | modem_get_device_imei ${device} &>/dev/null | |
353 | } | |
354 | ||
355 | # Exit codes of the network registration function. | |
356 | EXIT_REG_REGISTERED_TO_HOME_NETWORK=0 | |
357 | EXIT_REG_NOT_REGISTERED_NOT_SEARCHING=1 | |
358 | EXIT_REG_NOT_REGISTERED_SEARCHING=2 | |
359 | EXIT_REG_REGISTRATION_DENIED=3 | |
360 | EXIT_REG_REGISTERED_ROAMING=4 | |
361 | EXIT_REG_UNKNOWN=5 | |
362 | ||
363 | function modem_get_network_registration() { | |
364 | local device=${1} | |
365 | assert isset device | |
366 | ||
367 | # Make sure the SIM card is unlocked for this operation. | |
368 | assert modem_sim_unlocked ${device} | |
369 | ||
370 | local output | |
371 | output=$(modem_chat ${device} "AT+CREG?") | |
372 | assert_check_retval $? | |
373 | ||
374 | # Cut out unneeded parts of the message. | |
375 | output=${output#*: } | |
376 | ||
377 | case "${output}" in | |
378 | [0-2],[0-5]) | |
379 | local status=${output%,*} | |
380 | ||
381 | # The status variable must be zero. | |
382 | [ ${status} -ne 0 ] && break | |
383 | ||
384 | local stat=${output#*,} | |
385 | case "${stat}" in | |
386 | 0) | |
387 | return ${EXIT_REG_NOT_REGISTERED_NOT_SEARCHING} | |
388 | ;; | |
389 | 1) | |
390 | return ${EXIT_REG_REGISTERED_TO_HOME_NETWORK} | |
391 | ;; | |
392 | 2) | |
393 | return ${EXIT_REG_NOT_REGISTERED_SEARCHING} | |
394 | ;; | |
395 | 3) | |
396 | return ${EXIT_REG_REGISTRATION_DENIED} | |
397 | ;; | |
398 | 5) | |
399 | return ${EXIT_REG_REGISTERED_ROAMING} | |
400 | ;; | |
401 | *) | |
402 | return ${EXIT_REG_UNKNOWN} | |
403 | ;; | |
404 | esac | |
405 | ;; | |
406 | esac | |
407 | ||
408 | # Apparently the output of the CREG? command was not in | |
409 | # the right format. The modem will be tried to be set to the | |
410 | # right mode. | |
411 | ||
412 | modem_set_network_registration ${device} 0 | |
413 | modem_get_network_registration ${device} | |
414 | } | |
415 | ||
416 | function modem_set_network_registration() { | |
417 | local device=${1} | |
418 | assert isset device | |
419 | ||
420 | # Make sure the SIM card is unlocked for this operation. | |
421 | assert modem_sim_unlocked ${device} | |
422 | ||
423 | local mode=${2} | |
424 | assert isset mode | |
425 | ||
426 | modem_chat ${device} "AT+CREG=${mode}" | |
427 | } | |
428 | ||
429 | function modem_scan_networks() { | |
430 | local device=${1} | |
431 | assert isset device | |
432 | ||
433 | # Make sure the SIM card is unlocked for this operation. | |
434 | assert modem_sim_unlocked ${device} | |
435 | ||
436 | local output | |
437 | output=$(modem_chat --timeout=60 ${device} "AT+COPS=?") | |
438 | assert_check_retval $? | |
439 | ||
440 | output=${output#*: } | |
441 | ||
442 | # XXX the output is not very nice to parse. | |
443 | } | |
444 | ||
445 | function __modem_get_network_operator() { | |
446 | local device=${1} | |
447 | assert isset device | |
448 | ||
449 | # Make sure the SIM card is unlocked for this operation. | |
450 | assert modem_sim_unlocked ${device} | |
451 | ||
452 | local argument=${2} | |
453 | assert isset argument | |
454 | ||
455 | local output | |
456 | output=$(modem_chat ${device} "AT+COPS?") | |
457 | assert_check_retval $? | |
458 | ||
459 | output=${output#*: } | |
c5feadb0 MT |
460 | output=${output//,/ } |
461 | ||
462 | local arg mode format operator act | |
463 | local i=0 | |
464 | while read -r arg; do | |
465 | case "${i}" in | |
466 | 0) | |
467 | mode="${arg}" | |
468 | ;; | |
469 | 1) | |
470 | format="${arg}" | |
471 | ;; | |
472 | 2) | |
473 | operator="$(strip ${arg})" | |
474 | ;; | |
475 | 3) | |
476 | act="${arg}" | |
477 | ;; | |
478 | *) | |
479 | break | |
480 | ;; | |
481 | esac | |
482 | i="$(( ${i} + 1 ))" | |
483 | done <<< "$(args ${output})" | |
6c74a64c MT |
484 | |
485 | print "${!argument}" | |
486 | return ${EXIT_OK} | |
487 | } | |
488 | ||
489 | function modem_get_network_operator() { | |
490 | local device=${1} | |
491 | assert isset device | |
492 | ||
493 | __modem_get_network_operator ${device} operator | |
494 | } | |
495 | ||
496 | # Exit codes of the network operator mode function. | |
497 | EXIT_OPMODE_GSM=0 | |
498 | EXIT_OPMODE_COMPACTGSM=1 | |
499 | EXIT_OPMODE_GSM_WITH_EGPRS=2 | |
500 | EXIT_OPMODE_UMTS=3 | |
501 | EXIT_OPMODE_UMTS_WITH_HSDPA=4 | |
502 | EXIT_OPMODE_UMTS_WITH_HSUPA=5 | |
503 | EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA=6 | |
504 | EXIT_OPMODE_UNKNOWN=7 | |
505 | ||
506 | function modem_get_network_mode() { | |
507 | local device=${1} | |
508 | assert isset device | |
509 | ||
510 | local output | |
511 | output=$(__modem_get_network_operator ${device} act) | |
512 | assert_check_retval $? | |
513 | ||
514 | case "${output}" in | |
515 | 0) | |
516 | print "GSM" | |
517 | return ${EXIT_OPMODE_GSM} | |
518 | ;; | |
519 | 1) | |
520 | print "Compact GSM" | |
521 | return ${EXIT_OPMODE_COMPACTGSM} | |
522 | ;; | |
523 | 2) | |
524 | print "UMTS" | |
525 | return ${EXIT_OPMODE_UMTS} | |
526 | ;; | |
527 | 3) | |
528 | print "UMTS with HSDPA" | |
529 | return ${EXIT_OPMODE_UMTS_WITH_HSDPA} | |
530 | ;; | |
531 | 4) | |
532 | print "UMTS with HSUPA" | |
533 | return ${EXIT_OPMODE_UMTS_WITH_HSUPA} | |
534 | ;; | |
535 | 5) | |
536 | print "UMTS with HSDPA and HSUPA" | |
537 | return ${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA} | |
538 | ;; | |
539 | *) | |
540 | print "Unknown" | |
541 | return ${EXIT_OPMODE_UNKNOWN} | |
542 | ;; | |
543 | esac | |
544 | } | |
545 | ||
546 | function __modem_get_signal_quality() { | |
547 | local device=${1} | |
548 | assert isset device | |
549 | ||
550 | # Make sure the SIM card is unlocked for this operation. | |
551 | assert modem_sim_unlocked ${device} | |
552 | ||
553 | local argument=${2} | |
554 | assert isset argument | |
555 | ||
556 | local output | |
557 | output=$(modem_chat ${device} "AT+CSQ") | |
558 | assert_check_retval $? | |
559 | ||
560 | output=${output#*: } | |
561 | ||
562 | case "${output}" in | |
563 | *,*) | |
564 | local rssi=${output%,*} | |
565 | local ber=${output#*,} | |
566 | ||
567 | print "${!argument}" | |
568 | return ${EXIT_OK} | |
569 | ;; | |
570 | *) | |
571 | log ERROR "Unknown format for AT+CSQ: ${device}: ${output}" | |
572 | ;; | |
573 | esac | |
574 | ||
575 | return ${EXIT_ERROR} | |
576 | } | |
577 | ||
578 | function modem_get_signal_quality() { | |
579 | local device=${1} | |
580 | assert isset device | |
581 | ||
582 | local rssi | |
583 | rssi=$(__modem_get_signal_quality ${device} rssi) | |
584 | assert_check_retval $? | |
585 | ||
586 | isset rssi || return ${EXIT_ERROR} | |
587 | ||
588 | # 99 indicates an unknown signal strength. | |
589 | [ ${rssi} -eq 99 ] && return ${EXIT_UNKNOWN} | |
590 | ||
591 | local dbm=$(( ${rssi} * 2 )) | |
592 | dbm=$(( ${dbm} - 113 )) | |
593 | ||
594 | print "%d" "${dbm}" | |
595 | return ${EXIT_OK} | |
596 | } | |
597 | ||
598 | function modem_get_bit_error_rate() { | |
599 | local device=${1} | |
600 | assert isset device | |
601 | ||
602 | local ber | |
603 | ber=$(__modem_get_signal_quality ${device} ber) | |
604 | assert_check_retval $? | |
605 | ||
606 | isset ber || return ${EXIT_ERROR} | |
607 | ||
608 | # 99 indicates that the bit error rate could not be detected or | |
609 | # is unknown for some other reason. | |
610 | [ ${ber} -eq 99 ] && return ${EXIT_UNKNOWN} | |
611 | ||
612 | print "%d" "${ber}" | |
613 | return ${EXIT_OK} | |
614 | } | |
5cf0edf9 MT |
615 | |
616 | # USDD stuff | |
617 | ||
618 | function modem_ussd_send_command() { | |
619 | local device="${1}" | |
620 | assert isset device | |
621 | ||
622 | local command="${2}" | |
623 | assert isset command | |
624 | shift 2 | |
625 | ||
626 | local cleartext="false" | |
627 | local timeout="20" | |
628 | ||
629 | while [ $# -gt 0 ]; do | |
630 | case "${1}" in | |
631 | --cleartext) | |
632 | cleartext="true" | |
633 | ;; | |
634 | --timeout=*) | |
635 | timeout="$(cli_get_val "${1}")" | |
636 | ;; | |
637 | *) | |
638 | warning "Unrecognized argument: ${1}" | |
639 | ;; | |
640 | esac | |
641 | shift | |
642 | done | |
643 | ||
644 | local encoded_command="${command}" | |
645 | if ! enabled cleartext; then | |
646 | encoded_command="$(modem_ussd_encode "${command}")" | |
647 | fi | |
648 | ||
649 | log INFO "Sending USSD command '${command}' on ${device}" | |
650 | ||
651 | local at_command="AT+CUSD=1,\"${encoded_command}\",${encoding}" | |
652 | ||
653 | # Send the AT command and parse the output. | |
654 | modem_chat --answer="nothing" --timeout="${timeout}" \ | |
655 | "${device}" "${at_command}" | __modem_ussd_parse_output | |
656 | ||
657 | local ret=${PIPESTATUS[1]} | |
658 | return ${ret} | |
659 | } | |
660 | ||
661 | function __modem_ussd_parse_output() { | |
662 | local line | |
663 | while read -r line; do | |
664 | # Find the expected answer. | |
665 | [ "${line:0:7}" = "+CUSD: " ] || continue | |
666 | ||
667 | # Strip +CUSD: | |
668 | line="${line:7}" | |
669 | ||
670 | local response_type | |
671 | local response | |
672 | local response_encoding | |
673 | ||
674 | line="${line//,/ }" | |
675 | ||
676 | local section=0 arg | |
677 | while read -r arg; do | |
678 | case "${section}" in | |
679 | 0) | |
680 | response_type="${arg}" | |
681 | ;; | |
682 | 1) | |
683 | response="${arg}" | |
684 | ;; | |
685 | 2) | |
686 | response_encoding="${arg}" | |
687 | ;; | |
688 | *) | |
689 | break | |
690 | ;; | |
691 | esac | |
692 | section=$(( ${section} + 1 )) | |
693 | done <<< "$(args ${line})" | |
694 | ||
695 | log DEBUG "USSD response type: ${response_type}" | |
696 | log DEBUG "USSD response encoding: ${response_encoding}" | |
697 | log DEBUG "USSD encoded response: ${response}" | |
698 | ||
699 | # If we got anything else than a response (type 0), | |
700 | # we don't know how to handle that. | |
701 | if [ "${response_type}" -ne "0" ]; then | |
702 | return ${EXIT_ERROR} | |
703 | fi | |
704 | ||
705 | # Decode the string if needed. | |
706 | case "${response_encoding}" in | |
707 | 15) | |
708 | response="$(modem_ussd_decode "${response}")" | |
709 | ;; | |
710 | esac | |
711 | log DEBUG "USSD response: ${response}" | |
712 | ||
713 | print "${response}" | |
714 | return ${EXIT_OK} | |
715 | done | |
716 | ||
717 | return ${EXIT_ERROR} | |
718 | } | |
719 | ||
720 | function modem_ussd_encode() { | |
721 | local string="${1}" | |
722 | assert isset string | |
723 | ||
724 | local output buffer char | |
725 | while read -r char; do | |
726 | char="$(char2bin "${char}")" | |
727 | char="$(string_reverse "${char:1:7}")" | |
728 | ||
729 | buffer="${buffer}${char}" | |
730 | done <<< "$(string_split "${string}")" | |
731 | ||
732 | local pos=0 len=8 | |
733 | while [ ${pos} -lt ${#buffer} ]; do | |
734 | char="$(string_reverse "${buffer:${pos}:${len}}")" | |
735 | pos=$(( ${pos} + ${len} )) | |
736 | ||
737 | char="$(bin2hex "${char}")" | |
738 | output="${output}${char}" | |
739 | done | |
740 | ||
741 | # Make everything uppercase. | |
742 | output="${output^^}" | |
743 | ||
744 | print "${output}" | |
745 | } | |
746 | ||
747 | function modem_ussd_decode() { | |
748 | local string="${1}" | |
749 | assert isset string | |
750 | ||
751 | local buffer1 buffer2 | |
752 | local output char | |
753 | ||
754 | local pos=0 len=2 | |
755 | while [ ${pos} -lt ${#string} ]; do | |
756 | char="${string:${pos}:${len}}" | |
757 | pos=$(( ${pos} + ${len} )) | |
758 | ||
759 | char="$(hex2bin "${char}")" | |
760 | char="$(string_reverse "${char}")" | |
761 | buffer1="${buffer1}${char}" | |
762 | done | |
763 | ||
764 | # Reset pointers. | |
765 | pos=0 | |
766 | len=7 | |
767 | ||
768 | while [ ${pos} -lt ${#buffer1} ]; do | |
769 | char="${buffer1:${pos}:${len}}" | |
770 | pos=$(( ${pos} + ${len} )) | |
771 | ||
772 | buffer2="${buffer2}0${char}" | |
773 | done | |
774 | buffer2="${buffer2:1}" | |
775 | ||
776 | # Reset pointers again. | |
777 | pos=0 | |
778 | len=8 | |
779 | ||
780 | while [ ${pos} -lt ${#buffer2} ]; do | |
781 | char="${buffer2:${pos}:${len}}" | |
782 | pos=$(( ${pos} + ${len} )) | |
783 | ||
784 | char="$(string_reverse "${char}")" | |
785 | char="$(bin2char "${char}")" | |
786 | output="${output}${char}" | |
787 | done | |
788 | ||
789 | print "${output}" | |
790 | return ${EXIT_OK} | |
791 | } |