// // File: // // Written by: David M. Stanhope [voip@fobbit.com] // // Thanks to Ivan F. Martinez [ivanfm@ecodigit.com.br] for the code to // support keyboard control and use of the headset. // // Thanks to Don Mahurin [dmahurin@dma.org] for the InfoAccell USB and // '#' dialing support. // // Thanks to Dalin S. Owen for the FreeBSD patches // // For NetBSD uses the standard 'ugen' driver, but current version does not // support writing to 'interrupt' pipes, but if just pretend they are 'bulk' // pipes then works fine, except for slow opens. Add the following lines, // marked with '+', to the function 'ugenopen' around line 333, just before // the switch statement as shown: // // edesc = sce->edesc; // + // DMS HACK (BEGIN) // + if ((dir == OUT) && // + ((edesc->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT)) { // + edesc->bmAttributes = UE_BULK; // + } // + // DMS HACK (END) // switch (edesc->bmAttributes & UE_XFERTYPE) { // // g.723.1 20 byte packets, packet every 30 milliseconds, spec allows 24 byte // packets as well as silence-packets but don't seem to be used here // 30 millisecond packets allow 33.3333... packets/second, uncompressed // packet is 240 samples, so get 7999.9999... samples/second. // // VOIP Blaster has 4 pipes, 2 in each directions, all are marked as interrupt // two are used to send and receive status and only send and receive 1 byte // at a time. // The other two pipes are generally used to send voice data, encoded using // g.723.1, also used to send setup data and receive software version and // serial number. // // Parts: // MAX629 // ADSP-2185M (analog-devices) // LMC6034IM (CMOS Quad Operational Amplifier) // PDIUSBD12 (phillips) (USB interface) // STLC7546 (bascically same as STLC7550) DtoA and AtoD // 39VF010 (1 Mbit Multi-Purpose Flash) // DS2401 (dallas) (serial number chip (01 + 48bit + crc) (64bits) // LM317 (voltage-regulator) // // TODO: // 1: how to get serial number from device, normally only sent once // may want to un-power/re-power the device? Also may want to be able // to ignore value from device since would allow new device to pretend // to be the old one. // 2: lines marked with 'CMD' are for a possible future real protocol // 3: resolve DIALED as load table to insure valid, store as address struct // 4: how to do hunt groups, multiple devices at same ip/port? // (server in front to hand out connections?) // // CHANGES: // 1: refomated to a more standard way of doing braces, somewhere between // what I like and the rest of the world. // 2: merged NetBSD and Windows sources // 3: added auto-dial timer so don't have to press '#' to actually call, // in fact '#' and '*' are now valid dialed digits and are no longer // treated special. // 4: now doing dialtone from a file instead of using builtin tone // 5: if do not set dialtone file then will use builtin dialtone // 6: phonebook server stuff now in place // 7: much more windowish version, really just a wrapper around program // // --------------------------------------------------------------------------- #include "vblast.h" // --------------------------------------------------------------------------- // for now only register when idle #if 1 # define REGISTRATION_UPDATE_OFFHOOK(a) // nothing # define REGISTRATION_CALLBACK NULL #else # define REGISTRATION_UPDATE_OFFHOOK(a) registration_update(a); # define REGISTRATION_CALLBACK registration_update #endif #ifndef IS_VBWIN static char *logfile = NULL; static char ini_path[256]; #endif static char *vb_ini_file = "vb.ini"; // default #define WAIT_REMOTE_TIME 5 // --------------------------------------------------------------------------- SOCKET create_socket(int type, int port, unsigned long net_address) { SOCKET fd; int n; struct sockaddr_in local_address; if((fd = socket(PF_INET, type, 0)) == INVALID_SOCKET) { ERR(("create-socket(%d,%d): error(%s)\n", type, port, Socket_Error())) return SOCKET_ERROR; } n = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)); #ifdef SO_REUSEPORT n = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &n, sizeof(n)); #endif init_address(port, &local_address); local_address.sin_addr.s_addr = net_address; // already in network order if(bind(fd, (struct sockaddr *) &local_address, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { ERR(("create-socket: bind(%d,%d) error(%s)\n", type, port, Socket_Error())) return SOCKET_ERROR; } return fd; } static SOCKET create_socket_external(int type, int port) { return create_socket(type, port, htonl(INADDR_ANY)); } // --------------------------------------------------------------------------- static void send_event(VBLAST *v, char *event, struct sockaddr_in *udp_address) { u_char msg[TXT_BUF_SIZE]; var_add_str(msg + MSG_HEADER_SIZE, "remote", serial_number_string); var_add_str(NULL , "event" , event ); send_msg(v, msg, udp_address); } // --------------------------------------------------------------------------- static int have_remote_id = 0, allow_udp = 0, use_udp = 0, udp_trys = 0; static time_t udp_timer; static struct sockaddr_in remote_address_udp; static char remote_serial_number[32]; void update_remote_udp(struct sockaddr_in *new) { if(remote_address_udp.sin_port != new->sin_port) { MSG(("Updating Remote Udp Port From (%d) to (%d)\n", ntohs(remote_address_udp.sin_port), ntohs(new->sin_port))) remote_address_udp.sin_port = new->sin_port; } if(remote_address_udp.sin_addr.s_addr != new->sin_addr.s_addr) { MSG(("Updating Remote Udp Address From (0x%x) to (0x%x)\n", ntohl(remote_address_udp.sin_addr.s_addr), ntohl(new->sin_addr.s_addr))) remote_address_udp.sin_addr.s_addr = new->sin_addr.s_addr; } } void process_remote_cmd(VBLAST *v, u_char *bp) { char *s; int n, port; struct sockaddr_in address_peer; char *d_serial_number ; char *d_listen_ip ; char *d_listen_port ; char *d_remote_name ; char *d_remote_location; char *d_remote_email ; char *d_remote_flags ; char *d_remote_version ; char *d_remote_os ; char *d_received ; char *d_cmd ; char *d_udp_ip ; char *d_udp_port ; char *d_remote_ip ; char *d_event_type ; MSG1(("REMOTE(%s)\n", bp)) var_parse(bp); if((s = var_find("ALERT")) != NULL) { // TODO: DO A POPUP, BUT MAY BLOCK? MSG((s)) // host should format it so can be multiple lines } if((d_serial_number = var0_chk("id")) != NULL) { if((d_listen_ip = var_find("ip" )) == NULL) { goto is_bad; } if((d_listen_port = var_find("port" )) == NULL) { goto is_bad; } if((d_remote_name = var_find("name" )) == NULL) { goto is_bad; } if((d_remote_location = var_find("location")) == NULL) { goto is_bad; } if((d_remote_email = var_find("email" )) == NULL) { goto is_bad; } if((d_remote_flags = var_find("flags" )) == NULL) { goto is_bad; } if((d_remote_version = var_find("version" )) == NULL) { goto is_bad; } if((d_remote_os = var_find("os" )) == NULL) { goto is_bad; } d_received = var_find("rtime" ); // not required yet d_cmd = var_find("cmd" ); // not required yet d_udp_ip = var_find("udp_ip" ); // not required yet d_udp_port = var_find("udp_port"); // not required yet n = sizeof(struct sockaddr_in); if(getpeername(v->fd_peer_tcp, (struct sockaddr *) &address_peer, &n) == SOCKET_ERROR) { ERR(("remote id: getpeername() for (%s,%s) failed(%s)\n", d_serial_number, d_remote_name, Socket_Error())) goto is_bad; } d_remote_ip = inet_ntoa(address_peer.sin_addr); MSG(("REMOTE ID - Serial-Number: %s\n" " IP : %s (%s)\n" " Port : %s\n" " Name : %s\n" " Location : %s\n" " Email : %s\n" " Flags : %s\n" " Version : %s\n" " OS : %s\n", d_serial_number , d_listen_ip , d_remote_ip , d_listen_port , d_remote_name , d_remote_location, d_remote_email , d_remote_flags , d_remote_version , d_remote_os )) if(d_received) { MSG((" Registered : %s\n", time_string_str(d_received ))) } if(d_cmd ) { MSG((" Cmd : %s\n", d_cmd )) } if(d_udp_ip ) { MSG((" Udp_IP : %s\n", d_udp_ip )) } if(d_udp_port) { MSG((" Udp_Port : %s\n", d_udp_port)) } UPDATE_REMOTE(" (From Peer)") allow_udp = 0; use_udp = 0; // assume want tcp if((port_listen_udp >= 0) && d_udp_ip && d_udp_port) // have all need? { port = atoi(d_udp_port); if(strcmp(d_udp_ip, "0.0.0.0") == 0) { s = d_remote_ip; } else { s = d_udp_ip ; } if(resolve_address(s, port, &remote_address_udp) == 0) { allow_udp = 1; // flag ok to try udp udp_trys = 5; udp_timer = RIGHT_NOW; // 0.42 was first version to support UDP, but did not // test for it, it just enabled it if both sides setup if(strcmp(d_remote_version, "0.42") == 0) { MSG(("Using UDP for voice\n")) use_udp = 1; // say ok to use } } } strcpy(remote_serial_number, d_serial_number); have_remote_id = 1; return; } else if((d_serial_number = var0_chk("remote")) != NULL) { if((d_event_type = var_find("event")) == NULL) { goto is_bad; } if(strcmp(d_event_type, "TCP_ALIVE") == 0) // normally do not show { MSG1(("From (%s) TCP_ALIVE\n", d_serial_number)) } else { MSG(("From (%s) Event (%s)\n", d_serial_number, d_event_type)) if(strcmp(d_event_type, "UDP_CHK") == 0) { if(allow_udp) { send_event(v, "UDP_RCV", NULL); // say can rcv udp messages } else { MSG(("Got Unexpected UDP_CHK Message\n")) } } else if(strcmp(d_event_type, "UDP_RCV") == 0) { if(allow_udp) { if(use_udp == 0) { use_udp = 1; // say is ok to send voice via udp MSG(("Switching to UDP for voice\n")) } } else { MSG(("Got Unexpected UDP_RCV Message\n")) } } } return; } is_bad: MSG(("UNKNOWN_REMOTE_CMD(%s)\n", bp)) } // --------------------------------------------------------------------------- static void beep_wait_on_hook(VBLAST *v) { PMSG(("beep_wait_on_hook\n")) while(1) { if(v->status_hook == STATUS_HOOK_ON) break; vblast_play_file(v, sound_busy, VB_PLAY_REPEAT, REGISTRATION_CALLBACK); } MSG(("beep_wait_on_hook: on-hook\n")) } // --------------------------------------------------------------------------- // // must use sendto() instead of connect() and send() since if go thru NAT the // remote port won't match the source port so won't receive // static void open_udp(VBLAST *v) { if(allow_udp) { if(use_udp == 0) { MSG(("Trying UDP for voice\n")) } // create socket and bind the local address if((v->fd_peer_udp = create_socket_external(SOCK_DGRAM, port_listen_udp)) == INVALID_SOCKET) { ERR(("Falling back to TCP\n")) allow_udp = 0; } else { set_non_blocking(v->fd_peer_udp); } } } // --------------------------------------------------------------------------- // copy voice data back and forth, exit if socket goes down or phone on-hook static void talk(VBLAST *v, int called) { time_t last_tcp_sent = RIGHT_NOW, st = RIGHT_NOW; u_char *bp; int r, max_queue = 0, hangup_reason = 0; static int call_count = 1; if(v->status_hook == STATUS_HOOK_ON) { if(v->fd_peer_tcp != INVALID_SOCKET) { Socket_Close(v->fd_peer_tcp); v->fd_peer_tcp = INVALID_SOCKET; } if(v->fd_peer_udp != INVALID_SOCKET) { Socket_Close(v->fd_peer_udp); v->fd_peer_udp = INVALID_SOCKET; } return; } if(v->fd_peer_tcp == INVALID_SOCKET) { if(v->fd_peer_udp != INVALID_SOCKET) { Socket_Close(v->fd_peer_udp); v->fd_peer_udp = INVALID_SOCKET; } if(v->status_hook != STATUS_HOOK_ON) { beep_wait_on_hook(v); } return; } set_non_blocking(v->fd_peer_tcp); // to catch net-write errors if(called) { send_event(v, "ANSWERED", NULL); } MSG(("Talking (%s)\n", timestamp())) vblast_command(v, COMMAND_VOL_0 + device_volume); vblast_command(v, COMMAND_VOUT_START); vblast_command(v, COMMAND_VINP_START); // CMD: SEND OR GET CLOSE while(1) { REGISTRATION_UPDATE_OFFHOOK(v) if(v->fd_peer_tcp == INVALID_SOCKET) { MSG(("talking: connection closed\n")) hangup_reason = 1; // POSSIBLE ERROR CODE break; } if(v->status_hook == STATUS_HOOK_ON) { MSG(("talking: on-hook\n")) send_event(v, "HANGUP", NULL); hangup_reason = 2; break; } #ifndef IS_VBWIN if(logfile == NULL) // spew to console if no logfile { // only last needs fixed width since only one that can shrink MSG(("\r%d/%d %d/%d %d/%d %d/%03d ", v->net_sent_tcp, v->net_sent_udp, v->net_received_tcp, v->net_received_udp, v->net_dropped, v->net_blocked, max_queue, v->queue_net.count)) } #else UPDATE_STATUS(v->net_sent_tcp, v->net_sent_udp, v->net_received_tcp, v->net_received_udp, v->net_dropped, max_queue, v->queue_net.count) #endif // if using UDP for voice may leave the TCP connection idle for quite // some time, some NAT boxes will shutdown the connection if idle for // too long so insure send something ever so often if((RIGHT_NOW - last_tcp_sent) > tcp_alive_timer) { send_event(v, "TCP_ALIVE", NULL); last_tcp_sent = RIGHT_NOW; v->net_sent_tcp++; } // maintain for performance info if(v->queue_net.count > max_queue) { max_queue = v->queue_net.count; } while(vblast_status_deque(v) >= 0) { ; } // keep status queue empty while(bp = vblast_deque_voice(v)) { if(use_udp == 0) // default is to send voice via tcp { if((r = vblast_socket_writer("net-write-tcp", v->fd_peer_tcp, bp, NP_SIZE)) < 0) { // error message already done in 'vblast_socket_writer' hangup_reason = 3; break; } else if(r == 0) // would block { v->net_blocked++; } else { last_tcp_sent = RIGHT_NOW; v->net_sent_tcp++; } } if(allow_udp) { if(use_udp) // since know udp works, use it for voice { if((r = sendto(v->fd_peer_udp, bp, NP_SIZE, 0, (struct sockaddr *)&remote_address_udp, sizeof(struct sockaddr_in))) != NP_SIZE) { if(r < 0) { ERR(("net-write-udp: r(%d) error(%s)\n", r, Socket_Error())) hangup_reason = 4; break; } else if(r == 0) // would block { v->net_blocked++; } else { ERR(("net-write-udp: r(%d) != (%d)\n", r, NP_SIZE)) hangup_reason = 5; break; } } else { v->net_sent_udp++; } } else if(udp_trys > 0) // tx udp probe once a sec up to 5 times { if(RIGHT_NOW >= udp_timer) { send_event(v, "UDP_CHK", &remote_address_udp); udp_timer = RIGHT_NOW + 1; udp_trys--; } } } } if(hangup_reason) { break; } // since this write is most likely place to block when get behind, // only do one at a time if(bp = vblast_deque_net(v)) { vblast_voice_write(v, bp + 1, VP_SIZE); // check if too much queued, if queue stays up for while, drop some if(v->queue_net.count > net_high_water) { v->net_over_count++; if(v->net_over_count > net_high_count) { if(vblast_deque_net(v)) { v->net_over_count = 0; v->net_dropped++ ; #ifndef IS_VBWIN if(logfile) // if logfile, send drops to it { MSG(("DROP: %d %d %d\n", v->net_received_tcp + v->net_received_udp, v->net_dropped, v->queue_net.count)) } #endif } else { ERR(("CONFUSED, net queue can't be empty\n")) exit(-1); } } } else { v->net_over_count = 0; } } if(vblast_ready_net(v)) { while(vblast_wait_input(v, 0) > 0) { ; } // just poll till nothing } else { if(vblast_wait_input(v, 10) > 0) // wait for some input { while(vblast_wait_input(v, 0) > 0) { ; } // poll till no more } } } vblast_command(v, COMMAND_VINP_STOP); // shutdown voice input vblast_shutdown_voice(v); // shutdown voice output if((v->status_hook != STATUS_HOOK_ON ) && (v->status_headset != STATUS_HEADSET_IN) && (cpc_duration > 0)) { MSG(("Sending CPC(%d)\n", cpc_duration)) vblast_command(v, COMMAND_PHONE_OFF); nap(cpc_duration); vblast_command(v, COMMAND_PHONE_ON); } #if 0 // headset seems to require this, should be ok if not headset but be safe if(called && (v->status_headset == STATUS_HEADSET_IN)) { vblast_command(v, COMMAND_RING_OFF); } #endif st = RIGHT_NOW - st; // duration of the call MSG(("Stats: Tx %d/%d Rx %d/%d Drop %d Block %d MaxQ %d Secs %ld\n", v->net_sent_tcp, v->net_sent_udp, v->net_received_tcp, v->net_received_udp, v->net_dropped, v->net_blocked, max_queue, st)) send_stats(v->net_sent_tcp, v->net_sent_udp, v->net_received_tcp, v->net_received_udp, v->net_dropped, v->net_blocked, max_queue, st, called, remote_serial_number, hangup_reason); MSG(("Shutdown Call(%d) (%s)\n", call_count++, timestamp())) if(v->fd_peer_udp != INVALID_SOCKET) { Socket_Close(v->fd_peer_udp); v->fd_peer_udp = INVALID_SOCKET; } if(v->fd_peer_tcp != INVALID_SOCKET) { Socket_Close(v->fd_peer_tcp); v->fd_peer_tcp = INVALID_SOCKET; } if(v->status_hook != STATUS_HOOK_ON) { beep_wait_on_hook(v); } } // --------------------------------------------------------------------------- #define FATAL_ERROR -1 #define CALL_DONE 1 #define CALL_ANSWERED 2 // callback for ring while ringing local phone static int ring_callback(VBLAST *v) { REGISTRATION_UPDATE_OFFHOOK(v) if(v->fd_peer_tcp == INVALID_SOCKET) { MSG(("ring_callback: network-close\n")) return 1; // tell 'vblast_ring()' to return } return 0; } // callback for 'vblast_play_file' while ringing remote end and playing // the ringback file, check for network-down or remote-answer, don't have // to check for hangup since already handled in 'vblast_play_file' static int check_remote_answer(VBLAST *v) { REGISTRATION_UPDATE_OFFHOOK(v) if(v->fd_peer_tcp == INVALID_SOCKET) // network error { MSG(("check_remote_answer: network close\n")) return 1; // tell 'vblast_play_file' to return } if(vblast_ready_net(v)) // if any voice data assume remote side answered { MSG(("check_remote_answer: answered\n")) return 1; // tell 'vblast_play_file' to return } return 0; } // state data for collecting dialed digits static time_t dialed_timer ; static int dialed_count ; static char dialed_buffer[128]; static int build_dialed(int c) { if((c == '*') || (c == '#') || ((c >= '0') && (c <= '9'))) { if(dialed_count <= 126) { dialed_buffer[dialed_count++] = c ; dialed_buffer[dialed_count ] = '\0'; PMSG(("Pressed(%c) Dialed(%s)\n", c, dialed_buffer)) } else { ERR(("build_dialed: no room for (0x%0x)\n", c)) } dialed_timer = RIGHT_NOW + dial_timeout_2; return 1; } else { ERR(("build_dialed: not expecting (0x%0x)\n", c)) } return 0; } // callback for 'vblast_play_file' while playing dialtone // hangup since already handled in 'vblast_play_file' static int check_dialed_digit(VBLAST *v) { int c; REGISTRATION_UPDATE_OFFHOOK(v) while((c = vblast_status_deque(v)) >= 0) { if(build_dialed(c)) return 1; } if(RIGHT_NOW >= dialed_timer) { return 1; } return 0; } // --------------------------------------------------------------------------- int wait_remote_id(VBLAST *v, char *type, int hook_flag) { time_t end_time = RIGHT_NOW + WAIT_REMOTE_TIME; have_remote_id = 0; while(1) { if(v->fd_peer_tcp == INVALID_SOCKET) { ERR(("wait_remote_id: no connection\n")) break; } if(v->status_hook != hook_flag) { ERR(("wait_remote_id: %s-hook\n", (v->status_hook == STATUS_HOOK_ON) ? "on" : "off")) break; } if(have_remote_id) { // TODO SEE IF GOT WHAT WANTED, BUT NOT YET SINCE WOULD // FAIL IF 4.1 SYSTEM MSG1(("wait_remote_id: got it\n")) break; } if(RIGHT_NOW >= end_time) { ERR(("wait_remote_id: timeout\n")) break; } vblast_wait_input(v, 1); } if(v->fd_peer_tcp == INVALID_SOCKET) { if(v->status_hook != STATUS_HOOK_ON) { beep_wait_on_hook(v); } return -1; } if((v->status_hook != hook_flag) || (have_remote_id == 0)) { Socket_Close(v->fd_peer_tcp); v->fd_peer_tcp = INVALID_SOCKET; if(v->status_hook != STATUS_HOOK_ON) { beep_wait_on_hook(v); } return -1; } return 0; } // --------------------------------------------------------------------------- int process_call(VBLAST *v) { int c, len, port; SOCKET fd_accept, fd_peer_tcp; char *peer_name, *dialtone_file; struct sockaddr_in peer, *pa; struct sockaddr peer_addr; // ----------------------------------------------------------------------- // build socket to listen for connections on // ----------------------------------------------------------------------- // create socket and bind the local address if((fd_accept = create_socket_external(SOCK_STREAM, port_listen_tcp)) == INVALID_SOCKET) { return FATAL_ERROR; } if(listen(fd_accept, 5) == SOCKET_ERROR) { ERR(("listen: error(%s)\n", Socket_Error())) return FATAL_ERROR; } v->fd_accept = fd_accept; vblast_vp_flush(v); // insure voice and network queues empty // wait for off-hook or incoming call PMSG(("Waiting For Call (%s)%s\n", timestamp(), (v->status_headset == STATUS_HEADSET_IN) ? " [HEADSET]" : "")) while(1) { registration_update(v); // register every so often vblast_wait_input(v, 10); // wait for input or call while(vblast_status_deque(v) >= 0) { ; } // keep queue empty if(v->have_call) { PMSG(("Incoming Call (%s)\n", timestamp())) CLEAR_REMOTE len = sizeof(struct sockaddr); set_timeout(); if((fd_peer_tcp = accept(fd_accept, &peer_addr, &len)) == INVALID_SOCKET) { clr_timeout(); ERR(("socket-accept: error(%s)\n", Socket_Error())) Socket_Close(fd_accept); v->fd_accept = INVALID_SOCKET; return FATAL_ERROR; } clr_timeout(); Socket_Close(v->fd_accept); v->fd_accept = INVALID_SOCKET; pa = (struct sockaddr_in *) &peer_addr; PMSG(("incoming call: from (%s:%d)\n", inet_ntoa(pa->sin_addr), ntohs(pa->sin_port))) v->fd_peer_tcp = fd_peer_tcp; // so 'ring' will watch it send_my_id(v, "HELLO"); // tell caller who we are // wait for caller identity if(wait_remote_id(v, "RING", STATUS_HOOK_ON) < 0) { return CALL_DONE; } open_udp(v); // start ringing, returns if off-hook, network-down // CMD: COULD GET HANGUP COMMAND vblast_ring(v, ring_callback); // ring phone, wait offhook talk(v, 1); return CALL_DONE; } // if phone goes off-hook then user whats to make a call if(v->status_hook == STATUS_HOOK_OFF) break; } CLEAR_REMOTE // don't allow incoming since doing outgoing Socket_Close(fd_accept); v->fd_accept = INVALID_SOCKET; PMSG(("Offhook (%s)\n", timestamp())) vblast_shutdown_voice(v); // if don't do, get garbled output for a second // wait for and collect dialed digits // if don't get first digit in a while, then is an error dialed_count = 0; dialed_timer = RIGHT_NOW + dial_timeout_1; if(v->status_headset == STATUS_HEADSET_IN) { dialtone_file = sound_dialtone_hs; } else { dialtone_file = sound_dialtone; } if(dialtone_file) { vblast_play_file(v, dialtone_file, VB_PLAY_REPEAT|VB_PLAY_KEEP_STATUS, check_dialed_digit); } // fall thru to error check if nothing entered if((dialed_count > 0) || (dialtone_file == NULL)) { while(RIGHT_NOW < dialed_timer) { REGISTRATION_UPDATE_OFFHOOK(v) vblast_wait_input(v, 1); while((c = vblast_status_deque(v)) >= 0) { if((c == '#') && (dialed_count >= 1) && (force_dial)) { break; } build_dialed(c); } if((v->status_hook == STATUS_HOOK_ON) || (c == '#')) { break; } } } if(v->status_hook == STATUS_HOOK_ON) { MSG(("back on-hook\n")) return CALL_DONE; } if(dialed_count < 1) { ERR(("nothing-dialed\n")) beep_wait_on_hook(v); return CALL_DONE; } dialed_buffer[dialed_count] = '\0'; PMSG(("dialed(%s)\n", dialed_buffer)) if((peer_name = lookup_dialed(dialed_buffer, &port)) == NULL) { ERR(("lookup-dialed(%s): not found\n", dialed_buffer)) beep_wait_on_hook(v); return CALL_DONE; } PMSG(("Calling (%s)(%s:%d) (%s)\n", dialed_buffer, peer_name, port, timestamp())) if(resolve_address(peer_name, port, &peer) < 0) { beep_wait_on_hook(v); return CALL_DONE; } PMSG(("Resolved(%s)(%s)(%s)\n", dialed_buffer, peer_name, inet_ntoa(peer.sin_addr))) if((fd_peer_tcp = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { ERR(("socket-connect: error(%s)\n", Socket_Error())) return FATAL_ERROR; } set_timeout(); if(connect(fd_peer_tcp, (struct sockaddr *) &peer, sizeof(peer)) == SOCKET_ERROR) { clr_timeout(); ERR(("connect: error(%s)\n", Socket_Error())) Socket_Close(fd_peer_tcp); beep_wait_on_hook(v); return CALL_DONE; } clr_timeout(); v->fd_peer_tcp = fd_peer_tcp; // mark as connected so will be processed // wait for destination identity if(wait_remote_id(v, "HELLO", STATUS_HOOK_OFF) < 0) { return CALL_DONE; } send_my_id(v, "RING"); // tell destination who is calling open_udp(v); // wait for local-hangup or network-down or remote-answer vblast_play_file(v, sound_ringback, VB_PLAY_REPEAT, check_remote_answer); // CMD: SEND STOP RING IF HANGUP // CMD: EXPECT ANSWERED talk(v, 0); return CALL_DONE; } // --------------------------------------------------------------------------- int _cdecl main(int argc, char *argv[], char *env[]) { VBLAST *v; char buf[16]; #ifdef ALLOW_KEYBOARD int use_keyboard = 1; #endif #ifndef IS_VBWIN int i, err = 0; for(i = 1; i < argc; i++) { if(strcmp(argv[i], "logfile") == 0) { if(++i >= argc) { ERR(("no filename given for 'logfile'!\n")) err = 1; break; } logfile = argv[i]; // don't redirect till arg processing done continue; } if(strcmp(argv[i], "inifile") == 0) { if(++i >= argc) { ERR(("no filename given for 'inifile'!\n")) err = 1; break; } strcpy(ini_path, argv[i]); vb_ini_file = ini_path; continue; } if(strcmp(argv[i], "nokey") == 0) { use_keyboard = 0; continue; } ERR(("invalid argument (%s) given!\n", argv[i])) err = 1; break; } if(err) { ERR(("Usage: vb [logfile (filename)] [inifile (filename)] [nokey]\n")) ERR(("Exiting!\n")) exit(-1); } if(logfile) // redirect messages to the logfile { freopen(logfile, "a", stdout); freopen(logfile, "a", stderr); MSG(("Logfile (%s) Started (%s)\n", logfile, timestamp())) } #endif MSG(("VB Version %d.%d, %s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_DATE)) MSG(("Written By: David M. Stanhope\n")) MSG(("OS is %s\n", os_name())) init_network(); // mainly for windoze process_ini(vb_ini_file); CHECK_NOT_RUNNING(device_index) sprintf(buf, "vb%d: ", device_index); init_title(argc, argv, env, buf); // ----------------------------------------------------------------------- // open the USB Voip-Blaster device // ----------------------------------------------------------------------- MSG(("Opening VB Device(%d) (%s)\n", device_index, timestamp())) v = vblast_open(device_index); #ifdef ALLOW_KEYBOARD if(use_keyboard) { keyboard_open(v, device_index); } #endif UPDATE_LOCAL(0) // ----------------------------------------------------------------------- while(process_call(v) == CALL_DONE) { ; } // ----------------------------------------------------------------------- MSG(("Exiting (%s)\n", timestamp())) vblast_close(v); exit(0); NEED_RETURN(0) // make some compilers happy } // // The End! //