removed debug output
[k900ussd.git] / src / main.c
blob371cb836ec9bc5e0e794035a2bc1d0137f852d91
1 /*
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.
11 #include <ctype.h>
12 #include <iconv.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
19 #include "pnpty.h"
20 #include "ussdd.h"
23 ////////////////////////////////////////////////////////////////////////////////
24 static const char *skipField (const char *str) {
25 const char *e;
27 if (str != NULL && str[0]) {
28 if (str[0] == '"') {
29 //???
30 for (e = str+1; *e; ++e) {
31 if (*e == '\\') ++e;
32 else if (*e == '"') { ++e; break; }
34 if (*e && *e != ',') e = NULL; // too bad
35 } else {
36 e = strchr(str, ',');
37 if (e == NULL) e = str+strlen(str); else ++e;
39 } else {
40 // invalid string or no more fields
41 e = NULL;
44 return e;
48 static char *getField (const char *str) {
49 if (str != NULL && str[0]) {
50 char *res;
52 if (str[0] == '"') {
53 //???
54 int len = 0;
55 const char *e;
57 for (e = str+1; *e; ++e) {
58 if (*e == '\\') ++e;
59 else if (*e == '"') { ++e; break; }
60 ++len;
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) {
65 if (*e == '\\') ++e;
66 else if (*e == '"') break;
67 res[len] = *e;
69 res[len] = 0;
71 } else {
72 const char *e = strchr(str, ',');
73 int len;
75 if (e == NULL) e = str+strlen(str);
76 len = e-str;
77 if ((res = malloc(len+1)) != NULL) {
78 if (len > 0) memcpy(res, str, len);
79 res[len] = 0;
83 return res;
85 // error
86 return NULL;
90 ////////////////////////////////////////////////////////////////////////////////
91 // parse '+CSQ' command output (signal strength)
92 // return dBm or -1
93 // '113' means '-113 or less', '51' means '-51 or greater', '0' means 'unknown'
94 // RSSI (dBm) = (-113)+(2*CSQ)
95 // +CSQ: 15,99
96 static int parseCSQ (const char *ans) {
97 if (ans != NULL && strncmp(ans, "+CSQ: ", 6) == 0 && isdigit(ans[6])) {
98 int res = 0;
100 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);
106 return -1;
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");
118 if (res != NULL) {
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);
126 if (val != NULL) {
127 printf("your phone number is %s\n", val);
128 free(val);
129 free(res);
131 return 0;
135 free(res);
138 fprintf(stderr, "BAD: some error occured.\n");
139 return 1; // error
143 // +CSQ: 15,99
144 static int cmdRSSI (PnPty *pd, int argc, char *argv[]) {
145 char *res = pnptySendCommand(pd, "AT+CSQ");
147 if (res != NULL) {
148 int p = parseCSQ(res);
150 if (p >= 0) {
151 printf("rssi: ");
152 if (p == 0) printf("unknown\n"); else printf("-%d dBm\n", p);
153 free(res);
154 return 0;
156 free(res);
159 fprintf(stderr, "BAD: some error occured.\n");
160 return 1; // error
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);
169 free(res);
170 return 0;
171 } else if (res != NULL) {
172 free(res);
175 fprintf(stderr, "BAD: some error occured.\n");
176 return 1; // error
180 static int cmdBalanceMTS (PnPty *pd, int argc, char *argv[]) {
181 char *res = pnptySendCommand(pd, "AT+CUSD=1,\"*101#\",15"); // 15: lang=neutral
182 char *val = NULL;
184 if (res != NULL && strncmp(res, "+CUSD: ", 7) == 0) {
185 const char *str = res+7;
187 val = getField(str);
188 if (val != NULL && strcmp(val, "0") == 0) {
189 free(val);
190 str = skipField(str);
191 val = getField(str);
192 if (val != NULL && (isdigit(val[0]) || val[0] == '.')) {
193 int wasdot = 0;
194 char *p;
196 for (p = val; *p; ++p) {
197 if (!isdigit(*p)) {
198 if (*p == '.') {
199 if (wasdot) { p = NULL; break; }
200 wasdot = 1;
201 continue;
204 if (*p == ' ') {
205 if (strncmp(p, " UAH", 4) == 0) p += 4;
206 break;
209 p = NULL;
210 break;
214 if (p != NULL) {
215 *p = 0;
216 printf("balance: %s\n", val);
217 free(val);
218 free(res);
219 return 0;
225 if (val != NULL) free(val);
226 if (res != NULL) free(res);
228 fprintf(stderr, "BAD: some error occured.\n");
229 return 1; // error
233 static int cmdCommand (PnPty *pd, int argc, char *argv[]) {
234 char *res = pnptySendCommand(pd, "AT%s", argv[1]);
236 if (res != NULL) {
237 if (res[0] == '\1') {
238 printf("=== ERROR ===\n%s\n", res);
239 } else {
240 printf("=== REPLY ===\n%s\n", res);
242 free(res);
243 return 0;
246 fprintf(stderr, "BAD: some error occured.\n");
247 return 1; // error
251 static char *str2hex (const char *encoding, const char *str) {
252 iconv_t cd;
253 char *outs = NULL, *ibuf, *obuf, *ress;
254 size_t il, ol, ool;
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
259 ibuf = (char *)str;
260 il = strlen(str);
261 outs = malloc(il*2+8);
262 obuf = outs;
263 ool = ol = il*2+2;
264 il = iconv(cd, &ibuf, &il, &obuf, &ol);
265 if (il == (size_t)-1) goto error;
266 //if (reslen) *reslen = ool-ol;
267 iconv_close(cd);
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]));
270 free(outs);
271 return ress;
272 error:
273 iconv_close(cd);
274 free(outs);
275 return NULL;
279 enum {
280 MAXSMSLEN = 160-6*2, // 6 octets for UDHI
281 FULLSMSLEN = 160
285 static int cmdSMS (PnPty *pd, int argc, char *argv[]) {
286 char *res = NULL;
287 int msglen, msgmax;
288 char *hexstr;
289 int msgid = 42;
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; }
303 free(res);
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).
310 // validity period:
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");
315 // 167: one day
317 // 17: options (32 -- with delivery report?)
318 // 167: validity
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) {
324 char msg[400];
325 const char *mpos;
326 int mlen;
328 // make UDHI
329 memset(msg, 0, sizeof(msg));
330 sprintf(msg, "050003%02X%02X%02X", msgid, msgmax, f+1);
331 // append text part
332 mpos = hexstr+f*MAXSMSLEN/**2*/;
333 mlen = strlen(mpos);
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; }
340 free(res);
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; }
344 free(res);
345 res = NULL;
347 //fprintf(stderr, "[%s]\n", msg);
348 if (pnptySendSMSCommand(pd, argv[1], msg) < 0) { fprintf(stderr, "ERROR: SMS\n"); goto error; }
351 free(hexstr);
352 return 0;
353 error:
354 if (res != NULL) free(res);
355 free(hexstr);
356 fprintf(stderr, "BAD: some error occured.\n");
357 return 1; // error
361 ////////////////////////////////////////////////////////////////////////////////
362 typedef struct {
363 const char *name;
364 const char *desc;
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)
368 } CmdHandler;
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[]) {
387 int maxcnlen = 0;
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);
406 exit(0);
407 return 0;
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;
419 return NULL;
423 ////////////////////////////////////////////////////////////////////////////////
424 int main (int argc, char *argv[]) {
425 PnPty *pd;
426 const CmdHandler *ch;
427 int res;
428 char *s;
430 if (argc < 2) cmdHelp(NULL, 0, NULL);
432 --argc;
433 ++argv;
435 if ((ch = findCommand(argv[0])) == NULL) {
436 fprintf(stderr, "FATAL: unknown command: '%s'!\n", argv[0]);
437 return 1;
440 if (ch->minargs >= 0 && argc-1 < ch->minargs) {
441 fprintf(stderr, "FATAL: '%s' needs at least %d arguments!\n", argv[0], ch->minargs);
442 return 1;
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]);
448 } else {
449 fprintf(stderr, "FATAL: '%s' needs no more than %d arguments!\n", argv[0], ch->minargs);
451 return 1;
454 if ((pd = pnptyNew()) == NULL) {
455 fprintf(stderr, "FATAL: can't start pnatd!\n");
456 return 1;
459 // switch output encoding to GSM default encoding
460 if ((s = pnptySendCommand(pd, "AT+CSCS=\"GSM\"")) == NULL || s[0] == '\1') {
461 pnptyFree(pd);
462 return 1;
464 free(s);
466 ussdd_skipnext();
467 res = ch->fn(pd, argc, argv);
468 ussdd_shownext();
470 pnptyFree(pd);
472 return res;