diff --git a/constants.h b/constants.h index 440615e3..fa9e5b36 100644 --- a/constants.h +++ b/constants.h @@ -346,13 +346,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define VOMP_CODEC_CODECSISUPPORT 0xfe #define VOMP_CODEC_CHANGEYOURCODECTO 0xff -#define VOMP_STATE_NOCALL 1 -#define VOMP_STATE_CALLPREP 2 -#define VOMP_STATE_RINGINGOUT 3 -#define VOMP_STATE_RINGINGIN 4 -#define VOMP_STATE_INCALL 5 -#define VOMP_STATE_CALLENDED 6 - /* in milliseconds of inactivity */ #define VOMP_CALL_TIMEOUT 120000 #define VOMP_CALL_STATUS_INTERVAL 1000 diff --git a/log.c b/log.c index 923a0653..053d844c 100644 --- a/log.c +++ b/log.c @@ -175,8 +175,7 @@ static int _log_prepare(int level, const char *file, unsigned int line, const ch return 1; } -static void _log_finish(int level) -{ +static void _log_internal(int level, struct strbuf *buf){ #ifdef ANDROID int alevel = ANDROID_LOG_UNKNOWN; switch (level) { @@ -186,17 +185,28 @@ static void _log_finish(int level) case LOG_LEVEL_WARN: alevel = ANDROID_LOG_WARN; break; case LOG_LEVEL_DEBUG: alevel = ANDROID_LOG_DEBUG; break; } - __android_log_print(alevel, "servald", "%s", strbuf_str(&logbuf)); - strbuf_reset(&logbuf); + __android_log_print(alevel, "servald", "%s", strbuf_str(buf)); + strbuf_reset(buf); #else FILE *logf = open_logging(); if (logf) { - fprintf(logf, "%s\n%s", strbuf_str(&logbuf), strbuf_overrun(&logbuf) ? "LOG OVERRUN\n" : ""); - strbuf_reset(&logbuf); + fprintf(logf, "%s\n%s", strbuf_str(buf), strbuf_overrun(buf) ? "LOG OVERRUN\n" : ""); + strbuf_reset(buf); } #endif } +void (*_log_implementation)(int level, struct strbuf *buf)=_log_internal; + +static void _log_finish(int level){ + if(_log_implementation) + _log_implementation(level, &logbuf); +} + +void set_log_implementation(void (*log_function)(int level, struct strbuf *buf)){ + _log_implementation=log_function; +} + void logArgv(int level, const char *file, unsigned int line, const char *function, const char *label, int argc, const char *const *argv) { if (_log_prepare(level, file, line, function)) { diff --git a/log.h b/log.h index 8d0fff1d..3b7d2573 100644 --- a/log.h +++ b/log.h @@ -61,6 +61,8 @@ extern unsigned int debug; #define LOG_LEVEL_ERROR (3) #define LOG_LEVEL_FATAL (4) +struct strbuf; + void set_logging(FILE *f); FILE *open_logging(); void close_logging(); @@ -74,6 +76,7 @@ char *toprint(char *dstStr, ssize_t dstBufSiz, const char *srcBuf, size_t srcByt size_t toprint_strlen(const char *srcBuf, size_t srcBytes); ssize_t get_self_executable_path(char *buf, size_t len); int log_backtrace(const char *file, unsigned int line, const char *function); +void set_log_implementation(void (*log_function)(int level, struct strbuf *buf)); #define alloca_toprint(dstlen,buf,len) toprint((char *)alloca((dstlen) == -1 ? toprint_strlen((buf),(len)) + 1 : (dstlen)), (dstlen), (buf), (len)) diff --git a/monitor-client.c b/monitor-client.c index ac4772e4..369438e7 100644 --- a/monitor-client.c +++ b/monitor-client.c @@ -16,99 +16,261 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "monitor-client.h" +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include +#include +#include +#ifdef WIN32 +#include "win32/win32.h" +#endif +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include + +#include "constants.h" +#include "conf.h" +#include "log.h" +#include "str.h" + +#include "monitor-client.h" +#include + +#define STATE_INIT 0 +#define STATE_DATA 1 +#define STATE_READY 2 + +#define MONITOR_CLIENT_BUFFER_SIZE 8192 +#define MAX_ARGS 32 + +struct monitor_state { + char *cmd; + int argc; + char *argv[MAX_ARGS]; + unsigned char *data; + int dataBytes; + int cmdBytes; + + int state; + unsigned char buffer[MONITOR_CLIENT_BUFFER_SIZE]; + int bufferBytes; +}; + +// FIX ME, COPY-PASTA from monitor.c +int monitor_socket_name(struct sockaddr_un *name){ + int len; +#ifdef linux + /* Use abstract namespace as Android has no writable FS which supports sockets. + Abstract namespace is just plain better, anyway, as no dead files end up + hanging around. */ + name.sun_path[0]=0; + /* XXX: 104 comes from OSX sys/un.h - no #define (note Linux has UNIX_PATH_MAX and it's 108(!)) */ + snprintf(&name->sun_path[1],104-2, + confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); + /* Doesn't include trailing nul */ + len = 1+strlen(&name->sun_path[1]) + sizeof(name->sun_family); +#else + snprintf(name->sun_path,104-1,"%s/%s", + serval_instancepath(), + confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); + /* Includes trailing nul */ + len = 1+strlen(name->sun_path) + sizeof(name->sun_family); +#endif + return len; +} /* Open monitor interface abstract domain named socket */ -int monitor_client_open() +int monitor_client_open(struct monitor_state **res) { int fd; struct sockaddr_un addr; if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); - exit(-1); + return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - /* XXX - On non-linux systems, we need to use a regular named socket */ - addr.sun_path[0]=0; - snprintf(&addr.sun_path[1],100, - "%s", confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); - int len = 1+strlen(&addr.sun_path[1]) + sizeof(addr.sun_family); - char *p=(char *)&addr; - printf("last char='%c' %02x\n",p[len-1],p[len-1]); + int len = monitor_socket_name(&addr); + + INFOF("Attempting to connect to %s", &addr.sun_path[1]); if (connect(fd, (struct sockaddr*)&addr, len) == -1) { perror("connect"); - exit(-1); + return -1; } + + *res = (struct monitor_state*)malloc(sizeof(struct monitor_state)); return fd; } -int monitor_client_writeline(int fd,char *msg) -{ - return write(fd,msg,strlen(msg)); +int monitor_client_close(int fd, struct monitor_state *res){ + free(res); + close(fd); + return 0; } -int monitor_client_writeline_and_data(int fd,char *msg,unsigned char *data,int bytes) +int monitor_client_writeline(int fd,char *fmt, ...) { - int maxlen=strlen(msg)+20+bytes; + char msg[512]; + int n; + va_list ap; + + if (fd<0) + return -1; + + va_start(ap, fmt); + n=vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + return write(fd,msg,n); +} + +int monitor_client_writeline_and_data(int fd,unsigned char *data,int bytes,char *fmt,...) +{ + int maxlen=512+bytes; char out[maxlen]; - snprintf(out,maxlen,"*%d:%s\n",bytes,msg); - int len=strlen(msg); - bcopy(&data[0],&msg[len],bytes); - len+=bytes; - return write(fd,msg,len); + va_list ap; + int n; + + if (fd<0) + return -1; + + n=snprintf(out,maxlen-bytes,"*%d:",bytes); + + va_start(ap, fmt); + n+=vsnprintf(out+n, maxlen-bytes-n, fmt, ap); + va_end(ap); + + bcopy(data,out+n,bytes); + n+=bytes; + return write(fd,out,n); } -static unsigned char buffer[MONITOR_CLIENT_BUFFER_SIZE]; -static int buffer_bytes=0; - -int monitor_client_readline(int fd, monitor_result **res) +int monitor_client_read(int fd, struct monitor_state *res, struct monitor_command_handler *handlers, int handler_count) { - monitor_result *r=*res; - /* Read any available bytes */ - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL) | O_NONBLOCK); - int bytesRead=read(fd,&buffer[buffer_bytes], - MONITOR_CLIENT_BUFFER_SIZE-buffer_bytes); - if (bytesRead>0) buffer_bytes+=bytesRead; - - /* Now see if we have a full line of results to return */ - int i; - for(i=0;ibuffer_bytes) - { - /* We don't yet have enough bytes to return */ - return -1; - } - /* Copy data section */ - r->dataBytes=dataBytes; - bcopy(&buffer[i],&r->data[0],dataBytes); - /* copy line from after the *len: part, and without the - new line. Then null-terminate it */ - bcopy(&buffer[lineStart],&r->line[0],i-lineStart); - r->line[i-lineStart]=0; - /* remember to discard the data section from the buffer */ - i+=dataBytes; - } else { - /* no data section */ - r->dataBytes=0; - } - /* shuffle buffer down */ - bcopy(&buffer[i],&buffer[0],buffer_bytes-i); - buffer_bytes-=i; - return 0; + int oldOffset = res->bufferBytes; + + if (res->bufferBytes==0) + res->cmd = (char *)res->buffer; + + int bytesRead=read(fd, res->buffer + oldOffset, MONITOR_CLIENT_BUFFER_SIZE - oldOffset); + if (bytesRead<1){ + switch(errno) { + case EINTR: + case ENOTRECOVERABLE: + /* transient errors */ + WHY_perror("read"); + case EAGAIN: + return 0; } - /* no end of line, so need to read more */ - return -1; + WHY_perror("read"); + return -1; + } + + res->bufferBytes+=bytesRead; + +again: + // wait until we have the whole command line + if (res->state == STATE_INIT){ + int i; + + for(i=oldOffset;ibufferBytes;i++){ + if (res->buffer[i]=='\n'){ + // skip any leading \n's + if ((char*)(res->buffer+i) == res->cmd){ + res->cmd++; + continue; + } + + res->buffer[i]=0; + res->dataBytes = 0; + res->cmdBytes = i + 1; + if (*res->cmd=='*'){ + res->cmd++; + for (; isdigit(*res->cmd); ++res->cmd) + res->dataBytes = res->dataBytes * 10 + *res->cmd - '0'; + if (*res->cmd==':') + res->cmd++; + } + + // find all arguments, initialise argc / argv && null terminate strings + { + char *p=res->cmd; + res->argc=0; + while (*p){ + if (*p==':'){ + *p=0; + res->argv[res->argc]=p+1; + res->argc++; + } + p++; + } + } + + if (res->dataBytes){ + res->data=(unsigned char *)&res->buffer[i+1]; + res->state = STATE_DATA; + }else{ + res->data=NULL; + res->state = STATE_READY; + } + break; + } + } + } + + // make sure all the data has arrived + if (res->state == STATE_DATA){ + if (res->bufferBytes >= res->dataBytes + res->cmdBytes){ + res->state = STATE_READY; + } + } + + // ok, now we can try to process the command + if (res->state == STATE_READY){ + int handled=0; + int i; + // call all handlers that match (yes there might be more than one) + for (i=0;icmd is terminated with a '\n', + and there shouldn't be a '\n' in h->command, + this shouldn't run past the end of the buffer */ + if (handlers[i].handler && (!handlers[i].command || strcase_startswith(res->cmd,handlers[i].command, NULL))){ + if (handlers[i].handler(res->cmd, res->argc, res->argv, res->data, res->dataBytes, handlers[i].context)>0) + handled=1; + } + } + + if (!handled){ + INFOF("Event \"%s\" was not handled", res->cmd); + } + + // shuffle any unprocessed bytes + int remaining = res->bufferBytes - (res->dataBytes + res->cmdBytes); + if (remaining>0){ + bcopy(res->buffer+res->dataBytes + res->cmdBytes,res->buffer,remaining); + } + res->bufferBytes=remaining; + res->cmdBytes=0; + res->dataBytes=0; + res->state = STATE_INIT; + res->cmd = (char *)res->buffer; + oldOffset = 0; + goto again; + } + + + return 0; } diff --git a/monitor-client.h b/monitor-client.h index d0fd791e..2e9601fe 100644 --- a/monitor-client.h +++ b/monitor-client.h @@ -16,34 +16,16 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include -#include -#ifdef HAVE_STRINGS_H -#include -#endif -#include -#include -#include +struct monitor_state; -#ifdef WIN32 -#include "win32/win32.h" -#endif -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#include -#include +struct monitor_command_handler{ + char *command; + void *context; + int (*handler)(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context); +}; -#include "constants.h" -#include "conf.h" -#include "log.h" - -#define MONITOR_CLIENT_BUFFER_SIZE 8192 -typedef struct monitor_result { - char line[MONITOR_CLIENT_BUFFER_SIZE]; - unsigned char data[MONITOR_CLIENT_BUFFER_SIZE]; - int dataBytes; -} monitor_result; +int monitor_client_open(struct monitor_state **res); +int monitor_client_writeline(int fd,char *fmt, ...); +int monitor_client_writeline_and_data(int fd,unsigned char *data,int bytes,char *fmt,...); +int monitor_client_read(int fd, struct monitor_state *res, struct monitor_command_handler *handlers, int handler_count); +int monitor_client_close(int fd, struct monitor_state *res); diff --git a/monitor.c b/monitor.c index 43b9abfe..0adb96d0 100644 --- a/monitor.c +++ b/monitor.c @@ -60,6 +60,28 @@ struct sched_ent named_socket; struct profile_total named_stats; struct profile_total client_stats; +int monitor_socket_name(struct sockaddr_un *name){ + int len; +#ifdef linux + /* Use abstract namespace as Android has no writable FS which supports sockets. + Abstract namespace is just plain better, anyway, as no dead files end up + hanging around. */ + name->sun_path[0]=0; + /* XXX: 104 comes from OSX sys/un.h - no #define (note Linux has UNIX_PATH_MAX and it's 108(!)) */ + snprintf(&name->sun_path[1],104-2, + confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); + /* Doesn't include trailing nul */ + len = 1+strlen(&name->sun_path[1]) + sizeof(name->sun_family); +#else + snprintf(name->sun_path,104-1,"%s/%s", + serval_instancepath(), + confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); + /* Includes trailing nul */ + len = 1+strlen(name->sun_path) + sizeof(name->sun_family); +#endif + return len; +} + int monitor_setup_sockets() { struct sockaddr_un name; @@ -74,23 +96,9 @@ int monitor_setup_sockets() goto error; } -#ifdef linux - /* Use abstract namespace as Android has no writable FS which supports sockets. - Abstract namespace is just plain better, anyway, as no dead files end up - hanging around. */ - name.sun_path[0]=0; - /* XXX: 104 comes from OSX sys/un.h - no #define (note Linux has UNIX_PATH_MAX and it's 108(!)) */ - snprintf(&name.sun_path[1],104-2, - confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); - /* Doesn't include trailing nul */ - len = 1+strlen(&name.sun_path[1]) + sizeof(name.sun_family); -#else - snprintf(name.sun_path,104-1,"%s/%s", - serval_instancepath(), - confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME)); + len = monitor_socket_name(&name); +#ifndef linux unlink(name.sun_path); - /* Includes trailing nul */ - len = 1+strlen(name.sun_path) + sizeof(name.sun_family); #endif if(bind(sock, (struct sockaddr *)&name, len)==-1) { @@ -176,7 +184,7 @@ void monitor_client_close(struct monitor_context *c){ void monitor_client_poll(struct sched_ent *alarm) { - /* Read from any open connections */ + /* Read available data from a monitor socket */ struct monitor_context *c=(struct monitor_context *)alarm; errno=0; int bytes; @@ -288,9 +296,12 @@ static void monitor_new_client(int s) { #endif if (otheruid != getuid()) { - WHYF("monitor.socket client has wrong uid (%d versus %d)", otheruid,getuid()); - write_str(s, "\nCLOSE:Incorrect UID\n"); - goto error; + int allowed_id = confValueGetInt64("monitor.uid",-1); + if (otheruid != allowed_id){ + WHYF("monitor.socket client has wrong uid (%d versus %d)", otheruid,getuid()); + write_str(s, "\nCLOSE:Incorrect UID\n"); + goto error; + } } if (monitor_socket_count >= MAX_MONITOR_SOCKETS ||monitor_socket_count < 0) { @@ -333,7 +344,6 @@ int monitor_process_command(struct monitor_context *c) } char msg[1024]; - int flag; if (cmd[0]=='*') { /* command with content */ @@ -346,7 +356,7 @@ int monitor_process_command(struct monitor_context *c) c->data_offset=0; c->sample_codec=-1; - if (sscanf(cmd,"AUDIO:%x:%d", + if (sscanf(cmd,"AUDIO %x %d", &callSessionToken,&sampleType)==2) { /* Start getting sample */ @@ -356,18 +366,18 @@ int monitor_process_command(struct monitor_context *c) } } } - else if (!strcasecmp(cmd,"monitor vomp")) + else if (strcase_startswith(cmd,"monitor vomp",NULL)) // TODO add supported codec list argument c->flags|=MONITOR_VOMP; - else if (!strcasecmp(cmd,"ignore vomp")) + else if (strcase_startswith(cmd,"ignore vomp",NULL)) c->flags&=~MONITOR_VOMP; - else if (!strcasecmp(cmd,"monitor rhizome")) + else if (strcase_startswith(cmd,"monitor rhizome", NULL)) c->flags|=MONITOR_RHIZOME; - else if (!strcasecmp(cmd,"ignore rhizome")) + else if (strcase_startswith(cmd,"ignore rhizome", NULL)) c->flags&=~MONITOR_RHIZOME; - else if (!strcasecmp(cmd,"monitor peers")) + else if (strcase_startswith(cmd,"monitor peers", NULL)) c->flags|=MONITOR_PEERS; - else if (!strcasecmp(cmd,"ignore peers")) + else if (strcase_startswith(cmd,"ignore peers", NULL)) c->flags&=~MONITOR_PEERS; else if (sscanf(cmd,"call %s %s %s",sid,localDid,remoteDid)==3) { DEBUG("here"); @@ -490,18 +500,12 @@ int monitor_announce_bundle(rhizome_manifest *m) int monitor_announce_peer(const unsigned char *sid) { - char msg[1024]; - int n = snprintf(msg, sizeof msg, "\nNEWPEER:%s\n", alloca_tohex_sid(sid)); - monitor_tell_clients(msg, n, MONITOR_PEERS); - return 0; + return monitor_tell_formatted(MONITOR_PEERS, "\nNEWPEER:%s\n", alloca_tohex_sid(sid)); } int monitor_announce_unreachable_peer(const unsigned char *sid) { - char msg[1024]; - int n = snprintf(msg, sizeof msg, "\nOLDPEER:%s\n", alloca_tohex_sid(sid)); - monitor_tell_clients(msg, n, MONITOR_PEERS); - return 0; + return monitor_tell_formatted(MONITOR_PEERS, "\nOLDPEER:%s\n", alloca_tohex_sid(sid)); } // test if any monitor clients are interested in a particular type of event @@ -532,3 +536,15 @@ int monitor_tell_clients(char *msg, int msglen, int mask) } RETURN(0); } + +int monitor_tell_formatted(int mask, char *fmt, ...){ + char msg[1024]; + int n; + va_list ap; + + va_start(ap, fmt); + n=vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + monitor_tell_clients(msg, n, mask); + return 0; +} diff --git a/serval.h b/serval.h index 6cc802b1..3ca4b176 100644 --- a/serval.h +++ b/serval.h @@ -1092,6 +1092,7 @@ int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax); int monitor_announce_peer(const unsigned char *sid); int monitor_announce_unreachable_peer(const unsigned char *sid); int monitor_tell_clients(char *msg, int msglen, int mask); +int monitor_tell_formatted(int mask, char *fmt, ...); int monitor_client_interested(int mask); extern int monitor_socket_count; diff --git a/vomp.c b/vomp.c index e1d07d06..5672ed28 100644 --- a/vomp.c +++ b/vomp.c @@ -34,26 +34,40 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # incoming command from monitor client $ outgoing monitor status <> vomp packet with state change sent across the network - + Monitor Init - # MONITOR VOMP + # MONITOR VOMP [supported codec list] Dialing - # CALL [sid] [myDid] [TheirDid] [token] + // client requests an outgoing call + # CALL [sid] [myDid] [TheirDid] > CALLPREP + codecs - $ CALLTO [token] + // let the client know what token we are going to use for the remainder of the call + $ CALLTO [token] [mySid] [myDid] [TheirSid] [TheirDid] + // allocate a session number and tell them our codecs, + // but we don't need to do anything else yet, + // this might be a replay attack < NOCALL + codecs + // Ok, we have a network path, lets try to establish the call > RINGOUT - $ CALLFROM [token] [sid] [myDid] [TheirDid] + // (Note that if both parties are trying to dial each other, + // the call should jump straight to INCALL) + // inform client about the call request + $ CALLFROM [token] [mySid] [myDid] [TheirSid] [TheirDid] + // Note that we may need to wait for other external processes + // before a phone is actually ringing # RING [token] - < RINGIN (they start ringing) + < RINGIN + // All good, there's a phone out there ringing, you can indicate that to the user $ RINGING [token] Answering # PICKUP [token] < INCALL + // The client can now start sending audio > INCALL $ INCALL [token] + // The client can now start sending audio $ INCALL [token] Tell any clients that the call hasn't timed out yet @@ -68,7 +82,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $ HANGUP [token] */ - +// ideally these id's should only be used on the network, with monitor events to inform clients of state changes +#define VOMP_STATE_NOCALL 1 +#define VOMP_STATE_CALLPREP 2 +#define VOMP_STATE_RINGINGOUT 3 +#define VOMP_STATE_RINGINGIN 4 +#define VOMP_STATE_INCALL 5 +#define VOMP_STATE_CALLENDED 6 struct vomp_call_state { struct sched_ent alarm; @@ -147,9 +167,7 @@ int vomp_generate_session_id() struct vomp_call_state *vomp_create_call(unsigned char *remote_sid, unsigned char *local_sid, unsigned int remote_session, - unsigned int local_session, - int remote_state, - int local_state) + unsigned int local_session) { int i; if (!local_session) @@ -164,8 +182,8 @@ struct vomp_call_state *vomp_create_call(unsigned char *remote_sid, bcopy(remote_sid,call->remote.sid,SID_SIZE); call->local.session=local_session; call->remote.session=remote_session; - call->local.state=local_state; - call->remote.state=remote_state; + call->local.state=VOMP_STATE_NOCALL; + call->remote.state=VOMP_STATE_NOCALL; call->last_sent_status=-1; call->create_time=gettime_ms(); call->last_activity=call->create_time; @@ -245,7 +263,7 @@ struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid, /* Only create a call record if either party is in CALLPREP state */ if (sender_state==VOMP_STATE_CALLPREP || recvr_state==VOMP_STATE_CALLPREP) - return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session, VOMP_STATE_NOCALL, VOMP_STATE_NOCALL); + return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session); return NULL; } @@ -448,10 +466,9 @@ int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned i Put newline at start of these so that receiving data in command mode doesn't confuse the parser. */ int msglen = snprintf(msg, 1024, - "\n*%d:AUDIOPACKET:%06x:%06x:%d:%d:%d:%d:%d\n", + "\n*%d:AUDIOPACKET:%x:%d:%d:%d\n", sample_bytes, - call->local.session,call->remote.session, - call->local.state,call->remote.state, + call->local.session, audio_codec, start_time, end_time); bcopy(audio, &msg[msglen], sample_bytes); @@ -461,6 +478,54 @@ int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned i return 0; } +// update local state and notify interested clients with the correct message +int vomp_update_local_state(struct vomp_call_state *call, int new_state){ + if (call->local.state>=new_state) + return 0; + + switch(new_state){ + case VOMP_STATE_CALLPREP: + // tell client our session id. + monitor_tell_formatted(MONITOR_VOMP, "\nCALLTO:%x:%s:%s:%s:%s\n", + call->local.session, + alloca_tohex_sid(call->local.sid), call->local.did, + alloca_tohex_sid(call->remote.sid), call->remote.did); + break; + case VOMP_STATE_CALLENDED: + monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%x\n", call->local.session); + break; + } + + call->local.state=new_state; + return 0; +} + +// update remote state and notify interested clients with the correct message +int vomp_update_remote_state(struct vomp_call_state *call, int new_state){ + if (call->remote.state>=new_state) + return 0; + + switch(new_state){ + case VOMP_STATE_RINGINGOUT: + monitor_tell_formatted(MONITOR_VOMP, "\nCALLFROM:%x:%s:%s:%s:%s\n", + call->local.session, + alloca_tohex_sid(call->local.sid), call->local.did, + alloca_tohex_sid(call->remote.sid), call->remote.did); + break; + case VOMP_STATE_RINGINGIN: + monitor_tell_formatted(MONITOR_VOMP, "\nRINGING:%x\n", call->local.session); + break; + case VOMP_STATE_INCALL: + if (call->remote.state==VOMP_STATE_RINGINGIN){ + monitor_tell_formatted(MONITOR_VOMP, "\nANSWERED:%x\n", call->local.session); + } + break; + } + + call->remote.state=new_state; + return 0; +} + // send call state updates if required. int vomp_update(struct vomp_call_state *call) { @@ -568,7 +633,7 @@ int vomp_ringing(struct vomp_call_state *call){ if (debug & DEBUG_VOMP) DEBUGF("RING RING!"); if (call->local.stateremote.state==VOMP_STATE_RINGINGOUT){ - call->local.state=VOMP_STATE_RINGINGIN; + vomp_update_local_state(call, VOMP_STATE_RINGINGIN); vomp_update(call); } } @@ -584,8 +649,7 @@ int vomp_call_destroy(struct vomp_call_state *call) DEBUGF("Destroying call %s <--> %s", call->local.did,call->remote.did); /* tell everyone the call has died */ - call->local.state=VOMP_STATE_CALLENDED; call->remote.state=VOMP_STATE_CALLENDED; - + vomp_update_local_state(call, VOMP_STATE_CALLENDED); vomp_update(call); /* now release the call structure */ @@ -621,11 +685,8 @@ int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, char *local_d remote_sid, local_sid, 0, - 0, - VOMP_STATE_NOCALL, - VOMP_STATE_CALLPREP - ); - + 0); + vomp_update_local_state(call, VOMP_STATE_CALLPREP); // remember that we initiated this call, not the other party call->initiated_call = 1; @@ -643,7 +704,7 @@ int vomp_pickup(struct vomp_call_state *call) DEBUG("Picking up"); if (call->local.state!=VOMP_STATE_RINGINGIN) return WHY("Call is not ringing"); - call->local.state=VOMP_STATE_INCALL; + vomp_update_local_state(call, VOMP_STATE_INCALL); call->create_time=gettime_ms(); /* state machine does job of starting audio stream, just tell everyone about the changed state. */ @@ -658,7 +719,7 @@ int vomp_hangup(struct vomp_call_state *call) if (debug & DEBUG_VOMP) DEBUG("Hanging up"); if (call->local.state==VOMP_STATE_INCALL) vomp_call_stop_audio(call); - call->local.state=VOMP_STATE_CALLENDED; + vomp_update_local_state(call, VOMP_STATE_CALLENDED); vomp_update(call); } return 0; @@ -947,6 +1008,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) if (!recvr_session && (debug & DEBUG_VOMP)) DEBUG("recvr_session==0, created call"); + recvr_state = call->local.state; // TODO ignore state changes if sequence is stale? // TODO ignore state changes that seem to go backwards? @@ -954,17 +1016,17 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) if ((!vomp_interested_usock_count) &&(!monitor_socket_count) &&(!monitor_client_interested(MONITOR_VOMP))) - { + { /* No registered listener, so we cannot answer the call, so just reject it. */ - if (debug & DEBUG_VOMP) - DEBUGF("Rejecting call due to lack of a listener: states=%d,%d", call->local.state,sender_state); + if (debug & DEBUG_VOMP) + DEBUGF("Rejecting call due to lack of a listener: states=%d,%d", recvr_state, sender_state); - call->local.state=VOMP_STATE_CALLENDED; + recvr_state=VOMP_STATE_CALLENDED; /* now let the state machine progress to destroy the call */ } - if (call->local.state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){ + if (recvr_state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){ // the other party should have given us their list of supported codecs vomp_extract_remote_codec_list(call,mdp); } @@ -974,7 +1036,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) so we must also move to CALLENDED no matter what state we were in */ if (call->audio_started) vomp_call_stop_audio(call); - call->local.state=VOMP_STATE_CALLENDED; + recvr_state=VOMP_STATE_CALLENDED; } /* Consider states: our actual state, sender state, what the sender thinks @@ -982,7 +1044,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) breaks down to what we think our state is, and what they think their state is. That leaves us with just 6X6=36 cases. */ - int combined_state=call->local.state<<3 | sender_state; + int combined_state=recvr_state<<3 | sender_state; switch(combined_state) { case (VOMP_STATE_NOCALL<<3)|VOMP_STATE_CALLPREP: @@ -1000,7 +1062,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) if (call->initiated_call) // hey, quit it, we were trying to call you. - call->local.state=VOMP_STATE_CALLENDED; + recvr_state=VOMP_STATE_CALLENDED; else{ // Don't automatically transition to RINGIN, wait for a client to tell us when. } @@ -1016,9 +1078,9 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) */ if (call->initiated_call){ // TODO fail the call if we can't agree on codec's - call->local.state=VOMP_STATE_RINGINGOUT; + recvr_state=VOMP_STATE_RINGINGOUT; }else{ - call->local.state=VOMP_STATE_CALLENDED; + recvr_state=VOMP_STATE_CALLENDED; } break; @@ -1035,7 +1097,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_RINGINGOUT: /* Woah, we're trying to dial each other?? That must have been well timed. Jump to INCALL and start audio */ - call->local.state=VOMP_STATE_INCALL; + recvr_state=VOMP_STATE_INCALL; // reset create time when call is established call->create_time=gettime_ms(); break; @@ -1047,7 +1109,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_INCALL: /* They have answered, we can jump to incall as well */ - call->local.state=VOMP_STATE_INCALL; + recvr_state=VOMP_STATE_INCALL; // reset create time when call is established call->create_time=gettime_ms(); // Fall through @@ -1073,11 +1135,14 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) Any state not explicitly listed above is considered invalid and possibly stale, the packet will be completely ignored. */ + WHYF("Ignoring invalid call state %d.%d",sender_state,recvr_state); return 0; } call->remote.sequence=sender_seq; - call->remote.state=sender_state; + + vomp_update_remote_state(call, sender_state); + vomp_update_local_state(call, recvr_state); call->last_activity=gettime_ms(); // TODO if we hear a stale echo of our state should we force another outgoing packet now? @@ -1086,8 +1151,8 @@ int vomp_mdp_received(overlay_mdp_frame *mdp) /* send an update to the call status if required */ vomp_update(call); - if (call->remote.state==VOMP_STATE_CALLENDED - &&call->local.state==VOMP_STATE_CALLENDED) + if (sender_state==VOMP_STATE_CALLENDED + &&recvr_state==VOMP_STATE_CALLENDED) return vomp_call_destroy(call); } return 0; @@ -1512,22 +1577,10 @@ static void vomp_process_tick(struct sched_ent *alarm) slots getting full of cruft. */ vomp_call_destroy(call); return; - } else if (call->last_activity+VOMP_CALL_TIMEOUTlocal.state) - { - case VOMP_STATE_INCALL: - /* Timeout while call in progress, so end call. - Keep call structure hanging around for a bit so that we can - synchonrise with the far end if possible. */ - call->local.state=VOMP_STATE_CALLENDED; - vomp_call_stop_audio(call); - call->last_activity=now; - break; - default: - /* Call timed out while not actually in progress, so just immmediately - tear the call down */ - vomp_call_destroy(call); - return; + } else if (call->last_activity+VOMP_CALL_TIMEOUT