/* Copyright (C) 2007-2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "remote_call.h"
#include "android/utils/bufprint.h"
#include "android/utils/debug.h"
#include "sysdeps.h"
#include "gsm.h"
#include "android/android.h"
#include "sockets.h"
#include <stdlib.h>
#define DEBUG 1
#if 1
# define D_ACTIVE VERBOSE_CHECK(modem)
#else
# define D_ACTIVE DEBUG
#endif
#if 1
# define S_ACTIVE VERBOSE_CHECK(socket)
#else
# define S_ACTIVE DEBUG
#endif
#if DEBUG
# include <stdio.h>
# define D(...) do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
# define S(...) do { if (S_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
#else
# define D(...) ((void)0)
# define S(...) ((void)0)
#endif
/** By convention, remote numbers are the console ports, i.e. 5554, 5556, etc...
**/
#define REMOTE_NUMBER_BASE 5554
#define REMOTE_NUMBER_MAX 16
#define REMOTE_NUMBER_MAX_CHARS 4
#define REMOTE_CONSOLE_PORT 5554
int
remote_number_from_port( int port )
{
if (port & 1) /* must be even */
return -1;
port = (port - REMOTE_CONSOLE_PORT) >> 1;
if ((unsigned)port >= REMOTE_NUMBER_MAX)
return -1;
return REMOTE_NUMBER_BASE + port*2;
}
int
remote_number_to_port( int number )
{
if (number & 1) /* must be even */
return -1;
number = (number - REMOTE_NUMBER_BASE) >> 1;
if ((unsigned)number >= REMOTE_NUMBER_MAX)
return -1;
return REMOTE_CONSOLE_PORT + number*2;
}
int
remote_number_string_to_port( const char* number )
{
char* end;
long num = strtol( number, &end, 10 );
if (end == NULL || *end || (int)num != num )
return -1;
return remote_number_to_port( (int)num );
}
/** REMOTE CALL OBJECTS
**/
typedef struct RemoteCallRec {
struct RemoteCallRec* next;
struct RemoteCallRec** pref;
RemoteCallType type;
int to_port;
int from_port;
SysChannel channel;
RemoteResultFunc result_func;
void* result_opaque;
char quitting;
/* the output buffer */
char* buff;
int buff_pos;
int buff_len;
int buff_size;
char buff0[32];
} RemoteCallRec, *RemoteCall;
static void
remote_call_done( RemoteCall call )
{
call->pref[0] = call->next;
call->next = NULL;
call->pref = &call->next;
if (call->buff && call->buff != call->buff0) {
free(call->buff);
call->buff = call->buff0;
call->buff_size = (int) sizeof(call->buff0);
}
if ( call->channel ) {
sys_channel_close( call->channel );
call->channel = NULL;
}
call->buff_pos = 0;
call->buff_len = 0;
}
static void
remote_call_free( RemoteCall call )
{
if (call) {
remote_call_done( call );
free(call);
}
}
static void remote_call_event( void* opaque, int events ); /* forward */
static RemoteCall
remote_call_alloc( RemoteCallType type, int to_port, int from_port )
{
RemoteCall rcall = calloc( sizeof(*rcall), 1 );
int from_num = remote_number_from_port(from_port);
if (rcall != NULL) {
char *p, *end;
rcall->pref = &rcall->next;
rcall->type = type;
rcall->to_port = to_port;
rcall->from_port = from_port;
rcall->buff = rcall->buff0;
rcall->buff_size = sizeof(rcall->buff0);
rcall->buff_pos = 0;
p = rcall->buff;
end = p + rcall->buff_size;
switch (type) {
case REMOTE_CALL_DIAL:
p = bufprint(p, end, "gsm call %d\n", from_num );
break;
case REMOTE_CALL_BUSY:
p = bufprint(p, end, "gsm busy %d\n", from_num);
break;
case REMOTE_CALL_HOLD:
p = bufprint(p, end, "gsm hold %d\n", from_num);
break;
case REMOTE_CALL_ACCEPT:
p = bufprint(p, end, "gsm accept %d\n", from_num);
break;
case REMOTE_CALL_HANGUP:
p = bufprint(p, end, "gsm cancel %d\n", from_num );
break;
default:
;
}
if (p >= end) {
D("%s: buffer too short\n", __FUNCTION__ );
remote_call_free(rcall);
return NULL;
}
rcall->buff_len = p - rcall->buff;
rcall->channel = sys_channel_create_tcp_client( "localhost", to_port );
if (rcall->channel == NULL) {
D("%s: could not create channel to port %d\n", __FUNCTION__, to_port);
remote_call_free(rcall);
return NULL;
}
sys_channel_on( rcall->channel, SYS_EVENT_WRITE, remote_call_event, rcall );
}
return rcall;
}
static int
remote_call_set_sms_pdu( RemoteCall call,
SmsPDU pdu )
{
char *p, *end;
int msg2len;
msg2len = 32 + smspdu_to_hex( pdu, NULL, 0 );
if (msg2len > call->buff_size) {
char* old_buff = call->buff == call->buff0 ? NULL : call->buff;
char* new_buff = realloc( old_buff, msg2len );
if (new_buff == NULL) {
D("%s: not enough memory to alloc %d bytes", __FUNCTION__, msg2len);
return -1;
}
call->buff = new_buff;
call->buff_size = msg2len;
}
p = call->buff;
end = p + call->buff_size;
p = bufprint(p, end, "sms pdu ");
p += smspdu_to_hex( pdu, p, end-p );
*p++ = '\n';
*p = 0;
call->buff_len = p - call->buff;
call->buff_pos = 0;
return 0;
}
static void
remote_call_add( RemoteCall call,
RemoteCall *plist )
{
RemoteCall first = *plist;
call->next = first;
call->pref = plist;
if (first)
first->pref = &call->next;
}
static void
remote_call_event( void* opaque, int events )
{
RemoteCall call = opaque;
S("%s: called for call (%d,%d), events=%02x\n", __FUNCTION__,
call->from_port, call->to_port, events);
if (events & SYS_EVENT_READ) {
/* simply drain the channel */
char temp[32];
int n = sys_channel_read( call->channel, temp, sizeof(temp) );
if (n <= 0) {
/* remote emulator probably quitted */
//S("%s: emulator %d quitted with %d: %s\n", __FUNCTION__, call->to_port, errno, errno_str);
remote_call_free( call );
return;
}
}
if (events & SYS_EVENT_WRITE) {
int n;
if (S_ACTIVE) {
int nn;
S("%s: call (%d,%d) sending %d bytes '", __FUNCTION__,
call->from_port, call->to_port, call->buff_len - call->buff_pos );
for (nn = call->buff_pos; nn < call->buff_len; nn++) {
int c = call->buff[nn];
if (c < 32) {
if (c == '\n')
S("\\n");
else if (c == '\t')
S("\\t");
else if (c == '\r')
S("\\r");
else
S("\\x%02x", c);
} else
S("%c", c);
}
S("'\n");
}
n = sys_channel_write( call->channel,
call->buff + call->buff_pos,
call->buff_len - call->buff_pos );
if (n <= 0) {
/* remote emulator probably quitted */
S("%s: emulator %d quitted unexpectedly with error %d: %s\n",
__FUNCTION__, call->to_port, errno, errno_str);
if (call->result_func)
call->result_func( call->result_opaque, 0 );
remote_call_free( call );
return;
}
call->buff_pos += n;
if (call->buff_pos >= call->buff_len) {
/* cool, we sent everything */
S("%s: finished sending data to %d\n", __FUNCTION__, call->to_port);
if (!call->quitting) {
call->quitting = 1;
sprintf( call->buff, "quit\n" );
call->buff_len = strlen(call->buff);
call->buff_pos = 0;
} else {
call->quitting = 0;
if (call->result_func)
call->result_func( call->result_opaque, 1 );
sys_channel_on( call->channel, SYS_EVENT_READ, remote_call_event, call );
}
}
}
}
static RemoteCall _the_remote_calls;
#if 0
static int
remote_from_number( const char* from )
{
char* end;
long num = strtol( from, &end, 10 );
if (end == NULL || *end)
return -1;
if ((unsigned)(num - REMOTE_NUMBER_BASE) >= REMOTE_NUMBER_MAX)
return -1;
return (int) num;
}
#endif
static RemoteCall
remote_call_generic( RemoteCallType type, const char* to_number, int from_port )
{
int to_port = remote_number_string_to_port(to_number);
RemoteCall call;
if ( remote_number_from_port(from_port) < 0 ) {
D("%s: from_port value %d is not valid", __FUNCTION__, from_port);
return NULL;
}
if ( to_port < 0 ) {
D("%s: phone number '%s' is not decimal or remote", __FUNCTION__, to_number);
return NULL;
}
if (to_port == from_port) {
D("%s: trying to call self\n", __FUNCTION__);
return NULL;
}
call = remote_call_alloc( type, to_port, from_port );
if (call == NULL) {
return NULL;
}
remote_call_add( call, &_the_remote_calls );
D("%s: adding new call from port %d to port %d\n", __FUNCTION__, from_port, to_port);
return call;
}
int
remote_call_dial( const char* number,
int from,
RemoteResultFunc result_func,
void* result_opaque )
{
RemoteCall call = remote_call_generic( REMOTE_CALL_DIAL, number, from );
if (call != NULL) {
call->result_func = result_func;
call->result_opaque = result_opaque;
}
return call ? 0 : -1;
}
void
remote_call_other( const char* to_number, int from_port, RemoteCallType type )
{
remote_call_generic( type, to_number, from_port );
}
/* call this function to send a SMS to a remote emulator */
int
remote_call_sms( const char* number,
int from,
SmsPDU pdu )
{
RemoteCall call = remote_call_generic( REMOTE_CALL_SMS, number, from );
if (call == NULL)
return -1;
if (call != NULL) {
if ( remote_call_set_sms_pdu( call, pdu ) < 0 ) {
remote_call_free(call);
return -1;
}
}
return call ? 0 : -1;
}
void
remote_call_cancel( const char* to_number, int from_port )
{
remote_call_generic( REMOTE_CALL_HANGUP, to_number, from_port );
}