Logo Search packages:      
Sourcecode: isdnutils version File versions  Download package

capiplugin.c

/*
 * capiconnect.c - pppd plugin to make ISDN connections over CAPI2.0
 *
 * Copyright 2000-2004 Carsten Paeth (calle@calle.in-berlin.de)
 * Copyright 2000-2004 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 <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "pppd.h"
#include "capiconn.h"
#include <malloc.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
#include <netinet/in.h>
#include <linux/if.h>


static char *revision = "$Revision: 1.36 $";

/* -------------------------------------------------------------------- */

#define AVMADSLPARAMFILE "/etc/drdsl/adsl.conf"

#define DIM(array)      (sizeof(array)/sizeof((array)[0]))

/* -------------------------------------------------------------------- */

#include "patchlevel.h"
#ifdef VERSION
char pppd_version[] = VERSION;
#endif

#define PPPVersion(v1,v2,v3,v4) ((v1)*1000000+(v2)*10000+(v3)*100+(v4))

/* -------------------------------------------------------------------- */

#if PPPVER >= PPPVersion(2,4,1,0)
#define _timeout(a,b,c) timeout(a, b, c, 0);
#else
#define _timeout(a,b,c) timeout(a, b, c);
#endif
#if PPPVER >= PPPVersion(2,4,0,0)
#define _script_setenv(a,b) script_setenv(a,b,0)
#else
#define _script_setenv(a,b) script_setenv(a,b)
#endif

/* -------------------------------------------------------------------- */

static int exiting = 0;

/* -------------------------------------------------------------------- */

00066 typedef struct stringlist {
    struct stringlist *next;
      char              *s;
} STRINGLIST;

/* -------------------------------------------------------------------- */

static int curphase = -1;
static int wakeupnow = 0;

/* -------------------------------------------------------------------- */

static void handlemessages(void) ;
static int shmatch(char *string, char *expr);
static void stringlist_free(STRINGLIST **pp);
static int stringlist_append_string(STRINGLIST **pp, char *s);
static STRINGLIST *stringlist_split(char *tosplit, char *seps);
static void makecallback(void);
static char *phase2str(int phase);
static void wakeupdemand(void);

/* -------------------------------------------------------------------- */

static capiconn_context *ctx;
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 char *opt_controller[4];

00101 static struct contrparams {
  int             controller;
  capi_contrinfo  cinfo;
  char           *opt_numberprefix;
  char           *opt_msn;
  char           *opt_inmsn;
  STRINGLIST     *inmsns;
} contrparams[4] = {
   { 1, { 0, 0, 0 } },
};

static struct contrparams *actcontroller = &contrparams[0];

/*
 * numbers
 */
static char *opt_number = 0;
static char *opt_callbacknumber = 0;
static char *opt_cli = 0;
static STRINGLIST *numbers;
static STRINGLIST *callbacknumbers;
static STRINGLIST *clis;
/*
 * protocol
 */
#define PROTO_HDLC            0
#define PROTO_X75       1
#define PROTO_V42BIS          2
#define PROTO_MODEM           3
#define PROTO_V110_ASYNC      4
#define PROTO_V120_ASYNC      5
#define PROTO_ADSLPPPOE       6
#define PROTO_ADSLPPPOA       7
#define PROTO_ADSLPPPOALLC    8
#define PROTO_ANALOGMODEM     9
static char *opt_proto = "hdlc";
static int proto = PROTO_HDLC;
static int opt_avmadsl = 0;
static int opt_vpi = -1; /* T-DSL: 1  */
static int opt_vci = -1; /* T-DSL: 32 */
/*
 * leased line
 */
static char *opt_channels = 0;
static unsigned char AdditionalInfo[1+2+2+31];
static unsigned char B1Config[1+2+2+2];
/*
 * redialing
 */
static int opt_dialtimeout = 60;
static int opt_dialmax = 4;
static int opt_redialdelay = 5;
/*
 * callback & COSO
 */
static int opt_cbdelay = 2;
static int opt_cbflag  = 0;
static int opt_cbwait = 60;
#define COSO_CALLER     0
#define COSO_LOCAL      1
#define COSO_REMOTE     2
static char *opt_coso = 0;
static int coso = COSO_CALLER;
/*
 * connect delay
 */
static int opt_connectdelay = 0;
/*
 * accept delay
 */
static int opt_acceptdelayflag  = 0;
/*
 * demand wakeup by phone call
 */
static int opt_voicecallwakeup  = 0;

/* -------------------------------------------------------------------- */

static int optcb(void) { return opt_cbflag = 1; }
static int optacceptdelay(void) { return opt_acceptdelayflag = 1; }
static int optvoicecallwakeup(void) { return opt_voicecallwakeup = 1; }
static int optavmadsl(void) { return opt_avmadsl = 1; }

static option_t my_options[] = {

/* controller */

      {
            "controller", o_string, &opt_controller[0],
            "capi controller specification"
      },
      {
            "numberprefix", o_string, &contrparams[0].opt_numberprefix,
            "prefix for number"
        },
      {
            "msn", o_string, &contrparams[0].opt_msn,
            "number to call from"
        },
      {
            "inmsn", o_string, &contrparams[0].opt_inmsn,
            "called number for incoming calls"
        },

/* controller1 */

      {
            "controller1", o_string, &opt_controller[1],
            "capi controller specification"
      },
      {
            "numberprefix1", o_string, &contrparams[1].opt_numberprefix,
            "prefix for number"
        },
      {
            "msn1", o_string, &contrparams[1].opt_msn,
            "number to call from"
        },
      {
            "inmsn1", o_string, &contrparams[1].opt_inmsn,
            "called number for incoming calls"
        },

/* controller2 */

      {
            "controller2", o_string, &opt_controller[2],
            "capi controller specification"
      },
      {
            "numberprefix2", o_string, &contrparams[2].opt_numberprefix,
            "prefix for number"
        },
      {
            "msn2", o_string, &contrparams[2].opt_msn,
            "number to call from"
        },
      {
            "inmsn2", o_string, &contrparams[2].opt_inmsn,
            "called number for incoming calls"
        },

/* controller3 */

      {
            "controller3", o_string, &opt_controller[3],
            "capi controller specification"
      },
      {
            "numberprefix3", o_string, &contrparams[3].opt_numberprefix,
            "prefix for number"
        },
      {
            "msn3", o_string, &contrparams[3].opt_msn,
            "number to call from"
        },
      {
            "inmsn3", o_string, &contrparams[3].opt_inmsn,
            "called number for incoming calls"
        },

/* */

      {
            "number", o_string, &opt_number,
            "number to call (may be comma separated)"
        },
      {
            "protocol", o_string, &opt_proto,
            "protocol x75, hdlc, modem, adslpppoe"
        },
      {
            "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"
      },
      {
            "coso", o_string, &opt_coso,
            "COSO: caller,local or remote"
        },
      {
            "voicecallwakeup", o_special_noarg, &optvoicecallwakeup,
            "call number and wait for callback"
      },
      {
            "vpi", o_int, &opt_vpi,
            "VPI for Fritz!Card DSL"
      },
      {
            "vci", o_int, &opt_vci,
            "VCI for Fritz!Card DSL"
      },
      {
            "avmadsl", o_special_noarg, &optavmadsl,
            "read DSL parameters from /etc/drdsl/adsl.conf"
      },
      { NULL }
};

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */

static int nextcontroller(void)
{
   int i;
   for (i = 0; i < DIM(contrparams); i++) {
      if (&contrparams[i] <= actcontroller)
       continue;
      if (contrparams[i].controller == 0)
       continue;
      actcontroller = &contrparams[i];
      return 1;
   }
   return 0;
}

/* -------------------------------------------------------------------- */
/* -------- 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 plugin_check_options(void)
{
      static int init = 0;
      int i;

      if (init)
            return;
      init = 1;

      if (opt_avmadsl) {
         if (access(AVMADSLPARAMFILE, R_OK) == 0) {
            dbglog("loading adsl parameters from %s ...", AVMADSLPARAMFILE);
              if (options_from_file (AVMADSLPARAMFILE, 0, 0, 0) == 0)
               die(1);
         } else {
             dbglog("using default adsl parameters");
             if (!opt_controller[0]) opt_controller[0] = "2";
             opt_proto = "adslpppoe";
         }
      }

      /*
       * protocol
       */
      if (strcasecmp(opt_proto, "hdlc") == 0) {
         proto = PROTO_HDLC;
      } else if (strcasecmp(opt_proto, "x75") == 0) {
         proto = PROTO_X75;
      } else if (strcasecmp(opt_proto, "v42bis") == 0) {
         proto = PROTO_V42BIS;
      } else if (strcasecmp(opt_proto, "modem") == 0) {
         proto = PROTO_MODEM;
      } else if (strcasecmp(opt_proto, "v110async") == 0) {
         proto = PROTO_V110_ASYNC;
      } else if (strcasecmp(opt_proto, "v120async") == 0) {
         proto = PROTO_V120_ASYNC;
      } else if (strcasecmp(opt_proto, "adslpppoe") == 0) {
         proto = PROTO_ADSLPPPOE;
         if (!opt_channels) opt_channels = "1";
      } else if (strcasecmp(opt_proto, "adslpppoa") == 0) {
         proto = PROTO_ADSLPPPOA;
         if (!opt_channels) opt_channels = "1";
      } else if (strcasecmp(opt_proto, "adslpppoallc") == 0) {
         proto = PROTO_ADSLPPPOALLC;
         if (!opt_channels) opt_channels = "1";
      } else if (strcasecmp(opt_proto, "analogmodem") == 0) {
         proto = PROTO_ANALOGMODEM;
      } else {
         option_error("capiplugin: unknown protocol \"%s\"", opt_proto);
         die(1);
      }
        if (strcasecmp(opt_proto, "modem") == 0 || 
          strcasecmp(opt_proto, "analogmodem") == 0)
            cipmask = CIPMASK_VOICE;
      else cipmask = CIPMASK_DATA;

      /*
       * coso
       */
      if (opt_coso == 0) {
         if (opt_cbflag) {
            if (opt_number) coso = COSO_REMOTE;
            else coso = COSO_LOCAL;
         } else {
            coso = COSO_CALLER;
         }
      } else if (strcasecmp(opt_coso, "caller") == 0) {
         coso = COSO_CALLER;
      } else if (strcasecmp(opt_coso, "local") == 0) {
         coso = COSO_LOCAL;
      } else if (strcasecmp(opt_coso, "remote") == 0) {
         coso = COSO_REMOTE;
      } else {
          option_error("capiplugin: wrong value for option coso");
          die(1);
      }
      if (opt_cbflag) {
         if (opt_coso) {
            option_error("capiplugin: option clicb ignored");
         } else if (coso == COSO_REMOTE) {
            dbglog("clicb selected coso remote");
         } else if (coso == COSO_LOCAL) {
            dbglog("clicb selected coso local");
         } else {
            option_error("capiplugin: option clicb ignored");
         }
      }

      /*
       * leased line
       */
      if (opt_channels) {
            channels2capi20(opt_channels, AdditionalInfo);
            if (opt_number)
                  option_error("capiplugin: option number ignored");
            if (contrparams[0].opt_numberprefix)
                  option_error("capiplugin: option numberprefix ignored");
            if (opt_callbacknumber)
                  option_error("capiplugin: option callbacknumber ignored");
            if (contrparams[0].opt_msn)
                  option_error("capiplugin: option msn ignored");
            if (contrparams[0].opt_inmsn)
                  option_error("capiplugin: option inmsn ignored");
      /*
       * dialout
       */
      } else if (opt_number) {
            stringlist_free(&numbers);
            numbers = stringlist_split(opt_number, " \t,");
      /*
       * dialin
       */
      } else {
         if (coso == COSO_LOCAL) {
            if (opt_callbacknumber == 0) {
             option_error("capiplugin: option callbacknumber missing");
             die(1);
             }
         }
      }

      if (opt_callbacknumber) {
         stringlist_free(&callbacknumbers);
         callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
      }

      /*
       * controller
       */
      for (i = 0; i < DIM(opt_controller); i++) {
            if (opt_controller[i]) {
                  STRINGLIST *parsed_controller = 0;
                  STRINGLIST *sl;
                  char *tmp;
                  memset(&contrparams[i].cinfo, 0, sizeof(capi_contrinfo));
                    parsed_controller = stringlist_split(opt_controller[i], " \t,");
                    sl = parsed_controller;
                    if (!sl) goto illcontr;
                    tmp = sl->s;
                    contrparams[i].controller = strtol(sl->s, &tmp, 10);
                    if (tmp == sl->s || *tmp) goto illcontr;
                    if (sl->next) {
                          sl = sl->next;
                          contrparams[i].cinfo.ddi = strdup(sl->s);
                          if (contrparams[i].cinfo.ddi == 0)
                             goto illcontr;
                          if (sl->next && sl->next->s) {
                                  sl = sl->next;
                                contrparams[i].cinfo.ndigits = strtol(sl->s, &tmp, 10);
                                if (tmp == sl->s || *tmp) {
                                       stringlist_free(&parsed_controller);
                                   goto illcontr;
                                  }
                          }
                    }
                    stringlist_free(&parsed_controller);
              } else {
                    memset(&contrparams[i].cinfo, 0, sizeof(capi_contrinfo));
                    contrparams[i].controller = 1;
              }
            continue;
illcontr:
              option_error("capiplugin: illegal controller specification \"%s\"",
                        opt_controller[i]);
              die(1);
      }

      /*
       * cli & inmsn
       */
      if (opt_cli) {
            STRINGLIST *sl;
            char *old;
            stringlist_free(&clis);
            clis = stringlist_split(opt_cli, " \t,");
            for (sl = clis; sl; sl = sl->next) {
               if (*sl->s != '*') {
                  old = sl->s;
                  sl->s = (char *)malloc(strlen(old)+2);
                  if (sl->s) {
                   *sl->s = '*';
                   strcpy(sl->s+1, old);
                   free(old);
                  } else {
                   sl->s = old;
                       option_error("capiplugin: prepend '*' to cli failed");
                  }
               }
            }
      }
      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].opt_inmsn) {
                  stringlist_free(&contrparams[i].inmsns);
                  contrparams[i].inmsns = stringlist_split(contrparams[i].opt_inmsn, " \t,");
            }
      }

      /*
       * dial on demand
       */
      if (demand) {
         if (!opt_number && !opt_channels) {
             option_error("capiplugin: number or channels missing for demand");
             die(1);
         }
         if (opt_voicecallwakeup)
            cipmask |= CIPMASK_VOICE;
      } else if (opt_voicecallwakeup) {
         option_error("capiplugin: option voicecallwakeup ignored");
         opt_voicecallwakeup = 0;
      }

      if (   proto == PROTO_ADSLPPPOE
          || proto == PROTO_ADSLPPPOA
          || proto == PROTO_ADSLPPPOALLC) {
         if (opt_cbflag) {
            option_error("capiplugin: option cbflag not alloed with protocol adslpppoe");
            die(1);
         }
         if (opt_coso) {
            option_error("capiplugin: option coso not alloed with protocol adslpppoe");
            die(1);
         }
           if (opt_number) {
            option_error("capiplugin: option number not alloed with protocol adslpppoe");
            die(1);
         }
           if (opt_callbacknumber) {
            option_error("capiplugin: option cbnumber not alloed with protocol adslpppoe");
            die(1);
         }
           if (contrparams[0].opt_inmsn) {
            option_error("capiplugin: option inmsn ignored");
            contrparams[0].opt_inmsn = 0;
         }
           if (opt_cli) {
            option_error("capiplugin: option cli ignored");
            opt_cli = 0;
         }
           if (proto == PROTO_ADSLPPPOA || proto == PROTO_ADSLPPPOALLC) {
            if (opt_vpi == -1 || opt_vci == -1) {
               option_error("capiplugin: need options vci and vpi");
             die(1);
            }
         } else {
            if (   (opt_vpi == -1 && opt_vci != -1)
              || (opt_vpi != -1 && opt_vci == -1)) {
               option_error("capiplugin: need options vci and vpi");
             die(1);
            }
         }
           B1Config[0] = 0;
         if (opt_vpi != -1) {
            int vcctype = 1;
            if (proto == PROTO_ADSLPPPOA) vcctype = 3;
            else if (proto == PROTO_ADSLPPPOALLC) vcctype = 2;
            B1Config[0] = 6;
              B1Config[1] = vcctype & 0xff;
              B1Config[2] = (vcctype >> 8) & 0xff;
            B1Config[3] = opt_vpi & 0xff;
            B1Config[4] = (opt_vpi >> 8) & 0xff;
            B1Config[5] = opt_vci & 0xff;
            B1Config[6] = (opt_vci >> 8) & 0xff;
         }
      }
}

/* -------------------------------------------------------------------- */
/* -------- Match with * and ? ---------------------------------------- */
/* -------------------------------------------------------------------- */

static int shmatch(char *string, char *expr)
{
   char *match = expr;
   char *s = string;
   char *p;
   int escape = 0;

   while (*match && *s) {
      if (escape) {
           if (*s != *match)
                return 0;
           s++;
             match++;
      } else if (*match == '\\') {
         match++;
         escape = 1;
      } else if (*match == '*') {
             match++;
             if (*match == 0) 
                return 1;
         if (*match == '\\') 
            match++;
         while ((p = strchr(s, *match)) != 0) {
                if (shmatch(p+1, match+1))
                     return 1;
                s = p + 1;
         }
             return 0;
        } else if (*match == '?') {
           s++;
             match++;
        } else {
           if (*s != *match)
                return 0;
           s++;
             match++;
        }
   }
   if (*s == 0) {
      if (*match == 0) return 1;
      if (*match == '*' && match[1] == 0) return 1;
   }
   return 0;
}

/* -------------------------------------------------------------------- */
/* -------- STRINGLIST for parsing ------------------------------------ */
/* -------------------------------------------------------------------- */

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;
}

/* -------------------------------------------------------------------- */
/* -------- connection management ------------------------------------- */
/* -------------------------------------------------------------------- */

00829 typedef struct conn {
    struct conn     *next;
    capi_connection *conn;
    int              type;
#define CONNTYPE_OUTGOING     0
#define CONNTYPE_INCOMING     1
#define CONNTYPE_IGNORE       2
#define CONNTYPE_REJECT       3
#define CONNTYPE_FOR_CALLBACK 4
    int              inprogress;    
    int              isconnected;    
} CONN;

static CONN *connections;

/* -------------------------------------------------------------------- */

static CONN *conn_remember(capi_connection *conn, int type)
{
      CONN *p, **pp;
      for (pp = &connections; *pp; pp = &(*pp)->next) ;
      if ((p = (CONN *)malloc(sizeof(CONN))) == 0) {
                  int serrno = errno;
                  fatal("capiplugin: malloc failed - %s (%d)",
                                          strerror(serrno), serrno);
            return 0;
      }
      memset(p, 0, sizeof(CONN));
      p->conn = conn;
      p->type = type;
      p->next = 0;
      switch (type) {
           case CONNTYPE_OUTGOING:
           case CONNTYPE_INCOMING:
           case CONNTYPE_FOR_CALLBACK:
            p->inprogress = 1;
            p->isconnected = 0;
            break;
         default:
            break;
      }
      *pp = p;
      return p;
}

static int conn_forget(capi_connection *conn)
{
      CONN **pp, *p;
      for (pp = &connections; *pp && (*pp)->conn != conn; pp = &(*pp)->next) ;
      if (*pp) {
         p = *pp;
         *pp = (*pp)->next;
         free(p);
         return 0;
        }
      return -1;
}

static CONN *conn_find(capi_connection *cp)
{
      CONN *p;
      for (p = connections; p; p = p->next) {
         if (p->conn == cp)
            return p;
      }
      return 0;
}

/* -------------------------------------------------------------------- */

static int conn_connected(capi_connection *conn)
{
      CONN *p;
      for (p = connections; p; p = p->next) {
         if (p->conn == conn) {
            p->isconnected = 1;
            p->inprogress = 0;
            return 0;
         }
      }
      fatal("capiplugin: connected connection not found ??");
      return -1;
}

/* -------------------------------------------------------------------- */

static int conn_incoming_inprogress(void)
{
    CONN *p;
    for (p = connections; p; p = p->next) {
       if (p->type == CONNTYPE_INCOMING)
        return p->inprogress;    
    }
    return 0;
}

static int conn_incoming_connected(void)
{
    CONN *p;
    for (p = connections; p; p = p->next) {
       if (p->type == CONNTYPE_INCOMING)
        return p->isconnected;    
    }
    return 0;
}

static int conn_incoming_exists(void)
{
    CONN *p;
    for (p = connections; p; p = p->next) {
       if (p->type == CONNTYPE_INCOMING)
        return p->isconnected || p->inprogress;    
    }
    return 0;
}

/* -------------------------------------------------------------------- */

static int conn_inprogress(capi_connection *cp)
{
    CONN *p;
    for (p = connections; p; p = p->next) {
       if (p->conn == cp)
        return p->inprogress;    
    }
    return 0;
}

static int conn_isconnected(capi_connection *cp)
{
    CONN *p;
    if (cp) {
       for (p = connections; p; p = p->next) {
          if (p->conn == cp)
           return p->isconnected;    
       }
    } else {
       for (p = connections; p; p = p->next) {
        if (p->isconnected)
           return 1;
       }
    }
    return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */


/* -------------------------------------------------------------------- */
/* -------- Handle CAPI messages every second ------------------------- */
/* -------------------------------------------------------------------- */

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 (wakeupnow && curphase == PHASE_DORMANT)
           wakeupdemand();
      if (timeoutshouldrun)
            _timeout (timeoutfunc, 0, 1);
}

static void setup_timeout(void)
{
      timeoutshouldrun = 1;
      if (!timeoutrunning)
            _timeout (timeoutfunc, 0, 1);
}

#if PPPVER >= PPPVersion(2,4,0,0)
static void unsetup_timeout(void)
{
      timeoutshouldrun = 0;
      if (timeoutrunning)
            untimeout (timeoutfunc, 0);
      timeoutrunning = 0;
}
#endif

/* -------------------------------------------------------------------- */
/* -------- demand & wakeup pppd -------------------------------------- */
/* -------------------------------------------------------------------- */

static u_int32_t ouripaddr;
static u_int32_t gwipaddr;
static int nwakeuppackets = 0;

static void setupincoming_for_demand(void)
{
    struct ifreq ifr; 
    int serrno;
    int sock_fd;
    int i;

    nwakeuppackets = 0;
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
       serrno = errno;
       fatal("capiplugin: socket(AF_INET,SOCK_DRAM): failed - %s (%d)",
                        strerror(serrno), serrno);
       return;
    }
    
    memset (&ifr, '\0', sizeof (ifr));
    strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
    if (ioctl(sock_fd, SIOCGIFADDR, (caddr_t) &ifr) < 0) {
       serrno = errno;
       close(sock_fd);
       fatal("capiplugin: ioctl(SIOCGIFADDR): failed - %s (%d)",
                        strerror(serrno), serrno);
       return;
    }
    ouripaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;

    memset (&ifr, '\0', sizeof (ifr));
    strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
    if (ioctl(sock_fd, SIOCGIFDSTADDR, (caddr_t) &ifr) < 0) {
       serrno = errno;
       close(sock_fd);
       fatal("capiplugin: ioctl(SIOCGIFDSTADDR): failed - %s (%d)",
                        strerror(serrno), serrno);
       return;
    } 
    gwipaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;

    close(sock_fd);
    for (i = 0; i < DIM(contrparams); i++) {
      if (contrparams[i].controller == 0)
         continue;
        (void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
    }
    info("capiplugin: waiting for demand wakeup ...");
}

static void wakeupdemand(void)
{
    char data[] = "Ignore, is for demand wakeup";
    struct sockaddr_in laddr, raddr;
    size_t addrlen;
    int sock_fd;
    int serrno;

    if (curphase != PHASE_DORMANT) {
       info("capiplugin: wakeup not possible in phase %s, delayed",
            phase2str(curphase));
       wakeupnow++;
       return;
    }

    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
       serrno = errno;
       fatal("capiplugin: socket(AF_INET,SOCK_DRAM): failed - %s (%d)",
                        strerror(serrno), serrno);
       return;
    }
    memset (&laddr, '\0', sizeof(laddr));
    laddr.sin_family = AF_INET;
    laddr.sin_addr.s_addr = ouripaddr;
    laddr.sin_port = 0;
    if (bind(sock_fd, (struct sockaddr *)&laddr, sizeof(laddr)) != 0) {
       serrno = errno;
       close(sock_fd);
       fatal("capiplugin: bind(%I): failed - %s (%d)",
                        ouripaddr, strerror(serrno), serrno);
       return;
    }
    addrlen = sizeof(laddr);
    getsockname(sock_fd, (struct sockaddr *)&laddr, &addrlen);
    memset (&raddr, '\0', sizeof(raddr));
    raddr.sin_family = AF_INET;
    raddr.sin_addr.s_addr = gwipaddr;
    raddr.sin_port = htons(9); /* discard */
    if (connect(sock_fd, (struct sockaddr *)&raddr, sizeof(raddr)) != 0) {
       serrno = errno;
       close(sock_fd);
       fatal("capiplugin: connect(%I:%d): failed - %s (%d)",
            gwipaddr, ntohs(raddr.sin_port), strerror(serrno), serrno);
       return;
    }
    notice("capiplugin: sending wakeup packet (UDP %I:%d -> %I:%d)",
                ouripaddr, ntohs(laddr.sin_port),
                gwipaddr, ntohs(raddr.sin_port));
    if (send(sock_fd, data, sizeof(data), 0) < 0) {
       serrno = errno;
       close(sock_fd);
       fatal("capiplugin: send wakup packet failed - %s (%d)",
                        strerror(serrno), serrno);
       return;
    }
    close(sock_fd);
    nwakeuppackets++;
}

/* -------------------------------------------------------------------- */
/* -------- CAPI setup & handling ------------------------------------- */
/* -------------------------------------------------------------------- */

static void init_capiconn(void)
{
      static int init = 0;
      int i;

      if (init)
            return;
      init = 1;

      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            if (capiconn_addcontr(ctx, contrparams[i].controller, &contrparams[i].cinfo) != CAPICONN_OK) {
                  (void)capiconn_freecontext(ctx);
                  (void)capi20_release(applid);
                  fatal("capiplugin: add controller %d failed", contrparams[i].controller);
                  return;
            }
            if (contrparams[i].cinfo.ddi) 
                  dbglog("capiplugin: contr=%d ddi=\"%s\" n=%d",
                        contrparams[i].controller, contrparams[i].cinfo.ddi, contrparams[i].cinfo.ndigits);
            else
                  dbglog("capiplugin: contr=%d", contrparams[i].controller);
      }
      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
              (void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
      }
      handlemessages();
      add_fd(capi20_fileno(applid));
      setup_timeout();
}

/* -------------------------------------------------------------------- */

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(capi_connection *cp)
{
      CONN *conn;
      time_t t;

      if ((conn = conn_find(cp)) == 0)
            return;
      (void)capiconn_disconnect(cp, 0);
      conn->isconnected = conn->inprogress = 0;

      t = time(0)+10;
      do {
          handlemessages();
        } while (conn_find(cp) && time(0) < t);

        if (conn_find(cp))
            fatal("capiplugin: timeout while waiting for disconnect");
}

static void disconnectall(void)
{
      time_t t;
      CONN *p;
      int i;

      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            (void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
      }
      for (p = connections; p; p = p->next) {
            if (p->inprogress || p->isconnected) {
             p->isconnected = p->inprogress = 0;
             capiconn_disconnect(p->conn, 0);
            }
      }
      t = time(0)+10;
      do {
          handlemessages();
        } while (connections && time(0) < t);

      if (connections && !exiting)
            fatal("capiplugin: disconnectall failed");
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */

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;
}

/* -------------------------------------------------------------------- */
/* -------- reject reason handling (wuerg) ---------------------------- */
/* -------------------------------------------------------------------- */

static unsigned dreason = 0;

static int was_no_reject(void)
{
      if ((dreason & 0xff00) != 0x3400)
       return 1;
      switch (dreason) {
       case 0x34a2: /* No circuit / channel available */
          return 1;
      }
      return 0;
}

static int try_next_controller(void)
{
   switch (dreason) {
      case 0x3301:  /* Protocol error layer 1 (broken line or B-channel removed by signalling protocol */
      case 0x34a2: /* No circuit / channel available */
       return 1;
   }
   return 0;
}

/* -------------------------------------------------------------------- */
/* -------- disconnect handler ---------------------------------------- */
/* -------------------------------------------------------------------- */

static void disconnected(capi_connection *cp,
                        int localdisconnect,
                        unsigned reason,
                        unsigned reason_b3)
{
      CONN *p;

      if ((p = conn_find(cp)) == 0)
         return;
        conn_forget(cp);
      switch (p->type) {
           case CONNTYPE_INCOMING:
            break;
           case CONNTYPE_IGNORE:
           case CONNTYPE_REJECT:
            return;
           case CONNTYPE_OUTGOING:
           case CONNTYPE_FOR_CALLBACK:
            dreason = reason;
              break;

      }
      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));
}

/* -------------------------------------------------------------------- */
/* -------- incoming call handler ------------------------------------- */
/* -------------------------------------------------------------------- */

static void incoming(capi_connection *cp,
                        unsigned contr,
                        unsigned cipvalue,
                        char *callednumber,
                        char *callingnumber)
{
      struct contrparams *ctlp = 0;
      STRINGLIST *p;
        char *s;
      int i;

      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            if (contrparams[i].controller != (int)contr)
                  continue;
            ctlp = &contrparams[i];
            break;
      }

      info("capiplugin: incoming call(%u): %s (0x%x)", contr, conninfo(cp), cipvalue);
      if (!ctlp) {
                 info("capiplugin: ignoring call, controller %u not found ??",
                        contr);
                   conn_remember(cp, CONNTYPE_IGNORE);
               (void) capiconn_ignore(cp);
               return;
      }

        if (conn_incoming_exists()) {
         info("capiplugin: ignoring call, incoming connection exists");
           conn_remember(cp, CONNTYPE_IGNORE);
         (void) capiconn_ignore(cp);
           return;
      }
      
      if (ctlp->opt_inmsn) {
         for (p = ctlp->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, ctlp->opt_inmsn);
                   conn_remember(cp, CONNTYPE_IGNORE);
               (void) capiconn_ignore(cp);
               return;
           }
        } else if (ctlp->opt_msn) {
         if (   (s = strstr(callednumber, ctlp->opt_msn)) == 0
               || strcmp(s, ctlp->opt_msn) != 0) {
                 info("capiplugin: ignoring call, msn mismatch (%s != %s)",
                        ctlp->opt_msn, callednumber);
                   conn_remember(cp, CONNTYPE_IGNORE);
               (void) capiconn_ignore(cp);
               return;
           }
        }

      if (opt_cli) {
         for (p = clis; p; p = p->next) {
             if (shmatch(callingnumber, p->s))
              break;
         }
         if (!p) {
                 info("capiplugin: ignoring call, cli mismatch (%s != %s)",
                        opt_cli, callingnumber);
                   conn_remember(cp, CONNTYPE_IGNORE);
               (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);
                   conn_remember(cp, CONNTYPE_IGNORE);
               (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 (proto == PROTO_MODEM || proto == PROTO_ANALOGMODEM) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                        } else if (opt_voicecallwakeup) {
                     goto wakeupdemand;
                        } else {
                         info("capiplugin: ignoring speech call from %s",
                              callingnumber);
                           conn_remember(cp,CONNTYPE_IGNORE);
                     (void) capiconn_ignore(cp);
                  }
                  break;
            case 2:  /* Unrestricted digital information */
            case 3:  /* Restricted digital information */
                      if (proto == PROTO_HDLC) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                      } else if (proto == PROTO_X75) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                      } else if (proto == PROTO_V42BIS) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                      } else if (proto == PROTO_V110_ASYNC) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                      } else if (proto == PROTO_V120_ASYNC) {
                     if (demand) goto wakeupmatch;
                     if (coso == COSO_LOCAL) goto callback;
                     goto accept;
                  } else {
                         info("capiplugin: ignoring digital call from %s",
                              callingnumber);
                           conn_remember(cp,CONNTYPE_IGNORE);
                     (void) capiconn_ignore(cp);
                  }
                  break;
            case 17: /* Group 2/3 facsimile */
                    info("capiplugin: ignoring fax call from %s",
                              callingnumber);
                        conn_remember(cp,CONNTYPE_IGNORE);
                  (void) capiconn_ignore(cp);
                  break;
            default:
                    info("capiplugin: ignoring type %d call from %s",
                              cipvalue, callingnumber);
                        conn_remember(cp,CONNTYPE_IGNORE);
                  (void) capiconn_ignore(cp);
                  break;
      }
      return;

callback:
      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            (void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
      }
      dbglog("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
      conn_remember(cp, CONNTYPE_REJECT);
      capiconn_reject(cp);
        makecallback();
      return;

wakeupdemand:
      dbglog("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
      conn_remember(cp, CONNTYPE_REJECT);
      capiconn_reject(cp);
        wakeupdemand();
      return;

wakeupmatch:
      if (coso == COSO_LOCAL)
         goto wakeupdemand;

accept:
      switch (proto) {
         default:
         case PROTO_HDLC:
            (void) capiconn_accept(cp, 0, 1, 0, 0, 0, 0, 0);
            break;
         case PROTO_X75:
            (void) capiconn_accept(cp, 0, 0, 0, 0, 0, 0, 0);
            break;
           case PROTO_V42BIS:
            (void) capiconn_accept(cp, 0, 8, 0, 0, 0, 0, 0);
            break;
           case PROTO_MODEM:
            (void) capiconn_accept(cp, 8, 1, 0, 0, 0, 0, 0);
            break;
           case PROTO_V110_ASYNC:
            (void) capiconn_accept(cp, 2, 1, 0, 0, 0, 0, 0);
            break;
           case PROTO_V120_ASYNC:
            (void) capiconn_accept(cp, 0, 9, 0, 0, 0, 0, 0);
            break;
           case PROTO_ANALOGMODEM:
            (void) capiconn_accept(cp, 7, 7, 7, 0, 0, 0, 0);
            break;
      }
      conn_remember(cp, CONNTYPE_INCOMING);
      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            (void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
      }
}

/* -------------------------------------------------------------------- */
/* -------- connection established handler ---------------------------- */
/* -------------------------------------------------------------------- */

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;
         dbglog("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);
      }
      retry = 0;
      while (access(tty, 0) != 0 && errno == ENOENT) {
         if (++retry > 4)  {
            fatal("capiplugin: tty %s doesn't exist - CAPI Filesystem Support not enabled in kernel or not mounted ?", tty);
         }
         sleep(1);
      }
      info("capiplugin: using %s: %s", tty, conninfo(cp));
      strcpy(devnam, tty);

      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);
        _script_setenv("CALLINGNUMBER", callingnumber);
      sprintf(buf, "%d", p->cipvalue); _script_setenv("CIPVALUE", buf);
      sprintf(buf, "%d", p->b1proto); _script_setenv("B1PROTOCOL", buf);
      sprintf(buf, "%d", p->b2proto); _script_setenv("B2PROTOCOL", buf);
      sprintf(buf, "%d", p->b3proto); _script_setenv("B3PROTOCOL", buf);

      conn_connected(cp);
      if (curphase == PHASE_DORMANT)
           wakeupdemand();
}

/* -------------------------------------------------------------------- */
/* -------- charge information ---------------------------------------- */
/* -------------------------------------------------------------------- */

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);
      }
}

/* -------------------------------------------------------------------- */
/* -------- tranfer capi message to CAPI ------------------------------ */
/* -------------------------------------------------------------------- */

int put_message(unsigned appid, unsigned char *msg)
{
      unsigned err;
      err = capi20_put_message (appid, msg);
      if (err && !exiting)
            fatal("capiplugin: putmessage(appid=%d) - %s 0x%x",
                        appid, capi_info2str(err), err);
      return 0;
}

/* -------------------------------------------------------------------- */
/* -------- capiconn module setup ------------------------------------- */
/* -------------------------------------------------------------------- */

capiconn_callbacks callbacks = {
      malloc: malloc,
      free: free,

      disconnected: disconnected,
      incoming: incoming,
      connected: connected,
      received: 0, 
      datasent: 0, 
      chargeinfo: chargeinfo,
      dtmf_received: 0,

      capi_put_message: put_message,

      debugmsg: (void (*)(const char *, ...))dbglog,
      infomsg: (void (*)(const char *, ...))info,
      errmsg: (void (*)(const char *, ...))error
};

/* -------------------------------------------------------------------- */
/* -------- create a connection --------------------------------------- */
/* -------------------------------------------------------------------- */

static capi_connection *setupconnection(char *num, int awaitingreject)
{
      struct capi_connection *cp;
      char number[256];

      snprintf(number, sizeof(number), "%s%s", 
                  actcontroller->opt_numberprefix ? actcontroller->opt_numberprefix : "", num);

      if (proto == PROTO_HDLC) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        0, 1, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_X75) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        0, 0, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_V42BIS) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        0, 8, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_MODEM) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        4, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        8, 1, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_V110_ASYNC) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        2, 1, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_V120_ASYNC) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        0, 9, 0,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_ADSLPPPOE) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        0,
                        0,
                        28, 30, 30,
                        B1Config[0] ? B1Config : 0,
                        0, /* B2Config */
                        0, /* B3Config */
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_ADSLPPPOA || proto == PROTO_ADSLPPPOALLC) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        2, /* cipvalue */
                        0,
                        0,
                        28, 1, 0,
                        B1Config[0] ? B1Config : 0,
                        0, /* B2Config */
                        0, /* B3Config */
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else if (proto == PROTO_ANALOGMODEM) {
            cp = capiconn_connect(ctx,
                        actcontroller->controller, /* contr */
                        4, /* cipvalue */
                        opt_channels ? 0 : number, 
                        opt_channels ? 0 : actcontroller->opt_msn,
                        7, 7, 7,
                        0, 0, 0,
                        opt_channels ? AdditionalInfo : 0,
                        0);
      } else {
            fatal("capiplugin: unknown protocol \"%s\"", opt_proto);
            return 0;
      }
      if (opt_channels) {
            info("capiplugin: leased line (%s)", opt_proto);
      } else if (awaitingreject) {
            info("capiplugin: dialing %s (awaiting reject)", number);
      } else {
            info("capiplugin: dialing %s (%s)", number, opt_proto);
      }
        if (awaitingreject)
           conn_remember(cp, CONNTYPE_FOR_CALLBACK);
      else
           conn_remember(cp, CONNTYPE_OUTGOING);
        return cp;
}

/* -------------------------------------------------------------------- */
/* -------- connect leased line --------------------------------------- */
/* -------------------------------------------------------------------- */

static void makeleasedline(void)
{
      capi_connection *cp;
      int retry = 0;
      time_t t;

      do {
           if (retry) {
            t = time(0)+opt_redialdelay;
            do {
               handlemessages();
               if (status != EXIT_OK)
                  die(status);
            } while (time(0) < t);
           }

           cp = setupconnection("", 0);

           t = time(0)+opt_dialtimeout;
           do {
            handlemessages();
            if (status != EXIT_OK) {
               if (conn_find(cp)) {
                  info("capiplugin: pppd status %d, disconnecting ...", status);
                  dodisconnect(cp);
               } else {
                  die(status);
               }
            }
           } while (time(0) < t && conn_inprogress(cp));

           if (conn_isconnected(cp))
            goto connected;

           if (status != EXIT_OK)
            die(status);

      } while (++retry < opt_dialmax);

connected:
        if (conn_isconnected(cp)) {
         t = time(0)+opt_connectdelay;
         do {
            handlemessages();
            if (status != EXIT_OK)
             die(status);
         } while (time(0) < t);
      }

      if (status != EXIT_OK)
            die(status);

        if (!conn_isconnected(cp))
         fatal("capiplugin: couldn't make connection");
}

/* -------------------------------------------------------------------- */
/* -------- connect a dialup connection ------------------------------- */
/* -------------------------------------------------------------------- */

static void makeconnection(STRINGLIST *numbers)
{
      capi_connection *cp = 0;
      time_t t;
      STRINGLIST *p;
      int retry = 0;

      do {
         for (p = numbers; p; p = p->next) {
               if (retry || p != numbers) {
again:
                  t = time(0)+opt_redialdelay;
                  do {
                   handlemessages();
                   if (status != EXIT_OK)
                      die(status);
                  } while (time(0) < t);
               }

               cp = setupconnection(p->s, 0);

               t = time(0)+opt_dialtimeout;
               do {
                  handlemessages();
                  if (status != EXIT_OK) {
                   if (conn_find(cp)) {
                      info("capiplugin: pppd status %d, disconnecting ...", status);
                      dodisconnect(cp);
                   } else if (try_next_controller()) {
                      if (nextcontroller())
                         goto again;
                      die(status);
                   } else {
                      die(status);
                   }
                  }
               } while (time(0) < t && conn_inprogress(cp));

               if (conn_isconnected(cp))
                  goto connected;

               if (status != EXIT_OK)
                  die(status);
         }
      } while (++retry < opt_dialmax);

connected:
        if (conn_isconnected(cp)) {
         t = time(0)+opt_connectdelay;
         do {
            handlemessages();
            if (status != EXIT_OK)
             die(status);
         } while (time(0) < t);
      }

        if (!conn_isconnected(cp))
         fatal("capiplugin: couldn't make connection after %d retries",
                  retry);
}

/* -------------------------------------------------------------------- */
/* -------- dial and wait for callback -------------------------------- */
/* -------------------------------------------------------------------- */

static void makeconnection_with_callback(void)
{
      capi_connection *cp;
      STRINGLIST *p;
      time_t t;
      int retry = 0;
      int i;

      do {
         for (p = numbers; p; p = p->next) {

            if (retry || p != numbers) {
again:
               t = time(0)+opt_redialdelay;
               do {
                  handlemessages();
                  if (status != EXIT_OK)
                   die(status);
               } while (time(0) < t);
            }

            cp = setupconnection(p->s, 1);

            /* 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_inprogress(cp));

            if (conn_isconnected(cp)) {
                  dodisconnect(cp);
                  fatal("capiplugin: callback failed - other party answers the call (no reject)");
            } else if (try_next_controller()) {
                if (nextcontroller())
                   goto again;
              } else if (was_no_reject()) {
                  goto again;
            } else {
                  for (i = 0; i < DIM(contrparams); i++) {
                        if (contrparams[i].controller == 0)
                              continue;
                        (void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
                  }
                  info("capiplugin: waiting for callback...");

                  /* Wait for server calling back */
                  t = time(0)+opt_cbwait;
                  do {
                        handlemessages();
                        if (status != EXIT_OK) {
                           for (i = 0; i < DIM(contrparams); i++) {
                               if (contrparams[i].controller == 0)
                                continue;
                               (void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
                           }
                           die(status);
                        }
                  } while (!conn_incoming_connected() && time(0) < t);

                        if (conn_incoming_connected()) {
                        add_fd(capi20_fileno(applid));
                        setup_timeout();
                        return;
                  }
                  info("capiplugin: callback failed (no call)");
            }
         }
      } while (++retry < opt_dialmax);

      fatal("capiplugin: callback failed (no call)");
}

/* -------------------------------------------------------------------- */
/* -------- execute a callback ---------------------------------------- */
/* -------------------------------------------------------------------- */

static void makecallback(void)
{
      time_t t;

      t = time(0)+opt_cbdelay;
      do {
            handlemessages();
            if (status != EXIT_OK)
             die(status);
      } while (time(0) < t);

      if (callbacknumbers) 
         makeconnection(callbacknumbers);
      else makeconnection(numbers);
}

/* -------------------------------------------------------------------- */
/* -------- wait for an incoming call --------------------------------- */
/* -------------------------------------------------------------------- */

static void waitforcall(void)
{
      int try = 0;
      int i;

      for (i = 0; i < DIM(contrparams); i++) {
            if (contrparams[i].controller == 0)
                  continue;
            (void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
      }
      info("capiplugin: waiting for incoming call ...");

      do {
            handlemessages();
            if (status != EXIT_OK) {
               for (i = 0; i < DIM(contrparams); i++) {
                  if (contrparams[i].controller == 0)
                   continue;
                  (void)capiconn_listen(ctx, contrparams[i].controller, 0, 0);
               }
               die(status);
            }
            if (conn_incoming_inprogress()) try=1;
            if (try && !conn_incoming_inprogress()) {
               try = 0;
               if (!conn_incoming_connected()) {
                  for (i = 0; i < DIM(contrparams); i++) {
                     if (contrparams[i].controller == 0)
                      continue;
                     (void)capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
                  }
                  info("capiplugin: waiting for incoming call ...");
               }
            }
      } while (!conn_incoming_connected());

        if (conn_incoming_connected()) {
         time_t t = time(0)+opt_connectdelay;
         do {
            handlemessages();
            if (status != EXIT_OK)
             die(status);
         } while (time(0) < t);
      }
      add_fd(capi20_fileno(applid));
      setup_timeout();
}

/* -------------------------------------------------------------------- */
/* -------- PPPD state change hook ------------------------------------ */
/* -------------------------------------------------------------------- */

static char *phase2str(int phase)
{
      static struct tmpbuf {
            struct tmpbuf *next;
            char           buf[32];
      } buffer[] = {
            { &buffer[1] },
            { &buffer[2] },
            { &buffer[0] },
      };
      static struct tmpbuf *p = &buffer[0];

      switch (phase) {
            case PHASE_DEAD: return "dead";
            case PHASE_INITIALIZE: return "initialize";
            case PHASE_SERIALCONN: return "serialconn";
            case PHASE_DORMANT: return "dormant";
            case PHASE_ESTABLISH: return "establish";
            case PHASE_AUTHENTICATE: return "authenticate";
            case PHASE_CALLBACK: return "callback";
            case PHASE_NETWORK: return "network";
            case PHASE_RUNNING: return "running";
            case PHASE_TERMINATE: return "terminate";
            case PHASE_DISCONNECT: return "disconnect";
            case PHASE_HOLDOFF: return "holdoff";
      }
      p = p->next;
      sprintf(p->buf,"unknown-%d", phase);
      return p->buf;
}

/* -------------------------------------------------------------------- */

static int capi_new_phase_hook(int phase)
{
      if (phase == curphase) {
         info("capiplugin: phase %s, again.", phase2str(phase));
         return 0;
      }
        if (curphase != -1) {
           info("capiplugin: phase %s (was %s).",
            phase2str(phase), phase2str(curphase));
      } else {
         info("capiplugin: phase %s.", phase2str(phase));
      }
      curphase = phase;
      switch (phase) {
            case PHASE_INITIALIZE:
            case PHASE_ESTABLISH:
            case PHASE_AUTHENTICATE:
            case PHASE_CALLBACK:
            case PHASE_NETWORK:
            case PHASE_RUNNING:
            case PHASE_TERMINATE:
            case PHASE_DISCONNECT:
            case PHASE_HOLDOFF:
                  break;

            case PHASE_DEAD:
                        disconnectall();
                  break;

            case PHASE_DORMANT:
                  status = EXIT_OK;
                  plugin_check_options();
                  init_capiconn();
                  if (contrparams[0].opt_inmsn || opt_cli) {
                     if (wakeupnow)
                              wakeupdemand();
                       setupincoming_for_demand();
                  }
                  break;

            case PHASE_SERIALCONN:
                  status = EXIT_OK;
                    wakeupnow = 0;
                      if (conn_isconnected(0))
                     break;
                  plugin_check_options();
                  init_capiconn();
                  if (opt_number) {
                       if (coso == COSO_REMOTE) {
                             makeconnection_with_callback();
                       } else {
                             makeconnection(numbers);
                       }
                  } else if (opt_channels) {
                       makeleasedline();
                  } else {
                       waitforcall();
                  }
                  break;
      }
      return 0;
}

/* -------------------------------------------------------------------- */
/* -------- init/exit function ---------------------------------------- */
/* -------------------------------------------------------------------- */

#if PPPVER >= PPPVersion(2,4,0,0)

static void plugin_exit(void)
{
      int fd;
      if ((fd = capi20_fileno(applid)) >= 0)
            remove_fd(fd);
      unsetup_timeout();
      disconnectall();
      info("capiplugin: exit");
}

static void phase_notify_func(void *p, int phase)
{
      (void)capi_new_phase_hook(phase);
}

static void exit_notify_func(void *p, int phase)
{
        exiting = 1;
      plugin_exit();
}
#endif

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 (2, 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;
      }

#if PPPVER >= PPPVersion(2,4,0,0)
        add_notifier(&phasechange, phase_notify_func, 0);
        add_notifier(&exitnotify, exit_notify_func, 0);
#else
        new_phase_hook = capi_new_phase_hook;
#endif
}

Generated by  Doxygen 1.6.0   Back to index