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