From 2346f259ef61b03640861cc3adb7bc72238e0f4d Mon Sep 17 00:00:00 2001 From: Andre Date: Sat, 30 Apr 2011 17:09:33 +0200 Subject: [PATCH] initial import from svn --- Makefile | 74 ++++++ il_call.c | 319 ++++++++++++++++++++++++ il_capi.c | 469 ++++++++++++++++++++++++++++++++++ il_event.c | 193 ++++++++++++++ il_poll.c | 112 +++++++++ il_proc.c | 80 ++++++ il_tty.c | 160 ++++++++++++ isdnlogin.c | 328 ++++++++++++++++++++++++ isdnlogin.h | 266 ++++++++++++++++++++ mkinstalldirs | 40 +++ xmodem.c | 679 ++++++++++++++++++++++++++++++++++++++++++++++++++ xmodem.h | 93 +++++++ 12 files changed, 2813 insertions(+) create mode 100644 Makefile create mode 100644 il_call.c create mode 100644 il_capi.c create mode 100644 il_event.c create mode 100644 il_poll.c create mode 100644 il_proc.c create mode 100644 il_tty.c create mode 100644 isdnlogin.c create mode 100644 isdnlogin.h create mode 100644 mkinstalldirs create mode 100644 xmodem.c create mode 100644 xmodem.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..17d06d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ +# Makefile - BinTec Remote CAPI Library +# + +SHELL = /bin/sh + +# be sure to get a BINTEC_PUBLIC_RELEASE_1_7 tagged capi, because the HEAD +# release is still buggy. +CAPIDIR = ../bintec_capi + +CC = gcc +LD = ld +DEFS = -I. -I/usr/local/include -I$(CAPIDIR) +COPTS = -O -Wall +CWARN = +CFLAGS = $(COPTS) $(CWARN) $(DEFS) +LIBS = $(CAPIDIR)/libcapi.a +LORDER = sh ./lorder +TSORT = tsort +LN = ln +AR = ar rv +INSTALL = install +RANLIB = ranlib +OSTYPE = `uname -srm | tr " " _` + +.SUFFIXES: +.SUFFIXES: .out .o .po .So .s .S .c .cc .cpp .cxx .m .C .f .y .l + +.c.o: isdnlogin.h Makefile + ${CC} ${CFLAGS} -c $< -o $@ + +prefix = /usr/local +bindir = $(prefix)/bin +libdir = $(prefix)/lib +incdir = $(prefix)/include + +CAPIAPP = isdnlogin +ISDNLOGIN = isdnlogin.o il_capi.o il_tty.o il_call.o il_poll.o \ + il_event.o il_proc.o xmodem.o + +all: $(CAPIAPP) + +# dynamically linked version +isdnlogin: Makefile $(ISDNLOGIN) + $(CC) -o $@ $(CFLAGS) $(ISDNLOGIN) $(LIBS) + +# the static version of isdnlogin app +isdnlogin.static: Makefile $(ISDNLOGIN) + $(CC) -o $@ $(CFLAGS) $(ISDNLOGIN) $(CAPIDIR)/libcapi.a + +install: all installdirs + @for prog in $(CAPIAPP); \ + do \ + echo $(INSTALL) $$prog $(bindir); \ + $(INSTALL) $$prog $(bindir) || exit 1; \ + done + +installdirs: + @$(SHELL) mkinstalldirs $(prefix) $(bindir) + +dist: Makefile isdnlogin + @echo "Generating distribution for OS $(OSTYPE)" + @rm -f isdnlogin.$(OSTYPE) + @rm -f isdnlogin.$(OSTYPE).gz + @cp isdnlogin isdnlogin.$(OSTYPE) + @strip isdnlogin.$(OSTYPE) + @gzip -9 isdnlogin.$(OSTYPE) + @echo "Your archive is named 'isdnlogin.$(OSTYPE).gz'." + +clean: + rm -f $(CAPIAPP) + rm -f core *.o + +distclean: clean + rm -f $(CAPIAPP) diff --git a/il_call.c b/il_call.c new file mode 100644 index 0000000..7509eb2 --- /dev/null +++ b/il_call.c @@ -0,0 +1,319 @@ +#include "isdnlogin.h" + +extern cfg_t cfg; +extern char * prog_logo; + +/******************************************************************* + * + *******************************************************************/ +call_t *alloc_call(void) +{ + call_t *ptrCall; + + ptrCall = (call_t *)malloc( sizeof(*ptrCall)); + + if (!ptrCall) return NULL; + memset( ptrCall, 0, sizeof(*ptrCall)); + + /* + * open file to transmit + */ + if (cfg.sendfile) { + ptrCall->pFilename = cfg.sendfile; + if (!(ptrCall->txfp=fopen( ptrCall->pFilename, "r"))) { + printf ("Could read from file: <%s>\n", ptrCall->pFilename); + exit ( 1 ); + } + if ( cfg.verbose > 2 ) { + printf("Reading from file: <%s>\n", ptrCall->pFilename); + } + } + + /* default call settings */ + + /* add service and controller to call */ + ptrCall->active = 1; + ptrCall->ident = cfg.controller; /* controller */ + ptrCall->service= cfg.service; + + /* set numbers */ + setCalledPartyNumber( ptrCall, cfg.rmttelno); + setCallingPartyNumber( ptrCall, cfg.loctelno, 1); + setCalledPartySubaddress( ptrCall, cfg.rmtsubaddr); + setCallingPartySubaddress( ptrCall, cfg.locsubaddr); + + /* adjust protocols */ + setBprotocol( ptrCall); + setLLC( ptrCall ); + setAdditionalInfo( ptrCall ); + + return ptrCall; +} + +/******************************************************************* + * + *******************************************************************/ +void free_call( call_t *ptrCall ) +{ + if (ptrCall) { + if (ptrCall->txfp) { + fclose( ptrCall->txfp); + } + free(ptrCall); + } +} + +/******************************************************************* + * + *******************************************************************/ +struct userdata *setCalledPartyNumber( call_t *ptrCall, + char *szCalledPartyNumber) +{ + size_t len = 0; + + ptrCall->CalledPartyNumber.length = 0; + if (szCalledPartyNumber) { + len = strlen( szCalledPartyNumber); + len = min(len, CAPI1_MAXMSGLEN-1); + if (len) { + ptrCall->CalledPartyNumber.data[0] = 0x81; + + memcpy( &ptrCall->CalledPartyNumber.data[1], + szCalledPartyNumber, len); + ptrCall->CalledPartyNumber.length = len+1; + } + } + + return &ptrCall->CalledPartyNumber; +} + +/******************************************************************* + * + *******************************************************************/ +struct userdata *setCallingPartyNumber( call_t *ptrCall, + char *szCallingPartyNumber, + int lPresentation) +{ + size_t len = 0; + + ptrCall->CallingPartyNumber.length = 0; + if (szCallingPartyNumber) { + len = strlen( szCallingPartyNumber); + len = min(len, CAPI1_MAXMSGLEN-1); + if (len) { + ptrCall->CallingPartyNumber.data[0] = 0x01; + ptrCall->CallingPartyNumber.data[1] = lPresentation ? 0x80 : 0xa0; + + memcpy( &ptrCall->CallingPartyNumber.data[2], + szCallingPartyNumber, len); + ptrCall->CallingPartyNumber.length = len+2; + } + } + + return &ptrCall->CallingPartyNumber; +} + +/******************************************************************* + * + *******************************************************************/ +struct userdata *setCalledPartySubaddress( ptrCall, szCalledPartySubaddress) +call_t *ptrCall; +char *szCalledPartySubaddress; +{ + size_t len; + + ptrCall->CalledPartySubaddress.data[0] = 0x80; + if ((len = strlen(szCalledPartySubaddress))) { + memcpy( &ptrCall->CalledPartySubaddress.data[1], + szCalledPartySubaddress, len); + } else { + ptrCall->CalledPartySubaddress.data[1] = 0; + len = 1; + } + + ptrCall->CalledPartySubaddress.length = len + 1; + return &ptrCall->CalledPartySubaddress; +} + +/******************************************************************* + * + *******************************************************************/ +struct userdata *setCallingPartySubaddress( call_t *ptrCall, + char *szCallingPartySubaddress) +{ + size_t len; + + ptrCall->CallingPartySubaddress.data[0] = 0x80; /* always there */ + + if ((len = strlen(szCallingPartySubaddress))) { + /* if subaddress suplied use it */ + memcpy( &ptrCall->CallingPartySubaddress.data[1], + szCallingPartySubaddress, len); + } else { + ptrCall->CallingPartySubaddress.data[1] = 0; + len = 1; + } + + ptrCall->CallingPartySubaddress.length = len + 1; /* one for the type */ + return &ptrCall->CallingPartySubaddress; +} + +userdata_t *setBprotocol( call_t *ptrCall) +{ + userdata_t *data; + struct bprotocol *bprot; + struct b1config *b1cfg; + struct b1config_modem *b1cfg_mdm; + struct b2config *b2cfg; + struct b3config *b3cfg; + + bprot = (struct bprotocol *)&ptrCall->Bprotocol; + + /* set layer 1 protocol */ + PUT_WORD( bprot->b1proto, ptrCall->service->layer1_mask); + + /* depending on service set layer 2 protocol */ + switch(ptrCall->service->hw) { + case ISDN_HDLC: + PUT_WORD( bprot->b2proto, B2X75); + break; + case FAX_G3: + PUT_WORD( bprot->b2proto, B2T30); + break; + case ISDN_V42: + PUT_WORD( bprot->b2proto, B2X75V42BIS); + break; + case V110_ASYNC: + case TRANS: + case MODEM: + default: + PUT_WORD( bprot->b2proto, B2TRANS); + break; + } + + /* check for layer 2 specific settings */ + switch (ptrCall->service->hw) { + case FAX_G3: + PUT_WORD( bprot->b3proto, B3T30); + break; + default: + /* layer 3 is always trans for now */ + PUT_WORD( bprot->b3proto, B3TRANS); + break; + } + data = (userdata_t *)&bprot->structlen; + + /* check if the service has special layer 2 config */ + switch(ptrCall->service->hw) { + case V110_ASYNC: + b1cfg = (struct b1config *) data; + /* v110 specific layer 2 params */ + PUT_WORD( b1cfg->rate, cfg.speed); + PUT_WORD( b1cfg->bpc, 8); + PUT_WORD( b1cfg->parity, 0); + PUT_WORD( b1cfg->stopbits, 0); + b1cfg->length = sizeof( struct b1config) - 1; + break; + case MODEM: + /* modem specific params */ + b1cfg_mdm = (b1config_modem_t *) data; + PUT_WORD( b1cfg_mdm->rate, cfg.speed); /* 0 = max speed */ + PUT_WORD( b1cfg_mdm->bpc, 8); + PUT_WORD( b1cfg_mdm->parity, 0); + PUT_WORD( b1cfg_mdm->stopbits, 0); + PUT_WORD( b1cfg_mdm->options, 0); + PUT_WORD( b1cfg_mdm->negotiation, 1); /* allow autoneg */ + b1cfg_mdm->length = sizeof( struct b1config_modem) -1; + break; + case FAX_G3: + /* FAX group 3 params */ + b1cfg = (struct b1config *) data; + PUT_WORD( b1cfg->rate, cfg.speed); + PUT_WORD( b1cfg->bpc, 0); + PUT_WORD( b1cfg->parity, 0); + PUT_WORD( b1cfg->stopbits, 0); + b1cfg->length = sizeof( struct b1config) - 1; + break; + default: + data->length = 0; + break; + } + data = (userdata_t *)&data->data[data->length]; + + b2cfg = (struct b2config *) data; + switch(ptrCall->service->hw) { + case ISDN_HDLC: + b2cfg->addressA = 3; + b2cfg->addressB = 1; + b2cfg->moduloMode = 8; + b2cfg->windowSize = 7; + b2cfg->xidlen = 0; + b2cfg->length = sizeof( struct b2config) - 1; + break; + default: + b2cfg->length = 0; + break; + } + data = (userdata_t *)&data->data[data->length]; + + /* Layer 3 always transparent */ + b3cfg = (struct b3config *) data; + b3cfg->length = 0; + + bprot->length = (char *)data - (char *)&bprot->length -1; +#if 0 + capi_hexdump((char*) bprot, bprot->length, 16, 2); + printf("b1cfg->length=%d\n", b1cfg->length); + printf("b1cfg_mdm->length=%d\n", b1cfg_mdm->length); + printf("b2cfg->length=%d\n", b2cfg->length); + printf("bprot->length=%d\n", bprot->length); + printf("data->length=%d\n", data->length); + //printf("data->structlen=%d\n", data->structlen); +#endif + + return (userdata_t *)bprot; +} + +userdata_t *setLLC( call_t *ptrCall ) { + userdata_t *llc; + /* Lower Layer Compatibility */ + static char llcs[] = { 0x90, 0x88, 0x90, 0x21, 0x42, 0x00, 0xbb }; + + llc = &ptrCall->llc; + + if (ptrCall->service->rate) { + llc = (userdata_t *)&ptrCall->llc; + + llcs[4] = cfg.v110_speed; /* set user rate */ + memcpy(&ptrCall->llc, &llcs, sizeof(llcs)); + llc->length = sizeof(llcs) - 1; + } + return llc; +} + +userdata_t *setAdditionalInfo( call_t *ptrCall ) +{ + struct userdata *add; + struct userdata *data; + + add = (struct userdata *)&ptrCall->AdditionalInfo; + data = (struct userdata *)add->data; + + data->length = 0; /* b-channel */ + + data = (struct userdata *)&data->data[data->length]; + data->length = 0; /* keypad */ + + data = (struct userdata *)&data->data[data->length]; + memcpy(&data->data[0], prog_logo, strlen(prog_logo)); + + data->length = strlen(data->data); /* user user data */ + + data = (struct userdata *)&data->data[data->length]; + data->length = 0; /* facility */ + + data = (struct userdata *)&data->data[data->length]; + add->length = (char *)data - (char *)&add->length - 1; + + return (struct userdata *)add; +} diff --git a/il_capi.c b/il_capi.c new file mode 100644 index 0000000..2657d97 --- /dev/null +++ b/il_capi.c @@ -0,0 +1,469 @@ +#include "isdnlogin.h" + +extern cfg_t cfg; +extern global_t global; + +int get_capi_info (void) +{ + int i; + int max_controller; + unsigned long controller_mask = 0; + capi_getprofile_t profile; + + memset( &profile, 0, sizeof(profile)); +#if WITH_RCAPI_LAYER + rcapi_get_profile( 0, &profile, &(cfg.capi_host)); +#else + capi2_get_profile( 0, &profile ); +#endif + + max_controller = profile.ncontrl; + + printf("Available CAPI controllers: %d\n", max_controller); + + if (max_controller) { + for( i=1; i<33; i++) { + memset( &profile, 0, sizeof(profile)); +#if WITH_RCAPI_LAYER + if (!rcapi_get_profile( i, &profile, &(cfg.capi_host))) { +#else + if (!capi2_get_profile( i, &profile)) { +#endif + controller_mask |= (1 << (i-1)); + if (!--max_controller) break; + } + } + } + + // XXX: doesnt seem to work! + if (!(((1 << cfg.controller)-1) & controller_mask)) { + fprintf( stderr, "No such Controller %d!\n", cfg.controller); + return 1; + } + memset( &profile, 0, sizeof(profile)); +#if WITH_RACPI_LAYER + capi2_get_profile( cfg.controller, &profile, &(cfg.capi_host) ); +#else + capi2_get_profile( cfg.controller, &profile ); +#endif + +#if DEBUG /* debug printf */ + fprintf( stderr, "ncontrl = 0x%x\n", profile.ncontrl); + fprintf( stderr, "nchannel = 0x%x\n", profile.nchannel); + fprintf( stderr, "options = 0x%x\n", profile.options); + fprintf( stderr, "b1protocol = 0x%x\n", profile.b1protocol); + fprintf( stderr, "b2protocol = 0x%x\n", profile.b2protocol); + fprintf( stderr, "b3protocol = 0x%x\n", profile.b3protocol); + fprintf( stderr, "\n"); +#endif + + if ( !(profile.b1protocol & (SI_PHONE)) || + !(profile.b2protocol & (SI_PHONE)) || + !(profile.b3protocol & (SI_PHONE))) { + fprintf( stderr, "Controller %d: does not support telephony service\n", + cfg.controller); + return 1; + } + return 0; +} + +/******************************************************************* + * + *******************************************************************/ +int init_capi() +{ + int fd; + + for(;;) { + if(cfg.verbose > 2){ + putchar('R'); + } +#if WITH_RCAPI_LAYER + fd = capi2_register( REG_CAPI_MSG_SIZE, + REG_CAPI_LEVEL3CNT, + cfg.usWindowSize, + REG_CAPI_DATA_BLK_SIZE, + NULL, + &(cfg.capi_host), + &(cfg.capi_auth)); +#else + fd = capi2_register( REG_CAPI_MSG_SIZE, + REG_CAPI_LEVEL3CNT, + cfg.usWindowSize, + REG_CAPI_DATA_BLK_SIZE, + NULL); +#endif + + if(fd >0 ) break; + fprintf( stderr, "capi2_register failed! retry in %d seconds\n", + REGISTER_FAIL_TIMER ); + sleep(REGISTER_FAIL_TIMER); + } + return fd; +} + +int setListen( call_t *ptrCall, int mask ) +{ + int ret; + ret = capi2_listen_req( ptrCall->capi_fd, + ptrCall->ident, /* Controller */ + 0x000001ff, /* Info Mask */ + mask, /* CIP Mask */ + 0, /* CIP Mask2 */ + NULL, /* Calling party number */ + NULL); /* Calling party subaddress */ + + return ret; +} + +void check_capi (void) +{ + if (global.alive_pending > MAX_KEEPALIVE_PENDING) { + global.endloop = E_KeepAliveFailed; /* indicate keepalive overflow */ + return; + } + + capi_control_req( global.calls->capi_fd, global.calls->ident, + CTRL_CAPISTATE , NULL); + + global.alive_pending++; +} + +/******************************************************************* + * + *******************************************************************/ +int getCapiInfo( union CAPI_primitives *capi ) +{ + int info; + + switch (GET_PRIMTYPE(capi)) { + case CAPI2_LISTEN_CONF: + info = GET_WORD(capi->c2listen_conf.info); + break; + case CAPI2_ALERT_CONF: + info = GET_WORD(capi->c2alert_conf.info); + break; + case CAPI2_CONNECT_CONF: + info = GET_WORD(capi->c2connect_conf.info); + break; + case CAPI2_INFO_CONF: + info = GET_WORD(capi->c2info_conf.info); + break; + case CAPI2_CONNECTB3_CONF: + info = GET_WORD(capi->c2connectb3_conf.info); + break; + case CAPI2_DATAB3_CONF: + info = GET_WORD(capi->c2datab3_conf.info); + break; + case CAPI2_RESETB3_CONF: + info = GET_WORD(capi->c2resetb3_conf.info); + break; + case CAPI2_DISCONNECTB3_CONF: + info = GET_WORD(capi->c2disconnectb3_conf.info); + break; + case CAPI2_DISCONNECT_CONF: + info = GET_WORD(capi->c2disconnect_conf.info); + break; + case CAPI2_FACILITY_CONF: + info = GET_WORD(capi->c2facility_conf.info); + break; + case CAPI2_SELECTB_CONF: + info = GET_WORD(capi->c2selectb_conf.info); + break; + case CAPI2_DISCONNECT_IND: + info = GET_WORD(capi->c2disconnect_ind.reason); + break; + case CAPI2_DISCONNECTB3_IND: + info = GET_WORD(capi->c2disconnectb3_ind.reason_b3); + break; + default: + info = 0; + break; + } + return (info); +} + +void doConnect( call_t *ptrCall ) +{ + ptrCall->messid = capi2_connect_req( ptrCall->capi_fd, + ptrCall->ident & 0x7f, + ptrCall->service->cip, + &ptrCall->CalledPartyNumber, + &ptrCall->CallingPartyNumber, + &ptrCall->CalledPartySubaddress, + &ptrCall->CallingPartySubaddress, + &ptrCall->Bprotocol, + NULL, &ptrCall->llc, NULL, + &ptrCall->AdditionalInfo); + if(cfg.verbose>8) { + printf("messid from capi2_connect_req() == 0x%x\n", ptrCall->messid); + } + NEWSTATE( ptrCall, D_ConnectPending); +} + +/******************************************************************* + * + *******************************************************************/ +void doDisconnect( call_t *ptrCall ) +{ + unsigned long plci, ncci; + + ncci = ptrCall->ident; + plci = ptrCall->ident & 0xffff; + + switch (ptrCall->state) { + case B_ConnectPending: + case Connected: + if(cfg.wait4dataconf && ptrCall->NotAcknowledged) { + if(cfg.verbose>1) { + printf("doDisconnect() wait for %d CAPI2_DATAB3_CONF\n", + ptrCall->NotAcknowledged); + } + /* don't send a capi2_disconnectb3_req() we have to wait */ + break; + } + if(ptrCall->NotAcknowledged && cfg.verbose>0) { + printf("doDisconnect() missing %d CAPI2_DATAB3_CONF\n", + ptrCall->NotAcknowledged); + } + capi2_disconnectb3_req( ptrCall->capi_fd, + ncci, + NULL); + break; + + case D_ConnectPending: + case B_DisconnectPending: + NEWSTATE( ptrCall, D_DisconnectPending); + capi2_disconnect_req( ptrCall->capi_fd, + plci, + NULL); + + break; + + case D_DisconnectPending: + case Disconnected: + default: + break; + } +} + +#if WITH_UNFINISHED_XMODEM +int SendXmodemData( call_t *ptrCall ) +{ + char buffer[1024]; + size_t len; + xmodem_t *xm = ptrCall->xmodem; + + len = fread( buffer, 1, sizeof(buffer), xm->sendfp); + if (len <= 0) { + /* XXX: check for errors */ + fclose(xm->sendfp); + } + + printf("\nSend XMODEM sector: %d errors: %d ", xm->sentsectcnt, xm->errors); + + if (!xmodem_putpkt(xm, buffer, len, ptrCall->error_msg)) { + xmodem_close(xm); + return 0; + } + return 1; +} +#endif + +/******************************************************************* + * + *******************************************************************/ +void SendData( call_t *ptrCall ) +{ + static unsigned short usDataHandle; + char buffer[REG_CAPI_DATA_BLK_SIZE]; + int len; + + if (0== ptrCall->doSend) { return; } + + /* + * Read next blk from file into buffer + */ +#if LOGIN_SCRIPT_LINE_BY_LINE + { + char *dataptr; + dataptr = fgets( buffer, sizeof(buffer), ptrCall->txfp); + if (dataptr == NULL) { + /* fgets returned noting, we are out */ + ptrCall->doSend = 0; + return; + } + len = strlen(dataptr); /* get line length */ + } + +#else + /* + * NOTE: Don't know why but if we send data with a length + * equal to REG_CAPI_DATA_BLK_SIZE, the brick gona + * complain about to long packet, so we just send + * half of the max buffer size. + */ + + len = fread( buffer, 1, (sizeof(buffer) / 2), ptrCall->txfp); + if (len <= 0) { + if (len < 0) { + perror("\nERR:SendData\n"); + } + ptrCall->doSend = 0; + return; + } +#endif + + ptrCall->NotAcknowledged++; /* count pending confirms */ + usDataHandle++; + + capi2_datab3_req( ptrCall->capi_fd, + ptrCall->ident, + buffer, + len, + 0, + usDataHandle); +} + +/******************************************************************* + * + *******************************************************************/ +void handleConnectB3ActiveInd( call_t *ptrCall ) +{ + if(cfg.verbose > 0) { + printf("Terminate sequence is '%c%c'\n", ESCAPE_CHAR, TERMINATE_CHAR); + } + + /* put terminal into raw mode after connect was okay */ + set_raw_terminal(); + + cfg.starttime = time(0); + ptrCall->wasConnected = 1; + ptrCall->doSend = 1; + + if (ptrCall->txfp) { + SendData( ptrCall ); + } +} + + +/******************************************************************* + * + *******************************************************************/ +int handleDataB3Ind( call_t *ptrCall, + unsigned short messid, + unsigned char *dataptr, + int datalen, + unsigned short handle, + unsigned short flags ) +{ + +#if 0 + printf("komisch hab ne data indicationb\n"); + capi_hexdump( dataptr, datalen, 16, 2); +#endif + + printf("%*.*s", datalen, datalen, dataptr); + + return 1; /* response */ +} + +/******************************************************************* + * + *******************************************************************/ +void handleDataB3Conf( call_t *ptrCall, + unsigned short messid, + unsigned short handle ) +{ + ptrCall->NotAcknowledged--; /* We got a acknowledge */ +#ifdef FAX + if(cfg.verbose > 2) { + putchar('c'); + } +#endif + if (ptrCall->txfp) { + SendData( ptrCall ); /* Send the next one */ + } +} + +/******************************************************************* + * + *******************************************************************/ +void handleInfoInd( call_t *ptrCall, + unsigned short infonumber, + struct userdata *data ) +{ + + unsigned long mask; + unsigned char *datap = data->data; + + switch(infonumber){ + default: + if(cfg.verbose>5){ + printf("\tINFOIND: 0x%04x\n", infonumber); + capi_hexdump( data->data, data->length, 16, 2); + } + break; + case 0x18: /* IBCHI */ + if (*datap & (1<<5)) { + /* primary rate interface */ + if (*datap++ & (1<<6)) { + /* interface identifier present */ + while((*datap++ & 0x80) == 0); + } + if (*datap++ & (1<<4)) { + cfg.b_channel = 1; + mask = 1; + while(!(mask & *datap)){ + mask<<=1; + cfg.b_channel++; + } + } + else { + cfg.b_channel = *datap & 0x7f; + } + } + else { + /* basic rate interface */ + cfg.b_channel = *datap & 0x03; + } + if(cfg.verbose>2){ + printf("\tINFOIND: ISDN-BCHANNEL %d\n", cfg.b_channel); + } + break; + } + +} + +/******************************************************************* + * + *******************************************************************/ +void handleDisconnectB3Ind( call_t *ptrCall, + unsigned short nReason, + struct userdata *ncpi ) +{ + int delta_time; + + cfg.endtime = time(0); + delta_time = cfg.endtime - cfg.starttime; + + reset_terminal(); + + /* No need for this here + * if (nReason) printf("\tDISCONNECT B3_Reason (0x%04x)\n", nReason); + */ + + if(ptrCall->NotAcknowledged && cfg.verbose>0) { + printf("handleDisconnectB3Ind() missing %d CAPI2_DATAB3_CONF\n", + ptrCall->NotAcknowledged); + } +} + +/******************************************************************* + * + *******************************************************************/ +void handleDisconnectInd( call_t *ptrCall, + unsigned short nReason) +{ + printf("ISDN D-channel disconnect, Reason (%04x)\n", nReason); +} diff --git a/il_event.c b/il_event.c new file mode 100644 index 0000000..89ceac3 --- /dev/null +++ b/il_event.c @@ -0,0 +1,193 @@ +#include "isdnlogin.h" + +extern global_t global; +extern cfg_t cfg; + +/******************************************************************* + * + *******************************************************************/ +int capi_event( int fd ) +{ + int info; + int lcause; + unsigned long ident; + unsigned char proto[1024]; + unsigned char data[4096]; + union CAPI_primitives *capi; + call_t *ptrCall; + + lcause = capi2_get_message( fd, &capi, + (union CAPI_primitives *)proto, data, sizeof(data)); + + /* + * check for errors + */ + switch (lcause) { + case CAPI2_E_MSG_QUEUE_EMPTY: return 0; + case CAPI2_E_INTERNAL_BUSY_CONDITION: + case CAPI2_E_ILLEGAL_APPLICATION: + case CAPI2_E_OS_RESOURCE_ERROR: + /* error occured, stop working */ + global.endloop = E_CapiError; + return -1; + default: break; + } + + info = getCapiInfo(capi); + if (info) { + /* TODO: some special output function for local output */ + capi2_perror("\nCAPI Info", info); + } + + /* + * get the call structure + */ + ident = GET_c2IDENT(capi); +#if 0 + ptrCall = getCallbyIdent( ident); + printf("CAPI: 0x%08x 0x%08x\n", ident, GET_PRIMTYPE(capi)); +#endif + ptrCall = global.calls; + + + switch (GET_PRIMTYPE(capi)) { + case CAPI2_LISTEN_CONF: + break; + + case CAPI2_CONNECT_CONF: +#if 0 + ptrCall = getCallbyMessid(GET_MESSID(capi)); +#endif + if (ptrCall) { + ptrCall->ident = ident; + if (info) { /* CONNECT_REQ failed -> end program */ + global.endloop = E_ConnectFailed; + } + } + break; + + case CAPI2_INFO_IND: + capi2_info_resp( fd, GET_MESSID(capi), ident); + if (ptrCall) { + handleInfoInd( ptrCall, + GET_WORD(capi->c2info_ind.info), + (struct userdata *)&capi->c2info_ind.structlen); + } + break; +#if 0 + case CAPI2_CONNECT_IND: + if (ptrCall) { + capi2_connect_resp( fd, + (unsigned short)GET_MESSID(capi), + ident, + 0, /* accept */ + &ptrCall->Bprotocol, + NULL, /* cad */ + NULL, /* csa */ + NULL, /* llc */ + NULL); /* add */ + } + break; +#endif + + case CAPI2_CONNECTACTIVE_IND: + if (cfg.verbose>8) { + printf("CAPI2_CONNECTACTIVE_IND %lx\n", ident); + } + capi2_connectactive_resp( fd, GET_MESSID(capi), ident); + if (ptrCall) { + NEWSTATE( ptrCall, D_Connected); + if (ptrCall->active) { + NEWSTATE( ptrCall, B_ConnectPending); + capi2_connectb3_req( fd, ident, NULL); + } + } + break; + + case CAPI2_CONNECTB3_CONF: +#if 0 + ptrCall = getCallbyPlci( ident & 0xffff ); +#endif + if (ptrCall) { + /* + * NCCI allocation -> store NCCI for later use + */ + ptrCall->ident = ident; + } + break; + + case CAPI2_CONNECTB3ACTIVE_IND: + capi2_connectb3active_resp( fd, GET_MESSID(capi), ident); + if (ptrCall) { + NEWSTATE( ptrCall, Connected); + handleConnectB3ActiveInd( ptrCall ); + } + break; + + case CAPI2_DATAB3_CONF: + if (ptrCall) { + handleDataB3Conf( ptrCall, + GET_MESSID(capi), + GET_WORD(capi->c2datab3_conf.handle)); + } + break; + + case CAPI2_DATAB3_IND: + if (ptrCall) { + if (handleDataB3Ind( ptrCall, + GET_MESSID(capi), + (char *)GET_DWORD(capi->c2datab3_ind.dataptr), + GET_WORD( capi->c2datab3_ind.datalen), + GET_WORD( capi->c2datab3_ind.handle), + GET_WORD( capi->c2datab3_ind.flags))) { + capi2_datab3_resp( fd, + GET_MESSID(capi), + ident, + GET_WORD(capi->c2datab3_ind.handle)); + } + } + break; + + case CAPI2_DISCONNECTB3_IND: + if (ptrCall) { + NEWSTATE( ptrCall, B_DisconnectPending); + handleDisconnectB3Ind( ptrCall, + GET_WORD(capi->c2disconnectb3_ind.reason_b3), + (struct userdata *)&capi->c2disconnectb3_ind.structlen); + } + capi2_disconnectb3_resp( fd, + GET_MESSID(capi), + ident); + if (ptrCall) NEWSTATE( ptrCall, D_DisconnectPending); + capi2_disconnect_req( fd, + ident & 0xffff, + NULL); + break; + + case CAPI2_DISCONNECT_IND: + capi2_disconnect_resp( fd, + GET_MESSID(capi), + ident); + + if (ptrCall) { + NEWSTATE( ptrCall, Disconnected); + handleDisconnectInd( ptrCall, + GET_WORD(capi->c2disconnect_ind.reason)); + + } + global.endloop = E_Disconnected; + break; + case CAPI_CONTROL_CONF: + /* decrement alive pendings */ + global.alive_pending--; + break; + case CAPI2_ALERT_CONF: + case CAPI2_INFO_CONF: + case CAPI2_DISCONNECTB3_CONF: + case CAPI2_DISCONNECT_CONF: + case CAPI2_FACILITY_CONF: + break; + } + return 0; +} + diff --git a/il_poll.c b/il_poll.c new file mode 100644 index 0000000..2c46acd --- /dev/null +++ b/il_poll.c @@ -0,0 +1,112 @@ +#include "isdnlogin.h" + +extern global_t global; + +/******************************************************************* + * + *******************************************************************/ +int pollset( fd, events, func) +int fd; +int events; +int (*func)PROTO((int)); +{ + int i; + struct pollfd *pfp; + pollag_t *pap; + + if (global.npollfds >= MAX_FD) return -1; + + for (i=0, pfp=global.pollfds, pap=global.pollags; + i < global.npollfds; + ++i, ++pfp, ++pap) + { + if (pfp->fd == fd && pfp->events == events) break; + } + if ( i < global.npollfds) { + printf("poll already set !!\n"); + return 0; + } + + pfp = global.pollfds + global.npollfds; + pap = global.pollags + global.npollfds; + + pfp->fd = fd; + pfp->events = events; + pfp->revents = 0; + if (poll(pfp, 1, 0) == -1) return -1; + + pap->func = func; + + global.npollfds ++; + return 0; +} + +/******************************************************************* + * + *******************************************************************/ +int pollloopt(t) +long t; +{ + struct pollfd *pfp; + pollag_t *pap; + int i; + int fds; + int donefds; + + while (global.npollfds > 0) { + fds = poll(global.pollfds, global.npollfds, t); + switch (fds) { + case -1: + if (errno == EINTR) continue; + if (errno == EAGAIN) continue; + return -1; + case 0: + return 0; + default: + pfp = global.pollfds; + pap = global.pollags; + donefds = 0; + for (i=0; i < global.npollfds; ++i, ++pfp, ++pap) { + if (pfp->revents) { + (*pap->func)(pfp->fd); + pfp->revents = 0; + donefds++; + } + } + return donefds; + break; + } + } + return 0; +} + +/******************************************************************* + * + *******************************************************************/ +int mypolldel(fd) +int fd; +{ + struct pollfd *pfp; + pollag_t *pap; + int i; + + for (i=0, pfp=global.pollfds, pap=global.pollags; + i < global.npollfds; + ++i, ++pfp, ++pap) { + if (pfp->fd == fd) break; + } + + if (i >= global.npollfds) { + errno = ENOENT; + return -1; + } + + for (++i, ++pfp, ++pap; i < global.npollfds; ++i, ++pfp, ++pap) { + pfp[-1] = pfp[0]; + pap[-1] = pap[0]; + } + global.npollfds --; + return 0; +} + + diff --git a/il_proc.c b/il_proc.c new file mode 100644 index 0000000..15cf2f9 --- /dev/null +++ b/il_proc.c @@ -0,0 +1,80 @@ +#include "isdnlogin.h" + +extern global_t global; +extern cfg_t cfg; +extern char *name; +extern char *prog_logo; + +/******************************************************************* + * + *******************************************************************/ +int my_kill(void) +{ + call_t *ptrCall = NULL; + + /* restore terminal settings */ + reset_terminal(); + + mypolldel( STDIN ); + + ptrCall = global.calls; + if ( ptrCall ) { + free_call( ptrCall ); + mypolldel( ptrCall->capi_fd ); + } + return 0; +} + +/******************************************************************* + * + *******************************************************************/ +void catch_signal( int signo ) +{ + switch (signo) { + case SIGHUP: + check_capi(); + break; + default: + if (signo) fprintf( stderr, "\ngot signal (%d)\n", signo); + exit ( 1 ); + } +} +/******************************************************************* + * + *******************************************************************/ + +/******************************************************************* + * + *******************************************************************/ +int init_program( int argc, char **argv ) +{ + name = argv[0]; + printf("%s\n", prog_logo); + + global.endloop = 0; + global.calls = NULL; + + /* store the actual terminal settings */ + tcgetattr(0, &(global.oldtermopts)); + + /* output completely unbuffered */ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + setbuf(stdin, NULL); + +#ifdef __STDC__ + atexit( my_exit ); +#else + on_exit( my_exit, 0); +#endif + signal(SIGHUP, catch_signal); + signal(SIGINT, catch_signal); + signal(SIGQUIT, catch_signal); + signal(SIGKILL, catch_signal); + signal(SIGTERM, catch_signal); + signal(SIGUSR1, catch_signal); + signal(SIGUSR2, catch_signal); + + return 0; +} + diff --git a/il_tty.c b/il_tty.c new file mode 100644 index 0000000..6ab317f --- /dev/null +++ b/il_tty.c @@ -0,0 +1,160 @@ +#include "isdnlogin.h" + +extern global_t global; + +int tty_event(int fd) +{ + size_t len; + call_t *ptrCall = global.calls; + static unsigned short usDataHandle; + char out[REG_CAPI_DATA_BLK_SIZE+1]; + char *c = &out[1]; + + len = read(fd, c, sizeof(out)); + /* check for read errors */ + if (len <=0) { + switch(errno) { + case EBADF: + printf("read: bad filedescriptor!\n"); + return 0; + case EFAULT: + printf("read: Buf points outside the allocated address space.\n"); + return 0; + case EIO: + printf("read: An I/O error occurred while reading from the file system.\n"); + return 0; + case EINTR: + printf("read: interrupted!\n"); + return 0; + case EINVAL: + printf("read: The pointer associated with d was negative.\n"); + return 0; + case EAGAIN: + printf("read: no data to read.\n"); + return 0; + default: + printf("got read error = %d\n", len); + return 0; + break; + } + } + + if (TTYS_ESCAPE_RECV == global.tty_state) { + switch (*c) { + case TERMINATE_CHAR: + global.endloop = E_LocalTerminate; + break; +#if WITH_UNFINISHED_XMODEM + case SENDFILE_CHAR: + /* this feature is not really working now, the first shot + * showed that it's much too slow for ISDN connections. So + * maybe i will never implement this. Also the prompt() + * feature breaks keepalive feature. readbyte() in xmodem.c + * won't work on other messages than DATAB3_IND, so a + * disconnect while in transfer will be ignored. Feel free to + * integrate this feature */ + if (ptrCall) { + /* send first data now */ + /* TODO: to be implemented here using prompt() function */ + + char buf[256]; + prompt("Local file name? ", buf, sizeof(buf)); + printf("buf=%s\n", buf); + + ptrCall->xmodem = xmodem_open( ptrCall, 115200, 1, ptrCall->error_msg, + buf); + while ( SendXmodemData(ptrCall) ); + + if (ptrCall->error_msg) printf("XMODEM error: %s\n", ptrCall->error_msg); + } + break; +#endif + default: + /* nothing */ + len++; + out[0] = ESCAPE_CHAR; + c = &out[0]; + break; + + } + global.tty_state = TTYS_ESCAPE_WAITFOR; + } else if (ESCAPE_CHAR == *c) { + /* eat the tilde */ + global.tty_state = TTYS_ESCAPE_RECV; + return 0; + } + + + /* send data if in right state */ + if ((ptrCall) && ptrCall->state == Connected) { + ptrCall->NotAcknowledged++; /* count pending confirms */ + + usDataHandle++; + capi2_datab3_req( ptrCall->capi_fd, + ptrCall->ident, + c, + len, + 0, + usDataHandle); + } + + return 0; +} + +/* copied from /usr/src/lib/libc/gen/termios.c of FreeBSD because this + * is no POSIX compatible function and not available on any OS such as + * HP-UX or Solaris */ + +/* + * Make a pre-existing termios structure into "raw" mode: character-at-a-time + * mode with no characters interpreted, 8-bit data path. + */ +void mymakeraw(struct termios *t) +{ + t->c_iflag &= ~(IMAXBEL|IXOFF|INPCK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IGNPAR); + t->c_iflag |= IGNBRK; + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|NOFLSH|TOSTOP|PENDIN); + t->c_cflag &= ~(CSIZE|PARENB); + t->c_cflag |= CS8|CREAD; + t->c_cc[VMIN] = 1; + t->c_cc[VTIME] = 0; +} + + + +void reset_terminal(void) { + /* restore old terminal opts */ + tcsetattr(0,TCSANOW, &(global.oldtermopts)); +} + +void set_raw_terminal(void) { + struct termios scheisse; + + /* set terminal to raw mode */ + memcpy(&scheisse, &(global.oldtermopts), sizeof(scheisse)); + mymakeraw(&scheisse); + tcsetattr(0,TCSANOW, &scheisse); +} + +void prompt(char *promptmsg, char *inputbuf, size_t buf_len) +{ + char *bufptr = inputbuf; + + reset_terminal(); + mypolldel(STDIN); + + printf("%s", promptmsg); + while ((*bufptr = getchar())) { + if (*bufptr == '\n') break; + if (buf_len-- <= 0) break; + + //printf("bufptr=%c buf_len=%d\n", *bufptr, buf_len); + bufptr++; + } + *bufptr = '\0'; + + //printf("end of conversation\n"); + pollset( STDIN, POLLIN, tty_event); + set_raw_terminal(); +} diff --git a/isdnlogin.c b/isdnlogin.c new file mode 100644 index 0000000..806c617 --- /dev/null +++ b/isdnlogin.c @@ -0,0 +1,328 @@ +/************************************************************************ + * (C)opyright 1991-1999 Andre Ilie, All Rights Reserved + * + * Title: + * Author: + * $RCSfile: isdnlogin.c,v $ + * $Revision: 74 $ + * $Date: 2005-12-22 01:06:50 +0100 (Thu, 22 Dec 2005) $ + * $State: Exp $ + * + * Type: streams driver/module | application | library + * Products: ALL | XS,XM,XL,XP,BGO,BGP,XCM + * Interfaces: IPI, DLPI + * Libraries: - + * Switches: - + * Description: -- + *----------------------------------------------------------------------- + * Current Log: + * - + ***********************************************************************/ + +#include "isdnlogin.h" + +/********************************** + * global variables + **********************************/ +const char * name = NULL; +const char * prog_logo = "BIANCA/CAPI ISDN Login Client 1.51 by Andrei Ilie "; +const char * rcs_id_isdnlogin_c = "$Id: isdnlogin.c,v 1.52 2003/07/30 12:14:36 shoki Exp $"; + +global_t global; /* global program data */ + +cfg_t cfg = { + "", /* remote telno */ + "+49 911 12345678", /* local telno */ + "", /* remote subaddr */ + "", /* local subaddr */ + DEFAULT_VERBOSE_LEVEL, /* verbose mode */ + 38400, /* maximum speed */ +#if WITH_RCAPI_LAYER + { CAPI_HOST_DEFAULT, NULL, 0 }, + /* capi host info */ + { CAPI_USER_DEFAULT, NULL }, + /* capi auth info */ +#endif + 1, /* controller */ + -1, /* b-channel number */ + 0, /* store start time */ + 0, /* store end time */ + REG_WINDOW_SIZE, /* Window Size */ + DEFAULT_WAIT4DATACONF, /* flag */ + NULL, /* service */ + 0, /* v110_rate */ + NULL /* file to send */ +}; + + +/* for service specific parameters */ +static service_t service[] = { + /* name cip rate hw speed layer1_mask */ + { "data", CIP_DATA, 0, ISDN_HDLC, 0, B1HDLC }, + { "telephony", CIP_PHONE, 0, ISDN_HDLC, 0, B1HDLC }, + /* TODO: check this out... don't know if it works + { "faxg3", CIP_FAXG3, 0, FAX_G3, 14400, B1FAXG3 }, + { "faxg4", CIP_FAXG4, 0, ISDN_HDLC, 0, B1HDLC }, + { "t_online", CIP_BTX, 0, ISDN_HDLC, 0, B1HDLC }, + { "datex_j", CIP_BTX, 0, ISDN_HDLC, 0, B1HDLC }, + { "btx", CIP_BTX, 0, ISDN_HDLC, 0, B1HDLC }, + { "v120", CIP_DATA, 0xb9, ISDN_HDLC, 56000, B1HDLC56 }, + { "56k", CIP_DATA, 0, ISDN_HDLC, 56000, B1HDLC56 }, + */ + { "modem", CIP_3KAUDIO,0, MODEM, 0, B1MODEMASYNC }, + { "trans", CIP_DATA, 0, TRANS, 0, B1TRANS }, + { "compr", CIP_DATA, 0, ISDN_V42, 0, B1HDLC }, + { "v110", CIP_DATA, 0x4d, V110_ASYNC, 38400, B1V110TRANS }, + { "", 0, 0, 0, 0, 0 }, +}; + +#ifdef __STDC__ +void my_exit( void ) +#else +void my_exit( func, arg) + int *func; + caddr_t arg; +#endif +{ + my_kill(); +} + + +/******************************************************************* + * + *******************************************************************/ +int usage( void ) +{ + service_t *sv; + int i = 1; + + fprintf( stderr, "\nUsage: %s [-%s] <#phone> [service]\n", name, + CMD_LINE_ARGS); + fprintf( stderr, "\tOptions:\n"); +#if WITH_RCAPI_LAYER + fprintf( stderr, "\t-b: specify remote CAPI host\n"); + fprintf( stderr, "\t-P: specify remote CAPI port\n"); + fprintf( stderr, "\t-u: specify remote CAPI user\n"); + fprintf( stderr, "\t-p: specify remote CAPI password\n"); +#endif + fprintf( stderr, "\t-l: specify local phone number\n"); + fprintf( stderr, "\t-s: specify maximum transfer speed in bps\n"); + fprintf( stderr, "\t-c: specify ISDN controller to use [1..n]\n"); + fprintf( stderr, "\t-q: use quiet mode\n"); + fprintf( stderr, "\t-C: specify file to send after dialin (autologin)\n"); + fprintf( stderr, "\t-v: specify verbose level (default=%d)\n", + DEFAULT_VERBOSE_LEVEL); + fprintf( stderr, "\t-w: specify data window size (default=%d)\n", + REG_WINDOW_SIZE); + fprintf( stderr, "\t-W: wait for DATAB3_CONF before DISCONNECTB3_REQ (default=%d)\n", + DEFAULT_WAIT4DATACONF); + + fprintf( stderr, "\nServices:\n\t" ); + for (sv = service; *sv->name; sv++) { + fprintf(stderr, "%s ", sv->name); + if (!(i++%6)) fprintf(stderr, "\n\t"); + } + fprintf( stderr, "\n"); + exit(1); +} + +void get_service(char *serv) +{ + service_t *sv; + + cfg.service = service; /* default service is the frist one */ + + /* get or guess service type */ + if (serv != NULL) { + for (sv = service; *sv->name; sv++) { + if (!strncmp( serv, sv->name, strlen(serv))) { + /* got service in table, save pointer to it */ + cfg.service = sv; + break; + } + } + if (!*sv->name) { + fprintf( stderr, "Service '%s' not found, using '%s'.\n", + serv, cfg.service->name); + } + } +} + +void set_speed(void) +{ + /* adjust speed setting */ + switch (cfg.speed) { + /* speed is in range */ + case 1200: cfg.v110_speed = 0x42; break; + case 2400: cfg.v110_speed = 0x43; break; + case 4800: cfg.v110_speed = 0x45; break; + case 9600: cfg.v110_speed = 0x48; break; + case 14400: cfg.v110_speed = 0x49; break; /* XXX: broken */ + case 19200: cfg.v110_speed = 0x4b; break; + case 38400: cfg.v110_speed = 0x4d; break; + default: + /* if service specific max speed given use this */ + printf("Invalid speed setting %d bps! ", cfg.speed); + if (cfg.service->speed) { + cfg.speed = cfg.service->speed; + cfg.v110_speed = cfg.service->rate; + } else { + cfg.speed = 1200; /* min rate */ + cfg.v110_speed = 0x42; + } + printf("Speed set to %d bps\n", cfg.speed); + break; + } +} + + +int exit_handler (exit_t type) +{ + int exitval; + + switch (type) { + case E_LocalTerminate: + printf("Local terminate requested.\n"); + exitval = 0; + break; + case E_KeepAliveFailed: + printf( "CAPI keepalive failed (%d pending)!\n" , + global.alive_pending); + exitval = 1; + break; + case E_ConnectFailed: + printf("Could not connect to remote system!\n"); + exitval = 1; + break; + case E_CapiError: + printf("Got fatal CAPI error!\n"); + exitval = 1; + break; + case E_PollError: + printf("Got fatal poll error!\n"); + exitval = 1; + break; + case E_Disconnected: + exitval = 0; + break; + default: + exitval = 1; + break; + } + return (exitval); +} + +int get_options (int argc, char **argv) +{ + int i; + extern int optind; + extern char *optarg; + + /* + * get options + */ + while ((i = getopt( argc, argv, CMD_LINE_ARGS )) != EOF) switch (i) { + case 'l': strncpy( cfg.loctelno, optarg, sizeof(cfg.loctelno)); break; +#if WITH_RCAPI_LAYER + case 'b': cfg.capi_host.hostname = strdup(optarg); break; + case 'P': cfg.capi_host.port = atoi(optarg); break; + case 'u': cfg.capi_auth.user = strdup(optarg); break; + case 'p': cfg.capi_auth.passwd = strdup(optarg); break; +#endif + case 'c': cfg.controller = atoi(optarg); break; + case 's': cfg.speed = atoi(optarg); break; + case 'q': cfg.verbose = 0; break; + case 'v': cfg.verbose = atoi(optarg); break; + case 'C': cfg.sendfile = strdup(optarg); break; + case 'w': cfg.usWindowSize = atoi(optarg); break; + case 'W': cfg.wait4dataconf = atoi(optarg); break; + case '?': + case 'h': + default: usage(); + } + + if (optind < argc) { + strncpy( cfg.rmttelno, argv[optind++], sizeof(cfg.rmttelno)); + } else { + usage(); + return 1; + } + return 0; +} + +/******************************************************************* + * + *******************************************************************/ +int main( int argc, char **argv ) +{ + call_t *ptrCall; + int ret; +#if CHECK_CAPI + int timeout = KEEP_ALIVE_TIMER; +#else + int timeout = -1; /* inifinite poll */ +#endif + + init_program(argc, argv); + if (get_options(argc, argv)) exit(1); + + /* service specific settings */ + get_service(argv[optind]); + set_speed(); + + /* CAPI stuff */ + if (get_capi_info()) exit( 1 ); + + /* allocate first call instance */ + ptrCall = alloc_call(); + global.calls = ptrCall; + if (!ptrCall) { + fprintf( stderr, "failed to allocate call!\n"); + exit ( 1 ); + } + + /* do register */ + ptrCall->capi_fd = init_capi(); + if (ptrCall->capi_fd <= 0) { + exit ( 1 ); + } + + // For server mode + //setListen(ptrCall, ptrCall->service->cip); + + printf("Trying...\n"); + doConnect( ptrCall ); + + /* poll on STDIN for incoming packets */ + pollset( STDIN, POLLIN, tty_event); + + /* poll on capi for incoming packets */ + pollset( ptrCall->capi_fd, POLLIN, capi_event); + + /* poll fds for incoming packets and handel them */ + while (!global.endloop) { + ret = pollloopt(timeout); + switch(ret) { + case -1: + /* poll error */ + global.endloop = E_PollError; + break; + case 0: + /* got timeout */ +#if CHECK_CAPI + /* check capi when we have time for it */ + check_capi(); +#endif + break; + default: + /* did some work on fds */ + break; + } + } + + /* reset terminal to unraw mode and output failure messages */ + reset_terminal(); + + exit(exit_handler(global.endloop)); +} + diff --git a/isdnlogin.h b/isdnlogin.h new file mode 100644 index 0000000..6f6ffaf --- /dev/null +++ b/isdnlogin.h @@ -0,0 +1,266 @@ +#include +#include /* for tcsetattr */ +#include +#include +#include +#include /* for the fucking types CONST &&& */ + +#include "xmodem.h" + +#define DEBUG 0 + +#define STDIN fileno(stdin) +#define STDOUT fileno(stdout) + +#define CAPI_HOST_DEFAULT "struppi.dev.bintec.de" +#define CAPI_USER_DEFAULT "default" + +#define MAX_FD 64 +#define CMD_LINE_ARGS "?hqC:l:c:s:w:W:v:b:p:u:P:" + +#define DEFAULT_VERBOSE_LEVEL 2 +#define DEFAULT_WAIT4DATACONF 0 /* flag */ +#define REGISTER_FAIL_TIMER 30 /* seconds */ + +#define CHECK_CAPI 1 /* keepalive capi and indicate broken connection */ +#define MAX_KEEPALIVE_PENDING 0 +#define KEEP_ALIVE_TIMER 5*1000 /* milliseconds */ + +#define LOGIN_SCRIPT_LINE_BY_LINE 0 /* send each login script line + * in one data_ind instead of + * block based sending + */ + +#define WITH_UNFINISHED_XMODEM 0 /* enable/disable Xmodem upload feature + * (*broken*) + */ +#define WITH_RCAPI_LAYER 0 /* enable/disbale using of rcapi functions + * which allow to pass a + * hostname and user to the + * capi. the problem is that + * the code is still buggy and + * won't work always */ + +/* tty stories */ +#define TTYS_ESCAPE_WAITFOR 0 +#define TTYS_ESCAPE_RECV 1 +#define ESCAPE_CHAR '~' +#define TERMINATE_CHAR '.' +#define SENDFILE_CHAR '>' + +#define MAX_CMD_SIZE 256 +#define NEWSTATE( ptrCall, newstate) (ptrCall)->state = newstate; + +#define REG_CAPI_MSG_SIZE 1024 +#define REG_CAPI_LEVEL3CNT 1 +#define REG_CAPI_DATA_BLK_SIZE 1400 /* mtu=1500-(tcp+capi)header */ +#define REG_WINDOW_SIZE 7 /* TODO with 10 fax aborts */ + +#define ISDN_HDLC 0 /* ISDN HDLC service mode */ +#define V110_ASYNC 1 /* V.110 Async service */ +#define TRANS 4 /* transparent */ +#define MODEM 5 /* modem service */ +#define FAX_G3 6 /* FAX group 3 */ +#define ISDN_V42 7 /* HDLC X.75 with V.42bis */ + +#define CIP_DATA 2 +#define CIP_PHONE 1 +#define CIP_3KAUDIO 4 +#define CIP_FAXG3 17 +#define CIP_FAXG4 18 +#define CIP_BTX 20 /* not sure about this */ + +#define TRUE 1 +#define FALSE 0 + +#undef max +#undef min +#define max(a,b) (((a)>(b))?(a):(b)) +#define min(a,b) (((a)<(b))?(a):(b)) + +/* exit states */ +enum exit_e { + E_LocalTerminate = 1, + E_KeepAliveFailed, + E_ConnectFailed, + E_CapiError, + E_PollError, + E_Disconnected +}; +typedef enum exit_e exit_t; + +/* call states */ +enum state_e { + Disconnected, + D_ConnectPending, + D_Connected, + B_ConnectPending, + Connected, + B_DisconnectPending, + D_DisconnectPending, +}; + +/* store services here */ +typedef struct service_s service_t ; +struct service_s { + const char *name; + unsigned char cip, rate, hw; + int speed; + int layer1_mask; +}; + +/********************************** + * call specific data + **********************************/ +typedef struct call_s call_t; +struct call_s { + call_t *next; + + /* + * name and handle for file to transmit + */ + CONST char *pFilename; + FILE *txfp; + + /* for Xmodem sending */ + xmodem_t *xmodem; + char error_msg[80]; /* xmodem error message buffer */ + + /* + * static configuration data + */ + struct userdata CallingPartyNumber; + struct userdata CalledPartyNumber; + struct userdata CallingPartySubaddress; + struct userdata CalledPartySubaddress; + struct userdata Bprotocol; + + /* service specfic configuration */ + service_t *service; + userdata_t llc; + userdata_t AdditionalInfo; + + /* + * dynamic link data + */ + int capi_fd; + enum state_e state; + unsigned short messid; /* message number */ + unsigned long ident; /* contrl, plci, ncci */ + + unsigned active:1; /* active, passive side */ + unsigned doSend:1; + unsigned wasConnected:1; + unsigned NotAcknowledged;/* count datab3_req not ack */ + +}; + +typedef struct cfg_s cfg_t; +struct cfg_s { + char rmttelno[40]; + char loctelno[40]; + char rmtsubaddr[40]; + char locsubaddr[40]; + int verbose; + int speed; +#if WITH_RCAPI_LAYER + rcapi_host_t capi_host; + rcapi_auth_t capi_auth; +#endif + int controller; + int b_channel; + time_t starttime; + time_t endtime; + unsigned short usWindowSize; + int wait4dataconf; + service_t *service; + unsigned char v110_speed; + char *sendfile; +}; + +typedef struct pollag_s pollag_t; +struct pollag_s { + int (*func)(int); +}; + +typedef struct global_s global_t; +struct global_s { + exit_t endloop; + call_t *calls; + struct termios oldtermopts; + size_t alive_pending; + int tty_state; + /* pollshiting */ + int npollfds; + struct pollfd pollfds[MAX_FD]; + pollag_t pollags[MAX_FD]; +}; + +/******************************************************************* + * other call struct specific includes + *******************************************************************/ +#include "xmodem.h" /* for xmodem functions */ + +/******************************************************************* + * local proto's + *******************************************************************/ +void catch_signal (int signo); +int start_external (void); +int pollset (int fd, int events, int (*func)(int)); +int pollloopt ( long t ); +int mypolldel ( int fd ); +call_t *alloc_call (void); +void free_call ( call_t *ptrCall ); +/* TODO: replace with userdata_t */ +struct userdata *setCalledPartyNumber ( call_t *ptrCall, char *szCalledPartyNumber); +struct userdata *setCallingPartyNumber ( call_t *ptrCall, char *szCallingPartyNumber, int lPresentation); +struct userdata *setCalledPartySubaddress ( call_t *ptrCall, char *szCalledPartySubaddress); +struct userdata *setCallingPartySubaddress ( call_t *ptrCall, char *szCallingPartySubaddress); +userdata_t *setBprotocol( call_t *ptrCall ); +userdata_t *setLLC ( call_t *ptrCall ); +userdata_t *setAdditionalInfo( call_t *ptrCall ); +int setListen( call_t *ptrCall, int mask ); +int getCapiInfo ( union CAPI_primitives *capi ); +void doDisconnect ( call_t *ptrCall ); +void doConnect ( call_t *ptrCall ); +void SendData ( call_t *ptrCall ); +void handleConnectB3ActiveInd ( call_t *ptrCall ); +int handleDataB3Ind ( call_t *ptrCall, unsigned short messid, unsigned char *dataptr, int datalen, unsigned short handle, unsigned short flags ); +void handleDataB3Conf ( call_t *ptrCall, unsigned short messid, unsigned short handle); +void handleInfoInd ( call_t *ptrCall, unsigned short infonumber, struct userdata *data); +void handleDisconnectB3Ind ( call_t *ptrCall, unsigned short nReason, struct userdata *ncpi); +void handleDisconnectInd ( call_t *ptrCall, unsigned short nReason); +int capi_event ( int fd ); +int usage ( void ); +int init_capi ( void); +int init_program (int argc, char **argv); +void mymakeraw (struct termios *t); +void reset_terminal (void); +void set_raw_terminal (void); +int get_capi_info (void); +int tty_event (int fd); +void prompt (char *promptmsg, char *inputbuf, size_t buf_len); +void check_capi (void); +int my_kill (void); +void get_service (char *serv); +void set_speeed (void); +int exit_handler (exit_t type); +int get_options (int argc, char **argv); +int main ( int argc, char **argv ); + +#if WITH_UNFINISHED_XMODEM +/* XModem stuff */ +extern xmodem_t *xmodem_open(call_t *ptrCall, int baud, int one_kb, char *emsg, char *filename); +extern int xmodem_getpkt(xmodem_t *x, char *rbuf, int *lenp, char *emsg); +extern int xmodem_putpkt(xmodem_t *x, char *sbuf, int len, char *emsg); +extern void xmodem_cancel(xmodem_t *x); +extern void xmodem_close(xmodem_t *x); +extern int xmodem_onekb(xmodem_t *x); +#endif + +#ifdef __STDC__ +void my_exit ( void ); +#else +void my_exit ( int *func, caddr_t arg); +#endif + diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100644 index 0000000..b5688d4 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.1 2002/11/08 14:53:43 shoki Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/xmodem.c b/xmodem.c new file mode 100644 index 0000000..37af59b --- /dev/null +++ b/xmodem.c @@ -0,0 +1,679 @@ +/************************************************************************ + * (C)opyright 1991-1999 BinTec Communications AG, All Rights Reserved + * + * Title: XMODEM transfer functions + * Author: bernd + * $RCSfile: xmodem.c,v $ + * $Revision: 74 $ + * $Date: 2005-12-22 01:06:50 +0100 (Thu, 22 Dec 2005) $ + * $State: Exp $ + * + * Type: library + * Products: ALL + * Interfaces: - + * Libraries: - + * Switches: - + * Description: -- + *----------------------------------------------------------------------- + * Current Log: + ***********************************************************************/ + +#ifdef USE_RCSID_C + static const char _rcsid_xmodem_c[] __UNUSED = "$Id: xmodem.c,v 1.2 2003/07/30 11:26:56 shoki Exp $"; +#endif + +#if WITH_UNFINISHED_XMODEM + +#include "isdnlogin.h" + +#define TRACE printf +static void trace(const char *fmt, ...) +{ } + + + +/* + * CRC-16 constant array... from Usenet contribution by Mark G. Mendel, + * Network Systems Corp. (ihnp4!umn-cs!hyper!mark) + */ +static unsigned short crctab[1 << B] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/******************************************************************************* + * function: readbyte + * Read one byte from data stream (with timeout) + * parameters: + * fd i/o pathes of data stream + * seconds timeout + * return: byte or TIMEOUT + ******************************************************************************/ +static int readbyte(rwfd_t fd, int seconds) +{ + struct pollfd pfd; + unsigned char c; + unsigned char proto[1024]; + unsigned char data[4096]; + union CAPI_primitives *capi; + int lcause; + + do { + if (capi2_wait_for_signal(fd.rd, seconds * 1000 ) == CAPI2_E_MSG_QUEUE_EMPTY) { + printf("readbyte timeout %d\n", seconds); + return TIMEOUT; + } else { + lcause = capi2_get_message( fd.rd, &capi, + (union CAPI_primitives *)proto, &c, 1); + //printf("capi=%x\n", GET_PRIMTYPE(capi)); + } + } while (GET_PRIMTYPE(capi) != CAPI2_DATAB3_IND); + + capi2_datab3_resp( fd.rd, + GET_MESSID(capi), + fd.ident, + GET_WORD(capi->c2datab3_ind.handle)); + return c; +} + +/******************************************************************************* + * function: readdata + * Read block of data from given i/o pathes (with timeout) + * parameters: + * x xmodem handle + * bufsize receiver buffer size + * checksum accumulated crc + * return: data byte or TIMEOUT + ******************************************************************************/ +static int readdata(xmodem_t *x, int bufsize, int *checksum) +{ + int c; /* character being processed */ + unsigned short chksm; /* working copy of checksum */ + int j; /* loop index */ + + chksm = 0; + for (j = 0; j < bufsize; j++) { + if ((c = readbyte(x->fd, 1)) == TIMEOUT) { /* 1s timeout */ + return TIMEOUT; + } + x->buf[j] = c & 0xff; + if (x->crcmode) /* CRC */ + chksm = (chksm << B) ^ crctab[(chksm >> (W - B)) ^ c]; + else /* checksum */ + chksm = ((chksm + c) & 0xff); + } + *checksum = chksm; + + TRACE("XMODEM read (%d bytes)", j); + return (0); +} + +/******************************************************************************* + * function: sendbyte + * Write one byte to data stream + * parameters: + * fd i/o pathes of data stream + * data data byte + * return: - + ******************************************************************************/ +static void sendbyte(rwfd_t fd, char data) +{ + TRACE("XMODEM write (%d bytes)", 1); + //write(fd.wr, &data, 1); + capi2_datab3_req( fd.wr, fd.ident, &data, 1, 0, fd.handle); +} + +/******************************************************************************* + * function: purge + * Wait for the line to clear (read rest) + * parameters: + * fd i/o pathes of data stream + * return: - + ******************************************************************************/ +static void purge(rwfd_t fd) +{ + int c; + while ((c = readbyte(fd, 1)) != TIMEOUT) ; +} + +/******************************************************************************* + * function: purge + * Send CAN twice + * parameters: + * fd i/o pathes of data stream + * return: - + ******************************************************************************/ +static void cancel(rwfd_t fd) +{ + sendbyte(fd, CAN); + sendbyte(fd, CAN); + purge(fd); + sendbyte(fd, CAN); + sleep(1); +} + +/******************************************************************************* + * function: xmodem_open + * Open console and set baud rate + * parameters: + * dev device name (e.g. "consexcl") + * or NULL for stdio (baud rate ignored!) + * baud baud rate [baud] + * one_kb xmodem-1k mode enable + * emsg buffer for error message (80 bytes) + * return: xmodem ptr or error (NULL) + ******************************************************************************/ +xmodem_t *xmodem_open(call_t *ptrCall, int baud, int one_kb, char *emsg, char *filename) +{ + int fd; + xmodem_t *x = malloc(sizeof *x); + + if (!x) { + if (emsg) sprintf(emsg, "XMODEM: out of memory"); + return 0; + } + memset(x, 0, sizeof *x); + + reset_terminal(); /* unraw terminal */ + + if (ptrCall) { + /*------------------------------+ + | i/o via stdio pathes | + +------------------------------*/ + TRACE("XMODEM via CAPI"); + x->conmode = 0; + x->fd.rd = ptrCall->capi_fd; /* use stdio pathes */ + x->fd.wr = ptrCall->capi_fd; + x->fd.ident = ptrCall->ident; + } else { + TRACE("XMODEM via stdio"); + x->conmode = 0; + x->fd.rd = STDIN; + x->fd.wr = STDOUT; + } + + if (filename) { + x->sendfp = fopen(filename, "r"); + /* XXX: check for fopen errors here */ + if (!x->sendfp) { + /* failure */ + return 0; + } + } else { + TRACE("XMODEM no filename to send specified"); + return 0; + } + + x->failed = FALSE; + x->crcmode = TRUE; + x->rcvstartch = x->crcmode ? CRCCHR : NAK; + x->one_kb = one_kb & 1; /* only relevant for sending packets */ + x->starttime = time(NULL); + + return x; +} + +/******************************************************************************* + * function: xmodem_onekb + * Return if modem-1k mode enabled + * parameters: + * x xmodem ptr + * return: modem-1k mode enabled (1) or not (0) + ******************************************************************************/ +int xmodem_onekb(xmodem_t *x) +{ + return x->one_kb; +} + +/******************************************************************************* + * function: xmodem_cancel + * Send CAN to stop an unterminated getpkt transfer + * parameters: + * x xmodem ptr + * return: - + ******************************************************************************/ +void xmodem_cancel(xmodem_t *x) +{ + cancel(x->fd); +} + +/******************************************************************************* + * function: xmodem_close + * Close device and free memory + * parameters: + * x xmodem ptr + * return: success (0) or error code + ******************************************************************************/ +void xmodem_close(xmodem_t *x) +{ + time_t tdiff; + + if (x->fd.rd != -1) { + if (x->conmode) { + close(x->fd.rd); + } + } + if (x->sendfp) { + fclose (x->sendfp); /* close opened file to send */ + x->sendfp = NULL; + } + x->fd.rd = -1; + + if (x->starttime) { + tdiff = time(NULL) - x->starttime; + printf("duration: %d\n", tdiff ); + } + + free(x); + set_raw_terminal(); /* enable raw terminal mode */ +} +#ifdef USE_UNPORTED +/******************************************************************************* + * function: xmodem_getpkt + * Receive one XMODEM-Packet and write it into buf + * + * The length of the packet is written into (*lenp). + * An error message is written into emsg. + * The length of data packets (128/1024) is determined by the remote site + * parameters: + * x xmodem ptr + * buf data packet buffer (1024 bytes) + * lenp length of the data packet + * emsg buffer for error message (80 bytes) + * return: status code + * XMODEM_RCVPKT: successfully received one packet + * XMODEM_EOT: received End-of-Transmission character + * XMODEM_ERROR: receive failed + ******************************************************************************/ +int xmodem_getpkt(xmodem_t *x, char *buf, int *lenp, char *emsg) +{ + int checksum, /* packet checksum */ + firstchar, /* first character of a packet */ + sectcurr, /* second byte of packet--should be packet */ + /* number (mod 128) */ + sectcomp, /* third byte of packet--should be complement */ + /* of sectcurr */ + errorflag, /* set true when packet (or first char of */ + /* putative packet) is invalid */ + inchecksum, /* incoming checksum or CRC */ + msb, lsb, /* incoming checksum (upper/lower byte) */ + duplicate, /* got duplicate packet */ + timeout, /* read timeout */ + bufsize; /* packet size (128 or 1024) */ + + + sendbyte(x->fd, x->rcvstartch); + + restart: + bufsize = 128; + errorflag = FALSE; + duplicate = FALSE; + timeout = FALSE; + *lenp = 0; + + do { /* start by reading first byte in packet */ + if ((firstchar = readbyte(x->fd, 4)) == TIMEOUT) { + errorflag = timeout = TRUE; + goto abort; + } + } while ((firstchar != SOH) + && (firstchar != STX) + && (firstchar != EOT) + && (firstchar != ACK || x->recvsectcnt > 0) + && (firstchar != CAN)); + + if (firstchar == EOT) { /* check for REAL EOT */ + TRACE("EOT"); + if (readbyte(x->fd, 1) != TIMEOUT) { + errorflag = timeout = TRUE; + TRACE("ignoring extra characters"); + goto abort; + } + } + if (firstchar == CAN) { /* bailing out? */ + TRACE("CAN"); + if ((readbyte(x->fd, 3) & 0x7f) == CAN) { + sleep(1); + if (emsg) strcpy(emsg, "XMODEM: canceled by user"); + return XMODEM_ERROR; + } else { + errorflag = TRUE; + goto abort; + } + } + if (firstchar == SOH || firstchar == STX) { /* start reading packet */ + bufsize = (firstchar == SOH) ? 128 : 1024; + + if (x->recvsectcnt == 0) { /* 1st data packet, initialize */ + if (bufsize == 1024) { + TRACE("1K packet mode chosen"); + } + x->errors = 0; + x->got_eot = 0; + } + if ((sectcurr = readbyte(x->fd, 3)) == TIMEOUT || + (sectcomp = readbyte(x->fd, 3)) == TIMEOUT) { + errorflag = timeout = TRUE; + goto abort; + } + TRACE("%s: sectcurr=0x%02x sectcomp=0x%02x", + firstchar==SOH ? "SOH":"STX", sectcurr, sectcomp); + if ((sectcurr + sectcomp) == 0xff) { /* is packet number + * checksum correct? */ + if (sectcurr == ((x->sectnum + 1) & 0xff)) { + /* + * Read, process and calculate checksum for a buffer of + * data + */ + if ((readdata(x, bufsize, &checksum)) == TIMEOUT) { + errorflag = timeout = TRUE; + goto abort; + } + /* verify checksum or CRC */ + if (x->crcmode) { + checksum &= 0xffff; + /* get 16-bit CRC */ + if ((msb = readbyte(x->fd, 3)) == TIMEOUT || + (lsb = readbyte(x->fd, 3)) == TIMEOUT) { + errorflag = timeout = TRUE; + goto abort; + } + inchecksum = (msb << 8) | lsb; + } else { + /* get simple 8-bit checksum */ + if ((inchecksum = readbyte(x->fd, 3)) == TIMEOUT) { + errorflag = timeout = TRUE; + goto abort; + } + } + + if (inchecksum == checksum) { /* good checksum, hence + * good packet */ + x->errors = 0; + x->recvsectcnt += (bufsize == 128) ? 1 : 8; + x->sectnum = sectcurr; + memcpy(buf, x->buf, bufsize); + *lenp = bufsize; + x->rcvstartch = ACK; + } + /* + * Start handling various errors and special + * conditions + */ + else { /* bad checksum */ + TRACE("checksum error crcmode=%d: inchecksum=%x crc=%x", + x->crcmode, inchecksum, checksum); + errorflag = TRUE; + } + } else { /* sector number is wrong */ + if (sectcurr == x->sectnum) { /* duplicate sector? */ + errorflag = TRUE; + duplicate = TRUE; + TRACE("duplicate sector"); + while (readbyte(x->fd, 3) != TIMEOUT); + } else { /* no, real phase error */ + errorflag = TRUE; + x->failed = TRUE; + cancel(x->fd); + TRACE("phase error, expected sector=%d", x->recvsectcnt); + } + } + } else { /* bad packet number checksum */ + TRACE("wrong header sector number"); + errorflag = TRUE; + } + } /* END reading packet loop */ + + abort: + if (timeout) TRACE("timeout sector=%d", x->recvsectcnt); + if ((errorflag && !x->failed) || x->recvsectcnt == 0) { + if (errorflag) x->errors++; + if (x->recvsectcnt != 0) { + while (readbyte(x->fd, 3) != TIMEOUT) ;/* wait for line to settle if + * not beginning */ + } + if (duplicate) { + sendbyte(x->fd, ACK); + } else if (x->crcmode && x->recvsectcnt == 0 && x->errors == CRCSWMAX) { + x->crcmode = FALSE; + TRACE("CRC mode not accepted"); + purge(x->fd); + sendbyte(x->fd, NAK); + } else if (!x->crcmode && x->recvsectcnt == 0 && x->errors == CRCSWMAX){ + x->crcmode = TRUE; + TRACE("checksum mode not accepted"); + purge(x->fd); + sendbyte(x->fd, CRCCHR); + } else if (x->crcmode && x->recvsectcnt == 0) { + purge(x->fd); + sendbyte(x->fd, CRCCHR); + } else { + purge(x->fd); + sendbyte(x->fd, NAK); + } + } + + if (firstchar == EOT) { + x->got_eot = 1; + sendbyte(x->fd, ACK); + sleep(1); + return XMODEM_EOT; + } else if (x->failed || x->errors >= ERRORMAX) { + cancel(x->fd); + x->failed = 1; + if (emsg) strcpy(emsg, "XMODEM: too many errors"); + return XMODEM_ERROR; + } else if (errorflag) { + goto restart; + } else { + return XMODEM_RCVPKT; + } +} +#endif + +/******************************************************************************* + * function: xmodem_putpkt + * Send one XMODEM packet + * + * The protocol packet length is 128 or 1024 bytes padded with CTRL-Z. + * If len argument is not 128/1024, this is the last packet and an EOT is + * sent to the remote site after sending the data packet. + * If the last packet sent had 128/1024 bytes, the application must invoke + * this funktion again with len=0 to indicate EOT. + * parameters: + * x xmodem ptr + * buf data packet buffer (1024 bytes) + * len length of the data packet + * emsg buffer for error message (80 bytes) + * return: 0 if error + ******************************************************************************/ +int xmodem_putpkt(xmodem_t *x, char *data, int len, char *emsg) +{ + int bufctr; /* array index for data buffer */ + unsigned short checksum; /* checksum/crc */ + int attempts, /* number of attempts made to xmit a packet */ + sendfin, /* flag that we are sending the last packet */ + bbufcnt, /* array index for packet */ + firstchar, /* first character in protocol transaction */ + bufsize, /* packet size (128 or 1024) */ + sendresp; /* response char received from remote */ + char *cp = data; + + bufsize = x->one_kb ? 1024 : 128; + sendfin = FALSE; + attempts = 0; + + if (!x->got_ack) { /* sending first packet */ + x->crcmode = FALSE; /* Receiver determines use of crc or checksum */ + x->sectnum = 1; + do { + /* wait for and read startup character */ + while ( + (firstchar = readbyte(x->fd, 30)) != NAK + && firstchar != CRCCHR + && firstchar != CAN + ) { + if (++attempts > NAKMAX) { + if (emsg) strcpy(emsg, + "XMODEM: remote system not responding"); + x->failed = 1; + return 0; + } + } + if ((firstchar & 0x7f) == CAN) { + TRACE("CAN"); + if (readbyte(x->fd, 3) == CAN) { + if (emsg) sprintf(emsg, "XMODEM: canceled by user"); + x->failed = 1; + return 0; + } + } + if (firstchar == CRCCHR) { + x->crcmode = TRUE; + TRACE("CRC mode requested"); + } + } while (firstchar != NAK && firstchar != CRCCHR); + } + + if (len == 0) goto sendeot; + + bbufcnt = 0; /* start building buf to be sent */ + x->buf[bbufcnt++] = (bufsize == 1024) ? STX : SOH; + x->buf[bbufcnt++] = x->sectnum; /* current sector # */ + x->buf[bbufcnt++] = ~x->sectnum; /* and its complement */ + + checksum = 0; + for (bufctr=0; bufctr < bufsize; bufctr++) { + if (bufctr >= len) { + sendfin = TRUE; /* this is the last sector */ + x->buf[bbufcnt + bufctr] = CTRLZ; /* Control-Z for CP/M EOF */ + } else { + x->buf[bbufcnt + bufctr] = *cp++; + } + if (x->crcmode) { + checksum = (checksum<>(W-B)) ^ x->buf[bbufcnt + bufctr]]; + } else { + checksum = ((checksum + x->buf[bbufcnt + bufctr]) & 0xff); + } + } + bbufcnt += bufsize; + + if (x->crcmode) { /* put in CRC */ + checksum &= 0xffff; + x->buf[bbufcnt++] = ((checksum >> 8) & 0xff); + x->buf[bbufcnt++] = (checksum & 0xff); + } else { /* put in checksum */ + x->buf[bbufcnt++] = checksum; + } + + attempts = 0; + do { /* inner packet loop */ + TRACE("XMODEM write (%d bytes) ", bbufcnt); + //write(x->fd.wr, x->buf, bbufcnt); /* write the buf */ + x->fd.handle++; /* increase data handle var for capi */ + capi2_datab3_req( x->fd.wr, x->fd.ident, x->buf, bbufcnt, 0, x->fd.handle); + + TRACE("%s: sectcurr=0x%02x", x->buf[0]==SOH ? "SOH":"STX", x->sectnum); +#if 0 + TRACE("%d byte %02xh (%02xh) sent, checksum %02xh %02xh secnum %d\n", + bbufcnt, x->buf[1]&0xff, x->buf[2]&0xff, x->buf[bufsize+3]&0xff, + x->buf[bufsize+4]&0xff, x->sectnum + ); +#endif + attempts++; + sendresp = readbyte(x->fd, 10); /* get response from remote */ + + if (sendresp != ACK) { + x->errors++; + + if ((sendresp & 0x7f) == CAN) { + TRACE("CAN"); + if ((readbyte(x->fd, 3) & 0x7f) == CAN) { + if (emsg) strcpy(emsg, "XMODEM: canceled by user"); + x->failed = 1; + return 0; + } + } + if (sendresp == TIMEOUT) { + TRACE("timeout on sector %d", x->sentsectcnt); + } else { + TRACE("non-ACK on sector %d", x->sentsectcnt); + } + } else { + x->got_ack = 1; + } + } while ( + sendresp != ACK && attempts < RETRYMAX && x->errors < ERRORMAX + ); + + x->sectnum++; + x->sentsectcnt += (bufsize == 128) ? 1 : 8; + + if (attempts >= RETRYMAX) { + sendbyte(x->fd, CAN); sendbyte(x->fd, CAN); sendbyte(x->fd, CAN); + if (emsg) strcpy(emsg, "XMODEM: remote system not responding"); + x->failed = 1; + return 0; + } + + if (attempts > ERRORMAX) { + sendbyte(x->fd, CAN); sendbyte(x->fd, CAN); sendbyte(x->fd, CAN); + if (emsg) strcpy(emsg, "XMODEM: too many errors"); + x->failed = 1; + return 0; + } + + if (sendfin) { + sendeot: + attempts = 0; + sendbyte(x->fd, EOT); /* send 1st EOT to close down transfer */ + + /* wait for ACK of EOT */ + while (readbyte(x->fd, 15) != ACK && attempts++ < RETRYMAX) { + TRACE("EOT not acked"); + sendbyte(x->fd, EOT); + } + + if (attempts >= RETRYMAX) { + if (emsg) strcpy(emsg, "XMODEM: last ACK missing"); + x->failed = 1; + return 0; + } + } + return 1; +} + +#endif + + + diff --git a/xmodem.h b/xmodem.h new file mode 100644 index 0000000..8bebe9d --- /dev/null +++ b/xmodem.h @@ -0,0 +1,93 @@ +/************************************************************************ + * (C)opyright 1991-1999 BinTec Communications AG, All Rights Reserved + * + * Title: + * Author: + * $RCSfile: xmodem.h,v $ + * $Revision: 74 $ + * $Date: 2005-12-22 01:06:50 +0100 (Thu, 22 Dec 2005) $ + * $State: Exp $ + * + * Type: include file for .. + * Products: ALL | XS,XM,XL,XP,BGO,BGP,XCM + * Description: -- + *----------------------------------------------------------------------- + * Current Log: + ***********************************************************************/ + +#ifndef _xmodem_h +#define _xmodem_h + +#ifdef USE_RCSID_H + static const char _rcsid_xmodem_h[] __UNUSED = "$Id: xmodem.h,v 1.1 2002/12/19 12:53:33 shoki Exp $"; +#endif + +/* ASCII Constants */ +#define SOH 001 +#define STX 002 +#define ETX 003 +#define EOT 004 +#define ENQ 005 +#define ACK 006 +#define LF 012 /* Unix LF/NL */ +#define CR 015 +#define NAK 025 +#define SYN 026 +#define CAN 030 +#define ESC 033 + +/* XMODEM Constants */ +#define TIMEOUT -1 +#define ERRORMAX 10 /* maximum errors tolerated */ +#define CRCSWMAX 4 /* maximum time to try CRC mode before switching */ +#define KSWMAX 5 /* maximum errors before switching to 128 byte packets */ +#define NAKMAX 2 /* maximum times to wait for initial NAK when sending */ +#define RETRYMAX 5 /* maximum retries to be made */ +#define CRCCHR 'C' /* CRC request character */ +#define CTRLZ 032 /* CP/M EOF for text (usually!) */ + +#undef DEBUG + +/* the CRC polynomial. */ +#define P 0x1021 + +/* number of bits in CRC */ +#define W 16 + +/* this the number of bits per char */ +#define B 8 + +#define XMODEM_EOT 0 /* End of transmission */ +#define XMODEM_ERROR 1 /* Too many errors */ +#define XMODEM_RCVPKT 2 /* Received Packet */ + +#define BBUFSIZ 1024 /* buffer size */ + +typedef struct { + int rd; /* read path */ + int wr; /* write path */ + unsigned long ident; /* for capi ident */ + unsigned short handle; /* for capi data handle */ +} rwfd_t; + +typedef struct { + CONST char *filename; /* filename of file to send */ + FILE *sendfp; /* filepointer to opened file to be sent */ + time_t starttime; /* when did the transfer start */ + int sectnum; /* number of last packet (modulo 128) */ + int errors; /* running cnt of errors(reset when 1st packet starts */ + int recvsectcnt; /* running sector count (128 byte sectors) */ + int sentsectcnt; + rwfd_t fd; /* i/o pathes */ + int ldsav; /* saved ld mode */ + char rcvstartch; /* char to request next packet */ + unsigned char buf[BBUFSIZ + 6]; + unsigned one_kb:1; /* 1024 byte mode configured (only sender) */ + unsigned got_ack:1; + unsigned crcmode:1; + unsigned failed:1; + unsigned got_eot:1; /* end-of-transmission was received */ + unsigned conmode:1; /* i/o via console */ +} xmodem_t; + +#endif