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 #include <ctype.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/ipc.h>
29 #include <sys/msg.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <netinet/in.h>
34 #include <time.h>
35 #include <unistd.h>
36 #ifdef __FreeBSD__
37 #include <signal.h>
38 #endif
39 #ifndef __FreeBSD__
40 #include <grp.h>
41 #endif
42 
43 #include "lib_wrapper.h"
44 #include "atsend.h"
45 #include "btio.h"
46 #include "conf.h"
47 #include "common.h"
48 #include "utils.h"
49 #include "executor.h"
50 #include "dispatcher.h"
51 #include "security.h"
52 #include "list.h"
53 #include "alarm.h"
54 
55 #include "pr_frontend.h"
56 #include "pr_btspp.h"
57 #include "pr_socket.h"
58 #ifdef USE_L2CAP
59 #include "pr_l2cap.h"
60 #endif
61 #include "pr_web.h"
62 #include "pr_stdin.h"
63 #include "queue.h"
64 #include "peer.h"
65 
66 #define TMPDISCONN_TIME 60
67 
68 extern CONF    conf;
69 extern char    tmp[MAXMAXLEN];
70 
71 extern char callerId[MAXLEN];
72 
73 boolean_t dispIsJoinable = BOOL_YES;
74 
75 
76 int  gotExitSignal  = 0;
77 
78 static int         _callTimer     = 0;
79 static int         _initialized  = 0;
80 
81 // Then port will be closed from a forked child use 0
closePort(int final)82 void closePort(int final)
83 {
84     if (final) {
85         logger(L_DBG,"[DS]: closePort");
86     }
87 
88     if (getFrontEnd() > 0) {
89         if (final)  {
90             writeToFrontEnd("Exiting");
91         }
92     }
93 
94     closePeers(final);
95 }
96 
handleActiveCall(void)97 static void handleActiveCall(void)
98 {
99     if (_callTimer >= 20) {  // about a second
100 
101         // Will set global callerId [MAXLEN];
102 
103         boolean_t stillActive = checkActiveCall();
104         if (!stillActive) {
105             strcpy(callerId, "NO CALLER ID");
106 
107             eMessage* em = (eMessage*) malloc(sizeof(eMessage));
108             em->peer  = PEER_ANY;
109             em->type  = EM_KEY;
110             em->value = strdup("Msg:EndCall(,)");
111 
112             DEBUG2("[DS]: Send message to executor thread %s", (char*) em->value);
113             sendToExecutor(em);
114         }
115 
116         _callTimer = 0;
117     } else {
118         _callTimer++;
119     }
120 }
121 
readFromFile(const char * cmdTag,const char * file,int * size)122 char* readFromFile(const char *cmdTag, const char* file, int* size)
123 {
124     FILE *fp;
125     struct stat buf;
126 
127     long fLen = 0;
128     if(stat(file, &buf) == -1) {
129         logger(L_ERR,"can't get file size!");
130     } else {
131         fLen = (int) buf.st_size;
132 
133         INFO2("[EX]: readFromFile file size id %ld", fLen);
134 
135         if (!S_ISREG (buf.st_mode)) {
136             logger(L_ERR,"not regular file");
137         } else {
138 
139             fp=fopen(file,"r");
140             if (fp == NULL) {
141                 logger(L_ERR,"can't open file!");
142             } else {
143 
144                 int prefixSz = strlen(cmdTag);
145 
146                 uint32_t szh32 = (uint32_t) buf.st_size;
147                 uint32_t szi32 = htonl(szh32);
148 
149                 char * fBuffer = (char*) calloc(fLen+prefixSz+7,1);    // 7 =   <size of bin data=4bytes>    ");\0"
150                 if (fBuffer == NULL) {
151                     logger(L_ERR,"no more memory!");
152 
153                     *size = -1;
154 
155                     fclose(fp);
156                     return NULL;
157                 }
158 
159                 strcat(fBuffer, cmdTag);
160                 memcpy((void*)fBuffer+prefixSz, (const void *) &szi32, 4);    // length on binary data
161 
162                 if (fp) {
163                     //size_t dummy =
164 		    fread(fBuffer+prefixSz+4, sizeof(char), fLen, fp);
165                     fclose(fp);
166                 }
167                 strcpy(fBuffer+prefixSz+fLen+4,");");
168 
169                 *size = fLen+prefixSz+7;
170                 INFO2("[EX]: Command: CONTENT %s", fBuffer);
171                 return fBuffer;
172             }
173         }
174     }
175 
176     if (strncmp(cmdTag, "Set(cover", 9) == 0) {  // cleanup the cover on client
177 
178         char * fBuffer = (char*) malloc(12);
179         strcpy(fBuffer, "Set(cover);");
180 
181         *size = 11;
182         return fBuffer;
183     }
184 
185     *size = -1;
186     return NULL;
187 }
188 
allocDMessage()189 dMessage* allocDMessage()
190 {
191     dMessage* dm = (dMessage*) malloc(sizeof(dMessage));
192     dm->peer     = PEER_ANY;
193     dm->type     = 0;
194     dm->subtype  = 0;
195     dm->size     = 0;
196     dm->value    = NULL;
197     dm->file     = NULL;
198     dm->scaled   = NULL;
199     return dm;
200 }
201 
getDFinalizer()202 dMessage* getDFinalizer()
203 {
204     dMessage* dm = allocDMessage();
205     dm->type     = DM_SET;
206     dm->subtype  = ID_SET_MAX;
207     dm->size     = 6;
208     dm->value    = (void*) strdup("End();");
209     return dm;
210 }
211 
freeDMessage(void * ptr)212 void freeDMessage(void *ptr)
213 {
214     dMessage* dm = (dMessage*) ptr;
215     if (dm) {
216         if (dm->value != NULL) {
217             free(dm->value);
218         }
219         if (dm->file != NULL) {
220             free(dm->file);
221         }
222         if (dm->scaled != NULL) {
223             free(dm->scaled);
224         }
225         free(dm);
226     }
227 }
228 
dispatchEvent(dMessage * dm)229 static int dispatchEvent(dMessage* dm)
230 {
231     if (dm->subtype == ID_EVENT_FRONTEND) {
232 
233         writeToFrontEnd(dm->value);
234 
235     } else if (dm->subtype == ID_EVENT_INIT) {
236 
237         logger(L_INF, "[DS]: Got init OK event");
238         return EXIT_INITOK;
239 
240     } else if (dm->subtype == ID_EVENT_DISCONNECT) {
241 
242         logger(L_INF, "[DS]: Got disconnect event");
243         disconnectPeers();
244         closePort(0);
245         return EXIT_DISCON;
246 
247     } else if (dm->subtype == ID_EVENT_EXIT) {
248 
249         logger(L_INF, "[DS]: Got exit event");
250 
251         gotExitSignal = 1;
252 
253         // in server mode wait client to close connection and then exit
254         if (disconnectPeers() == 1) {
255             logger(L_INF, "[DS]: Got exit event: exit immediately");
256             closePort(1);
257             return EXIT_ABORT;
258         }
259     }
260     return EXIT_OK;
261 }
262 
dispatchSendString(dMessage * dm)263 static int dispatchSendString(dMessage* dm)
264 {
265     DEBUG2("[DS]: Send(string) %s", (char*) dm->value);
266     return writePeers(dm);
267 }
268 
dispatchSendBytes(dMessage * dm)269 static int dispatchSendBytes(dMessage* dm)
270 {
271     DEBUG2("[DS]: Send(bytes) %s", (char*) dm->value);
272     return writeBytesPeers((char*) dm->value);
273 }
274 
dispatchSet(dMessage * dm)275 static int dispatchSet(dMessage* dm)
276 {
277     logger(L_DBG, "[DS]: Set(...)");
278     return writePeers(dm);
279 }
280 
dispatchNone(dMessage * dm)281 static int dispatchNone(dMessage* dm)
282 {
283     return EXIT_OK;
284 }
285 
286 // rely on DispMsgType enum
287 static struct {
288     int id;
289     int (*hook)(dMessage* p);
290 } _dispMsgHooks[] = {
291     {DM_SET,      dispatchSet        },
292     {DM_GET,      dispatchSet        },
293     {DM_SETFILE,  writeFilePeers     },
294     {DM_SENDB,    dispatchSendBytes  },
295     {DM_SENDS,    dispatchSendString },
296     {DM_CKPD,     writeCKPD          },
297     //{DM_CMER,     writeCMER        },
298     {DM_EVENT,    dispatchEvent      },
299     {DM_FRONTEND, dispatchNone       }
300 };
301 
processOutputData()302 static int processOutputData()
303 {
304     //logger(L_DBG,"[DS]: processOutputData");
305 
306     int ret = EXIT_OK;
307 
308     // Verify commands from queue (timeout about 1/2 sec)
309     dMessage* dm = (dMessage*) queuePop(Q_DISP);
310     if (dm != NULL) {
311 
312         //DEBUG2("[DS]: Got event %p %d", dm, dm->type);
313 
314         if (connected() == EXIT_NOK &&
315             !haveConnectionless() &&
316                 !(dm->type == DM_EVENT)) { // can process these even if no connection
317 
318             //logger(L_DBG, "[DS]: No connection. Skip event");
319 
320         } else {
321 
322             if (dm->type < DM_TYPE_MAX) {
323                 ret = (_dispMsgHooks[dm->type].hook)(dm);
324             }
325         }
326 
327         freeDMessage(dm);
328     }
329 
330     if (ret == EXIT_ABORT) {
331         DEBUG2("[DS]: processOutputData ret %d", ret);
332     }
333     return ret;
334 }
335 
parseCommand(int peerId,char * cmd)336 void parseCommand(int peerId, char* cmd)
337 {
338     //DEBUG2("[DS]: parseCommand >%s<", cmd);
339 
340     char *prev, *next;
341 
342     if (cmd == NULL) {
343         return ;
344     }
345 
346     //skip lines starting with \n and \r
347     if (cmd[0] == '\r') {
348         cmd++;
349     }
350     if (cmd[0] == '\n') {
351         cmd++;
352     }
353 
354     // most common case
355     if (!cmd[0]) {
356         return;
357     }
358 
359     // if recieved multiline command - handle line by line and return
360     prev = cmd;
361     next = strchr(cmd, '\r');
362 
363     if (next == NULL) {    // Java client will send +CKEV: 1,1; +CKEV: 1,0
364         next = strchr(cmd, ';');
365     }
366 
367     if (next) {
368         logger(L_DBG, "[DS]: parseCommand multiline");
369 
370         char copy[1024];
371         int len;
372 
373         do {
374             len = next-prev;
375 
376             if (len >= 2) {
377                 memcpy(copy, prev, len);
378                 copy[len] = 0;
379 
380                 // use recursion
381                 parseCommand(peerId, copy);
382             }
383             prev = next+1;
384             next = strchr(prev, '\r');
385             if (next == NULL) { // Java client will send +CKEV: 1,1; +CKEV: 1,0
386                 next = strchr(prev, ';');
387             }
388 
389             /* handle in reader
390             if (next == NULL && getIViewer()) {
391                 next = strchr(prev, '\3');        // end-of-text marker in CommandFusion
392             }*/
393 
394         } while (next) ;
395 
396         // now return
397         return;
398     }
399 
400     logger(L_DBG,"[DS]: -------------------- Command read --------------------");
401     DEBUG2("[DS]: parseCommand >%s<", cmd);
402 
403     if (IS_OK(cmd)) {   // OK - nothing to do
404         return;
405     }
406 
407     if (strncmp(cmd, DEF_AT_CKPD, 8)  == 0 ||  // This is echo of sent message in AT mode; nothing to do
408         strncmp(cmd, DEF_CLDEBUG, 12) == 0) {  // This is debug message from java client; nothing to do
409         return;
410     }
411 
412     // This is keepalive message, handle it internally
413     if (strncmp(cmd, DEF_CLPING, 8) == 0 && useKeepalive()) {
414         setKeepalive();
415         return;
416     }
417 
418     eMessage* em = (eMessage*) malloc(sizeof(eMessage));
419     em->peer  = peerId;
420     em->type  = EM_KEY;
421     em->value = strdup(cmd);
422 
423     DEBUG2("[DS]: Send message from peer %d to executor thread %s", peerId, (char*) em->value);
424     sendToExecutor(em);
425 
426     return;
427 }
428 
429 
hookInitOnce()430 static void hookInitOnce()
431 {
432     if (_initialized == 0) {
433 
434         // setgid
435         if(conf.uid && getuid()==0) {
436             DEBUG2("[DS]: setuid/setgid %d,%d",conf.uid,conf.gid);
437             #ifndef __FreeBSD__
438 	    setgroups(0, NULL);
439 	    #endif
440 	    setgid(conf.gid);
441             setuid(conf.uid);
442         }
443 
444         _initialized++;
445     }
446 }
447 
doDisconnect()448 static int doDisconnect()
449 {
450     logger(L_INF, "[DS]: Got disconnected");
451     //printf("Got disconnected\n");
452 
453     freeBtAddress();
454 
455     if (gotExitSignal) {
456         logger(L_INF, "[DS]: Got signal, exiting");
457         closePort(1);
458         return EXIT_ABORT;
459 
460     }
461     writeToFrontEnd("Disconnected");
462     sendDisconnect();
463     sendEventToExecutor(0, ID_EVT_DISCONNECT);
464 
465     if (disconnectPeers() == 1) {
466         logger(L_INF, "[DS]: Closing the port");
467         closePort(0);
468     }
469 
470     return EXIT_DISCON;
471 }
472 
sendFinalizer()473 void sendFinalizer()
474 {
475     if (needFinalizer() == EXIT_OK) {
476         logger(L_DBG,"[DS]: sendFinalizer()");
477         eMessage* em = (eMessage*) malloc(sizeof(eMessage));
478         em->peer  = PEER_ANY;
479         em->type  = EM_AS_IS;
480         em->value = strdup("End();");
481         sendToExecutor(em);
482     }
483 }
484 
dispatcherCleanup()485 void dispatcherCleanup()
486 {
487     logger(L_DBG,"[DS]: dispatcherCleanup()");
488     freePeers();
489 }
490 
processInputData()491 static int processInputData()
492 {
493     //logger(L_DBG,"[DS]: processInputData()");
494 
495     int retRead = processPeers();
496     //if (retRead != 0) printf("GOT %d\n",retRead);
497 
498     if (retRead == EOF) {
499 
500         logger(L_DBG,"[DS]: processInputData() EOF");
501         return doDisconnect();
502 
503     } else if (retRead > 0) {  // something was pricessed
504 
505         sendFinalizer();
506 
507     } // it is OK if retRead == 0
508 
509     return EXIT_OK;
510 }
511 
doMessageLoop()512 static int doMessageLoop()
513 {
514     int ret = EXIT_DISCON;
515 
516     while (1) {
517 
518         //logger(L_DBG,"[DS]: doMessageLoop LOOP");
519 
520         // read from sockets, etc.
521         ret = processInputData();
522 
523         if (ret == EXIT_ABORT) {
524             logger(L_DBG,"[DS]: doMessageLoop abort on read ");
525             break;
526         } else if (ret == EXIT_DISCON) {
527             logger(L_DBG,"[DS]: doMessageLoop disconnect on read");
528             break;
529         }
530 
531         // Is call still active ? (timeout about 1 seconds, check it inside)
532         if (hasActiveCall()) {
533             handleActiveCall();
534         }
535 
536         ret = processOutputData();
537         if (ret == EXIT_ABORT) {
538             logger(L_DBG,"[DS]: doMessageLoop abort on check queue");
539             break;
540         } else if (ret == EXIT_DISCON) {
541             logger(L_DBG,"[DS]: doMessageLoop disconnect on check queue");
542             break;
543         }
544 
545         // Main loop timer (1/50 of second)
546         usleep(20000);
547     } // while (forever)
548 
549     return ret;
550 }
551 
doConnectionLoop()552 static int doConnectionLoop()
553 {
554     if (setupPeersPre() != 1) {  // Init modem: AT, ATE0, AT+CMER, in server mode waits for connection
555         logger(L_DBG,"[DS]: Init connection error");
556         return EXIT_OK;
557     }
558 
559     logger(L_DBG,"[DS]: Init connection OK");
560 
561     if (connected() == EXIT_OK) {
562         logger(L_DBG,"[DS]: doConnectionLoop connectNotify");
563         connectNotify(0);
564     }
565 
566     dispIsJoinable = BOOL_YES;
567 
568     logger(L_DBG,"[DS]: Start message loop");
569 
570     int ret = doMessageLoop();
571 
572     DEBUG2("[DS]: Stop message loop %d", ret);
573 
574     return ret;
575 
576     // EXIT_DISCON / EXIT_NOK is OK here
577     return EXIT_OK;
578 }
579 
dispatcherRoutine(pointer_t thread)580 pointer_t dispatcherRoutine(pointer_t thread)
581 {
582     int ret = EXIT_OK;
583 
584     logger(L_DBG,"[DS]: start dispatcher thread");
585 
586     // wait init ok event
587     while (1) {
588         ret = processOutputData();
589         if (ret == EXIT_ABORT) {
590             dispatcherCleanup();
591             return NULL;
592         } else if (ret == EXIT_INITOK) {
593             break;
594         }
595         //logger(L_DBG,"[DS]: wait init OK event");
596         usleep(50000);
597     }
598     logger(L_DBG,"[DS]: got init event");
599 
600     int dmn = autoConnect();
601     int rs  = getRetrySecs();
602     strcpy(callerId, "NO CALLER ID");
603 
604     if (definePeers() == EXIT_ABORT) {
605         logger(L_DBG,"[DS]: Incorrect device specification");
606         dispatcherCleanup();
607         sendAbort();
608         return NULL;
609     }
610 
611     while (1) {
612 
613         logger(L_DBG,"[DS]: ************ outer loop **********");
614 
615         if (openPeers() == EXIT_OK) {   // Open device
616 
617             logger(L_DBG,"[DS]: Device open OK");
618 
619             hookInitOnce();
620 
621             dispIsJoinable = BOOL_NO;
622 
623             DEBUG2("[DS]: Start connection loop");
624             int retLoop = doConnectionLoop();
625             DEBUG2("[DS]: Stop connection loop %d", retLoop);
626 
627             if (retLoop == EXIT_ABORT) {
628                 DEBUG2("[DS]: Dispatcher abort");
629                 dispatcherCleanup();
630                 return NULL;
631             }
632 
633         } else {        // open port
634             logger(L_DBG,"[DS]: Device open error");
635         }
636 
637         //printf("Connection closed or lost\n");
638         logger(L_INF, "[DS]: Connection closed or lost");
639 
640         // Can't open port or it closed again
641 
642         int isServer = isServerMode();
643 
644         if (!gotExitSignal &&
645                 (dmn || isServer == EXIT_OK || ret == EXIT_DISCON)) {
646 
647             int timeout;
648 
649             if (isServer == EXIT_OK) {
650 
651                 timeout = 2;    // wait only 2 seconds
652 
653             } else if (ret == EXIT_DISCON) {
654 
655                 timeout = TMPDISCONN_TIME;
656                 ret = EXIT_OK;
657 
658             } else {
659                 timeout = rs;
660             }
661 
662             INFO2("[DS]: Wait %d seconds to connect/open server socket ...", timeout);
663             //printf("Wait %d seconds to connect/open server socket ...\n", timeout);
664 
665             dispIsJoinable = BOOL_NO;
666             sleep(timeout);
667             dispIsJoinable = BOOL_YES;
668 
669         } else {    // Proceed to exit
670             printf("Exiting ...\n");
671             sendAbort();
672             break;
673         }
674     }
675 
676     // Finish all
677     logger(L_INF, "[DS]: Stop dispatcher ...");
678     closePort(1);
679     dispatcherCleanup();
680 
681     return NULL;
682 }
683 
sendToDispatcher(dMessage * buf)684 void sendToDispatcher(dMessage *buf)
685 {
686     if (queuePush(Q_DISP, (void*) buf) == RC_OK) {
687         DEBUG2("send to dispatcher %d", buf->type);
688     }
689 }
690