2 * coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
3 * Understanding is not required. Only obedience.
5 * This program is free software. It comes without any warranty, to
6 * the extent permitted by applicable law. You can redistribute it
7 * and/or modify it under the terms of the Do What The Fuck You Want
8 * To Public License, Version 2, as published by Sam Hocevar. See
9 * http://sam.zoy.org/wtfpl/COPYING for more details.
23 ////////////////////////////////////////////////////////////////////////////////
24 static const char *skipField (const char *str
) {
27 if (str
!= NULL
&& str
[0]) {
30 for (e
= str
+1; *e
; ++e
) {
32 else if (*e
== '"') { ++e
; break; }
34 if (*e
&& *e
!= ',') e
= NULL
; // too bad
37 if (e
== NULL
) e
= str
+strlen(str
); else ++e
;
40 // invalid string or no more fields
48 static char *getField (const char *str
) {
49 if (str
!= NULL
&& str
[0]) {
57 for (e
= str
+1; *e
; ++e
) {
59 else if (*e
== '"') { ++e
; break; }
62 if (*e
&& *e
!= ',') return NULL
; // too bad
63 if ((res
= malloc(len
+1)) != NULL
) {
64 for (len
= 0, e
= str
+1; *e
; ++e
, ++len
) {
66 else if (*e
== '"') break;
72 const char *e
= strchr(str
, ',');
75 if (e
== NULL
) e
= str
+strlen(str
);
77 if ((res
= malloc(len
+1)) != NULL
) {
78 if (len
> 0) memcpy(res
, str
, len
);
90 ////////////////////////////////////////////////////////////////////////////////
91 // parse '+CSQ' command output (signal strength)
93 // '113' means '-113 or less', '51' means '-51 or greater', '0' means 'unknown'
94 // RSSI (dBm) = (-113)+(2*CSQ)
96 static int parseCSQ (const char *ans
) {
97 if (ans
!= NULL
&& strncmp(ans
, "+CSQ: ", 6) == 0 && isdigit(ans
[6])) {
101 for (; *ans
&& isdigit(*ans
); ++ans
) res
= res
*10+ans
[0]-'0';
102 if (*ans
!= ',') return -1;
103 return (res
> 31 ? 0 : 113-2*res
);
110 ////////////////////////////////////////////////////////////////////////////////
111 static int cmdHelp (PnPty
*pd
, int argc
, char *argv
[]);
114 // +CNUM: ,"+380959320794",145
115 static int cmdMe (PnPty
*pd
, int argc
, char *argv
[]) {
116 char *res
= pnptySendCommand(pd
, "AT+CNUM");
119 if (strncmp(res
, "+CNUM: ", 7) == 0) {
120 const char *n
= skipField(res
+7);
122 //printf("[%s]\n", n);
123 if (n
!= NULL
&& n
[0]) {
124 char *val
= getField(n
);
127 printf("your phone number is %s\n", val
);
138 fprintf(stderr
, "BAD: some error occured.\n");
144 static int cmdRSSI (PnPty
*pd
, int argc
, char *argv
[]) {
145 char *res
= pnptySendCommand(pd
, "AT+CSQ");
148 int p
= parseCSQ(res
);
152 if (p
== 0) printf("unknown\n"); else printf("-%d dBm\n", p
);
159 fprintf(stderr
, "BAD: some error occured.\n");
164 static int cmdUSSD (PnPty
*pd
, int argc
, char *argv
[]) {
165 char *res
= pnptySendCommand(pd
, "AT+CUSD=1,\"%s\",15", argv
[1]); // 15: lang=neutral
167 if (res
!= NULL
&& strncmp(res
, "+CUSD: ", 7) == 0) {
168 printf("reply: %s\n", res
+7);
171 } else if (res
!= NULL
) {
175 fprintf(stderr
, "BAD: some error occured.\n");
180 static int cmdBalanceMTS (PnPty
*pd
, int argc
, char *argv
[]) {
181 char *res
= pnptySendCommand(pd
, "AT+CUSD=1,\"*101#\",15"); // 15: lang=neutral
184 if (res
!= NULL
&& strncmp(res
, "+CUSD: ", 7) == 0) {
185 const char *str
= res
+7;
188 if (val
!= NULL
&& strcmp(val
, "0") == 0) {
190 str
= skipField(str
);
192 if (val
!= NULL
&& (isdigit(val
[0]) || val
[0] == '.')) {
196 for (p
= val
; *p
; ++p
) {
199 if (wasdot
) { p
= NULL
; break; }
205 if (strncmp(p
, " UAH", 4) == 0) p
+= 4;
216 printf("balance: %s\n", val
);
225 if (val
!= NULL
) free(val
);
226 if (res
!= NULL
) free(res
);
228 fprintf(stderr
, "BAD: some error occured.\n");
233 static int cmdCommand (PnPty
*pd
, int argc
, char *argv
[]) {
234 char *res
= pnptySendCommand(pd
, "AT%s", argv
[1]);
237 if (res
[0] == '\1') {
238 printf("=== ERROR ===\n%s\n", res
);
240 printf("=== REPLY ===\n%s\n", res
);
246 fprintf(stderr
, "BAD: some error occured.\n");
251 static char *str2hex (const char *encoding
, const char *str
) {
253 char *outs
= NULL
, *ibuf
, *obuf
, *ress
;
256 if (str
== NULL
) return NULL
;
257 if (encoding
== NULL
) encoding
= "KOI8-U";
258 if ((cd
= iconv_open("UCS-2BE", encoding
)) == (iconv_t
)-1) return NULL
; // invalid encoding
261 outs
= malloc(il
*2+8);
264 il
= iconv(cd
, &ibuf
, &il
, &obuf
, &ol
);
265 if (il
== (size_t)-1) goto error
;
266 //if (reslen) *reslen = ool-ol;
268 if ((ress
= calloc(ool
-ol
+1, 2)) == NULL
) { free(outs
); return NULL
; }
269 for (size_t f
= 0; f
< ool
-ol
; ++f
) sprintf(ress
+f
*2, "%02X", (unsigned char)(outs
[f
]));
280 MAXSMSLEN
= 160-6*2, // 6 octets for UDHI
285 static int cmdSMS (PnPty
*pd
, int argc
, char *argv
[]) {
291 if (!argv
[2][0] || !argv
[1][0]) { fprintf(stderr
, "BAD: invalid args\n"); return 1; }
293 if ((hexstr
= str2hex(NULL
, argv
[2])) == NULL
) { fprintf(stderr
, "BAD: invalid text\n"); return 1; }
295 msglen
= strlen(hexstr
)/*/2*/;
296 msgmax
= (msglen
+MAXSMSLEN
-1)/MAXSMSLEN
;
297 if (msgmax
> 250) { free(hexstr
); fprintf(stderr
, "BAD: text too long\n"); return 1; }
299 fprintf(stderr
, "sending %d message%s\n", msgmax
, (msgmax
== 1 ? "" : "s"));
301 res
= pnptySendCommand(pd
, "AT+CSCS=\"HEX\"");
302 if (res
== NULL
|| res
[0] == 1) { free(hexstr
); fprintf(stderr
, "ERROR: CSCS\n"); goto error
; }
305 // for delivery report to work the program should be listening until the message is delivered:
306 //+CDS: 6,<the SMS ref num above>,,,<date & time>,<date & time>,0 or 48
307 // last character - 0 being successful, 48 delivery failed
308 // this is not possible with a CLI program (should exit as the sms is sent).
311 // 0 - 143: (VP + 1) x 5 mins");
312 // 144 - 167: 12 Hours + ((VP-143) x 30 mins)");
313 // 168 - 196: (VP-166) x 1 day");
314 // 197 - 255: (VP-192) x 1 week");
317 // 17: options (32 -- with delivery report?)
319 // 8: dcs (24 for flash SMS) -- Data Coding Scheme
320 // see http://www.dreamfabric.com/sms/dcs.html
321 // 00 0 0 10 00: normal USC
322 // 00 0 1 10 00: alert
323 for (int f
= 0; f
< msgmax
; ++f
) {
329 memset(msg
, 0, sizeof(msg
));
330 sprintf(msg
, "050003%02X%02X%02X", msgid
, msgmax
, f
+1);
332 mpos
= hexstr
+f
*MAXSMSLEN
/**2*/;
334 if (mlen
> FULLSMSLEN
) mlen
= FULLSMSLEN
;
335 memcpy(msg
+strlen(msg
), mpos
, mlen
);
337 // 0: PDU mode; 1: text mode
338 res
= pnptySendCommand(pd
, "AT+CMGF=1");
339 if (res
== NULL
|| res
[0] == 1) { fprintf(stderr
, "ERROR: CMGF\n"); goto error
; }
342 res
= pnptySendCommand(pd
, "AT+CSMP=%d,%d,0,%d", 81, 167, 8);
343 if (res
== NULL
|| res
[0] == 1) { fprintf(stderr
, "ERROR: CSMP\n"); goto error
; }
347 //fprintf(stderr, "[%s]\n", msg);
348 if (pnptySendSMSCommand(pd
, argv
[1], msg
) < 0) { fprintf(stderr
, "ERROR: SMS\n"); goto error
; }
354 if (res
!= NULL
) free(res
);
356 fprintf(stderr
, "BAD: some error occured.\n");
361 ////////////////////////////////////////////////////////////////////////////////
365 int (*fn
) (PnPty
*pd
, int argc
, char *argv
[]); // argv[0]: command name
366 int minargs
; // -1: any; 0: no args (i.e. argv[0] is not counted)
367 int maxargs
; // -1: any; 0: no args (i.e. argv[0] is not counted)
370 static const CmdHandler commands
[] = {
371 {"help", "show all available commands with short descriptions", cmdHelp
, 0, 0},
372 {"me", "return phone number from SIM", cmdMe
, 0, 0},
373 {"rssi", "show signal power", cmdRSSI
, 0, 0},
375 {"ussd", "send USSD query (provide full query, such as '*101#')", cmdUSSD
, 1, 1},
377 {"balance-mts", "send MTS balance USSD query", cmdBalanceMTS
, 0, 0},
379 {"command", "send AT command (for example '+CPIN?')", cmdCommand
, 1, 1},
381 {"sms", "send SMS: number sms [number...]", cmdSMS
, 2, 2},
385 ////////////////////////////////////////////////////////////////////////////////
386 static int cmdHelp (PnPty
*pd
, int argc
, char *argv
[]) {
389 if (pd
!= NULL
) pnptyFree(pd
);
391 for (size_t f
= 0; f
< sizeof(commands
)/sizeof(CmdHandler
); ++f
) {
392 int len
= strlen(commands
[f
].name
);
394 if (len
> maxcnlen
) maxcnlen
= len
;
397 printf("command list:\n");
398 for (size_t f
= 0; f
< sizeof(commands
)/sizeof(CmdHandler
); ++f
) {
399 int len
= strlen(commands
[f
].name
)-maxcnlen
;
401 printf(" %s", commands
[f
].name
);
402 while (len
-- > 0) fputc(' ', stdout
);
403 printf(" -- %s\n", commands
[f
].desc
);
411 ////////////////////////////////////////////////////////////////////////////////
412 static const CmdHandler
*findCommand (const char *name
) {
413 if (name
!= NULL
&& name
[0]) {
414 for (size_t f
= 0; f
< sizeof(commands
)/sizeof(CmdHandler
); ++f
) {
415 if (strcasecmp(commands
[f
].name
, name
) == 0) return commands
+f
;
423 ////////////////////////////////////////////////////////////////////////////////
424 int main (int argc
, char *argv
[]) {
426 const CmdHandler
*ch
;
430 if (argc
< 2) cmdHelp(NULL
, 0, NULL
);
435 if ((ch
= findCommand(argv
[0])) == NULL
) {
436 fprintf(stderr
, "FATAL: unknown command: '%s'!\n", argv
[0]);
440 if (ch
->minargs
>= 0 && argc
-1 < ch
->minargs
) {
441 fprintf(stderr
, "FATAL: '%s' needs at least %d arguments!\n", argv
[0], ch
->minargs
);
445 if (ch
->maxargs
>= 0 && argc
-1 > ch
->maxargs
) {
446 if (ch
->maxargs
== 0) {
447 fprintf(stderr
, "FATAL: '%s' needs no arguments!\n", argv
[0]);
449 fprintf(stderr
, "FATAL: '%s' needs no more than %d arguments!\n", argv
[0], ch
->minargs
);
454 if ((pd
= pnptyNew()) == NULL
) {
455 fprintf(stderr
, "FATAL: can't start pnatd!\n");
459 // switch output encoding to GSM default encoding
460 if ((s
= pnptySendCommand(pd
, "AT+CSCS=\"GSM\"")) == NULL
|| s
[0] == '\1') {
467 res
= ch
->fn(pd
, argc
, argv
);