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.
20 #include <sys/types.h>
24 //#define dlog(...) fprintf(stderr, __VA_ARGS)
28 ////////////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////////////
36 static void pnptyKillChild (PnPty
*pt
) {
37 if (pt
!= NULL
&& pt
->pid
> 0) {
40 if (kill(pt
->pid
, 0) >= 0) kill(pt
->pid
, SIGKILL
);
41 //if (kill(pt->pid, 0) >= 0) kill(pt->pid, SIGTERM);
42 /*TODO: wait a little, then kill that stubborn child*/
43 if (waitpid(pt
->pid
, &status
, 0/*|WNOHANG*/) >= 0) {
50 static void pnptyClear (PnPty
*pt
) {
52 if (pt
->fd
>= 0) close(pt
->fd
);
59 static int setNonBlock (int fd
, int doset
) {
62 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1) flags
= 0;
63 if (doset
) flags
|= O_NONBLOCK
; else flags
&= ~O_NONBLOCK
;
64 if (fcntl(fd
, F_SETFL
, flags
) == -1) return -1;
70 static int ensureModemIsReady (int fd
) {
75 if (setNonBlock(fd
, 0) < 0) return -1;
76 if (write(fd
, "AT shit\r", 8) <= 0) return -1;
79 if (read(fd
, buf
, 1) != 1) return -1;
80 if (buf
[0] == '\n') break;
82 if (setNonBlock(fd
, 1) < 0) return -1;
84 // wait for 'ERROR'; 100 ms
86 for (int f
= 100; f
> 0; --f
) {
87 int rd
= read(fd
, buf
+bufpos
, sizeof(buf
)-bufpos
-1);
90 if (errno
== EINTR
) continue;
91 if (errno
== EAGAIN
) { usleep(1000); continue; }
101 if ((eol
= strchr(buf
, '\n')) != NULL
) {
102 int np
= (eol
-buf
)+1;
104 if (eol
> buf
&& eol
[-1] == '\r') --eol
;
106 //fprintf(stderr, "ensure: [%s]\n", buf);
107 if (strcmp(buf
, "ERROR") == 0) return 0; // ok
108 if (np
< bufpos
) memmove(buf
, buf
+np
, bufpos
-np
);
112 if (bufpos
>= sizeof(buf
)-2) return -1; // alas
115 // and do it all again
118 return 0; // never reached
122 static int pnptyRun (PnPty
*pt
) {
124 // let's prepare args
127 args
[0] = "/usr/bin/pnatd";
130 if ((pt
->pid
= forkpty(&pt
->fd
, NULL
, NULL
, NULL
)) >= 0) {
133 execvp(args
[0], args
);
144 ////////////////////////////////////////////////////////////////////////////////
145 void pnptyFree (PnPty
*pt
) {
153 PnPty
*pnptyNew (void) {
154 PnPty
*res
= malloc(sizeof(PnPty
));
160 if (pnptyRun(res
) != 0 || ensureModemIsReady(res
->fd
) != 0) {
175 // remove empty lines (SLOOOW)
176 static void trimemptylines (char *str
) {
177 for (char *eol
= strrchr(str
, '\n'); eol
!= NULL
; eol
= strrchr(str
, '\n')) {
178 if (eol
[1]) break; // non-empty line follows
179 if (eol
> str
&& eol
[-1] == '\r') --eol
;
185 // return string or NULL on error
186 char *pnptySendCommandVA (PnPty
*pt
, const char *fmt
, va_list args
) {
187 if (pt
!= NULL
&& pt
->fd
>= 0 && pt
->pid
> 0) {
188 //static const char *eol = "\r\n";
189 char s
[1024], *buf
= s
;
190 int sz
= sizeof(s
)-4;
191 int bufsz
= 1024, bufpos
= 0;
192 int waitcnt
= 0, firstline
= 1;
199 n
= vsnprintf(s
, sz
, fmt
, ap
);
202 if (n
> -1 && n
< sz
) { sz
= n
; break; } // ok
203 if (n
< 0) n
= sz
*2; else ++n
;
206 if ((buf
= malloc(n
+4)) == NULL
) return NULL
;
210 if ((t
= realloc(buf
, n
+4)) == NULL
) { free(buf
); return NULL
; }
216 if (setNonBlock(pt
->fd
, 0) < 0) {
217 if (buf
!= s
) free(buf
);
221 dlog("sending: [%s]\n", s
);
223 sz
= write(pt
->fd
, s
, strlen(s
));
224 if (buf
!= s
) free(buf
);
225 if (sz
<= 0) return NULL
;
227 if (setNonBlock(pt
->fd
, 1) < 0) return NULL
;
228 if ((buf
= calloc(bufsz
+4, 1)) == NULL
) return NULL
;
233 if (bufpos
>= bufsz
) {
234 int newsz
= bufsz
+1024;
235 char *nb
= realloc(buf
, newsz
+4);
237 if (nb
== NULL
) { free(buf
); return NULL
; }
238 memset(buf
+bufpos
, newsz
-bufsz
, 0);
243 if ((rd
= read(pt
->fd
, buf
+bufpos
, 1)) <= 0) {
246 if (errno
== EINTR
) continue;
247 if (errno
== EAGAIN
) {
248 if (waitcnt
++ < TIMEOUT
) {
255 msg
= strerror(errno
);
256 dlog("rd: %d; [%s]\n", rd
, msg
);
258 fprintf(stderr
, "ERROR!\n");
261 if (buf
[bufpos
] == 0) buf
[bufpos
] = ' ';
262 //dlog("rd=%d; char=%d (%c)\n", rd, (unsigned char)(buf[bufpos]), (buf[bufpos] >= 32 && buf[bufpos] != 128 ? buf[bufpos] : '.'));
265 if (buf
[bufpos
-1] == '\n') {
270 // ignore first line (this is our command echoed)
273 memset(buf
, 0, bufsz
);
277 if (bufpos
> 1 && buf
[bufpos
-2] == '\r') buf
[(--bufpos
)-1] = '\n';
278 dlog("===\n%s===\n", buf
);
280 if ((lastn
= strrchr(buf
, '\n')) == NULL
) lastn
= buf
; else ++lastn
;
282 if (strcmp(lastn
, "OK") == 0) {
283 if (buf
[0] == 1) buf
[0] = ' ';
284 // remove OK line and last EOL
287 lastn
[lastn
-2 >= buf
&& lastn
[-2] == '\r' ? -2 : -1] = 0;
292 //dlog("===\n%s===\n", buf);
296 if (lastn
[0] == '+' && lastn
[1] == 'C' && strlen(lastn
) >= 10) {
300 err
= (strncmp(lastn
, "+CM? ERROR", 10) == 0);
304 if (err
|| strcmp(lastn
, "ERROR") == 0) {
305 //dlog("===\n%s===\n", buf);
306 buf
[bufpos
-1] = '\n';
307 memmove(buf
+1, buf
, strlen(buf
)+1);
312 buf
[bufpos
-1] = '\n';
324 __attribute__((format(printf
,2,3))) char *pnptySendCommand (PnPty
*pt
, const char *fmt
, ...) {
329 res
= pnptySendCommandVA(pt
, fmt
, ap
);
336 static int skipLine (PnPty
*pt
, int dump
) {
343 if ((rd
= read(pt
->fd
, &ch
, 1)) <= 0) {
346 if (errno
== EINTR
) continue;
347 if (errno
== EAGAIN
) {
348 if (waitcnt
++ < TIMEOUT
) { usleep(1000); continue; }
351 msg
= strerror(errno
);
352 dlog("rd: %d; [%s]\n", rd
, msg
);
353 fprintf(stderr
, "ERROR!\n");
356 if (dump
) fputc(ch
, stderr
);
357 if (ch
== '\n') return 0; // done
362 int pnptySendSMSCommand (PnPty
*pt
, const char *number
, const char *str
) {
363 if (pt
!= NULL
&& pt
->fd
>= 0 && pt
->pid
> 0 && number
!= NULL
&& number
[0] && str
!= NULL
&& str
[0]) {
364 //static const char *eol = "\r";
365 static const char *cmd0
= "AT+CMGS=\"";
366 static const char *cmd1
= "\"\r";
367 static const char *ctrlz
= "\x1a";
374 if (setNonBlock(pt
->fd
, 0) < 0) return -1;
375 //dlog("pnptySendSMSCommand: sending CMGS for '%s'\n", number);
376 if ((sz
= write(pt
->fd
, cmd0
, strlen(cmd0
))) < 0) return -1;
377 if ((sz
= write(pt
->fd
, number
, strlen(number
))) < 0) return -1;
378 if ((sz
= write(pt
->fd
, cmd1
, strlen(cmd1
))) < 0) return -1;
380 // now we should receive "> " or some shit (?)
381 if (setNonBlock(pt
->fd
, 1) < 0) return -1;
383 if (skipLine(pt
, 0) < 0) return -1;
384 //dlog("reading '> '...\n");
389 if ((rd
= read(pt
->fd
, repbuf
+reppos
, 1)) <= 0) {
392 if (errno
== EINTR
) continue;
393 if (errno
== EAGAIN
) {
394 if (waitcnt
++ < TIMEOUT
) { usleep(1000); continue; }
397 msg
= strerror(errno
);
398 dlog("rd: %d; [%s]\n", rd
, msg
);
399 fprintf(stderr
, "ERROR!\n");
405 //dlog("REP: '%c%c'\n", repbuf[0], repbuf[1]);
407 if (repbuf
[0] != '>' || repbuf
[1] != ' ') {
408 // invalid answer; read till EOL and return error
409 dlog("REP: '%c%c'\n", repbuf
[0], repbuf
[1]);
414 //dlog("sending text...\n");
415 if (setNonBlock(pt
->fd
, 0) < 0) return -1;
416 if ((sz
= write(pt
->fd
, str
, strlen(str
))) < 0) return -1;
417 if ((sz
= write(pt
->fd
, ctrlz
, strlen(ctrlz
))) < 0) return -1;
419 //dlog("reading reply...\n");
420 if (setNonBlock(pt
->fd
, 1) < 0) return -1;
421 if (skipLine(pt
, 0) < 0) return -1; // skip echoed str
426 if ((rd
= read(pt
->fd
, repbuf
+reppos
, 1)) <= 0) {
429 if (errno
== EINTR
) continue;
430 if (errno
== EAGAIN
) {
431 if (waitcnt
++ < TIMEOUT
) { usleep(1000); continue; }
434 msg
= strerror(errno
);
435 dlog("rd: %d; [%s]\n", rd
, msg
);
436 fprintf(stderr
, "ERROR!\n");
439 //dlog("%c (%d)\n", (repbuf[reppos] > ' ' && repbuf[reppos] < 127 ? repbuf[reppos] : '.'), (unsigned int)(repbuf[reppos]));
440 // here we should receive "+CMGS: 87" or so
441 if (reppos
== 0 && repbuf
[0] != '+') {
446 if (reppos == 0 && (repbuf[0] == '\n' || repbuf[0] == '\r')) {
447 dlog("%d skipped...\n", repbuf[0]);
448 continue; // skip all newlines
451 if (repbuf
[reppos
] == '\n') {
452 repbuf
[reppos
+1] = 0;
453 //dlog("SMSREP: %s", repbuf);
454 if (strncmp(repbuf
, "+CMGS: ", 7) != 0) {
455 fprintf(stderr
, "SMSERR: %s", repbuf
);
460 if (reppos
< sizeof(repbuf
)-2) ++reppos
;
462 //dlog("SMS COMPLETE!\n");
463 skipLine(pt
, 1); // this MUST be "OK"; we should check it, but...