1 //
2 // anyRemote
3 // a wi-fi or bluetooth remote for your PC.
4 //
5 // Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
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, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 //
21 
22 //
23 // AT+CKPD commands handling
24 //
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <termios.h>
32 
33 #include "parse.h"
34 #include "utils.h"
35 #include "btio.h"
36 #include "conf.h"
37 #include "dispatcher.h"
38 #include "peer.h"
39 #include "pr_rfcomm.h"
40 #include "pr_serial.h"
41 
42 #define PAUSE_TIME 1
43 
44 extern char tmp[MAXMAXLEN];
45 
46 static int  useQuestionInCLCC = 0;
47 char callerId[MAXLEN];
48 
49 //
50 // max should be >= 100
51 //
writeAtCommand(int fd,const char * command,char * answer,int max,int timeout,char * expect)52 int writeAtCommand(int fd,
53                    const char* command,
54                    char* answer,
55                    int   max,
56                    int   timeout,
57                    char* expect)
58 {
59     int count=0;
60     int readcount;
61     char tmp2[100];
62     int timeoutcounter=0;
63     int found=0;
64 
65     if (fd < 0) {
66         return 0;
67     }
68 
69     logger(L_DBG,command);
70     DEBUG2("writeAtCommand command >%s<", command);
71     if (expect != NULL) {
72         DEBUG2("writeAtCommand skip till >%s<", expect);
73     }
74 
75     // send command
76     if (command && command[0]) {
77         write(fd,command,strlen(command));
78         tcdrain(fd);
79     }
80 
81     if (max == 0) {
82         return 0;
83     }
84 
85     answer[0]=0;
86     do {
87         // try to read some bytes.
88         usleep(100000);
89 
90 	//write(1,".",1);
91         timeoutcounter++;
92 
93         // read data
94         readcount=read(fd,tmp2,sizeof(tmp2)-1);
95         if (readcount<0) {
96             readcount=0;
97         }
98         tmp2[readcount]=0;
99 
100         // add read bytes to the output buffer
101         if (readcount) {
102 	    //DEBUG2("writeAtCommand got reply >%s<", tmp2);
103 
104             strcat(answer,tmp2);
105             count+=readcount;
106 
107             // if we have more time to read, check if we got already the expected string
108             if ((timeoutcounter<timeout) && (found==0)) {
109 
110                 // check if it's the expected answer
111                 if (strstr(answer,"OK\r") || strstr(answer,"ERR")) {
112 		    if (expect == NULL) {
113                         found=1;
114 		    } else {
115 		        if (strstr(answer,"ERR")) {  // got error, so nothing to expect
116 			    expect = NULL;
117 			}
118 		    }
119                 }
120 
121                 if (expect && expect[0]) {
122                     if (strstr(answer,expect)) {
123                         sprintf(tmp, "Got expected %s (iteration %d)", answer, timeoutcounter);
124                         logger(L_DBG, tmp);
125                         found=1;
126                     }
127                 }
128 
129                 // if found then set timoutcounter to read only 0.1s after that and not more
130                 if (found) {
131                     //timeoutcounter=timeout-1;
132                     break;
133                 }
134             }
135         }
136     }
137     // repeat until timout
138     while (timeoutcounter<timeout && count < max);
139 
140     if (getLog()) {
141         char *a2 = answer;
142 
143         while (a2[0] == '\r' || a2[0] == '\n')  {
144             a2++;
145         }
146         logger(L_DBG,a2);
147     }
148 
149     return count;
150 }
151 
152 //
153 // Phones can send commad back as reply, so it needs to skip it
154 //
sendSyncCommand(int fd,const char * bytes,char * answer,int size,char * expect)155 static void sendSyncCommand(int fd, const char *bytes, char *answer, int size, char *expect)
156 {
157     DEBUG2("sendSyncCommand >%s<\n", bytes);
158 
159     // initial timeout was 5
160     writeAtCommand(fd, bytes, answer, size, 40, expect);
161 }
162 
sendCMER(int fd,int onOff)163 int sendCMER(int fd, int onOff)
164 {
165     char answer[100];
166     char cmd[64];
167 
168     char * cmer = getAT_CMER(onOff);
169     if (cmer == NULL) {
170         return EXIT_NOK;
171     }
172 
173     strcpy(cmd,cmer);
174     free(cmer);
175 
176     strcat(cmd,"\r");
177     //sendSyncCommand(cmd, answer, sizeof(answer), NULL);
178     writeAtCommand (fd, cmd, answer, sizeof(answer), 10, NULL);
179 
180     if(!IS_OK(answer)) {
181         DEBUG2("ERROR: AT_CMER SET >%s< -> %s\n", cmd, answer);
182         return EXIT_NOK;
183     }
184     return EXIT_OK;
185 }
186 
187 // Send AT+CKPD command
sendCKPD(int fd,char * key)188 static int sendCKPD(int fd, char *key)
189 {
190     char answer[1024];
191     char ckpd[MAXCKPDLEN];
192 
193     if (key == NULL || key[0] == '\0') {
194         logger(L_DBG,"Empty symbol in sendCKPD()");
195         return EXIT_NOK;
196     }
197     DEBUG2("sendCKPD >%s<", key);
198 
199     sprintf(ckpd,"%s\"%s\"\r", DEF_AT_CKPD, key);
200 
201     int model = getModel();
202 
203     if (model == MODEL_SE || model == MODEL_SIEMENS) {  // SE-K700, Siemens-S55
204         // If we send AT+CKPD=X
205         // SE phone will replay
206         // OK
207         // +CKEV: X,1
208         // +CKEV: X,0
209         // And we need to filter out that CKEVs
210         char expect[16];
211         strcpy(expect, DEF_CKEV);
212         strcat(expect, " ");
213         if (model == MODEL_SIEMENS) {
214             strcat(expect, "\"");
215         }
216         strcat(expect, key);
217         if (model == MODEL_SIEMENS) {
218             strcat(expect, "\"");
219         }
220         strcat(expect, ",0");  // Wait button up event
221 
222         sendSyncCommand(fd, ckpd, answer, sizeof(answer), expect);
223         //DEBUG2("Expect >%s<", expect);
224         //DEBUG2("Got all >%s<", answer);
225 
226         // Filter out button up
227         char *ptr = strstr(answer, expect);
228         if(ptr == NULL) {
229             logger(L_DBG,"cant find button up event");
230         } else {
231             int z;
232             int l = strlen(expect);
233             for (z=0; z<l; z++) {
234                 *ptr = '\r';
235                 ptr++;
236             }
237         }
238 
239         // Filter out button down
240         strcpy(expect, DEF_CKEV);
241         strcat(expect, " ");
242         if (model == MODEL_SIEMENS) {
243             strcat(expect, "\"");
244         }
245         strcat(expect, key);
246         if (model == MODEL_SIEMENS) {
247             strcat(expect, "\"");
248         }
249         strcat(expect, ",1");
250         ptr = strstr(answer, expect);
251         if(ptr == NULL) {
252             logger(L_DBG,"can not find button down event");
253         } else {
254             int z;
255             int l = strlen(expect);
256             for (z=0; z<l; z++) {
257                 *ptr = '\r';
258                 ptr++;
259             }
260         }
261         DEBUG2("Filtered >%s<", answer);
262 
263     } else {
264         sendSyncCommand(fd, ckpd, answer, sizeof(answer), NULL);
265     }
266 
267     parseCommand(PEER_ANY, answer);
268 
269     return EXIT_OK;
270 }
271 
272 //
273 // Should be used in AT mode only
274 //
sendSeq(int fd,const char * seq)275 int sendSeq(int fd, const char *seq)
276 {
277     if (getUseScreen() == 0 || seq == NULL) {
278         return EXIT_OK;
279     }
280 
281     DEBUG2("sendSeq() >%s<", seq);
282 
283     char* copy = strdup(seq);
284 
285     char *bufPtr = NULL;
286     char *ckpd = strtok_r(copy," ",&bufPtr);
287 
288     while (ckpd != NULL) {
289         //DEBUG2("NEXT sendSeq()->sendCKPD()>%s<", ckpd);
290 
291         if (strcmp(ckpd, PAUSE_STR)==0) {
292             sleep(PAUSE_TIME);	// Just wait a bit
293         } else {
294             sendCKPD(fd, ckpd);
295         }
296         ckpd = strtok_r(NULL," ",&bufPtr);
297     }
298     free(copy);
299 
300     return EXIT_OK;
301 }
302 
parse_CLCC(char * clcc,char * dest)303 static void parse_CLCC(char *clcc, char* dest)
304 {
305     // There are could be more than one call at time ... but here we will get then one-by-one
306     // +CLCC: 1,,4,0,0,"<phone number here>",129,
307     char *start ,*ptr;
308     int commaCount = 0;
309 
310     ptr = clcc;
311     while(ptr && commaCount < 5) {
312         if (*ptr == ',') {
313             commaCount++;
314         }
315         ptr++;
316     }
317 
318     if (ptr == NULL) { // Call was finished
319         strcpy(dest, "FINISHED");
320 	return;
321     }
322     if (ptr && *ptr == '"') {
323         ptr++;
324     }
325     start = ptr;
326     while(ptr && *ptr != '"' &&  *ptr != ',') {
327         ptr++;
328     }
329     if (ptr) {
330         *ptr = '\0';
331     }
332 
333     if(start) {
334         DEBUG2("parse_CLCC >%s<", start);
335     } else {
336         DEBUG2("parse_CLCC >NULL callerID<");
337         strcpy(dest, "NO CALLER ID");
338 	return;
339     }
340 
341     strcpy(dest, start);
342     return;
343 }
344 
getClip(int fd,char * CallerID)345 int getClip(int fd, char* CallerID)
346 {
347     char clcc[MTEXTLEN];
348     char answer[1024];
349     char *ptr;
350     int   tryAnyway = 0;
351 
352     answer[0] = '\0';
353 
354     logger(L_DBG, "getClip()");
355 
356     // Some phones uses AT+CLCC?, some just AT+CLCC (Sagem, Siemens-S55, SE-K750)
357 
358     if (useQuestionInCLCC == 0) {
359         //logger(L_DBG, "getClip() send AT+CLCC");
360 
361         strcpy(clcc,DEF_AT_CLCC);
362         strcat(clcc,"\r");
363 
364         sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL);
365 
366         if ((ptr = strstr(answer,DEF_CLCC)) != NULL) {
367             parse_CLCC(ptr,CallerID);
368             return EXIT_EXACT;
369         } else if (strstr(answer,"OK") != NULL) {
370             return EXIT_OK;
371         } else if (strstr(answer,"ERR") != NULL) {
372             logger(L_WARN, "Got ERROR on AT+CLCC. Will try AT+CLCC?");
373             useQuestionInCLCC = 1;      // Ask phone again; but will never ask AT+CLCC more
374         } else {
375             tryAnyway = 1;              // Ask phone again; to be sure
376         }
377     }
378 
379     if (useQuestionInCLCC == 1 || tryAnyway == 1) {
380 
381         tryAnyway = 0;
382 
383         //logger(L_DBG, "getClip() send AT+CLCC?");
384 
385         strcpy(clcc,DEF_AT_CLCC);
386         strcat(clcc,"?");
387         strcat(clcc,"\r");
388 
389         sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL);
390 
391         if ((ptr = strstr(answer,DEF_CLCC)) != NULL) {
392             parse_CLCC(ptr,CallerID);
393             return EXIT_EXACT;
394         } else if (strstr(answer,"OK") != NULL) {
395             return EXIT_OK;
396         } else if (strstr(answer,"ERR") != NULL) {
397             logger(L_ERR, "Got ERROR on AT+CLCC?");
398             return EXIT_NOK;
399         }
400     }
401 
402     DEBUG2("sendClip() unappropriate answer: >%s<", answer);
403 
404     return EXIT_NOK;
405 }
406 
407 // CLIENT_RFCOMM / CLIENT_AT
setupAtConnection(ConnectInfo * peer,int fd)408 int setupAtConnection(ConnectInfo* peer, int fd)
409 {
410     char answer[1024];
411     char cmd[32];
412 
413     answer[0] = '\0';
414     sendSyncCommand(fd, "ATZ\r", answer, sizeof(answer), NULL);
415     //sendSyncCommand("AT\r", answer, sizeof(answer), NULL);
416     // Do not care - it can fail after reconnect
417     //if(!IS_OK(answer)) {
418     //    ERROR2("AT -> %s\n", answer)
419     //    return -1;
420     //}
421 
422     sendSyncCommand(fd, "ATE0\r", answer, sizeof(answer), NULL);
423     if(!IS_OK(answer)) {
424         ERROR2("ATE0 -> %s\n", answer);
425     }
426 
427     char* ptr = getCharset();
428     if (ptr) {
429         sprintf(cmd,"%s\"%s\"\r", DEF_AT_CSCS, ptr);
430         sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
431         if(!IS_OK(answer)) {
432             ERROR2("Can't set charset to %s\n", ptr);
433         }
434         free(ptr);
435     }
436 
437     // Determine model
438     sprintf(cmd,"%s\r", DEF_AT_CGMI);
439     sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
440 
441     //Set model in conf. data
442     setModel(answer);
443 
444     if (getModel() == MODEL_MOTOROLA) {	// Motorola RIZR Z3 needs to set MODE=2 to allow AT+CLIP command
445         // do not care about responce
446         sendSyncCommand(fd, "AT+MODE=2\r", answer, sizeof(answer), NULL);
447     }
448 
449     sprintf(cmd,"%s\r", DEF_AT_CLIP);
450     sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
451     if(!IS_OK(answer)) {
452         ERROR2("Can't set CLIP ON\n");
453     }
454 
455 
456     int ret = sendCMER(fd, CMER_ON);
457     if(ret != EXIT_OK) {
458         ERROR2("fails in set event reporting on");
459         return -1;
460     }
461 
462     // Siemens S55 needs additional AT+CMEC=2 to make AT+CKPD works
463     // not sure about other Siemens phones
464     if (getModel() == MODEL_SIEMENS) {
465         sprintf(cmd,"%s\r", DEF_AT_CMEC);
466         sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
467         if(!IS_OK(answer)) {
468             ERROR2("ON AT+CMEC\n");
469         }
470     }
471 
472     // Will set global callerId [MAXLEN];
473     ret = getClip(fd, callerId);
474     if(ret == EXIT_NOK) {       // Got ERROR on AT+CLCC; probably phone does not supports this command
475         printf("ERROR: fails in getClip\n");
476 
477 	if (peer->mode == CLIENT_RFCOMM) {
478 	    _RfcommConnection* cn = (_RfcommConnection*) peer->connectionData;
479             if (cn) {
480                 cn->useCallId = BOOL_NO;
481             }
482         } else if (peer->mode == CLIENT_AT) {
483 	    _SerialConnection* cn = (_SerialConnection*) peer->connectionData;
484             if (cn) {
485                 cn->useCallId = BOOL_NO;
486             }
487 	}
488     }
489 
490     return 1;
491 }
492 
493 //
494 // rfcomm peer reader
495 // in case of incoming call try to get caller ID
496 //
atRead(ConnectInfo * peer,char * buffer,int max)497 int atRead(ConnectInfo* peer, char* buffer, int max)
498 {
499     if (! (peer->mode == CLIENT_RFCOMM || peer->mode == CLIENT_AT)) {
500         ERROR2("[DS]: atRead() peer type mismatch");
501 	return 0;
502     }
503 
504     if (!peer->connectionData) return EXIT_NOK;
505 
506     _RfcommConnection* cnr = NULL;
507     _SerialConnection* cns = NULL;
508     int fd = -1;
509     boolean_t useCallID = BOOL_NO;
510     boolean_t hasCall   = BOOL_NO;
511 
512     if (peer->mode == CLIENT_RFCOMM) {
513         cnr = (_RfcommConnection*) peer->connectionData;
514 	fd  = cnr->fileDescriptor;
515 	useCallID = cnr->useCallId;
516 	hasCall   = cnr->hasActiveCall;
517     } else if (peer->mode == CLIENT_AT) {
518         cns = (_SerialConnection*) peer->connectionData;
519 	fd  = cns->fileDescriptor;
520 	useCallID = cns->useCallId;
521 	hasCall   = cns->hasActiveCall;
522     }
523 
524     if (fd < 0) {
525         ERROR2("[DS]: atRead() not connected");
526 	return 0;
527     }
528 
529     int nbytes = read(fd, buffer, max);
530     if (nbytes < 0)  { // Read error
531         ERROR2("[DS]: atRead() error %d",errno);
532         errnoDebug("[DS]: atRead() read ",errno);  // testing debug
533     } else if (nbytes == 0) {
534         DEBUG2("[DS]: atRead() EOF");
535     } else {
536 
537 	buffer[nbytes] = '\0';
538 
539         if (strstr(buffer, DEF_RING) != NULL && !hasCall) {	  // Incoming call
540             logger(L_INF,"[DS]: Incoming call");  // This event sent periodically until used answered a call
541 
542 	    if (cnr) {
543 	       cnr->hasActiveCall = BOOL_YES;
544 	    } else if (cns) {
545 	       cns->hasActiveCall = BOOL_YES;
546 	    }
547 
548 	    if (useCallID) {
549 
550 	        char callerId[MAXLEN];
551 	        int ret = getClip(fd, callerId);
552 
553 		if (ret == EXIT_EXACT) { // Active call exists, got ID
554 		    strcat(buffer, "Msg:InCall(,");
555 		    strcat(buffer, callerId);
556 		    strcat(buffer, ")\r");
557 		    nbytes = strlen(buffer);
558 		}
559 	    } else {
560 	        strcat(buffer, "Msg:InCall(,)\r");
561 		nbytes =  strlen(buffer);
562 	    }
563 	}
564     }
565 
566     //DEBUG2("[DS]: atRead() got >%s< %d", buffer, nbytes);
567     return nbytes;
568 }
569 
570 
571