isdn4k-utils/pppdcapiplugin/capiplugin.c

1052 lines
25 KiB
C
Raw Normal View History

/*
2000-10-25 10:01:47 +00:00
* capiconnect.c - pppd plugin to make ISDN connections over CAPI2.0
*
2000-10-25 10:01:47 +00:00
* Copyright 2000 Carsten Paeth (calle@calle.in-berlin.de)
* Copyright 2000 AVM GmbH Berlin (info@avm.de)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <unistd.h>
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include "pppd.h"
#include "capiconn.h"
#include <malloc.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
static char *revision = "$Revision: 1.13 $";
2000-08-21 14:07:38 +00:00
static capiconn_context *ctx;
static capi_connection *conn = 0;
static int isconnected = 0;
static int connectinprogress = 0;
static unsigned applid;
#define CM(x) (1<<(x))
#define CIPMASK_ALL 0x1FFF03FF
#define CIPMASK_VOICE (CM(1)|CM(4)|CM(5)|CM(16)|CM(26))
#define CIPMASK_DATA (CM(2)|CM(3))
static unsigned long cipmask = CIPMASK_ALL;
static int controller = 1;
static capi_contrinfo cinfo = { 0 , 0, 0 };
static char *opt_controller = "1";
static char *opt_numberprefix = 0;
static char *opt_number = 0;
static char *opt_callbacknumber = 0;
static char *opt_msn = 0;
static char *opt_inmsn = 0;
static char *opt_proto = 0;
static char *opt_channels = 0;
static unsigned char AdditionalInfo[1+2+2+31];
static int opt_dialtimeout = 60;
static int opt_dialmax = 4;
static int opt_redialdelay = 5;
static int opt_cbdelay = 2;
static int opt_connectdelay = 0;
static char *opt_cli = 0;
static int opt_cbflag = 0;
static int opt_cbwait = 60;
static int opt_acceptdelayflag = 0;
typedef struct stringlist {
struct stringlist *next;
char *s;
} STRINGLIST;
static STRINGLIST *numbers;
static STRINGLIST *callbacknumbers;
static STRINGLIST *clis;
static STRINGLIST *parsed_controller;
static STRINGLIST *inmsns;
static int optcb(void)
{
return opt_cbflag = 1;
}
static int optacceptdelay(void)
{
return opt_acceptdelayflag = 1;
}
static option_t my_options[] = {
{
"controller", o_string, &opt_controller,
"capi controller specification"
},
{
"number", o_string, &opt_number,
"number to call (may be comma separated)"
},
{
"numberprefix", o_string, &opt_numberprefix,
"prefix for number"
},
{
"msn", o_string, &opt_msn,
"number to call from"
},
{
"protocol", o_string, &opt_proto,
"protocol x75, hdlc or modem"
},
{
"inmsn", o_string, &opt_inmsn,
"called number for incoming calls"
},
{
"cli", o_string, &opt_cli,
"calling number for incoming calls (may be comma separated list)"
},
{
"clicb", o_special_noarg, &optcb,
"call number and wait for callback"
},
{
"cbwait", o_int, &opt_cbwait,
"number of seconds to wait for callback"
},
{
"dialtimeout", o_int, &opt_dialtimeout,
"number of seconds to wait for connection or reject"
},
{
"dialmax", o_int, &opt_dialmax,
"number of dial retries"
},
{
"redialdelay", o_int, &opt_redialdelay,
"number of seconds to wait between dial retries"
},
{
"channels", o_string, &opt_channels,
"channel to use for leased line (may be comma separated list)"
},
{
"cbdelay", o_int, &opt_cbdelay,
"number of seconds to wait before calling back"
},
{
"cbnumber", o_string, &opt_callbacknumber,
"number to call (may be comma separated)"
},
{
"connectdelay", o_int, &opt_connectdelay,
"number of seconds to wait after connection is established"
},
{
"acceptdelay", o_special_noarg, &optacceptdelay,
"wait 1 second before accept incoming call"
},
{ NULL }
};
/* -------------------------------------------------------------------- */
static void stringlist_free(STRINGLIST **pp)
{
STRINGLIST *p, *next;
p = *pp;
while (p) {
next = p->next;
if (p->s) free(p->s);
free(p);
p = next;
}
*pp = 0;
}
static int stringlist_append_string(STRINGLIST **pp, char *s)
{
STRINGLIST *p;
for (; *pp; pp = &(*pp)->next) ;
if ((p = (STRINGLIST *)malloc(sizeof(STRINGLIST))) == 0)
return -1;
memset(p, 0, sizeof(STRINGLIST));
if ((p->s = strdup(s)) == 0) {
free(p);
return -1;
}
p->next = 0;
*pp = p;
return 0;
}
static STRINGLIST *stringlist_split(char *tosplit, char *seps)
{
STRINGLIST *p = 0;
char *str = strdup(tosplit);
char *s;
if (!str) return 0;
for (s = strtok(str, seps); s; s = strtok(0, seps)) {
if (*s == 0) continue; /* if strtok is not working correkt */
if (stringlist_append_string(&p, s) < 0) {
stringlist_free(&p);
free(str);
return 0;
}
}
free(str);
return p;
}
/* -------------------------------------------------------------------- */
static int timeoutrunning = 0;
static int timeoutshouldrun = 0;
static void timeoutfunc(void *arg)
{
unsigned char *msg = 0;
/* info("capiplugin: checking for capi messages"); */
while (capi20_get_message (applid, &msg) == 0)
capiconn_inject(applid, msg);
if (timeoutshouldrun)
timeout (timeoutfunc, 0, 1);
}
static void setup_timeout(void)
{
timeoutshouldrun = 1;
if (!timeoutrunning)
timeout (timeoutfunc, 0, 1);
}
static void unsetup_timeout(void)
{
timeoutshouldrun = 0;
if (timeoutrunning)
untimeout (timeoutfunc, 0);
timeoutrunning = 0;
}
/* -------------------------------------------------------------------- */
static void handlemessages(void)
{
unsigned char *msg = 0;
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
if (capi20_waitformessage(applid, &tv) == 0) {
if (capi20_get_message(applid, &msg) == 0)
capiconn_inject(applid, msg);
}
}
/* -------------------------------------------------------------------- */
static void dodisconnect(void)
{
time_t t;
if (!conn)
return;
(void)capiconn_disconnect(conn, 0);
t = time(0)+10;
while (conn && time(0) < t)
handlemessages();
if (conn)
fatal("capiplugin: timeout while waiting for disconnect");
}
static void setupconnection(char *num)
{
char number[256];
snprintf(number, sizeof(number), "%s%s",
opt_numberprefix ? opt_numberprefix : "", num);
if (opt_proto == 0 || strcasecmp(opt_proto, "hdlc") == 0) {
conn = capiconn_connect(ctx,
controller, /* contr */
2, /* cipvalue */
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 1, 0,
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "x75") == 0) {
conn = capiconn_connect(ctx,
controller, /* contr */
2, /* cipvalue */
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 0, 0,
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "v42bis") == 0) {
conn = capiconn_connect(ctx,
controller, /* contr */
2, /* cipvalue */
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 8, 0,
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "modem") == 0) {
conn = capiconn_connect(ctx,
controller, /* contr */
1, /* cipvalue */
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
8, 1, 0,
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else {
fatal("capiplugin: unknown protocol \"%s\"", opt_proto);
return;
}
if (opt_channels) {
info("capiplugin: leased line (%s)",
opt_proto ? opt_proto : "hdlc");
} else {
info("capiplugin: dial %s (%s)",
number, opt_proto ? opt_proto : "hdlc");
}
}
static void makeleasedline(void)
{
time_t t;
setupconnection("");
t = time(0)+opt_dialtimeout;
do {
handlemessages();
if (status != EXIT_OK && conn)
dodisconnect();
} while (time(0) < t && conn && !isconnected);
if (status != EXIT_OK)
die(status);
if (conn && isconnected) {
t = time(0)+opt_connectdelay;
do {
handlemessages();
} while (time(0) < t);
}
if (status != EXIT_OK)
die(status);
if (!conn)
fatal("capiplugin: couldn't make connection");
}
static void makeconnection(STRINGLIST *numbers)
{
time_t t;
STRINGLIST *p;
int retry = 0;
do {
for (p = numbers; p; p = p->next) {
if (retry || p != numbers) {
t = time(0)+opt_redialdelay;
do {
handlemessages();
if (status != EXIT_OK)
die(status);
} while (time(0) < t);
}
setupconnection(p->s);
t = time(0)+opt_dialtimeout;
do {
handlemessages();
if (status != EXIT_OK && conn)
dodisconnect();
} while (time(0) < t && conn && !isconnected);
if (conn && isconnected)
goto connected;
if (status != EXIT_OK)
die(status);
}
} while (++retry < opt_dialmax);
connected:
if (!conn)
fatal("capiplugin: couldn't make connection after %d retries",
retry);
}
static void makeconnection_with_callback(void)
{
STRINGLIST *p;
time_t t;
for (p = numbers; p; p = p->next) {
setupconnection(p->s);
info("capiplugin: wait for call reject");
/* Wait specific time for the server rejecting the call */
t = time(0)+opt_dialtimeout;
do {
handlemessages();
if (status != EXIT_OK)
die(status);
} while (time(0) < t && conn && !isconnected);
if (!conn) { /* Call has been rejected */
(void) capiconn_listen(ctx, controller, cipmask, 0);
info("capiplugin: waiting for callback...");
/* Wait for server calling back */
t = time(0)+opt_cbwait;
do {
handlemessages();
if (status != EXIT_OK) {
(void) capiconn_listen(ctx, controller, 0, 0);
die(status);
}
} while (!isconnected && time(0) < t);
if (isconnected) {
add_fd(capi20_fileno(applid));
setup_timeout();
return;
}
if (p->next == 0)
fatal("capiplugin: callback failed (no call)");
else
info("capiplugin: callback failed (no call)");
} else {
dodisconnect();
fatal("capiplugin: callback failed - other side answers the call (no reject)");
}
}
}
static void makecallback(void)
{
time_t t;
t = time(0)+opt_cbdelay;
do {
handlemessages();
if (status != EXIT_OK)
die(status);
} while (time(0) < t);
makeconnection(callbacknumbers);
}
static void waitforcall(void)
{
int try = 0;
(void) capiconn_listen(ctx, controller, cipmask, 0);
info("capiplugin: waiting for incoming call ...");
do {
handlemessages();
if (status != EXIT_OK) {
(void) capiconn_listen(ctx, controller, 0, 0);
die(status);
}
if (connectinprogress) try=1;
if (try && !connectinprogress) {
try = 0;
if (!isconnected) {
(void) capiconn_listen(ctx, controller, cipmask, 0);
info("capiplugin: waiting for incoming call ...");
}
}
} while (!isconnected);
add_fd(capi20_fileno(applid));
setup_timeout();
}
static void init_capiconn(void)
{
static int init = 0;
if (init)
return;
init = 1;
if (capiconn_addcontr(ctx, controller, &cinfo) != CAPICONN_OK) {
(void)capiconn_freecontext(ctx);
(void)capi20_release(applid);
fatal("capiplugin: add controller %d failed", controller);
return;
}
if (cinfo.ddi)
dbglog("capiplugin: contr=%d ddi=\"%s\" n=%d",
controller, cinfo.ddi, cinfo.ndigits);
else
dbglog("capiplugin: contr=%d", controller);
(void) capiconn_listen(ctx, controller, 0, 0);
handlemessages();
add_fd(capi20_fileno(applid));
setup_timeout();
}
/*
* Handle leased lines (CAPI-Bundling)
*/
static int decodechannels(char *teln, unsigned long *bmaskp, int *activep)
{
unsigned long bmask = 0;
int active = !0;
char *s;
int channel;
int i;
s = teln;
while (*s && *s == ' ') s++;
if (!*s)
fatal("capiplugin; option channels: list empty");
if (*s == 'p' || *s == 'P') {
active = 0;
s++;
}
if (*s == 'a' || *s == 'A') {
active = !0;
s++;
}
while (*s) {
int digit1 = 0;
int digit2 = 0;
if (!isdigit(*s))
goto illegal;
while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
channel = digit1;
if (channel <= 0 && channel > 30)
goto rangeerror;
if (*s == 0 || *s == ',' || *s == ' ') {
bmask |= (1 << digit1);
digit1 = 0;
if (*s) s++;
continue;
}
if (*s != '-')
goto illegal;
s++;
if (!isdigit(*s)) return -3;
while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
channel = digit2;
if (channel <= 0 && channel > 30)
goto rangeerror;
if (*s == 0 || *s == ',' || *s == ' ') {
if (digit1 > digit2)
for (i = digit2; i <= digit1 ; i++)
bmask |= (1 << i);
else
for (i = digit1; i <= digit2 ; i++)
bmask |= (1 << i);
digit1 = digit2 = 0;
if (*s) s++;
continue;
}
goto illegal;
}
if (activep) *activep = active;
if (bmaskp) *bmaskp = bmask;
return 0;
illegal:
fatal("capiplugin: option channels: illegal octet '%c'", *s);
return -1;
rangeerror:
fatal("capiplugin: option channels: channel %d out of range", channel);
return -1;
}
static int channels2capi20(char *teln, unsigned char *AdditionalInfo)
{
unsigned long bmask;
int active;
int i;
decodechannels(teln, &bmask, &active);
/* info("capiplugin: \"%s\" 0x%lx %d\n", teln, bmask, active); */
/* Length */
AdditionalInfo[0] = 2+2+31;
/* Channel: 3 => use channel allocation */
AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
/* Operation: 0 => DTE mode, 1 => DCE mode */
if (active) {
AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
} else {
AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
}
/* Channel mask array */
AdditionalInfo[5] = 0; /* no D-Channel */
for (i=1; i <= 30; i++)
AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
return 0;
}
static void check_options(void)
{
static int init = 0;
if (init)
return;
init = 1;
if ( opt_proto
&& strcasecmp(opt_proto, "hdlc")
&& strcasecmp(opt_proto, "x75")
&& strcasecmp(opt_proto, "v42bis")
&& strcasecmp(opt_proto, "modem")) {
option_error("capiplugin: unknown protocol \"%s\"", opt_proto);
die(1);
}
if (strcasecmp(opt_proto, "modem") == 0)
cipmask = CIPMASK_VOICE;
else cipmask = CIPMASK_DATA;
if (opt_channels) {
channels2capi20(opt_channels, AdditionalInfo);
if (opt_number)
option_error("capiplugin: option number ignored");
if (opt_numberprefix)
option_error("capiplugin: option numberprefix ignored");
if (opt_callbacknumber)
option_error("capiplugin: option callbacknumber ignored");
if (opt_msn)
option_error("capiplugin: option msn ignored");
if (opt_inmsn)
option_error("capiplugin: option inmsn ignored");
} else if (opt_number) {
stringlist_free(&numbers);
numbers = stringlist_split(opt_number, " \t,");
if (opt_callbacknumber)
option_error("capiplugin: option callbacknumber ignored");
} else if (opt_cbflag) {
if (opt_callbacknumber == 0) {
option_error("capiplugin: option callbacknumber missing");
die(1);
}
stringlist_free(&callbacknumbers);
callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
} else {
if (opt_callbacknumber) {
opt_cbflag = 1;
stringlist_free(&callbacknumbers);
callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
}
}
if (opt_cli) {
stringlist_free(&clis);
clis = stringlist_split(opt_cli, " \t,");
}
if (opt_inmsn) {
stringlist_free(&inmsns);
inmsns = stringlist_split(opt_inmsn, " \t,");
}
if (opt_controller) {
STRINGLIST *sl;
char *tmp;
stringlist_free(&parsed_controller);
memset(&cinfo, 0, sizeof(cinfo));
parsed_controller = stringlist_split(opt_controller, " \t,");
sl = parsed_controller;
if (!sl) goto illcontr;
tmp = sl->s;
controller = strtol(sl->s, &tmp, 10);
if (tmp == sl->s || *tmp) goto illcontr;
if (sl->next) {
sl = sl->next;
cinfo.ddi = sl->s;
if (sl->next && sl->next->s) {
sl = sl->next;
cinfo.ndigits = strtol(sl->s, &tmp, 10);
if (tmp == sl->s || *tmp) goto illcontr;
}
}
} else {
memset(&cinfo, 0, sizeof(cinfo));
controller = 1;
}
return;
illcontr:
option_error("capiplugin: illegal controller specification \"%s\"",
opt_controller);
die(1);
}
static int capi_new_phase_hook(int phase)
{
int fd;
switch (phase) {
case PHASE_DEAD:
info("capiplugin: phase dead");
if ((fd = capi20_fileno(applid)) >= 0)
remove_fd(fd);
unsetup_timeout();
dodisconnect();
break;
case PHASE_INITIALIZE:
info("capiplugin: phase initialize");
break;
case PHASE_SERIALCONN:
info("capiplugin: phase serialconn%s",
opt_cbflag ? " (callback)" : "");
check_options();
init_capiconn();
if (opt_number) {
if (opt_cbflag) {
makeconnection_with_callback();
} else {
makeconnection(numbers);
}
} else if (opt_channels) {
makeleasedline();
} else {
waitforcall();
}
break;
case PHASE_DORMANT:
check_options();
init_capiconn();
info("capiplugin: phase dormant");
break;
case PHASE_ESTABLISH:
info("capiplugin: phase establish");
break;
case PHASE_AUTHENTICATE:
info("capiplugin: phase authenticate");
break;
case PHASE_CALLBACK:
info("capiplugin: phase callback");
break;
case PHASE_NETWORK:
info("capiplugin: phase network");
break;
case PHASE_RUNNING:
info("capiplugin: phase running");
break;
case PHASE_TERMINATE:
info("capiplugin: phase terminate");
break;
case PHASE_DISCONNECT:
info("capiplugin: phase disconnect");
break;
case PHASE_HOLDOFF:
info("capiplugin: phase holdoff");
break;
}
return 0;
}
/* -------------------------------------------------------------------- */
static char *conninfo(capi_connection *p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
char *callingnumber = "";
char *callednumber = "";
if (cp->callingnumber && cp->callingnumber[0] > 2)
callingnumber = cp->callingnumber+3;
if (cp->callednumber && cp->callednumber[0] > 1)
callednumber = cp->callednumber+2;
if (debug) {
snprintf(buf, sizeof(buf),
"\"%s\" -> \"%s\" %s (pcli=0x%x/ncci=0x%x)",
callingnumber, callednumber,
cp->isincoming ? "incoming" : "outgoing",
cp->plci, cp->ncci
);
} else {
snprintf(buf, sizeof(buf),
"\"%s\" -> \"%s\" %s",
callingnumber, callednumber,
cp->isincoming ? "incoming" : "outgoing");
}
buf[sizeof(buf)-1] = 0;
return buf;
}
static void disconnected(capi_connection *cp,
int localdisconnect,
unsigned reason,
unsigned reason_b3)
{
if (reason != 0x3304 || debug) /* Another Applikation got the call */
info("capiplugin: disconnect(%s): %s 0x%04x (0x%04x) - %s",
localdisconnect ? "local" : "remote",
conninfo(cp),
reason, reason_b3, capi_info2str(reason));
conn = 0;
isconnected = 0;
connectinprogress = 0;
}
static void incoming(capi_connection *cp,
unsigned contr,
unsigned cipvalue,
char *callednumber,
char *callingnumber)
{
STRINGLIST *p;
char *s;
info("capiplugin: incoming call: %s (0x%x)", conninfo(cp), cipvalue);
if (opt_inmsn) {
for (p = inmsns; p; p = p->next) {
if ( (s = strstr(callednumber, p->s)) != 0
&& strcmp(s, p->s) == 0)
break;
}
if (!p) {
info("capiplugin: ignoring call, msn %s not in \"%s\"",
callednumber, opt_inmsn);
(void) capiconn_ignore(cp);
return;
}
} else if (opt_msn) {
if ( (s = strstr(callednumber, opt_msn)) == 0
|| strcmp(s, opt_msn) != 0) {
info("capiplugin: ignoring call, msn mismatch (%s != %s)",
opt_msn, callednumber);
(void) capiconn_ignore(cp);
return;
}
}
if (opt_cli) {
for (p = clis; p; p = p->next) {
if ( (s = strstr(callingnumber, p->s)) != 0
&& strcmp(s, p->s) == 0)
break;
}
if (!p) {
info("capiplugin: ignoring call, cli mismatch (%s != %s)",
opt_cli, callingnumber);
(void) capiconn_ignore(cp);
return;
}
} else if (opt_number) {
for (p = numbers; p; p = p->next) {
if ( (s = strstr(callingnumber, p->s)) != 0
&& strcmp(s, p->s) == 0)
break;
}
if (!p) {
info("capiplugin: ignoring call, number mismatch (%s != %s)",
opt_number, callingnumber);
(void) capiconn_ignore(cp);
return;
}
} else if (opt_acceptdelayflag) {
/*
* non cli or number match,
* give more specific listen a chance (bad)
*/
info("capiplugin: accept delayed, no cli or number match");
sleep(1);
}
switch (cipvalue) {
case 1: /* Speech */
case 4: /* 3.1 kHz audio */
case 5: /* 7 kHz audio */
case 16: /* Telephony */
case 26: /* 7 kHz telephony */
if (opt_proto && strcasecmp(opt_proto, "modem") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 8, 1, 0, 0, 0, 0, 0);
goto accepted;
} else {
info("capiplugin: ignoring speech call from %s",
callingnumber);
(void) capiconn_ignore(cp);
}
break;
case 2: /* Unrestricted digital information */
case 3: /* Restricted digital information */
if (opt_proto == 0
|| strcasecmp(opt_proto, "hdlc") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 1, 0, 0, 0, 0, 0);
goto accepted;
} else if (strcasecmp(opt_proto, "x75") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 0, 0, 0, 0, 0, 0);
goto accepted;
} else if (strcasecmp(opt_proto, "v42bis") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 8, 0, 0, 0, 0, 0);
goto accepted;
} else {
info("capiplugin: ignoring digital call from %s",
callingnumber);
(void) capiconn_ignore(cp);
}
break;
case 17: /* Group 2/3 facsimile */
info("capiplugin: ignoring fax call from %s",
callingnumber);
(void) capiconn_ignore(cp);
break;
default:
info("capiplugin: ignoring type %d call from %s",
cipvalue, callingnumber);
(void) capiconn_ignore(cp);
break;
}
return;
accepted:
connectinprogress = 1;
(void) capiconn_listen(ctx, controller, 0, 0);
return;
callback:
(void) capiconn_listen(ctx, controller, 0, 0);
info("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
capiconn_reject(cp);
makecallback();
return;
}
static void connected(capi_connection *cp, _cstruct NCPI)
{
capi_conninfo *p = capiconn_getinfo(cp);
char *callingnumber = "";
char *callednumber = "";
char buf[PATH_MAX];
char *tty;
int retry = 0;
int serrno;
info("capiplugin: connected: %s", conninfo(cp));
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
serrno = errno;
while (tty == 0 && serrno == ESRCH) {
if (++retry > 4)
break;
info("capiplugin: capitty not ready, waiting for driver ...");
sleep(1);
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
serrno = errno;
}
if (tty == 0) {
if (serrno == EINVAL) {
fatal("capiplugin: failed to get tty devname - CAPI Middleware Support not enabled in kernel ?");
}
fatal("capiplugin: failed to get tty devname - %s (%d)",
strerror(serrno), serrno);
}
if (access(tty, 0) != 0 && errno == ENOENT) {
fatal("capiplugin: tty %s doesn't exist - CAPI Filesystem Support not enabled in kernel or not mounted ?", tty);
}
info("capiplugin: using %s: %s", tty, conninfo(cp));
strcpy(devnam, tty);
if (opt_connectdelay)
sleep(opt_connectdelay);
if (p->callingnumber && p->callingnumber[0] > 2)
callingnumber = p->callingnumber+3;
if (p->callednumber && p->callednumber[0] > 1)
callednumber = p->callednumber+2;
script_setenv("CALLEDNUMBER", callednumber, 0);
script_setenv("CALLINGNUMBER", callingnumber, 0);
sprintf(buf, "%d", p->cipvalue); script_setenv("CIPVALUE", buf, 0);
sprintf(buf, "%d", p->b1proto); script_setenv("B1PROTOCOL", buf, 0);
sprintf(buf, "%d", p->b2proto); script_setenv("B2PROTOCOL", buf, 0);
sprintf(buf, "%d", p->b3proto); script_setenv("B3PROTOCOL", buf, 0);
isconnected = 1;
connectinprogress = 0;
}
void chargeinfo(capi_connection *cp, unsigned long charge, int inunits)
{
if (inunits) {
info("capiplugin: %s: charge in units: %d", conninfo(cp), charge);
} else {
info("capiplugin: %s: charge in currency: %d", conninfo(cp), charge);
}
}
void put_message(unsigned appid, unsigned char *msg)
{
unsigned err;
err = capi20_put_message (appid, msg);
if (err)
fatal("capiplugin: putmessage(appid=%d) = 0x%x", appid, err);
}
/* -------------------------------------------------------------------- */
capiconn_callbacks callbacks = {
malloc: malloc,
free: free,
disconnected: disconnected,
incoming: incoming,
connected: connected,
received: 0,
datasent: 0,
chargeinfo: chargeinfo,
capi_put_message: put_message,
debugmsg: dbglog,
infomsg: info,
errmsg: error
};
void plugin_init(void)
{
int serrno;
int err;
info("capiplugin: %s", revision);
info("capiconn: %s", capiconn_version());
add_options(my_options);
if ((err = capi20_register (30, 8, 2048, &applid)) != 0) {
serrno = errno;
fatal("capiplugin: CAPI_REGISTER failed - %s (0x%04x) [%s (%d)]",
capi_info2str(err), err,
strerror(serrno), errno);
return;
}
if (capi20ext_set_flags(applid, 1) < 0) {
serrno = errno;
(void)capi20_release(applid);
fatal("capiplugin: failed to set highjacking mode - %s (%d)",
strerror(serrno), serrno);
return;
}
if ((ctx = capiconn_getcontext(applid, &callbacks)) == 0) {
(void)capi20_release(applid);
fatal("capiplugin: get_context failed");
return;
}
new_phase_hook = capi_new_phase_hook;
}