All pastes #38073 Raw Edit

Someone

public text v1 · immutable
#38073 ·published 2006-01-22 20:58 UTC
rendered paste body
/*  intercom.c -- A real-time audio communication utility
    Copyright (C) 2001-2003  Shane Wegner
    This file is part of Intercom.

    Intercom is free software; you can redistribute it and/or modify it
    under the terms of version 2 of the GNU General Public License as
    published by the Free Software Foundation.

    Intercom 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.

    You should have received a copy of version 2 of the GNU General Public
    License along with Intercom; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    To contact the author, please send email to shane@cm.nu.
    */

/* $Id: intercom.c,v 1.83 2003/02/13 20:22:49 shane Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unistd.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#include <inttypes.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "subst/getopt.h"
#endif
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#if defined(HAVE_SYS_SOUNDCARD_H)
#include <sys/soundcard.h>
#elif defined(HAVE_MACHINE_SOUNDCARD_H)
#include <machine/soundcard.h>
#else
#include <soundcard.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#include <arpa/inet.h>
#include <netdb.h>
#ifdef USE_GETTEXT
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <libintl.h>
#define _(String) gettext(String)
#define gettext_noop(String) (String)
#define N_(String) gettext_noop(String)
#else
#define _(String) (String)
#define N_(String) (String)
#endif

#include "intercom.h"
#include "protocol.h"
#include "iobuff.h"
#include "console.h"
#include "pkthandler.h"
#include "userdefs.h"
#include "codecs.h"
#include "hooks.h"
#ifdef USE_CRYPTO
#include "crypto.h"
#endif

/* Global variables */
int done = 0;
#ifdef USE_CRYPTO
#define CALL_INIT_CRYPTO .cryptinfo = {CALL_CRYPT_NONE, },
#else
#define CALL_INIT_CRYPTO
#endif
#define CALL_INIT_STATE (struct call_info){ \
.state = CALL_STATE_IDLE, .csock = -1, .dsock = -1, .loopback = 0, \
.rdsp = -1, .pdsp = -1, \
.snd_sequence = 0, .rcv_sequence = 0, \
.ogain = NULL, .igain = NULL, \
CALL_INIT_CRYPTO \
.connstats = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }
struct call_info call = CALL_INIT_STATE;
struct compression_format compression_table[]={
{AUDIO_COMPRESSION_NONE, "none", "None"},
#ifdef HAVE_LIBGSM
{AUDIO_COMPRESSION_GSM, "gsm", "GSM 6.10"},
#endif
{AUDIO_COMPRESSION_G711U, "g.711-ulaw", "CCITT G.711 U-LAW"},
{AUDIO_COMPRESSION_G711A, "g.711-alaw", "CCITT G.711 A-LAW"},
#ifdef HAVE_G72X
{AUDIO_COMPRESSION_G721, "g.721", "CCITT G.721"},
{AUDIO_COMPRESSION_G723_24, "g.723-24", "CCITT G.723 (24 kbit/s)"},
{AUDIO_COMPRESSION_G723_40, "g.723-40", "CCITT G.723 (40 kbit/s)"},
#endif
{0, NULL, NULL}};

/* Local variables */
static fd_set f_rset, f_wset;
static char audiopkt[4096];
#ifdef USE_CRYPTO
static char cr_audiopkt[4096];
#endif
static int isplaying = 1, dropskip = 0;
static int listener;
/* Config variables */
static float silence_time;
static int silence_thresh, min_buffsize, max_buffsize;

void refresh_userdefs(void)
{
double g;
/* handle gain changes */
g = opt_get_float("input_gain");
if (g != 0.0 && (call.igain == NULL || call.igain->gain_db != g)) {
if (call.igain != NULL)
gain_free(call.igain);
call.igain = gain_init(g);
} else if (g == 0.0 && call.igain != NULL) {
gain_free(call.igain);
call.igain = NULL;
}
g = opt_get_float("output_gain");
if (g != 0.0 && (call.ogain == NULL || call.ogain->gain_db != g)) {
if (call.ogain != NULL)
gain_free(call.ogain);
call.ogain = gain_init(g);
} else if (g == 0.0 && call.ogain != NULL) {
gain_free(call.ogain);
call.ogain = NULL;
}
silence_time = opt_get_float("silence_time");
silence_thresh = opt_get_int("silence_thresh");
min_buffsize = opt_get_int("udp_minsize");
max_buffsize = opt_get_int("udp_maxsize");
}

void sighandler(int signum)
{
switch(signum) {
case SIGHUP:
hk_trigger(hk_sighup);
break;
case SIGINT:
case SIGTERM:
done = 1;
break;
}
signal(signum, sighandler);
}

static char **ic_environ = NULL;
static int ic_envlen = 0;

void icsetenv(const char *var, const char *value)
{
int i, exists = 0;
char rv[strlen(var) + 4];
strcpy(rv, "ic_");
strcpy(&rv[3], var);
for (i = 0; i < ic_envlen; i++)
if (!strcmp(rv, ic_environ[i]))
exists = 1;
if (!exists) {
ic_environ = realloc(ic_environ, sizeof(char **) * (ic_envlen + 1));
ic_environ[ic_envlen++] = strdup(rv);
}
setenv(rv, value, 1);
}

int do_connect(const char *hostname, int port, struct sockaddr_in *r_address)
{
int sock;
struct in_addr addr;
struct sockaddr_in address;
struct hostent *host = gethostbyname(hostname);
if (host == NULL) {
herror("gethostbyname");
return -1;
}
memcpy(&addr, host->h_addr_list[0], sizeof(addr));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = addr.s_addr;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("socket");
return -1;
}
if (connect(sock, (struct sockaddr *)&address, sizeof(address)) == -1) {
perror("connect");
close(sock);
return -1;
}
/* if (fcntl(sock, F_SETFL, O_RDWR | O_NONBLOCK) == -1) {
perror("fcntl");
close(sock);
return -1;
} */
if (r_address != NULL)
memcpy(r_address, &address, sizeof(address));
return sock;
} /* do_connect */

static int do_listen(int port)
{
int sock;
struct in_addr addr;
struct sockaddr_in address;
struct hostent *host = gethostbyname("0.0.0.0");
memcpy(&addr, host->h_addr_list[0], sizeof(addr));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = addr.s_addr;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("socket");
return -1;
}
if (bind(sock, (struct sockaddr *)&address, sizeof(address)) == -1) {
perror("bind");
close(sock);
return -1;
}
if (fcntl(sock, F_SETFL, O_RDWR | O_NONBLOCK) == -1) {
perror("fcntl");
close(sock);
return -1;
}
if (listen(sock, 1) == -1) {
perror("listen");
close(sock);
return -1;
}
return sock;
} /* do_listen */

int dsp_setup(const char *device, int mode, struct audio_params *params)
{
int fd, r, caps;
if ((fd = open(device, (mode|O_NONBLOCK))) == -1) {
perror("open");
return -1;
}
if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
perror("sndctl_dsp_getcaps failed:");
goto err;
}
if ((mode & O_RDWR) == O_RDWR) {
if ((caps & DSP_CAP_DUPLEX) != DSP_CAP_DUPLEX) {
fputs(_("Error: missing duplex capability\n"), stderr);
goto err;
}
if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0) == -1)
perror("SNDCTL_DSP_SETDUPLEX");
}
if (ioctl(fd, SNDCTL_DSP_RESET, 0) == -1) {
perror("sound ioctl(SNDCTL_DSP_RESET)");
goto err;
}
/* r = APF_NETWORK;
if (ioctl(fd, SNDCTL_DSP_PROFILE, &r) == -1)
perror("sound ioctl(SNDCTL_DSP_PROFILE)");
*/
r = (0x02 << 16) + logb(params->rate * (params->bitwidth / 8) / 20);
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &r) == -1)
perror("sound ioctl(SNDCTL_DSP_SETFRAGMENT)");
r = AFMT_S16_LE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &r) == -1) {
perror("sound ioctl(SNDCTL_DSP_SETFMT)");
goto err;
}
r = 0;
if (ioctl(fd, SNDCTL_DSP_STEREO, &r) == -1) {
perror("sound ioctl(SNDCTL_DSP_CHANNELS)");
goto err;
}
r = params->rate;
if (ioctl(fd, SNDCTL_DSP_SPEED, &r) == -1) {
perror("sound ioctl(SNDCTL_DSP_SPEED)");
goto err;
}
if ((caps & DSP_CAP_TRIGGER) == DSP_CAP_TRIGGER) {
if ((mode & O_RDWR) == O_RDWR)
r = (PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT);
else if ((mode & O_WRONLY) == O_WRONLY)
r = PCM_ENABLE_OUTPUT;
else
r = PCM_ENABLE_INPUT;
if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &r) == -1)
perror("SNDCTL_DSP_SETTRIGGER failed:");
}
return fd;
err:
close(fd);
return -1;
} /* dsp_setup */

#if (DEBUG >= 2)
void hexdump(const char *buff, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
printf(" %02x", (unsigned int)buff[i] & 0xff);
}

static void pkthexdump(const char *buff, size_t n)
{
fputs(_("debug: Packet hex dump:"), stdout);
hexdump(buff, n);
putchar('\n');
}
#endif

void queuepacket(struct iobuff *iob, uint16_t type, char *pkt, size_t pktlen)
{
char buff[sizeof(struct pkt_header) + pktlen];
struct pkt_header *ph = (struct pkt_header *)buff;
ph->p_type = type;
ph->p_len = sizeof(ph) + pktlen;
memcpy(&buff[sizeof(*ph)], pkt, pktlen);
call.connstats.cpkts_sent++;
call.connstats.cbytes_sent += ph->p_len;
if (opt_get_int("verbose")) {
#if (DEBUG >= 1)
printf(_("debug: Queueing packet type %u of %u bytes.\n"),
type, ph->p_len);
#if (DEBUG >= 2)
pkthexdump(buff, ph->p_len);
#endif
#endif
}
iob_queue(iob, buff, ph->p_len);
}

void closecall(void)
{
int i;
if (TEST_BIT(call.state, CALL_STATE_CONNECTED)) {
if (call.csock != -1) {
shutdown(call.csock, 2);
close(call.csock);
}
if (call.dsock != -1) {
shutdown(call.dsock, 2);
close(call.dsock);
}
iob_flush(&call.csrq);
iob_flush(&call.cssq);
iob_flush(&call.rdspq);
iob_flush(&call.rdspq);
if (call.rdsp != -1) {
close(call.rdsp);
free(call.rdsp_buff);
if (call.pdsp == call.rdsp)
call.pdsp = -1;
}
if (call.pdsp != -1) {
close(call.pdsp);
free(call.pdsp_buff);
}
/* Free the environment */
for (i = 0; i < ic_envlen; i++) {
unsetenv(ic_environ[i]);
free(ic_environ[i]);
}
free(ic_environ);
ic_environ = NULL;
ic_envlen = 0;
/* And repopulate with userdefs.  TODO: optimize this out. */
for (i = 0; useropts[i].key != NULL; i++)
if (strcmp(useropts[i].key, "crypt_passphrase"))
icsetenv(useropts[i].key, useropts[i].value);
memcpy(&call, &CALL_INIT_STATE, sizeof(call));
iob_init(&call.cssq);
iob_setminbytes(&call.cssq, IOB_PAGESIZE);
iob_init(&call.csrq);
iob_setminbytes(&call.csrq, IOB_PAGESIZE);
iob_init(&call.rdspq);
iob_init(&call.pdspq);
codecs_init();
}
}

int setup_udpsock(struct sockaddr_in *addr)
{
unsigned char tos;
int l = sizeof(*addr);
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (s == -1) {
perror("socket");
return -1;
}
addr->sin_family = AF_INET;
/* On Linux at least, this gives us a random port. */
/* TODO: verify portability */
addr->sin_port = 0;
addr->sin_addr.s_addr = INADDR_ANY;
if (bind(s, (struct sockaddr *)addr, sizeof(*addr)) == -1) {
perror("bind");
close(s);
return -1;
}
getsockname(s, (struct sockaddr *)addr, &l);
if (fcntl(s, F_SETFL, O_RDWR | O_NONBLOCK) == -1) {
perror("fcntl");
close(s);
return -1;
}
#ifdef HAVE_NETINET_IP_H
if (opt_get_int("iptos_lowdelay") > 0) {
tos = IPTOS_LOWDELAY;
if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, 1) == -1)
if (opt_get_int("verbose"))
perror("setsockopt failed for IP_LOWDELAY");
}
#endif
return s;
}

void send_errorpkt(struct iobuff *iob, uint16_t errorcode)
{
static const struct pkt_error err_list[]= {
{PKT_ERROR_PKTUNKNOWN, N_("Unknown packet type")},
{PKT_ERROR_PKTORDER, N_("Packet ordering error")},
{PKT_ERROR_AUDIOFMT, N_("Audio format error")},
{PKT_ERROR_PROTO_VERSION, N_("Protocol version mismatch")},
{PKT_ERROR_NOCRYPT, N_("Encryption format not supported")},
};
static size_t err_list_nr = sizeof(err_list) / sizeof(struct pkt_error);
size_t i;
struct pkt_error errorpkt;
errorpkt.errorcode = errorcode;
*errorpkt.errormsg = 0;
memset(errorpkt.errormsg, 0, sizeof(errorpkt.errormsg));
for (i = 0; i < err_list_nr; i++)
if (err_list[i].errorcode == errorcode) {
strncpy(errorpkt.errormsg, err_list[i].errormsg, sizeof(errorpkt.errormsg));
break;
}
queuepacket(iob, PKT_TYPE_ERROR, (char *)&errorpkt, sizeof(errorpkt));
}

int int_compression(const char *s)
{
size_t i;
for (i = 0; compression_table[i].name != NULL; i++)
if (!strcasecmp(s, compression_table[i].name))
return compression_table[i].fmt;
return -1;
}

char *str_compression(int v)
{
size_t i;
for (i = 0; compression_table[i].name != NULL; i++)
if (v == compression_table[i].fmt)
return compression_table[i].name;
return NULL;
}

/* Strip non-printable characters from s, modifying it in the process */
char *to_printable(char *s)
{
unsigned char *p;
for (p = s; *p; p++) {
if (*p < 32 || *p > 127)
*p = '.';
}
return s;
}

static void handle_packet(char *pktbuff)
{
struct pkt_header *ph = (struct pkt_header *)pktbuff;
char *pktdata = &pktbuff[sizeof(ph)];
if (opt_get_int("verbose")) {
printf(_("Received control packet of type %u\n"), ph->p_type);
#if (DEBUG >= 2)
pkthexdump(pktbuff, ph->p_len);
#endif
}
switch(ph->p_type) {
case PKT_TYPE_HELLO:
handle_pkt_hello((struct pkt_hello *)pktdata);
break;
case PKT_TYPE_CALL:
handle_pkt_call((struct pkt_call *)pktdata);
break;
case PKT_TYPE_CALLRESPONSE:
handle_pkt_callresponse((struct pkt_callresponse *)pktdata);
break;
case PKT_TYPE_AUDIOSTART:
handle_pkt_audiostart((struct pkt_audiostart *)pktdata);
break;
case PKT_TYPE_ERROR: {
struct pkt_error *err = (struct pkt_error *)pktdata;
err->errormsg[sizeof(err->errormsg) - 1] = 0;
to_printable(err->errormsg);
printf("Error from remote host: '%s'\n", err->errormsg);
}
break;
case PKT_TYPE_USRTEXT:
handle_pkt_usrtext(pktdata, ph->p_len - sizeof(struct pkt_header));
break;
default:
fprintf(stderr, _("Received unknown packet type %u.\n"), ph->p_type);
send_errorpkt(&call.cssq, PKT_ERROR_PKTUNKNOWN);
};
}

static void get_csockdata(void)
{
char buff[1024];
int nr;
int nopacket = 0;
size_t qsz;
nr = read(call.csock, buff, 1024);
if (!nr) {
printf(_("Connection to %s closed by remote host.\n"),
call.r_hostent->h_name);
if (TEST_BIT(call.state, CALL_STATE_INCALL))
hk_trigger(hk_hangup_rmt);
closecall();
return;
}
if (nr == -1)
return;
#if (DEBUG >= 1)
if (opt_get_int("verbose"))
printf(_("Received %u bytes from control socket.\n"), nr);
#endif
iob_queue(&call.csrq, buff, nr);
while(!nopacket) {
qsz = iob_buffsize(&call.csrq);
if (qsz >= sizeof(struct pkt_header)) {
struct pkt_header ph;
/* Peak at the header */
iob_readkeep(&call.csrq, (char *)&ph, sizeof(ph));
if (qsz >= ph.p_len) {
char pktbuff[ph.p_len];
iob_read(&call.csrq, pktbuff, ph.p_len);
call.connstats.cpkts_recv++;
call.connstats.cbytes_recv += ph.p_len;
handle_packet(pktbuff);
} else
nopacket = 1;
} else
nopacket = 1;
}
}

static void get_dsockdata(void)
{
struct pkt_udpaudio *ph = (struct pkt_udpaudio *)audiopkt;
int nr;
size_t qsz;
#ifdef USE_CRYPTO
size_t os;
#endif
if ((nr = recv(call.dsock, audiopkt, 4096, 0)) <= 0)
return;
call.connstats.apkts_recv++;
call.connstats.abytes_recv += nr;
if (ph->sequence <= call.rcv_sequence) {
call.connstats.apkts_order++;
return;
}
call.connstats.apkts_lost += ((ph->sequence - call.rcv_sequence) - 1);
call.rcv_sequence = ph->sequence;
if (call.loopback)
send(call.dsock, audiopkt, nr, 0);
if (call.pdsp == -1)
return;
#ifdef USE_CRYPTO
if (call.cryptinfo.type != CALL_CRYPT_NONE) {
cr_decrypt(&audiopkt[sizeof(struct pkt_udpaudio)], nr - sizeof(struct pkt_udpaudio),
cr_audiopkt, &os);
if (codec_decode(call.rcv_aparams.compression, cr_audiopkt,
ph->datalen, &call.pdspq) == -1) {
closecall();
return;
}
} else
#endif
if (codec_decode(call.rcv_aparams.compression, &audiopkt[sizeof(struct pkt_udpaudio)],
nr - sizeof(struct pkt_udpaudio), &call.pdspq) == -1) {
closecall();
return;
}
qsz = iob_buffsize(&call.pdspq);
if ((!isplaying && qsz >= (call.pdsp_blksize * 4)) ||
(FD_ISSET(call.pdsp, &f_wset) && qsz >= (call.pdsp_blksize * 2))) {
int nw, i, reps;
if (!dropskip && qsz > call.pdsp_blksize * 10)
dropskip = 8;
if (dropskip) {
iob_delete(&call.pdspq, call.pdsp_blksize / 8);
dropskip--;
}
if (dropskip == 1 && qsz > call.pdsp_blksize * 5)
dropskip = 8;
reps = (isplaying) ? 1 : 2;
for (i = 0; i < reps; i++) {
iob_readkeep(&call.pdspq, call.pdsp_buff, call.pdsp_blksize);
if (call.ogain != NULL)
gain_apply(call.ogain, (short *)call.pdsp_buff, call.pdsp_blksize / 2);
nw = write(call.pdsp, call.pdsp_buff, call.pdsp_blksize);
if (nw > 0) {
iob_delete(&call.pdspq, nw);
isplaying = 1;
}
if (nw != call.pdsp_blksize)
break;
} /* end for */
FD_CLR(call.pdsp, &f_wset);
} /* ENDIF ISSET pdsp */
}

static void handle_connection(int listener)
{
struct sockaddr_in r_address;
struct  pkt_hello hellopkt;
int sock2;
socklen_t l = sizeof(r_address);
if ((sock2 = accept(listener, (struct sockaddr *)&r_address, &l)) == -1) {
fprintf(stderr, "Accept failed: %s\n", strerror(errno));
return;
}
if (opt_get_int("verbose"))
printf(_("Got connection from: %s.\n"),
inet_ntoa(r_address.sin_addr));
if (TEST_BIT(call.state, CALL_STATE_CONNECTED)) {
shutdown(sock2, 2);
close(sock2);
return;
}
fcntl(sock2, F_SETFL, O_RDWR | O_NONBLOCK);
call.csock = sock2;
memcpy(&call.r_address, &r_address, sizeof(r_address));
icsetenv("remote_hostip", inet_ntoa(r_address.sin_addr));
if (opt_get_int("reverse_lookup")) {
if ((call.r_hostent = gethostbyaddr((char *)&r_address.sin_addr, sizeof(r_address.sin_addr), AF_INET)) == NULL)
call.r_hostent = gethostbyname(inet_ntoa(r_address.sin_addr));
icsetenv("remote_hostname", call.r_hostent->h_name);
} else
call.r_hostent = gethostbyname(inet_ntoa(r_address.sin_addr));
hellopkt.proto_version = INTERCOM_PROTOCOL_VERSION;
strcpy(hellopkt.username, opt_get_str("username"));
strcpy(hellopkt.clientname, "Intercom " VERSION);
queuepacket(&call.cssq, PKT_TYPE_HELLO, (char *)&hellopkt, sizeof(hellopkt));
SET_BIT(call.state, CALL_STATE_CONNECTED);
}

static void get_rdspdata(void)
{
static float silent_tm = 0.0;
int nr;
size_t i;
int only_silence = 1;
struct pkt_udpaudio *ph = (struct pkt_udpaudio *)audiopkt;
size_t bs;
#ifdef USE_CRYPTO
size_t os;
#endif
if ((nr = read(call.rdsp, call.rdsp_buff, call.rdsp_blksize)) <= 0)
return;
if (call.igain != NULL)
gain_apply(call.igain, (short *)call.rdsp_buff, call.rdsp_blksize / 2);
for (i = 0; i < (call.rdsp_blksize / 2); i++) {
if ((int)abs(((short *)call.rdsp_buff)[i]) > silence_thresh) {
only_silence = 0;
silent_tm = 0.0;
break;
}
}
if (only_silence) {
silent_tm += (float)call.rdsp_blksize /
(call.snd_aparams.rate * (call.snd_aparams.bitwidth / 8));
}
if (silent_tm < silence_time)
if (codec_encode(call.snd_aparams.compression, call.rdsp_buff, nr, &call.rdspq) == -1) {
closecall();
return;
}
#ifdef USE_CRYPTO
if (call.cryptinfo.type != CALL_CRYPT_NONE)
ph = (struct pkt_udpaudio *)cr_audiopkt;
#endif
while((bs = iob_buffsize(&call.rdspq)) >= min_buffsize && bs >= call.snd_datasize) {
if (bs > max_buffsize)
bs = max_buffsize;
if ((nr = bs % call.snd_datasize))
bs -= nr;
iob_read(&call.rdspq, &audiopkt[sizeof(struct pkt_udpaudio)], bs);
ph->sequence = ++call.snd_sequence;
ph->datalen = bs;
#ifdef USE_CRYPTO
if (call.cryptinfo.type != CALL_CRYPT_NONE) {
cr_encrypt(&audiopkt[sizeof(struct pkt_udpaudio)], bs,
&cr_audiopkt[sizeof(struct pkt_udpaudio)], &os);
ph->datalen = os;
send(call.dsock, cr_audiopkt, os + sizeof(struct pkt_udpaudio), 0);
} else
#endif
send(call.dsock, audiopkt, bs + sizeof(struct pkt_udpaudio), 0);
call.connstats.apkts_sent++;
call.connstats.abytes_sent += (bs + sizeof(struct pkt_udpaudio));
}
}

static void mainloop(void)
{
while(!done) {
FD_ZERO(&f_rset);
FD_ZERO(&f_wset);
FD_SET(0, &f_rset);
if (listener != -1)
FD_SET(listener, &f_rset);
if (call.csock != -1) {
FD_SET(call.csock, &f_rset);
if (iob_buffsize(&call.cssq))
FD_SET(call.csock, &f_wset);
}
if (TEST_BIT(call.state, CALL_STATE_AUDIORCV)) {
if (isplaying && call.pdsp != -1)
FD_SET(call.pdsp, &f_wset);
FD_SET(call.dsock, &f_rset);
}
if (TEST_BIT(call.state, CALL_STATE_AUDIOSND) && call.rdsp != -1)
FD_SET(call.rdsp, &f_rset);
if (select(16, &f_rset, &f_wset, NULL, NULL) == -1)
continue;
if (FD_ISSET(0, &f_rset))
console_processkey();
else if (listener != -1 && FD_ISSET(listener, &f_rset))
handle_connection(listener);
if (call.csock != -1) {
if (FD_ISSET(call.csock, &f_rset))
get_csockdata();
if (iob_buffsize(&call.cssq) && FD_ISSET(call.csock, &f_wset)) {
size_t buffsize = iob_buffsize(&call.cssq);
char buff[buffsize];
int nw;
iob_readkeep(&call.cssq, buff, buffsize);
nw = write(call.csock, buff, buffsize);
#if (DEBUG >= 1)
if (opt_get_int("verbose"))
printf(_("Wrote %d of %d bytes to control socket.\n"), nw, buffsize);
#endif
if (nw > 0)
iob_delete(&call.cssq, nw);
}
} /* endif csock != -1 */
if (TEST_BIT(call.state, CALL_STATE_AUDIORCV)) {
if (FD_ISSET(call.dsock, &f_rset))
get_dsockdata();
else if (call.pdsp != -1 && FD_ISSET(call.pdsp, &f_wset)) {
size_t qsz = iob_buffsize(&call.pdspq);
if (!dropskip && qsz > call.pdsp_blksize * 10)
dropskip = 8;
if (dropskip) {
iob_delete(&call.pdspq, call.pdsp_blksize / 8);
dropskip--;
}
if (dropskip == 1 && qsz > call.pdsp_blksize * 5)
dropskip = 8;
if (qsz >= call.pdsp_blksize) {
iob_read(&call.pdspq, call.pdsp_buff, call.pdsp_blksize);
if (call.ogain != NULL)
gain_apply(call.ogain, (short *)call.pdsp_buff, call.pdsp_blksize / 2);
write(call.pdsp, call.pdsp_buff, call.pdsp_blksize);
} else {
ioctl(call.pdsp, SNDCTL_DSP_POST, 0);
isplaying = 0;
}
} /* endif FD_ISSET call.pdsp */
} /* endif call_state_audiorcv */
if (TEST_BIT(call.state, CALL_STATE_AUDIOSND) &&
call.rdsp != -1 && FD_ISSET(call.rdsp, &f_rset))
get_rdspdata();
} /* while done */
}

int main(int ARGC, char **ARGV)
{
const struct option long_options[] = {
{"compression", required_argument, NULL, 'c'},
{"pdevice", required_argument, NULL, 'd'},
{"rdevice", required_argument, NULL, 'D'},
{"rate", required_argument, NULL, 'r'},
{"port", required_argument, NULL, 'p'},
{"silent", no_argument, NULL, 's'},
{"verbose", no_argument, NULL, 'V'},
{"version", no_argument, NULL, 'v'},
{0, 0, 0, 0}
};
char *home, *rcfiles;
int r;
#ifdef USE_GETTEXT
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#endif
printf("Intercom version %s, Copyright (C) 2001-2003 Shane Wegner\n"
"Intercom is free software, covered by the GNU General Public License, and you\n"
"are welcome to change it and/or distribute copies of it under certain\n"
"conditions. Type \"copyright\" to see the license details.\n\n", VERSION);
if ((home = getenv("HOME")) == NULL) {
fputs(_("HOME environment variable not set.\n"), stderr);
return 1;
}
signal(SIGHUP, sighandler);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
opt_init();
refresh_userdefs();
asprintf(&rcfiles, "read --silent " SYSCONFDIR "/intercomrc %s/.intercomrc",
home);
processline(0, rcfiles);
free(rcfiles);
optind = 0;
while ((r = getopt_long(ARGC, ARGV, "c:d:D:gh:r:p:sVv", long_options,
(int *)0)) != EOF)
switch (r) {
case 'c':
if (int_compression(optarg) != -1)
opt_set_str("snd_compression", optarg);
else
fprintf(stderr, _("Compression format %s is invalid or not supported.\n"), optarg);
break;
case 'd':
opt_set_str("snd_play_device", optarg);
break;
case 'D':
opt_set_str("snd_capture_device", optarg);
break;
case 'r':
opt_set_int("snd_rate", atoi(optarg));
break;
case 'p':
opt_set_int("port", atoi(optarg));
break;
case 's':
opt_set_str("snd_capture_device", "");
break;
case 'v':
return 0;
case 'V':
opt_set_int("verbose", 1);
break;
default:
return 1;
} /* switch */
iob_init(&call.cssq);
iob_setminbytes(&call.cssq, IOB_PAGESIZE);
iob_init(&call.csrq);
iob_setminbytes(&call.csrq, IOB_PAGESIZE);
iob_init(&call.rdspq);
iob_init(&call.pdspq);
codecs_init();
if ((listener = do_listen(opt_get_int("port"))) == -1)
return 1;
console_init();
if ((ARGC - optind) == 1) {
char *call_arg[2];
call_arg[0] = "call";
call_arg[1] = ARGV[optind];
cmd_call(2, call_arg);
} else if ((ARGC - optind) > 1) {
fprintf(stderr, "%s: too many command-line options.\n", ARGV[0]);
done = 1;
}
mainloop();
console_done();
closecall();
if (listener != -1) {
shutdown(listener, 2);
close(listener);
}
return 0;
}