diff -urN asterisk-1.2.10.orig/.version asterisk-1.2.10/.version --- asterisk-1.2.10.orig/.version 2006-07-14 23:29:33.000000000 +0200 +++ asterisk-1.2.10/.version 2006-07-31 14:13:27.000000000 +0200 @@ -1 +1 @@ -1.2.10 +1.2.10-BRIstuffed-0.3.0-PRE-1s diff -urN asterisk-1.2.10.orig/HARDWARE asterisk-1.2.10/HARDWARE --- asterisk-1.2.10.orig/HARDWARE 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/HARDWARE 2006-07-31 14:13:08.000000000 +0200 @@ -37,6 +37,19 @@ * Wildcard TE410P - Quad T1/E1 switchable interface. Supports PRI and RBS signalling, as well as PPP, FR, and HDLC data modes. +-- Junghanns.NET (Primary author of BRIstuff) + http://www.junghanns.net + + * quadBRI PCI ISDN - 4port BRI ISDN interface, supports NT and TE mode + + * octoBRI PCI ISDN - 8port BRI ISDN interface, supports NT and TE mode + + * singleE1 PCI ISDN - Single E1 interface + + * doubleE1 PCI ISDN - Double E1 interface + + * uno/duo/quad GSM PCI - 1/2/4 channel GSM interface cards + Non-zaptel compatible hardware ============================== diff -urN asterisk-1.2.10.orig/LICENSE asterisk-1.2.10/LICENSE --- asterisk-1.2.10.orig/LICENSE 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/LICENSE 2006-07-31 14:13:08.000000000 +0200 @@ -1,7 +1,7 @@ -Asterisk is distributed under the GNU General Public License version 2 -and is also available under alternative licenses negotiated directly -with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL -applies to all loadable Asterisk modules used on your system as well, +BRIstuffed Asterisk is distributed under the GNU General Public License version 2 +and is not available under any alternative licenses. +If you obtained BRIstuffed Asterisk under the GPL, then the GPL +applies to all loadable BRIstuffed Asterisk modules used on your system as well, except as defined below. The GPL (version 2) is included in this source tree in the file COPYING. diff -urN asterisk-1.2.10.orig/Makefile asterisk-1.2.10/Makefile --- asterisk-1.2.10.orig/Makefile 2006-06-29 21:23:18.000000000 +0200 +++ asterisk-1.2.10/Makefile 2006-08-10 11:07:53.000000000 +0200 @@ -772,6 +772,9 @@ echo ";astctlowner = root" ; \ echo ";astctlgroup = apache" ; \ echo ";astctl = asterisk.ctl" ; \ + echo "[options]" ; \ + echo "uniquename = `hostname`" ;\ + echo "silence_suppression = yes" ;\ ) > $(DESTDIR)$(ASTCONFPATH) ; \ else \ echo "Skipping asterisk.conf creation"; \ diff -urN asterisk-1.2.10.orig/README asterisk-1.2.10/README --- asterisk-1.2.10.orig/README 2006-03-03 09:12:33.000000000 +0100 +++ asterisk-1.2.10/README 2006-07-31 14:13:08.000000000 +0200 @@ -4,6 +4,8 @@ Copyright (C) 2001-2005 Digium, Inc. and other copyright holders. +Copyright (C) 2002-2005 Junghanns.NET GmbH +and other copyright holders. ================================================================ * SECURITY diff -urN asterisk-1.2.10.orig/README.chan_capi asterisk-1.2.10/README.chan_capi --- asterisk-1.2.10.orig/README.chan_capi 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/README.chan_capi 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,146 @@ +(CAPI*) chan_capi a Common ISDN API 2.0 implementation for Asterisk +(C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH +Klaus-Peter Junghanns + +This program is free software and may be modified and distributed under +the terms of the GNU Public License. There is _NO_ warranty for this! + +Thanks go to the debuggers and bugfixers (listed in chronological order) :) +=========================================================================== +Lele Forzani +Florian Overkamp +Gareth Watts +Jeff Noxon +Petr Michalek +Jan Stocker +(...and all the others that i forgot..) :-) + +chan_capi version 0.4.0-PRE1 includes: +====================================== + +- multiple controller support +- CID,DNID (callling party, called party) +- CLIR/CLIP +- supplementary services, CD,HOLD,RETRIEVE,ECT +- DTMF (dependend on card) + software DTMF support +- early B3 connects (always,success,never) +- digital audio (what did you think?) +- incoming/outgoing calls +- overlap sending (dialtone) +- E(xplicit) C(all) T(ransfer) (...although it's done implicit .. but dont tell!) +- tuneable latency ;) you can configure the size of B3 blocks at compile time + (in chan_capi_pvt.h, AST_CAPI_MAX_B3_BLOCK_SIZE) + the default is 160 samples, for non-VoIP use you can tune it down to 130 +- use asterisk's internal dsp functions for dtmf +- alaw support +- ulaw support! +- Eicon CAPI echo cancelation (echocancel=1) +- reject call waiting (ACO) +- DID for Point to Point mode (a.k.a overlap receiving) +- experimental echo squelching (echosquelch=1) +- call progress, no need to add ||r to your dialstring anymore +- rx/tx gains (rxgain=1.0) +- call deflection on circuitbusy (makefile option) (deflect=12345678) +- (inter)national dialing prefix (for callerid) configurable in capi.conf +- CLI command "capi info" shows B channel status +- capiECT will announce the callerID since it gets lost on most isdn pbxes + the called party can press # to drop the call +- audio syncing (timing outgoing dataB3 on incoming dataB3), supposed to fix + the DATA_B3_REQ (error = 0x1103) problem +- catch all MSN (incomingmsn=*) +- some configuration enhancements (msn=123,124,125 and controller=1,2,3,4) +- accountcode= added. +- finally the echo squelching works! +- callgroup support +- fixed pipe leak +- updated to support the new frame->delivery field +- compiles with latest cvs with a makefile option (LOOK AT THE MAKEFILE) +- fixed channel name bug in p2p mode +- added app_capiNoES for disabling the primitive echo suppressor, use this before + you start recording voicemail or your files may get choppy +- fixed for latest cvs (AST_MUTEX_DEFINE_STATIC) +- fixed for latest cvs (asterisk/parking.h -> asterisk/features.h) +- fixed for latest cvs ast_pthread_create + +- ATTENTION! the dialstring syntax now uses the zaptel dialstring syntax + it used to be: Dial(CAPI/[@]:[b|B]) + + now it is: Dial(CAPI/g/[b|B]) + or: Dial(CAPI/contr/[b|B]) + + CLIP/CLIR is now uses the calling presentation of the calling channel, this can + be modified using the CallingPres() application. Use CallinPres(32) for CLIR. + That is why the msn= param in capi.conf is now obsolete. The callerID is also + taken from the calling channel. + +- fixes for BSD (Jan Stocker) + +Helper applications +=================== +kapejod says: "No No No, dont use those yet....!" (except maybe HOLD,ECT...) + +app_capiCD.c forwards an unanswered call to another phone (does not rely on sservice CD) + example: + exten => s,1,Wait,1 + exten => s,2,capiCD,12345678 + +app_capiHOLD.c puts an answered call on hold, this has nothing to do with asterisk's onhold thingie (music et al) + after putting a call onhold, never use the Wait application! + +app_capiRETRIEVE.c gets the holded call back + +app_capiECT.c explicit call transfer of the holded call (must put call on hold first!) + example: + exten => s,1,Answer + exten => s,2,capiHOLD + exten => s,3,capiECT,55:50 + will ECT the call to 50 using 55 as the callerid/outgoing msn + + +Using CLIR +========== +Use the CallingPres() application before you dial: +exten => _X.,1,CallingPres(32) +exten => _X.,2,Dial(CAPI/contr1/${EXTEN}) + +Enjoying early B3 connects (inband call progress, tones and announcements) +========================================================================== +early B3 is now configurable in the dialstring :) +if you prefix the destination number with a 'b' early B3 will always be used, also if the call fails +because the number is unprovisioned, etc ... +if you prefix it with a 'B' early B3 will only be used on successful calls, giving you ring indication,etc... + +dont use indications in the Dial command, your local exchange will do that for you: +exten => _X.,1,Dial(CAPI/contr1/B${EXTEN},30) (early B3 on success) +exten => _X.,1,Dial(CAPI/contr1/b${EXTEN},30) (always early B3) +exten => _X.,1,Dial(CAPI/contr1/${EXTEN},30,r) (no early B3, fake ring indication) + +exten => _X.,1,Dial(CAPI/contr1/b${EXTEN},30,r) (always early B3, fake indicatons if the exchange + does not give us indications) +exten => _X.,1,Dial(CAPI/contr1/B${EXTEN},30,r) (early B3 on success, fake indicatons if the exchange + does not give us indications) + +you can totally turn B3 off in the Makefile at buildtime (-DNEVER_EVER_EARLY_B3_CONNECTS). + +For normal PBX usage you would use the "b" option, always early B3. + +Overlap sending (a.k.a. real dialtone) +====================================== +when you dial an empty number, and have early B3 enabled, with: + Dial(CAPI/g1/b) +the channel will come up at once and give you the dialtone it gets from the local exchange. +at this point the channel is like a legacy phone, now you can send dtmf digits to dial. + +Example context for incoming calls on MSN 12345678: +=================================================== + +[capi-in] +exten => 12345678,1,Dial(SIP/phone1) +exten => 12345678,2,Hangup + + +More information/documentation and commercial support can be found at: + http://www.junghanns.net/asterisk/ + + + diff -urN asterisk-1.2.10.orig/agi/Makefile asterisk-1.2.10/agi/Makefile --- asterisk-1.2.10.orig/agi/Makefile 2006-03-28 22:22:05.000000000 +0200 +++ asterisk-1.2.10/agi/Makefile 2006-07-31 14:13:08.000000000 +0200 @@ -11,7 +11,7 @@ # the GNU General Public License # -AGIS=agi-test.agi eagi-test eagi-sphinx-test +AGIS=agi-test.agi eagi-test eagi-sphinx-test xagi-test CFLAGS+=-DNO_AST_MM @@ -37,7 +37,7 @@ $(CC) $(CFLAGS) -o eagi-sphinx-test eagi-sphinx-test.o $(LIBS) clean: - rm -f *.so *.o look .depend eagi-test eagi-sphinx-test + rm -f *.so *.o look .depend eagi-test eagi-sphinx-test xagi-test %.so : %.o $(CC) -shared -Xlinker -x -o $@ $< diff -urN asterisk-1.2.10.orig/agi/xagi-test.c asterisk-1.2.10/agi/xagi-test.c --- asterisk-1.2.10.orig/agi/xagi-test.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/agi/xagi-test.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,175 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * XAGI sample script + * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * based on eagi-test.c + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#ifdef SOLARIS +#include +#endif + +#define AUDIO_FILENO_IN (STDERR_FILENO + 1) +#define AUDIO_FILENO_OUT (STDERR_FILENO + 2) + +static int read_environment(void) +{ + char buf[256]; + char *val; + /* Read environment */ + for(;;) { + fgets(buf, sizeof(buf), stdin); + if (feof(stdin)) + return -1; + buf[strlen(buf) - 1] = '\0'; + /* Check for end of environment */ + if (!strlen(buf)) + return 0; + val = strchr(buf, ':'); + if (!val) { + fprintf(stderr, "Invalid environment: '%s'\n", buf); + return -1; + } + *val = '\0'; + val++; + val++; + /* Skip space */ + // fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); + + /* Load into normal environment */ + setenv(buf, val, 1); + + } + /* Never reached */ + return 0; +} + +static void app_echo(void) +{ + fd_set fds; + int res; + int bytes = 0; + static char astresp[256]; + char audiobuf[16000]; /* 1 second of audio */ + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO_IN, &fds); + /* Wait for *some* sort of I/O */ + res = select(AUDIO_FILENO_IN + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return; + } + if (FD_ISSET(STDIN_FILENO, &fds)) { + fgets(astresp, sizeof(astresp), stdin); + if (feof(stdin)) { + return; + } + astresp[strlen(astresp) - 1] = '\0'; + fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); + return; + } + if (FD_ISSET(AUDIO_FILENO_IN, &fds)) { + /* what goes in.... */ + res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf)); + if (res > 0) { + bytes = res; + /* must come out */ + write(AUDIO_FILENO_OUT, audiobuf, bytes); + } + } + } +} + +static char *wait_result(void) +{ + fd_set fds; + int res; + static char astresp[256]; + char audiobuf[4096]; + for (;;) { + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(AUDIO_FILENO_IN, &fds); + /* Wait for *some* sort of I/O */ + res = select(AUDIO_FILENO_IN + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return NULL; + } + if (FD_ISSET(STDIN_FILENO, &fds)) { + fgets(astresp, sizeof(astresp), stdin); + if (feof(stdin)) { + fprintf(stderr, "Got hungup on apparently\n"); + return NULL; + } + astresp[strlen(astresp) - 1] = '\0'; + fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); + return astresp; + } + if (FD_ISSET(AUDIO_FILENO_IN, &fds)) { + res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf)); + /* drop it, like it's hot */ + } + } + +} + +static char *run_command(char *command) +{ + fprintf(stdout, "%s\n", command); + return wait_result(); +} + + +static int run_script(void) +{ + char *res; + res = run_command("STREAM FILE demo-echotest \"\""); + if (!res) { + fprintf(stderr, "Failed to execute command\n"); + return -1; + } + app_echo(); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *tmp; + int ver = 0; + int subver = 0; + /* Setup stdin/stdout for line buffering */ + setlinebuf(stdin); + setlinebuf(stdout); + if (read_environment()) { + fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); + exit(1); + } + tmp = getenv("agi_enhanced"); + if (tmp) { + if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) + ver = 0; + } + if (ver < 2) { + fprintf(stderr, "No XAGI services available. Use XAGI, not AGI or EAGI\n"); + exit(1); + } + if (run_script()) + return -1; + exit(0); +} diff -urN asterisk-1.2.10.orig/apps/Makefile asterisk-1.2.10/apps/Makefile --- asterisk-1.2.10.orig/apps/Makefile 2006-04-30 15:38:22.000000000 +0200 +++ asterisk-1.2.10/apps/Makefile 2006-07-31 14:13:08.000000000 +0200 @@ -28,8 +28,15 @@ app_test.so app_forkcdr.so app_math.so app_realtime.so \ app_dumpchan.so app_waitforsilence.so app_while.so app_setrdnis.so \ app_md5.so app_readfile.so app_chanspy.so app_settransfercapability.so \ + app_pickup.so app_segfault.so app_callingpres.so app_devstate.so \ app_dictate.so app_externalivr.so app_directed_pickup.so \ - app_mixmonitor.so app_stack.so + app_mixmonitor.so app_stack.so + + +ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/capi20.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/capi20.h),) + APPS+= app_capiNoES.so app_capiCD.so app_capiECT.so +endif + # # Obsolete things... diff -urN asterisk-1.2.10.orig/apps/app_callingpres.c asterisk-1.2.10/apps/app_callingpres.c --- asterisk-1.2.10.orig/apps/app_callingpres.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_callingpres.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,70 @@ +/* + * An application to change the CallingPresentation for an Asterisk channel. + * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *synopsis_callingpres = "Change the presentation for the callerid"; +static char *descrip_callingpres = "Callingpres(number): Changes the presentation for the callerid. Should be called before placing an outgoing call\n"; +static char *app_callingpres = "CallingPres"; +STANDARD_LOCAL_USER; +LOCAL_USER_DECL; + + +static int change_callingpres(struct ast_channel *chan, void *data) +{ + int mode = 0; + struct localuser *u; + LOCAL_USER_ADD(u); + if (data) { + mode = atoi((char *)data); + chan->cid.cid_pres = mode; + } else + ast_log(LOG_NOTICE, "Application %s requres an argument: %s(number)\n", app_callingpres,app_callingpres); + LOCAL_USER_REMOVE(u); + return 0; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app_callingpres); +} + +int load_module(void) +{ + return ast_register_application(app_callingpres, change_callingpres, synopsis_callingpres, descrip_callingpres); +} + +char *description(void) +{ + return descrip_callingpres; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_capiCD.c asterisk-1.2.10/apps/app_capiCD.c --- asterisk-1.2.10.orig/apps/app_capiCD.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_capiCD.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,172 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * Call Deflection, inspired by capircvd by Alexander Brickwedde + * + * Copyright (C) 2002,2003,2004,2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + + +static char *tdesc = "(CAPI*) Call Deflection, the magic thing."; +static char *app = "capiCD"; +static char *synopsis = "call deflection"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int capiCD_exec(struct ast_channel *chan, void *data) +{ + struct ast_capi_pvt *i = chan->tech_pvt; + MESSAGE_EXCHANGE_ERROR Info; + _cmsg CMSG; + char bchaninfo[1]; + char fac[60]; + int res=0; + int ms=3000; + struct localuser *u; + + if (!data) { + ast_log(LOG_WARNING, "cd requires an argument (destination phone number)\n"); + return -1; + } + LOCAL_USER_ADD(u); + /* Do our thing here */ + + if ((i->state == CAPI_STATE_CONNECTED) || (i->state == CAPI_STATE_BCONNECTED)) { + ast_log(LOG_ERROR, "call deflection does not work with calls that are already connected!\n"); + LOCAL_USER_REMOVE(u); + return -1; + } + // wait until the channel is alerting, so we dont drop the call and interfer with msgs + while ((ms > 0) && (i->state != CAPI_STATE_ALERTING)) { + sleep(100); + ms -= 100; + } + + // make sure we hang up correctly + i->state = CAPI_STATE_CONNECTPENDING; + + fac[0]=0; // len + fac[1]=0; //len + fac[2]=0x01; // Use D-Chan + fac[3]=0; // Keypad len + fac[4]=31; // user user data? len = 31 = 29 + 2 + fac[5]=0x1c; // magic? + fac[6]=0x1d; // strlen destination + 18 = 29 + fac[7]=0x91; // .. + fac[8]=0xA1; + fac[9]=0x1A; // strlen destination + 15 = 26 + fac[10]=0x02; + fac[11]=0x01; + fac[12]=0x70; + fac[13]=0x02; + fac[14]=0x01; + fac[15]=0x0d; + fac[16]=0x30; + fac[17]=0x12; // strlen destination + 7 = 18 + fac[18]=0x30; // ...hm 0x30 + fac[19]=0x0d; // strlen destination + 2 + fac[20]=0x80; // CLIP + fac[21]=0x0b; // strlen destination + fac[22]=0x01; // destination start + fac[23]=0x01; // + fac[24]=0x01; // + fac[25]=0x01; // + fac[26]=0x01; // + fac[27]=0x01; // + fac[28]=0x01; // + fac[29]=0x01; // + fac[30]=0x01; // + fac[31]=0x01; // + fac[32]=0x01; // + fac[33]=0x01; // 0x1 = sending complete + fac[34]=0x01; + fac[35]=0x01; + + memcpy((unsigned char *)fac+22,data,strlen(data)); + fac[22+strlen(data)]=0x01; // fill with 0x01 if number is only 6 numbers (local call) + fac[23+strlen(data)]=0x01; + fac[24+strlen(data)]=0x01; + fac[25+strlen(data)]=0x01; + fac[26+strlen(data)]=0x01; + + fac[6]=18+strlen(data); + fac[9]=15+strlen(data); + fac[17]=7+strlen(data); + fac[19]=2+strlen(data); + fac[21]=strlen(data); + + bchaninfo[0] = 0x1; + INFO_REQ_HEADER(&CMSG,ast_capi_ApplID,ast_capi_MessageNumber++,0); + INFO_REQ_CONTROLLER(&CMSG) = i->controller; + INFO_REQ_PLCI(&CMSG) = i->PLCI; + INFO_REQ_BCHANNELINFORMATION(&CMSG) = (unsigned char*)bchaninfo; // use D-Channel + INFO_REQ_KEYPADFACILITY(&CMSG) = 0; + INFO_REQ_USERUSERDATA(&CMSG) = 0; + INFO_REQ_FACILITYDATAARRAY(&CMSG) = (unsigned char*) fac + 4; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"Error sending INFO_REQ\n"); + return Info; + } else { + if (capidebug) { + // ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(&CMSG)); + ast_log(LOG_NOTICE,"sent INFO_REQ PLCI = %#x\n",i->PLCI); + } + } + + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, capiCD_exec,synopsis,tdesc); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_capiECT.c asterisk-1.2.10/apps/app_capiECT.c --- asterisk-1.2.10.orig/apps/app_capiECT.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_capiECT.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,210 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * ECT transfer the held call + * + * Copyright (C) 2002,2003,2004,2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static char *tdesc = "(CAPI*) ECT"; +static char *app = "capiECT"; +static char *synopsis = "transfer the call that is on hold"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + + +static int capiECT_exec(struct ast_channel *chan, void *data) +{ + struct ast_capi_pvt *i = chan->tech_pvt; + MESSAGE_EXCHANGE_ERROR Info; + _cmsg CMSG; + unsigned char fac[8]; + int res=0; + struct localuser *u; + char *ecodes = "*#"; + + if (!data) { + ast_log(LOG_WARNING, "ECT requires an argument (destination phone number)\n"); + return -1; + } + LOCAL_USER_ADD(u); + /* Do our thing here */ + if (i->onholdPLCI <= 0) { + ast_log(LOG_WARNING, "no call on hold that could be transfered\n"); + return -1; + } + + ast_log(LOG_NOTICE,"ECT to %s\n",(char *)data); + capi_call(chan,data,0); + + while ((i->state != CAPI_STATE_BCONNECTED) && (i->onholdPLCI != 0)) { + usleep(10000); + } + + + if (i->state == CAPI_STATE_BCONNECTED) { + ast_log(LOG_NOTICE,"call was answered\n"); + + capi_detect_dtmf(chan,1); + + // put the stuff to play announcement message here ---> <----- + res = ast_say_digit_str(chan,i->cid,ecodes,chan->language); + if ( res == '#') { + ast_log(LOG_NOTICE,"res = %d\n",res); + // user pressed #, hangup + // first the holded user +// ast_exec("capiRETRIEVE",chan); + + DISCONNECT_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG) = i->onholdPLCI; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_REQ PLCI=%#x\n",i->onholdPLCI); + } else { + ast_log(LOG_NOTICE, "sent DISCONNECT_REQ PLCI=%#x\n",i->onholdPLCI); + } + + // then the destination + + DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } else { + ast_log(LOG_NOTICE, "sent DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } + + // wait for the B3 layer to go down + while (i->state != CAPI_STATE_CONNECTED) { + usleep(10000); + } + + DISCONNECT_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG) = i->PLCI; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } else { + ast_log(LOG_NOTICE, "sent DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } + + + LOCAL_USER_REMOVE(u); + return -1; + + } else { + // now drop the bchannel + DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } else { + ast_log(LOG_NOTICE, "sent DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } + + // wait for the B3 layer to go down + while (i->state != CAPI_STATE_CONNECTED) { + usleep(10000); + } + } + } + + // the caller onhold hungup or died away, drop the answered call + if (i->onholdPLCI == 0) { + DISCONNECT_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG) = i->PLCI; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } else { + ast_log(LOG_NOTICE, "sent DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } + return -1; + } + + ast_log(LOG_NOTICE,"onholdPLCI = %d\n",i->onholdPLCI); + + + fac[0] = 7; // len + fac[1] = 0x06; // ECT (function) + fac[2] = 0x00; + fac[3] = 4; //len //sservice specific parameter , cstruct + fac[4] = (i->onholdPLCI << 8 ) >> 8; + fac[5] = i->onholdPLCI >> 8; + fac[6] = 0; + fac[7] = 0; + + FACILITY_REQ_HEADER(&CMSG,ast_capi_ApplID,ast_capi_MessageNumber++,0); + FACILITY_REQ_CONTROLLER(&CMSG) = i->controller; + FACILITY_REQ_PLCI(&CMSG) = i->onholdPLCI; + FACILITY_REQ_FACILITYSELECTOR(&CMSG) = 0x0003; // sservices + FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = (unsigned char *)&fac; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"Error sending FACILITY_REQ\n"); + return Info; + } else { + ast_log(LOG_NOTICE,"sent FACILITY_REQ PLCI = %#x (%#x %#x) onholdPLCI = %#x\n ",i->PLCI,fac[4],fac[5],i->onholdPLCI); + ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(&CMSG)); + } + +// i->outgoing = -1; // incoming + outgoing, this is a magic channel :) + + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, capiECT_exec,synopsis,tdesc); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_capiNoES.c asterisk-1.2.10/apps/app_capiNoES.c --- asterisk-1.2.10.orig/apps/app_capiNoES.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_capiNoES.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,96 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * Disable echo suppression (useful for fax and voicemail!) + * + * Copyright (C) 2004,2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + + +#ifdef CAPI_ES +static char *tdesc = "(CAPI*) No Echo Suppression."; +static char *app = "capiNoES"; +static char *synopsis = "Disable Echo Suppression"; +#else +static char *tdesc = "(CAPI*) No Echo Suppression at all!"; +static char *app = "capiNoES"; +static char *synopsis = "Bogus Application"; +#endif +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int capiNoES_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + LOCAL_USER_ADD(u); + +#ifdef CAPI_ES + if (strcasecmp("CAPI",chan->type) == 0) { +#ifdef CVS_HEAD + struct ast_capi_pvt *i = chan->tech_pvt; +#else + struct ast_capi_pvt *i = chan->pvt->pvt; +#endif + if (i->doES == 1) { + i->doES = 0; + } + } else { + ast_log(LOG_WARNING, "capiNoES only works on CAPI channels, check your extensions.conf!\n"); + } +#endif + + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, capiNoES_exec,synopsis,tdesc); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_chanisavail.c asterisk-1.2.10/apps/app_chanisavail.c --- asterisk-1.2.10.orig/apps/app_chanisavail.c 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/apps/app_chanisavail.c 2006-07-31 14:13:08.000000000 +0200 @@ -118,7 +118,7 @@ snprintf(trychan, sizeof(trychan), "%s/%s",cur,number); status = inuse = ast_device_state(trychan); } - if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) { + if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status, NULL))) { pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name); /* Store the originally used channel too */ snprintf(tmp, sizeof(tmp), "%s/%s", tech, number); diff -urN asterisk-1.2.10.orig/apps/app_chanspy.c asterisk-1.2.10/apps/app_chanspy.c --- asterisk-1.2.10.orig/apps/app_chanspy.c 2006-06-14 16:07:53.000000000 +0200 +++ asterisk-1.2.10/apps/app_chanspy.c 2006-07-31 14:13:08.000000000 +0200 @@ -55,6 +55,7 @@ static const char *synopsis = "Listen to the audio of an active channel\n"; static const char *app = "ChanSpy"; +static const char *app2 = "ChanSpyChan"; static const char *desc = " ChanSpy([chanprefix][|options]): This application is used to listen to the\n" "audio from an active Asterisk channel. This includes the audio coming in and\n" @@ -142,6 +143,19 @@ return ret; } +static struct ast_channel *local_get_channel_uniqueid(char *uniqueid) +{ + struct ast_channel *chan = NULL; + if (uniqueid) { + ast_mutex_lock(&modlock); + if ((chan = ast_get_channel_by_uniqueid_locked(uniqueid))) { + ast_mutex_unlock(&chan->lock); + } + ast_mutex_unlock(&modlock); + } + return chan; +} + static void *spy_alloc(struct ast_channel *chan, void *data) { /* just store the data pointer in the channel structure */ @@ -554,11 +568,87 @@ ALL_DONE(u, res); } +static int chanspychan_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + struct ast_channel *peer=NULL; + char *args, + *uniqueid = NULL, + *argv[5]; + int res = -1, + volfactor = 0, + argc = 0, + oldrf = 0, + oldwf = 0, + fd = 0; + signed char zero_volume = 0; + + if (!(args = ast_strdupa((char *)data))) { + ast_log(LOG_ERROR, "Out of memory!\n"); + return -1; + } + + LOCAL_USER_ADD(u); + + oldrf = chan->readformat; + oldwf = chan->writeformat; + if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) { + ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); + LOCAL_USER_REMOVE(u); + return -1; + } + + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + LOCAL_USER_REMOVE(u); + return -1; + } + + + if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) { + uniqueid = argv[0]; + if (ast_strlen_zero(uniqueid)) { + LOCAL_USER_REMOVE(u); + return -1; + } + } + + ast_answer(chan); + + ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ + + peer = local_get_channel_uniqueid(uniqueid); + if (peer && (peer != chan) && !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) { + res = channel_spy(chan, peer, &volfactor, fd); + } else { + ast_log(LOG_NOTICE, "no channel found with uniqueid %s\n", uniqueid); + } + + if (fd > 0) { + close(fd); + } + + if (oldrf && ast_set_read_format(chan, oldrf) < 0) { + ast_log(LOG_ERROR, "Could Not Set Read Format.\n"); + } + + if (oldwf && ast_set_write_format(chan, oldwf) < 0) { + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + } + + ast_clear_flag(chan, AST_FLAG_SPYING); + + ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); + + ALL_DONE(u, res); +} + int unload_module(void) { int res; - res = ast_unregister_application(app); + ast_unregister_application(app); + res = ast_unregister_application(app2); STANDARD_HANGUP_LOCALUSERS; @@ -567,7 +657,8 @@ int load_module(void) { - return ast_register_application(app, chanspy_exec, synopsis, desc); + ast_register_application(app, chanspy_exec, synopsis, desc); + return ast_register_application(app2, chanspychan_exec, synopsis, desc); } char *description(void) diff -urN asterisk-1.2.10.orig/apps/app_devstate.c asterisk-1.2.10/apps/app_devstate.c --- asterisk-1.2.10.orig/apps/app_devstate.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_devstate.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,225 @@ +/* + * Devstate application + * + * Since we like the snom leds so much, a little app to + * light the lights on the snom on demand .... + * + * Copyright (C) 2005, Druid Software + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static char type[] = "DS"; +static char tdesc[] = "Application for sending device state messages"; + +static char app[] = "Devstate"; + +static char synopsis[] = "Generate a device state change event given the input parameters"; + +static char descrip[] = " Devstate(device|state): Generate a device state change event given the input parameters. Returns 0. State values match the asterisk device states. They are 0 = unknown, 1 = not inuse, 2 = inuse, 3 = busy, 4 = invalid, 5 = unavailable, 6 = ringing\n"; + +static char devstate_cli_usage[] = +"Usage: devstate device state\n" +" Generate a device state change event given the input parameters.\n Mainly used for lighting the LEDs on the snoms.\n"; + +static int devstate_cli(int fd, int argc, char *argv[]); +static struct ast_cli_entry cli_dev_state = + { { "devstate", NULL }, devstate_cli, "Set the device state on one of the \"pseudo devices\".", devstate_cli_usage }; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + + +static int devstate_cli(int fd, int argc, char *argv[]) +{ + char devName[128]; + if ((argc != 3) && (argc != 4) && (argc != 5)) + return RESULT_SHOWUSAGE; + + if (ast_db_put("DEVSTATES", argv[1], argv[2])) + { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + } + snprintf(devName, sizeof(devName), "DS/%s", argv[1]); + if (argc == 4) { + ast_log(LOG_NOTICE, "devname %s cid %s\n", devName, argv[3]); + ast_device_state_changed_literal(devName, argv[3], NULL); + } else if (argc == 5) { + ast_log(LOG_NOTICE, "devname %s cid %s cidname %s\n", devName, argv[3], argv[4]); + ast_device_state_changed_literal(devName, argv[3], argv[4]); + } else { + ast_device_state_changed_literal(devName, NULL, NULL); + } + return RESULT_SUCCESS; +} + +static int devstate_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + char *device, *state, *info; + char devName[128]; + if (!(info = ast_strdupa(data))) { + ast_log(LOG_WARNING, "Unable to dupe data :(\n"); + return -1; + } + LOCAL_USER_ADD(u); + + device = info; + state = strchr(info, '|'); + if (state) { + *state = '\0'; + state++; + } + else + { + ast_log(LOG_DEBUG, "No state argument supplied\n"); + return -1; + } + + if (ast_db_put("DEVSTATES", device, state)) + { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + } + + snprintf(devName, sizeof(devName), "DS/%s", device); + ast_device_state_changed_literal(devName, NULL, NULL); + + LOCAL_USER_REMOVE(u); + return 0; +} + + +static int ds_devicestate(void *data) +{ + char *dest = data; + char stateStr[16]; + if (ast_db_get("DEVSTATES", dest, stateStr, sizeof(stateStr))) + { + ast_log(LOG_DEBUG, "ds_devicestate couldnt get state in astdb\n"); + return 0; + } + else + { + ast_log(LOG_DEBUG, "ds_devicestate dev=%s returning state %d\n", + dest, atoi(stateStr)); + return (atoi(stateStr)); + } +} + +static struct ast_channel_tech devstate_tech = { + .type = type, + .description = tdesc, + .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), + .devicestate = ds_devicestate, + .requester = NULL, + .send_digit = NULL, + .send_text = NULL, + .call = NULL, + .hangup = NULL, + .answer = NULL, + .read = NULL, + .write = NULL, + .bridge = NULL, + .exception = NULL, + .indicate = NULL, + .fixup = NULL, + .setoption = NULL, +}; + +static char mandescr_devstate[] = +"Description: Put a value into astdb\n" +"Variables: \n" +" Family: ...\n" +" Key: ...\n" +" Value: ...\n"; + +static int action_devstate(struct mansession *s, struct message *m) +{ + char *devstate = astman_get_header(m, "Devstate"); + char *value = astman_get_header(m, "Value"); + char *id = astman_get_header(m,"ActionID"); + char *cid_num = astman_get_header(m, "CallerID"); + char *cid_name = astman_get_header(m, "CallerIDName"); + char devName[128]; + + if (!strlen(devstate)) { + astman_send_error(s, m, "No Devstate specified"); + return 0; + } + if (!strlen(value)) { + astman_send_error(s, m, "No Value specified"); + return 0; + } + + if (!ast_db_put("DEVSTATES", devstate, value)) { + snprintf(devName, sizeof(devName), "DS/%s", devstate); +// ast_device_state_changed(devName); + ast_device_state_changed_literal(devName, cid_num, cid_name); + ast_cli(s->fd, "Response: Success\r\n"); + } else { + ast_log(LOG_DEBUG, "ast_db_put failed\n"); + ast_cli(s->fd, "Response: Failed\r\n"); + } + if (id && !ast_strlen_zero(id)) + ast_cli(s->fd, "ActionID: %s\r\n",id); + ast_cli(s->fd, "\r\n"); + return 0; +} + +int load_module(void) +{ + if (ast_channel_register(&devstate_tech)) { + ast_log(LOG_DEBUG, "Unable to register channel class %s\n", type); + return -1; + } + ast_cli_register(&cli_dev_state); + ast_manager_register2( "Devstate", EVENT_FLAG_CALL, action_devstate, "Change a device state", mandescr_devstate ); + return ast_register_application(app, devstate_exec, synopsis, descrip); +} + +int unload_module(void) +{ + int res = 0; + STANDARD_HANGUP_LOCALUSERS; + ast_manager_unregister( "Devstate"); + ast_cli_unregister(&cli_dev_state); + res = ast_unregister_application(app); + ast_channel_unregister(&devstate_tech); + return res; +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_dial.c asterisk-1.2.10/apps/app_dial.c --- asterisk-1.2.10.orig/apps/app_dial.c 2006-06-09 20:08:00.000000000 +0200 +++ asterisk-1.2.10/apps/app_dial.c 2006-08-04 11:23:28.000000000 +0200 @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -55,6 +59,7 @@ #include "asterisk/causes.h" #include "asterisk/manager.h" #include "asterisk/privacy.h" +#include "asterisk/transcap.h" static char *tdesc = "Dialing Application"; @@ -115,7 +120,8 @@ " action post answer options in conjunction with this option.\n" " h - Allow the called party to hang up by sending the '*' DTMF digit.\n" " H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n" -" j - Jump to priority n+101 if all of the requested channels were busy.\n" +" j - Jump to priority n+101 if the called party was busy.\n" +" Jump to priority n+201 if all of the requested channels were busy.\n" " L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n" " left. Repeat the warning every 'z' ms. The following special\n" " variables can be used with this option:\n" @@ -162,8 +168,11 @@ " family/key is not specified.\n" " r - Indicate ringing to the calling party. Pass no audio to the calling\n" " party until the called channel has answered.\n" +" R - indicate ringing to the calling party when the called party indicates\n" +" ringing, pass no audio until answered.\n" " S(x) - Hang up the call after 'x' seconds *after* the called party has\n" -" answered the call.\n" +" answered the call.\n" +" c - callback initiation, ring once and hangup.\n" " t - Allow the called party to transfer the calling party by sending the\n" " DTMF sequence defined in features.conf.\n" " T - Allow the calling party to transfer the called party by sending the\n" @@ -214,6 +223,8 @@ OPT_CALLEE_MONITOR = (1 << 21), OPT_CALLER_MONITOR = (1 << 22), OPT_GOTO = (1 << 23), + OPT_NOINBAND = (1 << 24), + OPT_CALLBACK_INIT = (1 << 25), } dial_exec_option_flags; #define DIAL_STILLGOING (1 << 30) @@ -252,6 +263,8 @@ AST_APP_OPTION('p', OPT_SCREENING), AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), AST_APP_OPTION('r', OPT_RINGBACK), + AST_APP_OPTION('R', OPT_NOINBAND), + AST_APP_OPTION('c', OPT_CALLBACK_INIT), AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), AST_APP_OPTION('t', OPT_CALLEE_TRANSFER), AST_APP_OPTION('T', OPT_CALLER_TRANSFER), @@ -389,7 +402,7 @@ char *context = NULL; char cidname[AST_MAX_EXTENSION]; - single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK)); + single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK | OPT_NOINBAND)); if (single) { /* Turn off hold music, etc */ @@ -468,7 +481,7 @@ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); /* Setup parameters */ - o->chan = ast_request(tech, in->nativeformats, stuff, &cause); + o->chan = ast_request(tech, in->nativeformats, stuff, &cause, NULL); if (!o->chan) ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); else @@ -586,12 +599,18 @@ HANDLE_CAUSE(AST_CAUSE_CONGESTION, in); break; case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); - if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { - ast_indicate(in, AST_CONTROL_RINGING); - (*sentringing)++; - } + if (ast_test_flag(peerflags, OPT_CALLBACK_INIT)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is ringing, hanging up.\n", o->chan->name); + return NULL; + } else { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); + if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { + ast_indicate(in, AST_CONTROL_RINGING); + (*sentringing)++; + } + } break; case AST_CONTROL_PROGRESS: if (option_verbose > 2) @@ -766,6 +785,7 @@ int digit = 0, result = 0; time_t start_time, answer_time, end_time; struct ast_app *app = NULL; +/* char *aoceunits; */ char *parse; AST_DECLARE_APP_ARGS(args, @@ -939,13 +959,13 @@ } if( privdb_val == AST_PRIVACY_DENY ) { - strcpy(status, "NOANSWER"); + ast_copy_string(status, "NOANSWER", sizeof(status)); ast_verbose( VERBOSE_PREFIX_3 "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); res=0; goto out; } else if( privdb_val == AST_PRIVACY_KILL ) { - strcpy(status, "DONTCALL"); + ast_copy_string(status, "DONTCALL", sizeof(status)); if (option_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201); } @@ -953,7 +973,7 @@ goto out; /* Is this right? */ } else if( privdb_val == AST_PRIVACY_TORTURE ) { - strcpy(status, "TORTURE"); + ast_copy_string(status, "TORTURE", sizeof(status)); if (option_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301); } @@ -1001,7 +1021,7 @@ /* If a channel group has been specified, get it for use when we create peer channels */ outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"); - ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP); + ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP| OPT_CALLBACK_INIT | OPT_NOINBAND); cur = args.peers; do { /* Remember where to start next time */ @@ -1043,7 +1063,7 @@ ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst); } /* Request the peer */ - tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause); + tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause, NULL); if (!tmp->chan) { /* If we can't, just go on to the next call */ ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause)); @@ -1074,7 +1094,7 @@ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name); ast_hangup(tmp->chan); /* Setup parameters */ - tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause); + tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause, NULL); if (!tmp->chan) ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); else @@ -1187,7 +1207,7 @@ if (outgoing) { /* Our status will at least be NOANSWER */ - strcpy(status, "NOANSWER"); + ast_copy_string(status, "NOANSWER", sizeof(status)); if (ast_test_flag(outgoing, OPT_MUSICBACK)) { moh=1; ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]); @@ -1195,8 +1215,11 @@ ast_indicate(chan, AST_CONTROL_RINGING); sentringing++; } - } else - strcpy(status, "CHANUNAVAIL"); + } else { + ast_copy_string(status, "CHANUNAVAIL", sizeof(status)); + /* See if there is a special message */ + ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201); + } time(&start_time); peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result); @@ -1561,18 +1584,22 @@ ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING); if (play_to_callee) ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING); - if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) + + if ((chan->transfercapability != AST_TRANS_CAP_DIGITAL) && (chan->transfercapability != AST_TRANS_CAP_RESTRICTED_DIGITAL)) { + /* only non-digital calls are allowed to go through userspace */ + if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) + if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) + if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) + if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) + if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) + if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); + } config.timelimit = timelimit; config.play_warning = play_warning; @@ -1608,7 +1635,15 @@ } snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time)); pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast); - + + /* forward AOC-E units from peer, if possible */ +/* aoceunits = pbx_builtin_getvar_helper(peer, "AOCEUNITS"); + + if (aoceunits) { + snprintf(toast, sizeof(toast), "%d", atoi(aoceunits)); + pbx_builtin_setvar_helper(chan, "AOCEUNITS", toast); + } */ + if (res != AST_PBX_NO_HANGUP_PEER) { if (!chan->_softhangup) chan->hangupcause = peer->hangupcause; diff -urN asterisk-1.2.10.orig/apps/app_directed_pickup.c asterisk-1.2.10/apps/app_directed_pickup.c --- asterisk-1.2.10.orig/apps/app_directed_pickup.c 2006-04-06 19:00:10.000000000 +0200 +++ asterisk-1.2.10/apps/app_directed_pickup.c 2006-07-31 14:13:08.000000000 +0200 @@ -41,7 +41,7 @@ #include "asterisk/app.h" static const char *tdesc = "Directed Call Pickup Application"; -static const char *app = "Pickup"; +static const char *app = "DPickup"; static const char *synopsis = "Directed Call Pickup"; static const char *descrip = " Pickup(extension[@context]): This application can pickup any ringing channel\n" diff -urN asterisk-1.2.10.orig/apps/app_meetme.c asterisk-1.2.10/apps/app_meetme.c --- asterisk-1.2.10.orig/apps/app_meetme.c 2006-06-11 23:08:04.000000000 +0200 +++ asterisk-1.2.10/apps/app_meetme.c 2006-07-31 14:13:08.000000000 +0200 @@ -453,7 +453,7 @@ ast_copy_string(cnf->pin, pin, sizeof(cnf->pin)); ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin)); cnf->markedusers = 0; - cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL); + cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL, NULL); if (cnf->chan) { cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */ } else { @@ -823,9 +823,10 @@ char exitcontext[AST_MAX_CONTEXT] = ""; char recordingtmp[AST_MAX_EXTENSION] = ""; int dtmf, opt_waitmarked_timeout = 0; time_t timeout = 0; + int dyna_buff = CONF_SIZE; ZT_BUFFERINFO bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; + char __buf[ZT_MAX_BUF_SPACE / ZT_DEFAULT_NUM_BUFS + AST_FRIENDLY_OFFSET]; char *buf = __buf + AST_FRIENDLY_OFFSET; if (!user) { @@ -990,7 +991,7 @@ } /* Setup buffering information */ memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE/2; + bi.bufsize = dyna_buff / 2; bi.txbufpolicy = ZT_POLICY_IMMEDIATE; bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; bi.numbufs = audio_buffers; @@ -1275,6 +1276,14 @@ f = ast_read(c); if (!f) break; + if (f->datalen && f->datalen != dyna_buff) { + ast_log(LOG_NOTICE, "Audio bytes: %d Buffer size: %d\n", f->datalen, dyna_buff); + if (f->datalen < ZT_MAX_BUF_SPACE/audio_buffers) { /* skip too large frame to avoid overflow */ + dyna_buff = f->datalen; + close(fd); + goto zapretry; + } + } if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) { if (user->talk.actual) ast_frame_adjust_volume(f, user->talk.actual); @@ -1509,7 +1518,7 @@ } ast_frfree(f); } else if (outfd > -1) { - res = read(outfd, buf, CONF_SIZE); + res = read(outfd, buf, dyna_buff); if (res > 0) { memset(&fr, 0, sizeof(fr)); fr.frametype = AST_FRAME_VOICE; diff -urN asterisk-1.2.10.orig/apps/app_milliwatt.c asterisk-1.2.10/apps/app_milliwatt.c --- asterisk-1.2.10.orig/apps/app_milliwatt.c 2006-01-19 05:17:45.000000000 +0100 +++ asterisk-1.2.10/apps/app_milliwatt.c 2006-07-31 14:13:08.000000000 +0200 @@ -74,20 +74,28 @@ { struct ast_frame wf; unsigned char buf[AST_FRIENDLY_OFFSET + 640]; + const int maxsamples = (sizeof (buf) - AST_FRIENDLY_OFFSET) / sizeof (buf[0]); int i,*indexp = (int *) data; - if (len + AST_FRIENDLY_OFFSET > sizeof(buf)) - { - ast_log(LOG_WARNING,"Only doing %d bytes (%d bytes requested)\n",(int)(sizeof(buf) - AST_FRIENDLY_OFFSET),len); - len = sizeof(buf) - AST_FRIENDLY_OFFSET; - } + /* Instead of len, use samples, because channel.c generator_force + * generate(chan, tmp, 0, 160) ignores len. In any case, len is + * a multiple of samples, given by number of samples times bytes per + * sample. In the case of ulaw, len = samples. for signed linear + * len = 2 * samples */ + + if (samples > maxsamples) + { + ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n", maxsamples, samples); + samples = maxsamples; + } + len = samples * sizeof (buf[0]); wf.frametype = AST_FRAME_VOICE; wf.subclass = AST_FORMAT_ULAW; wf.offset = AST_FRIENDLY_OFFSET; wf.mallocd = 0; wf.data = buf + AST_FRIENDLY_OFFSET; wf.datalen = len; - wf.samples = wf.datalen; + wf.samples = samples; wf.src = "app_milliwatt"; wf.delivery.tv_sec = 0; wf.delivery.tv_usec = 0; diff -urN asterisk-1.2.10.orig/apps/app_page.c asterisk-1.2.10/apps/app_page.c --- asterisk-1.2.10.orig/apps/app_page.c 2006-04-13 19:40:21.000000000 +0200 +++ asterisk-1.2.10/apps/app_page.c 2006-07-31 14:13:08.000000000 +0200 @@ -85,7 +85,7 @@ { struct calloutdata *cd = data; ast_pbx_outgoing_app(cd->tech, AST_FORMAT_SLINEAR, cd->resource, 30000, - "MeetMe", cd->meetmeopts, NULL, 0, cd->cidnum, cd->cidname, cd->variables, NULL, NULL); + "MeetMe", cd->meetmeopts, NULL, 0, 0, cd->cidnum, cd->cidname, cd->variables, NULL, NULL, NULL); free(cd); return NULL; } diff -urN asterisk-1.2.10.orig/apps/app_parkandannounce.c asterisk-1.2.10/apps/app_parkandannounce.c --- asterisk-1.2.10.orig/apps/app_parkandannounce.c 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/apps/app_parkandannounce.c 2006-07-31 14:13:08.000000000 +0200 @@ -183,7 +183,7 @@ memset(&oh, 0, sizeof(oh)); oh.parent_channel = chan; - dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh); + dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, 0, chan->cid.cid_num, chan->cid.cid_name, &oh, NULL); if(dchan) { if(dchan->_state == AST_STATE_UP) { diff -urN asterisk-1.2.10.orig/apps/app_pickup.c asterisk-1.2.10/apps/app_pickup.c --- asterisk-1.2.10.orig/apps/app_pickup.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_pickup.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,319 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Pickup, channel independent call pickup + * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * Copyright (C) 2004, Florian Overkamp + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static char *tdesc = "PickUp/PickDown/Steal/PickupChan/StealChan"; + +static char *app = "PickUp"; + +static char *synopsis = "Channel independent call pickup."; + +static char *descrip = +" PickDown([group]): Tries to pickup the first ringing channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app2 = "Steal"; + +static char *synopsis2 = "Channel independent call stealing. Just like pickup but for answered channels."; + +static char *descrip2 = +" Steal([group]): Tries to steal the first bridged channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app3 = "PickDown"; + +static char *synopsis3 = "Channel independent call pickdown."; + +static char *descrip3 = +" PickDown([group]): Tries to hangup the first ringing channel with callgroup == group.\n" +" If called without the group argument, the pickupgroup of the channel will be used.\n"; + +static char *app4 = "PickupChan"; + +static char *synopsis4 = "Channel independent call pickup."; + +static char *descrip4 = +" PickupChan(Technology/resource[&Technology2/resource2...]): Tries to pickup the first ringing channel in the parameter list.\n"; + +static char *app5 = "StealChan"; + +static char *synopsis5 = "Channel independent call stealing. Just like pickup but for answered channels."; + +static char *descrip5 = +" StealChan(Technology/resource[&Technology2/resource2...]): Tries to steal the first ringing channel in the parameter list.\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int my_pickup_call(struct ast_channel *chan, unsigned int pickupgroup, int chanstate, int bridge) { + struct ast_channel *cur; + int res = -1; + cur = ast_channel_walk_locked(NULL); + while(cur) { + if ((cur != chan) && + (pickupgroup & cur->callgroup) && + (cur->_state == chanstate)) { + break; + } + ast_mutex_unlock(&cur->lock); + cur = ast_channel_walk_locked(cur); + } + if (cur) { + if(option_verbose > 2) { + if (chanstate == AST_STATE_RINGING) { + if (bridge == 1) { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name); + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name); + } + } + if (bridge == 1) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + if (ast_channel_masquerade(cur, chan)) { + ast_log(LOG_ERROR, "unable to masquerade\n"); + } + ast_mutex_unlock(&cur->lock); + ast_mutex_unlock(&chan->lock); + } else { + cur->_softhangup = AST_SOFTHANGUP_DEV; + ast_mutex_unlock(&cur->lock); + } + } else { + if(option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "No channel found %d.\n",pickupgroup); + } + } + return res; +} + +static int my_pickup_channel(struct ast_channel *chan, void *data, int chanstate, int bridge) { + struct ast_channel *cur; + char channels[256]; + char evalchan[256]; + char *endptr; + int res = -1; + cur = ast_channel_walk_locked(NULL); + strncpy(channels, (char *)data, sizeof(channels) - 1); + while(cur) { + if ((cur != chan) && + (cur->_state == chanstate)) { + /* This call is a candidate (correct ringstate and not ourselves), now check if the channel is in our list */ + strncpy(evalchan, (char *)cur->name, sizeof(evalchan) - 1); + /* strip the subchannel tag */ + endptr = strrchr(evalchan, '-'); + if(endptr) { + *endptr = '\0'; + } + endptr = strrchr(evalchan, '/'); + if(endptr) { + *endptr = '\0'; + } + /* check for each of the members if they match (probably a stristr will do ?) */ + /* if we match the code, break */ + if(strstr(channels, evalchan) != NULL) { + ast_verbose(VERBOSE_PREFIX_1 "Nice channel, I'll take it: %s\n",evalchan); + break; + } + } + ast_mutex_unlock(&cur->lock); + cur = ast_channel_walk_locked(cur); + } + if (cur) { + if(option_verbose > 2) { + if (chanstate == AST_STATE_RINGING) { + if (bridge == 1) { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name); + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name); + } + } else { + ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name); + } + } + if (bridge == 1) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + if (ast_channel_masquerade(cur, chan)) { + ast_log(LOG_ERROR, "unable to masquerade\n"); + } + ast_mutex_unlock(&cur->lock); + ast_mutex_unlock(&chan->lock); + } else { + cur->_softhangup = AST_SOFTHANGUP_DEV; + ast_mutex_unlock(&cur->lock); + } + } else { + if(option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "No channel found %s.\n",channels); + } + } + return res; +} + + +static int pickup_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct localuser *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + LOCAL_USER_ADD(u); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 1); + } + if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int steal_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct localuser *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + LOCAL_USER_ADD(u); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_UP, 1); + } + if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int pickdown_exec(struct ast_channel *chan, void *data) +{ + int res=0; + unsigned int pickupgroup=0; + struct localuser *u; + if (!data || !strlen(data)) { + pickupgroup = chan->pickupgroup; + } else { + pickupgroup = ast_get_group(data); + } + LOCAL_USER_ADD(u); + if (!res) { + res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 0); + } + if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int pickupchan_exec(struct ast_channel *chan, void *data) { + int res=0; + struct localuser *u; + if (!data) { + ast_log(LOG_WARNING, "PickupChan requires an argument (technology1/number1&technology2/number2...)\n"); + return -1; + } + LOCAL_USER_ADD(u); + if (!res) { + res = my_pickup_channel(chan, data, AST_STATE_RINGING, 1); + } + if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int stealchan_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + if (!data) { + ast_log(LOG_WARNING, "StealChan requires an argument (technology1/number1&technology2/number2...)\n"); + return -1; + } + + LOCAL_USER_ADD(u); + if (!res) { + res = my_pickup_channel(chan, data, AST_STATE_UP, 1); + } + if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + ast_unregister_application(app5); + ast_unregister_application(app4); + ast_unregister_application(app3); + ast_unregister_application(app2); + return ast_unregister_application(app); +} + +int load_module(void) +{ + ast_register_application(app5, stealchan_exec, synopsis5, descrip5); + ast_register_application(app4, pickupchan_exec, synopsis4, descrip4); + ast_register_application(app3, pickdown_exec, synopsis3, descrip3); + ast_register_application(app2, steal_exec, synopsis2, descrip2); + return ast_register_application(app, pickup_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_queue.c asterisk-1.2.10/apps/app_queue.c --- asterisk-1.2.10.orig/apps/app_queue.c 2006-06-23 13:30:17.000000000 +0200 +++ asterisk-1.2.10/apps/app_queue.c 2006-07-31 14:13:08.000000000 +0200 @@ -526,7 +526,7 @@ return NULL; } -static int statechange_queue(const char *dev, int state, void *ign) +static int statechange_queue(const char *dev, int state, void *ign, char *cid_num, char *cid_name) { /* Avoid potential for deadlocks by spawning a new thread to handle the event */ @@ -1509,7 +1509,7 @@ location = ""; /* Request the peer */ - tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); + tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status, NULL); if (!tmp->chan) { /* If we can't, just go on to the next call */ #if 0 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech); @@ -1816,7 +1816,7 @@ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); /* Setup parameters */ - o->chan = ast_request(tech, in->nativeformats, stuff, &status); + o->chan = ast_request(tech, in->nativeformats, stuff, &status, NULL); if (status != o->oldstatus) update_dial_status(qe->parent, o->member, status); if (!o->chan) { @@ -2360,14 +2360,14 @@ else which = peer; if (monitorfilename) - ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); + ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 ); else if (qe->chan->cdr) - ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); + ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 ); else { /* Last ditch effort -- no CDR, make up something */ char tmpid[256]; snprintf(tmpid, sizeof(tmpid), "chan-%x", rand()); - ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); + ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 ); } if (qe->parent->monjoin) ast_monitor_setjoinfiles(which, 1); diff -urN asterisk-1.2.10.orig/apps/app_readfile.c asterisk-1.2.10/apps/app_readfile.c --- asterisk-1.2.10.orig/apps/app_readfile.c 2006-03-23 21:13:48.000000000 +0100 +++ asterisk-1.2.10/apps/app_readfile.c 2006-07-31 14:13:08.000000000 +0200 @@ -40,7 +40,7 @@ #include "asterisk/app.h" #include "asterisk/module.h" -static char *tdesc = "Stores output of file into a variable"; +static char *tdesc = "Stores content of file into a variable"; static char *app_readfile = "ReadFile"; diff -urN asterisk-1.2.10.orig/apps/app_segfault.c asterisk-1.2.10/apps/app_segfault.c --- asterisk-1.2.10.orig/apps/app_segfault.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_segfault.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,75 @@ +/* + * Segfault application + * + * An application to provoke a segmentation fault from the dialplan. + * (I know what you are thinking now...., but since Asterisk is too stable... + * I needed something to test my failover switches.) + * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License. THIS APPLICATION _WILL_ CRASH YOUR + * ASTERISK SERVER SO OF COURSE THERE IS NOT LIABILITY FOR NOTHING! + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *tdesc = "Application for crashing Asterisk with a segmentation fault"; + +static char *app = "Segfault"; + +static char *synopsis = "This application will crash Asterisk with a segmentation fault."; + +static char *descrip = +" Segfault(): Crash with a segfault. Never returns nufin.\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int segfault_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + LOCAL_USER_ADD(u); + ((char *)0)[0] = 0; + LOCAL_USER_REMOVE(u); + return 0; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, segfault_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/apps/app_zapras.c asterisk-1.2.10/apps/app_zapras.c --- asterisk-1.2.10.orig/apps/app_zapras.c 2006-07-12 15:54:10.000000000 +0200 +++ asterisk-1.2.10/apps/app_zapras.c 2006-07-31 14:13:08.000000000 +0200 @@ -180,7 +180,7 @@ } } /* Throw back into audio mode */ - x = 1; + x = 0; ioctl(chan->fds[0], ZT_AUDIOMODE, &x); /* Restore saved values */ diff -urN asterisk-1.2.10.orig/apps/app_zapras.c.orig asterisk-1.2.10/apps/app_zapras.c.orig --- asterisk-1.2.10.orig/apps/app_zapras.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/apps/app_zapras.c.orig 2006-07-12 15:54:10.000000000 +0200 @@ -0,0 +1,274 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Execute an ISDN RAS + * + * \ingroup applications + */ + +#include +#include +#ifdef __linux__ +#include +#else +#include +#endif /* __linux__ */ + +#include +#include +#include +#include +#include +#include +#include + +/* Need some zaptel help here */ +#ifdef __linux__ +#include +#else +#include +#endif /* __linux__ */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 37419 $") + +#include "asterisk/lock.h" +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/options.h" + +static char *tdesc = "Zap RAS Application"; + +static char *app = "ZapRAS"; + +static char *synopsis = "Executes Zaptel ISDN RAS application"; + +static char *descrip = +" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n" +"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n" +"channel to be able to use this function (No modem emulation is included).\n" +"Your pppd must be patched to be zaptel aware. Arguments should be\n" +"separated by | characters.\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +#define PPP_MAX_ARGS 32 +#define PPP_EXEC "/usr/sbin/pppd" + +static pid_t spawn_ras(struct ast_channel *chan, char *args) +{ + pid_t pid; + int x; + char *c; + + char *argv[PPP_MAX_ARGS]; + int argc = 0; + char *stringp=NULL; + + /* Start by forking */ + pid = fork(); + if (pid) + return pid; + + /* Execute RAS on File handles */ + dup2(chan->fds[0], STDIN_FILENO); + + /* Drop high priority */ + if (option_highpriority) + ast_set_priority(0); + + /* Close other file descriptors */ + for (x=STDERR_FILENO + 1;x<1024;x++) + close(x); + + /* Restore original signal handlers */ + for (x=0;xfds[0], ZT_GET_BUFINFO, &savebi); + if(res) { + ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name); + return; + } + + pid = spawn_ras(chan, args); + if (pid < 0) { + ast_log(LOG_WARNING, "Failed to spawn RAS\n"); + } else { + for (;;) { + res = wait4(pid, &status, WNOHANG, NULL); + if (!res) { + /* Check for hangup */ + if (chan->_softhangup && !signalled) { + ast_log(LOG_DEBUG, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid); + kill(pid, SIGTERM); + signalled=1; + } + /* Try again */ + sleep(1); + continue; + } + if (res < 0) { + ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno)); + } + if (option_verbose > 2) { + if (WIFEXITED(status)) { + ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n", + chan->name, WTERMSIG(status)); + } else { + ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name); + } + } + /* Throw back into audio mode */ + x = 1; + ioctl(chan->fds[0], ZT_AUDIOMODE, &x); + + /* Restore saved values */ + res = ioctl(chan->fds[0], ZT_SET_BUFINFO, &savebi); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name); + } + break; + } + } +} + +static int zapras_exec(struct ast_channel *chan, void *data) +{ + int res=-1; + char *args; + struct localuser *u; + ZT_PARAMS ztp; + + if (!data) + data = ""; + + LOCAL_USER_ADD(u); + + args = ast_strdupa(data); + if (!args) { + ast_log(LOG_ERROR, "Out of memory\n"); + LOCAL_USER_REMOVE(u); + return -1; + } + + /* Answer the channel if it's not up */ + if (chan->_state != AST_STATE_UP) + ast_answer(chan); + if (strcasecmp(chan->type, "Zap")) { + /* If it's not a zap channel, we're done. Wait a couple of + seconds and then hangup... */ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a Zap channel\n", chan->name); + sleep(2); + } else { + memset(&ztp, 0, sizeof(ztp)); + if (ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp)) { + ast_log(LOG_WARNING, "Unable to get zaptel parameters\n"); + } else if (ztp.sigtype != ZT_SIG_CLEAR) { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a clear channel\n", chan->name); + } else { + /* Everything should be okay. Run PPP. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Starting RAS on %s\n", chan->name); + /* Execute RAS */ + run_ras(chan, args); + } + } + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + int res; + + res = ast_unregister_application(app); + + STANDARD_HANGUP_LOCALUSERS; + + return res; +} + +int load_module(void) +{ + return ast_register_application(app, zapras_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/asterisk.c asterisk-1.2.10/asterisk.c --- asterisk-1.2.10.orig/asterisk.c 2006-07-12 15:54:10.000000000 +0200 +++ asterisk-1.2.10/asterisk.c 2006-07-31 14:13:08.000000000 +0200 @@ -228,6 +228,7 @@ char ast_config_AST_CTL_OWNER[AST_CONFIG_MAX_PATH] = "\0"; char ast_config_AST_CTL_GROUP[AST_CONFIG_MAX_PATH] = "\0"; char ast_config_AST_CTL[AST_CONFIG_MAX_PATH] = "asterisk.ctl"; +char ast_config_AST_SYMBOLIC_NAME[20]; static char *_argv[256]; static int shuttingdown = 0; @@ -1877,6 +1878,7 @@ ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID)); ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET)); ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_RUN_DIR)); + ast_copy_string(ast_config_AST_SYMBOLIC_NAME, AST_SYMBOLIC_NAME, sizeof(ast_config_AST_SYMBOLIC_NAME)); /* no asterisk.conf? no problem, use buildtime config! */ if (!cfg) { @@ -1991,6 +1993,8 @@ /* What group to run as */ } else if (!strcasecmp(v->name, "rungroup")) { ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP)); + } else if (!strcasecmp(v->name, "uniquename")) { + strncpy(ast_config_AST_SYMBOLIC_NAME,v->value,sizeof(ast_config_AST_SYMBOLIC_NAME)); } v = v->next; } diff -urN asterisk-1.2.10.orig/build_tools/make_defaults_h asterisk-1.2.10/build_tools/make_defaults_h --- asterisk-1.2.10.orig/build_tools/make_defaults_h 2005-06-20 19:26:08.000000000 +0200 +++ asterisk-1.2.10/build_tools/make_defaults_h 2006-07-31 14:13:08.000000000 +0200 @@ -16,6 +16,7 @@ #define AST_KEY_DIR "${INSTALL_PATH}${ASTVARLIBDIR}/keys" #define AST_DB "${INSTALL_PATH}${ASTVARLIBDIR}/astdb" #define AST_TMP_DIR "${INSTALL_PATH}${ASTSPOOLDIR}/tmp" +#define AST_SYMBOLIC_NAME "asterisk" #define AST_CONFIG_FILE "${INSTALL_PATH}${ASTCONFPATH}" diff -urN asterisk-1.2.10.orig/channel.c asterisk-1.2.10/channel.c --- asterisk-1.2.10.orig/channel.c 2006-07-10 23:01:35.000000000 +0200 +++ asterisk-1.2.10/channel.c 2006-07-31 14:13:08.000000000 +0200 @@ -94,8 +94,8 @@ */ static int shutting_down = 0; -AST_MUTEX_DEFINE_STATIC(uniquelock); static int uniqueint = 0; +AST_MUTEX_DEFINE_EXPORTED(uniquelock); unsigned long global_fin = 0, global_fout = 0; @@ -512,6 +512,17 @@ .description = "Null channel (should not see this)", }; +char *ast_alloc_uniqueid(void) { + char *uniqueid; + uniqueid = malloc(64); + if (!uniqueid) return NULL; + ast_mutex_lock(&uniquelock); + snprintf(uniqueid, 63, "%s-%d-%li.%d", ast_config_AST_SYMBOLIC_NAME, ast_mainpid, (long)time(NULL), uniqueint++); + ast_mutex_unlock(&uniquelock); + return uniqueid; +} + + /*--- ast_channel_alloc: Create a new channel structure */ struct ast_channel *ast_channel_alloc(int needqueue) { @@ -519,6 +530,7 @@ int x; int flags; struct varshead *headp; + char *tmpuniqueid; /* If shutting down, don't allocate any new channels */ @@ -584,9 +596,12 @@ tmp->data = NULL; tmp->fin = global_fin; tmp->fout = global_fout; - ast_mutex_lock(&uniquelock); - snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long) time(NULL), uniqueint++); - ast_mutex_unlock(&uniquelock); + tmpuniqueid = ast_alloc_uniqueid(); + snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), tmpuniqueid); + if (tmpuniqueid) { + free(tmpuniqueid); + tmpuniqueid = NULL; + } headp = &tmp->varshead; ast_mutex_init(&tmp->lock); AST_LIST_HEAD_INIT_NOLOCK(headp); @@ -729,7 +744,7 @@ */ static struct ast_channel *channel_find_locked(const struct ast_channel *prev, const char *name, const int namelen, - const char *context, const char *exten) + const char *context, const char *exten, const char *uniqueid) { const char *msg = prev ? "deadlock" : "initial deadlock"; int retries, done; @@ -740,9 +755,14 @@ for (c = channels; c; c = c->next) { if (!prev) { /* want head of list */ - if (!name && !exten) + if (!name && !exten && !uniqueid) break; - if (name) { + if (uniqueid) { + if (!strcasecmp(c->uniqueid, uniqueid)) + break; + else + continue; + } else if (name) { /* want match by full name */ if (!namelen) { if (!strcasecmp(c->name, name)) @@ -793,33 +813,39 @@ /*--- ast_channel_walk_locked: Browse channels in use */ struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev) { - return channel_find_locked(prev, NULL, 0, NULL, NULL); + return channel_find_locked(prev, NULL, 0, NULL, NULL, NULL); } /*--- ast_get_channel_by_name_locked: Get channel by name and lock it */ struct ast_channel *ast_get_channel_by_name_locked(const char *name) { - return channel_find_locked(NULL, name, 0, NULL, NULL); + return channel_find_locked(NULL, name, 0, NULL, NULL, NULL); } /*--- ast_get_channel_by_name_prefix_locked: Get channel by name prefix and lock it */ struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen) { - return channel_find_locked(NULL, name, namelen, NULL, NULL); + return channel_find_locked(NULL, name, namelen, NULL, NULL, NULL); } /*--- ast_walk_channel_by_name_prefix_locked: Get next channel by name prefix and lock it */ struct ast_channel *ast_walk_channel_by_name_prefix_locked(struct ast_channel *chan, const char *name, const int namelen) { - return channel_find_locked(chan, name, namelen, NULL, NULL); + return channel_find_locked(chan, name, namelen, NULL, NULL, NULL); } /*--- ast_get_channel_by_exten_locked: Get channel by exten (and optionally context) and lock it */ struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context) { - return channel_find_locked(NULL, NULL, 0, context, exten); + return channel_find_locked(NULL, NULL, 0, context, exten, NULL); } +struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid) +{ + return channel_find_locked(NULL, NULL, 0, NULL, NULL, uniqueid); +} + + /*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */ int ast_safe_sleep_conditional( struct ast_channel *chan, int ms, int (*cond)(void*), void *data ) @@ -912,8 +938,10 @@ free(chan->tech_pvt); } - if (chan->sched) - sched_context_destroy(chan->sched); + if (chan->sched) { + sched_context_destroy(chan->sched); + chan->sched = NULL; + } ast_copy_string(name, chan->name, sizeof(name)); @@ -956,10 +984,11 @@ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) ast_var_delete(vardata); + free(chan); ast_mutex_unlock(&chlock); - ast_device_state_changed_literal(name); + ast_device_state_changed_literal(name, NULL, NULL); } int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy) @@ -2377,7 +2406,7 @@ &chan->writetrans, 1); } -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) +struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cid_num, const char *cid_name, struct outgoing_helper *oh, char* uniqueid) { int state = 0; int cause = 0; @@ -2385,7 +2414,7 @@ struct ast_frame *f; int res = 0; - chan = ast_request(type, format, data, &cause); + chan = ast_request(type, format, data, &cause, uniqueid); if (chan) { if (oh) { if (oh->vars) @@ -2399,6 +2428,7 @@ } ast_set_callerid(chan, cid_num, cid_name, cid_num); + chan->cid.cid_pres = callingpres; if (!ast_call(chan, data, 0)) { res = 1; /* in case chan->_state is already AST_STATE_UP */ while (timeout && (chan->_state != AST_STATE_UP)) { @@ -2422,6 +2452,7 @@ if (f->subclass == AST_CONTROL_RINGING) state = AST_CONTROL_RINGING; else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { + res = 0; state = f->subclass; ast_frfree(f); break; @@ -2491,12 +2522,12 @@ return chan; } -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) +struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cidnum, const char *cidname, char *uniqueid) { - return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL); + return __ast_request_and_dial(type, format, data, timeout, outstate, 0, cidnum, cidname, NULL, uniqueid); } -struct ast_channel *ast_request(const char *type, int format, void *data, int *cause) +struct ast_channel *ast_request(const char *type, int format, void *data, int *cause, char *uniqueid) { struct chanlist *chan; struct ast_channel *c; @@ -2533,6 +2564,7 @@ if (!(c = chan->tech->requester(type, capabilities, data, cause))) return NULL; + if (uniqueid) strncpy(c->uniqueid, uniqueid, sizeof(c->uniqueid)); if (c->_state == AST_STATE_DOWN) { manager_event(EVENT_FLAG_CALL, "Newchannel", "Channel: %s\r\n" @@ -2808,6 +2840,29 @@ return res; } +int ast_channel_masquerade_locked(struct ast_channel *original, struct ast_channel *clone) +{ + struct ast_frame null = { AST_FRAME_NULL, }; + int res = -1; + ast_log(LOG_DEBUG, "Planning to masquerade %s into the structure of %s\n", + clone->name, original->name); + if (original->masq) { + ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", + original->masq->name, original->name); + } else if (clone->masqr) { + ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", + clone->name, clone->masqr->name); + } else { + original->masq = clone; + clone->masqr = original; + ast_queue_frame(original, &null); + ast_queue_frame(clone, &null); + ast_log(LOG_DEBUG, "Done planning to masquerade %s into the structure of %s\n", original->name, clone->name); + res = 0; + } + return res; +} + void ast_change_name(struct ast_channel *chan, char *newname) { char tmp[256]; @@ -2947,7 +3002,7 @@ ast_copy_string(clone->name, masqn, sizeof(clone->name)); /* Notify any managers of the change, first the masq then the other */ - manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid); + manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\nNewUniqueid: %s\r\n", newn, masqn, clone->uniqueid, original->uniqueid); manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid); /* Swap the technlogies */ @@ -3174,15 +3229,14 @@ ); } -int ast_setstate(struct ast_channel *chan, int state) -{ +int ast_setstate_and_cid(struct ast_channel *chan, int state, char *cid_num, char *cid_name) { int oldstate = chan->_state; if (oldstate == state) return 0; chan->_state = state; - ast_device_state_changed_literal(chan->name); + ast_device_state_changed_literal(chan->name, cid_num, cid_name); manager_event(EVENT_FLAG_CALL, (oldstate == AST_STATE_DOWN) ? "Newchannel" : "Newstate", "Channel: %s\r\n" @@ -3198,6 +3252,10 @@ return 0; } +int ast_setstate(struct ast_channel *chan, int state) { + return ast_setstate_and_cid(chan, state, NULL, NULL); +} + /*--- Find bridged channel */ struct ast_channel *ast_bridged_channel(struct ast_channel *chan) { @@ -3375,6 +3433,7 @@ char callee_warning = 0; int to; + if (c0->_bridge) { ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", c0->name, c0->_bridge->name); @@ -3385,6 +3444,10 @@ c1->name, c1->_bridge->name); return -1; } + + if (IS_DIGITAL(c0->transfercapability) || IS_DIGITAL(c1->transfercapability)) { + config->flags = 0; + } /* Stop if we're a zombie or need a soft hangup */ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || diff -urN asterisk-1.2.10.orig/channels/Makefile asterisk-1.2.10/channels/Makefile --- asterisk-1.2.10.orig/channels/Makefile 2006-04-30 16:27:56.000000000 +0200 +++ asterisk-1.2.10/channels/Makefile 2006-07-31 14:13:08.000000000 +0200 @@ -102,6 +102,11 @@ ZAPR2=-lmfcr2 endif +ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/lib/libgsmat.so.1)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/lib/libgsmat.so.1),) + CFLAGS+=-DZAPATA_GSM + ZAPGSM=-lgsmat +endif + ALSA_SRC=chan_alsa.c ifneq ($(wildcard alsa-monitor.h),) @@ -122,6 +127,35 @@ endif endif # WITHOUT_ZAPTEL +ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/capi20.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/capi20.h),) + CHANNEL_LIBS+=chan_capi.so +# uncomment the following line if you really never ever want early b3 connects, +# you can also configure it in the dialstring, this is just for performance +# NOTE: this is probably obsolete by using the "R" dial option +#CFLAGS+=-DCAPI_NEVER_EVER_EARLY_B3_CONNECTS + +# uncommnet next line to force dtmf software detection/generation, can also be configured +# in capi.conf on a perdevice basis (softdtmf=1) +#CFLAGS+=-DCAPI_FORCE_SOFTWARE_DTMF + +# uncomment the next line if you are in the ulaw world +#CFLAGS+=-DCAPI_ULAW + +# very experimental echo squelching +CFLAGS+=-DCAPI_ES + +#gains +CFLAGS+=-DCAPI_GAIN + +# what do to with call waiting connect indications? +# uncomment the next line for call deflection in that case +CFLAGS+=-DCAPI_DEFLECT_ON_CIRCUITBUSY + +# audio sync +CFLAGS+=-DCAPI_SYNC + +endif + ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/vpbapi.h),) CHANNEL_LIBS+=chan_vpb.so CFLAGS+=-DLINUX @@ -204,7 +238,7 @@ $(CC) -c $(CFLAGS) -o chan_zap.o chan_zap.c chan_zap.so: chan_zap.o - $(CC) $(SOLINK) -o $@ $< $(ZAPPRI) $(ZAPR2) -ltonezone + $(CC) $(SOLINK) -o $@ $< $(ZAPPRI) $(ZAPGSM) $(ZAPR2) -ltonezone chan_sip.so: chan_sip.o $(CC) $(SOLINK) -o $@ ${CYGSOLINK} chan_sip.o ${CYGSOLIB} @@ -220,6 +254,9 @@ chan_nbs.so: chan_nbs.o $(CC) $(SOLINK) -o $@ $< -lnbs +chan_capi.so: chan_capi.o + $(CC) $(SOLINK) -o $@ $< -lcapi20 + chan_vpb.o: chan_vpb.c $(CXX) -c $(CFLAGS) -o $@ chan_vpb.c diff -urN asterisk-1.2.10.orig/channels/chan_agent.c asterisk-1.2.10/channels/chan_agent.c --- asterisk-1.2.10.orig/channels/chan_agent.c 2006-07-06 22:38:45.000000000 +0200 +++ asterisk-1.2.10/channels/chan_agent.c 2006-07-31 14:13:08.000000000 +0200 @@ -440,7 +440,7 @@ if ((pointer = strchr(filename, '.'))) *pointer = '-'; snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename); - ast_monitor_start(ast, recordformat, tmp, needlock); + ast_monitor_start(ast, recordformat, tmp, NULL, NULL, needlock); ast_monitor_setjoinfiles(ast, 1); snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext); #if 0 @@ -1323,7 +1323,7 @@ chan = agent_new(p, AST_STATE_DOWN); } else if (!p->owner && !ast_strlen_zero(p->loginchan)) { /* Adjustable agent */ - p->chan = ast_request("Local", format, p->loginchan, cause); + p->chan = ast_request("Local", format, p->loginchan, cause, NULL); if (p->chan) chan = agent_new(p, AST_STATE_DOWN); } diff -urN asterisk-1.2.10.orig/channels/chan_capi.c asterisk-1.2.10/channels/chan_capi.c --- asterisk-1.2.10.orig/channels/chan_capi.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/channels/chan_capi.c 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,2888 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) +#include +#else +#include +#endif +#include +#include +#include +#include + +unsigned ast_capi_ApplID; +_cword ast_capi_MessageNumber=1; +static char desc[] = "Common ISDN API for Asterisk"; +#ifdef CAPI_ULAW +static char tdesc[] = "Common ISDN API Driver (0.4.0) muLaw"; +#else +static char tdesc[] = "Common ISDN API Driver (0.4.0) aLaw "; +#endif +static char type[] = "CAPI"; + + +static int usecnt; +AST_MUTEX_DEFINE_STATIC(usecnt_lock); +AST_MUTEX_DEFINE_STATIC(iflock); +AST_MUTEX_DEFINE_STATIC(pipelock); +AST_MUTEX_DEFINE_STATIC(monlock); +AST_MUTEX_DEFINE_STATIC(contrlock); +AST_MUTEX_DEFINE_STATIC(capi_send_buffer_lock); +AST_MUTEX_DEFINE_STATIC(capi_put_lock); + +#ifdef CAPI_ULAW +static int capi_capability = AST_FORMAT_ULAW; +#else +static int capi_capability = AST_FORMAT_ALAW; +#endif + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) +static CAPIProfileBuffer_t profile; +#else +static struct ast_capi_profile profile; +#endif +static pthread_t monitor_thread = -1; + +static struct ast_capi_pvt *iflist = NULL; +static struct capi_pipe *pipelist = NULL; +static int capi_last_plci = 0; +static struct ast_capi_controller *capi_controllers[AST_CAPI_MAX_CONTROLLERS]; +static int capi_num_controllers = 0; +static int capi_counter = 0; +static unsigned long capi_used_controllers=0; + +static char capi_send_buffer[AST_CAPI_MAX_B3_BLOCKS * AST_CAPI_MAX_B3_BLOCK_SIZE]; +static int capi_send_buffer_handle = 0; + +char capi_national_prefix[AST_MAX_EXTENSION]; +char capi_international_prefix[AST_MAX_EXTENSION]; + +int capidebug = 0; + +static const struct ast_channel_tech capi_tech; + +MESSAGE_EXCHANGE_ERROR _capi_put_cmsg(_cmsg *CMSG) { + MESSAGE_EXCHANGE_ERROR error; + if (ast_mutex_lock(&capi_put_lock)) { + ast_log(LOG_WARNING,"Unable to lock capi put!\n"); + return -1; + } + error = capi20_put_cmsg(CMSG); + if (ast_mutex_unlock(&capi_put_lock)) { + ast_log(LOG_WARNING,"Unable to unlock capi put!\n"); + return -1; + } + return error; +} + + +MESSAGE_EXCHANGE_ERROR check_wait_get_cmsg(_cmsg *CMSG) { + MESSAGE_EXCHANGE_ERROR Info; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10000; + Info = capi20_waitformessage(ast_capi_ApplID,&tv); + if ((Info != 0x0000) && (Info != 0x1104)) { + if (capidebug) { + ast_log(LOG_DEBUG, "Error waiting for cmsg... INFO = %#x\n", Info); + } + return Info; + } + + if (Info == 0x0000) { + Info = capi_get_cmsg(CMSG,ast_capi_ApplID); + } + return Info; +} + + +unsigned ListenOnController(unsigned long CIPmask,unsigned controller) { + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG,CMSG2; + + LISTEN_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, controller); +#ifdef CAPI_NEVER_EVER_EARLY_B3_CONNECTS + LISTEN_REQ_INFOMASK(&CMSG) = 0x00ff; // lots of info ;) +#else + LISTEN_REQ_INFOMASK(&CMSG) = 0x03ff; // lots of info ;) + early B3 connect +#endif + LISTEN_REQ_CIPMASK(&CMSG) = CIPmask; + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + return error; + } + while (!IS_LISTEN_CONF(&CMSG2)) { + error = check_wait_get_cmsg(&CMSG2); + } + return 0; +} + +// Echo cancellation is for cards w/ integrated echo cancellation only +// (i.e. Eicon active cards support it) + +#define EC_FUNCTION_ENABLE 1 +#define EC_FUNCTION_DISABLE 2 +#define EC_FUNCTION_FREEZE 3 +#define EC_FUNCTION_RESUME 4 +#define EC_FUNCTION_RESET 5 +#define EC_OPTION_DISABLE_NEVER 0 +#define EC_OPTION_DISABLE_G165 (1<<1) +#define EC_OPTION_DISABLE_G164_OR_G165 (1<<1 | 1<<2) +#define EC_DEFAULT_TAIL 64 + +static int capi_echo_canceller(struct ast_channel *c, int function) { + struct ast_capi_pvt *i = c->tech_pvt; + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + unsigned char buf[7]; + + /* If echo cancellation is not requested or supported, don't attempt to enable it */ + ast_mutex_lock(&contrlock); + if (!capi_controllers[i->controller]->echocancel || !i->doEC) { + ast_mutex_unlock(&contrlock); + return 0; + } + ast_mutex_unlock(&contrlock); + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Setting up echo canceller (PLCI=%#x, function=%d, options=%d, tail=%d)\n",i->PLCI,function,i->ecOption,i->ecTail); + + FACILITY_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + FACILITY_REQ_NCCI(&CMSG) = i->NCCI; + FACILITY_REQ_FACILITYSELECTOR(&CMSG) = 6; /* Echo canceller */ + + buf[0]=6; /* msg size */ + buf[1]=function; + if (function == EC_FUNCTION_ENABLE) { + buf[3]=i->ecOption; /* bit field - ignore echo canceller disable tone */ + buf[5]=i->ecTail; /* Tail length, ms */ + } + else { + buf[3]=0; + buf[5]=0; + } + + // Always null: + buf[2]=0; + buf[4]=0; + buf[6]=0; + + FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = buf; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending FACILITY_REQ (error=%#x)\n",error); + return error; + } + + if (option_verbose > 5) + ast_verbose(VERBOSE_PREFIX_4 "sent FACILITY_REQ (PLCI=%#x)\n",i->PLCI); + + return 0; +} + +int capi_detect_dtmf(struct ast_channel *c, int flag) { + struct ast_capi_pvt *i = c->tech_pvt; +#ifndef CAPI_FORCE_SOFTWARE_DTMF + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + unsigned char buf[9]; + // does the controller support dtmf? and do we want to use it? + ast_mutex_lock(&contrlock); + if ((capi_controllers[i->controller]->dtmf == 1) && (i->doDTMF == 0)) { + ast_mutex_unlock(&contrlock); + FACILITY_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + FACILITY_REQ_PLCI(&CMSG) = i->PLCI; + FACILITY_REQ_FACILITYSELECTOR(&CMSG) = 1; + buf[0] = 8; + if (flag == 1) { + buf[1] = 1; + } else { + buf[1] = 2; + } + buf[2] = 0; + buf[3] = AST_CAPI_DTMF_DURATION; + buf[4] = 0; + buf[5] = AST_CAPI_DTMF_DURATION; + buf[6] = 0; + buf[7] = 0; + buf[8] = 0; + FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = buf; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending FACILITY_REQ (error=%#x)\n",error); + return error; + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent FACILITY_REQ (PLCI=%#x)\n",i->PLCI); + } + } + } else { + ast_mutex_unlock(&contrlock); + +#endif + // do software dtmf detection + i->doDTMF = 1; // just being paranoid again... +#ifndef CAPI_FORCE_SOFTWARE_DTMF + } +#endif + return 0; +} +static int capi_send_digit(struct ast_channel *c,char digit) { + struct ast_capi_pvt *i = c->tech_pvt; + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + unsigned char buf[10]; + + if (i->state != CAPI_STATE_BCONNECTED) { + return 0; + } + + +#ifndef CAPI_NEVER_EVER_EARLY_B3_CONNECTS + if(i->earlyB3 == 1) + /* we should really test for the network saying the number is incomplete + since i'm only doing a test and this is true at the right time + i'm going with this */ + { + + INFO_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + INFO_REQ_PLCI(&CMSG) = i->PLCI; + buf[0] = 2; + buf[1] = 0x80; + buf[2] = digit; + INFO_REQ_CALLEDPARTYNUMBER(&CMSG) = buf; + + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending CALLEDPARTYNUMBER INFO (error=%#x)\n",error); + return error; + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent CALLEDPARTYNUMBER INFO digit = %c (PLCI=%#x)\n", digit, i->PLCI); + } + } + + } else { +#endif +#ifndef CAPI_FORCE_SOFTWARE_DTMF + ast_mutex_lock(&contrlock); + if ((capi_controllers[i->controller]->dtmf == 0) || (i->doDTMF == 1)) { +#endif + // let * fake it +#ifndef CAPI_FORCE_SOFTWARE_DTMF + ast_mutex_unlock(&contrlock); +#endif + return -1; +#ifndef CAPI_FORCE_SOFTWARE_DTMF + } + ast_mutex_unlock(&contrlock); + + FACILITY_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + FACILITY_REQ_PLCI(&CMSG) = i->NCCI; + FACILITY_REQ_FACILITYSELECTOR(&CMSG) = 1; + buf[0] = 8; + + buf[1] = 3; + buf[2] = 0; + + buf[3] = AST_CAPI_DTMF_DURATION; + buf[4] = 0; + + buf[5] = AST_CAPI_DTMF_DURATION; + buf[6] = 0; + + buf[7] = 1; + buf[8] = digit; + buf[9] = 0; + FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = buf; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending FACILITY_REQ (error=%#x)\n",error); + return error; + } else { + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_3 "sent dtmf '%c'\n",digit); + } + } +#endif +#ifndef CAPI_NEVER_EVER_EARLY_B3_CONNECTS + } +#endif + return 0; +} + +static int capi_alert(struct ast_channel *c) { + struct ast_capi_pvt *i = c->tech_pvt; + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + + ALERT_REQ_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0); + ALERT_REQ_PLCI(&CMSG) = i->PLCI; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending ALERT_REQ PLCI = %#x\n",i->PLCI); + return -1; + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent ALERT_REQ PLCI = %#x\n",i->PLCI); + } + } + + i->state = CAPI_STATE_ALERTING; + return 0; +} + +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY +static int capi_deflect(struct ast_channel *chan, void *data) +{ + struct ast_capi_pvt *i = chan->tech_pvt; + MESSAGE_EXCHANGE_ERROR Info; + _cmsg CMSG; + char bchaninfo[1]; + char fac[60]; + int res=0; + int ms=3000; + + if (!data) { + ast_log(LOG_WARNING, "cd requires an argument (destination phone number)\n"); + return -1; + } + + if ((i->state == CAPI_STATE_CONNECTED) || (i->state == CAPI_STATE_BCONNECTED)) { + ast_log(LOG_ERROR, "call deflection does not work with calls that are already connected!\n"); + return -1; + } + // wait until the channel is alerting, so we dont drop the call and interfer with msgs + while ((ms > 0) && (i->state != CAPI_STATE_ALERTING)) { + sleep(100); + ms -= 100; + } + + // make sure we hang up correctly + i->state = CAPI_STATE_CONNECTPENDING; + + fac[0]=0; // len + fac[1]=0; //len + fac[2]=0x01; // Use D-Chan + fac[3]=0; // Keypad len + fac[4]=31; // user user data? len = 31 = 29 + 2 + fac[5]=0x1c; // magic? + fac[6]=0x1d; // strlen destination + 18 = 29 + fac[7]=0x91; // .. + fac[8]=0xA1; + fac[9]=0x1A; // strlen destination + 15 = 26 + fac[10]=0x02; + fac[11]=0x01; + fac[12]=0x70; + fac[13]=0x02; + fac[14]=0x01; + fac[15]=0x0d; + fac[16]=0x30; + fac[17]=0x12; // strlen destination + 7 = 18 + fac[18]=0x30; // ...hm 0x30 + fac[19]=0x0d; // strlen destination + 2 + fac[20]=0x80; // CLIP + fac[21]=0x0b; // strlen destination + fac[22]=0x01; // destination start + fac[23]=0x01; // + fac[24]=0x01; // + fac[25]=0x01; // + fac[26]=0x01; // + fac[27]=0x01; // + fac[28]=0x01; // + fac[29]=0x01; // + fac[30]=0x01; // + fac[31]=0x01; // + fac[32]=0x01; // + fac[33]=0x01; // 0x1 = sending complete + fac[34]=0x01; + fac[35]=0x01; + + memcpy((unsigned char *)fac+22,data,strlen(data)); + fac[22+strlen(data)]=0x01; // fill with 0x01 if number is only 6 numbers (local call) + fac[23+strlen(data)]=0x01; + fac[24+strlen(data)]=0x01; + fac[25+strlen(data)]=0x01; + fac[26+strlen(data)]=0x01; + + fac[6]=18+strlen(data); + fac[9]=15+strlen(data); + fac[17]=7+strlen(data); + fac[19]=2+strlen(data); + fac[21]=strlen(data); + + bchaninfo[0] = 0x1; + INFO_REQ_HEADER(&CMSG,ast_capi_ApplID,ast_capi_MessageNumber++,0); + INFO_REQ_CONTROLLER(&CMSG) = i->controller; + INFO_REQ_PLCI(&CMSG) = i->PLCI; + INFO_REQ_BCHANNELINFORMATION(&CMSG) = (unsigned char*)bchaninfo; // use D-Channel + INFO_REQ_KEYPADFACILITY(&CMSG) = 0; + INFO_REQ_USERUSERDATA(&CMSG) = 0; + INFO_REQ_FACILITYDATAARRAY(&CMSG) = (unsigned char*) fac + 4; + + if ((Info = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"Error sending INFO_REQ\n"); + return Info; + } else { + if (capidebug) { + // ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(&CMSG)); + ast_log(LOG_NOTICE,"sent INFO_REQ PLCI = %#x\n",i->PLCI); + } + } + + return res; +} +#endif + +void remove_pipe(int PLCI) { + struct capi_pipe *p,*ptmp; + + ast_mutex_lock(&pipelock); + p = pipelist; + ptmp = NULL; + while (p) { + if (p->PLCI == PLCI) { + if (ptmp == NULL) { + // mypipe == head of pipelist + pipelist = p->next; + if(p->fd > -1) close(p->fd); + if(p->i != NULL && p->i->fd > -1) close(p->i->fd); + free(p); + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_3 "removed pipe for PLCI = %#x\n",PLCI); + } + break; + } else { + // somehwere inbetween or at the end + ptmp->next = p->next; + if (p->next == NULL) { + capi_last_plci = p->PLCI; + } + if(p->fd > -1) close(p->fd); + if(p->i != NULL && p->i->fd > -1) close(p->i->fd); + free(p); + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_3 "removed pipe for PLCI = %#x\n",PLCI); + } + break; + } + } + ptmp = p; + p = p->next; + } + ast_mutex_unlock(&pipelock); +} + +static int capi_activehangup(struct ast_channel *c) { + struct ast_capi_pvt *i = c->tech_pvt; + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + + if (option_verbose > 2) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "activehangingup\n"); + } + + if (i == NULL) { + return 0; + } + + if (c->_state == AST_STATE_RING) { + CONNECT_RESP_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0); + CONNECT_RESP_PLCI(&CMSG) = i->PLCI; + CONNECT_RESP_REJECT(&CMSG) = 2; + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_RESP for PLCI = %#x\n",i->PLCI); + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP for PLCI = %#x\n",i->PLCI); + } + } + return 0; + } + + // active disconnect + if (i->state == CAPI_STATE_BCONNECTED) { + DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR, "error sending DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI); + } + } + // wait for the B3 layer to go down + while (i->state != CAPI_STATE_CONNECTED) { + usleep(10000); + } + } + if ((i->state == CAPI_STATE_CONNECTED) || (i->state == CAPI_STATE_CONNECTPENDING)){ + DISCONNECT_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG) = i->PLCI; + + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR, "error sending DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_REQ PLCI=%#x\n",i->PLCI); + } + } + // wait for the B1 layer to go down + while (i->state != CAPI_STATE_DISCONNECTED) { + usleep(10000); + } + } + return 0; +} + +static int capi_hangup(struct ast_channel *c) { + struct ast_capi_pvt *i = c->tech_pvt; + + // hmm....ok...this is called to free the capi interface (passive disconnect) + // or to bring down the channel (active disconnect) + + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "CAPI Hangingup\n"); + + if (i == NULL) { + ast_log(LOG_ERROR,"channel has no interface!\n"); + return -1; + } + + // are we down, yet? + if (i->state != CAPI_STATE_DISCONNECTED) { + // no + capi_activehangup(c); + } + + remove_pipe(i->PLCI); + i->PLCI = 0; + i->NCCI = 0; + if ((i->doDTMF == 1) && (i->vad != NULL)) { + ast_dsp_free(i->vad); + } + ast_smoother_free(i->smoother); // discard any frames left hanging + i->smoother=ast_smoother_new(AST_CAPI_MAX_B3_BLOCK_SIZE * 2); + memset(i->cid,0,sizeof(i->cid)); + i->owner=NULL; + ast_mutex_lock(&usecnt_lock); + usecnt--; + ast_mutex_unlock(&usecnt_lock); + ast_update_use_count(); + i->mypipe = NULL; + i = NULL; + c->tech_pvt = NULL; + ast_setstate(c,AST_STATE_DOWN); + return 0; +} + +static char *capi_number(char *data,int strip) { + unsigned len = *data; + // XXX fix me + // convert a capi struct to a \0 terminated string + if (!len || len < (unsigned int) strip) return NULL; + len = len - strip; + data = (char *)(data + 1 + strip); + return strndup((char *)data,len); +} + +int capi_call(struct ast_channel *c, char *idest, int timeout) +{ + struct ast_capi_pvt *i; + struct capi_pipe *p = NULL; + int fds[2]; + char *dest,*interface; + char buffer[AST_MAX_EXTENSION]; + char called[AST_MAX_EXTENSION],calling[AST_MAX_EXTENSION]; + char bchaninfo[3]; + long flags; + + _cmsg CMSG; + MESSAGE_EXCHANGE_ERROR error; + + strncpy(buffer,idest,sizeof(buffer)-1); + interface = strtok(buffer, "/"); + dest = strtok(NULL, "/"); + + + if (!dest) { + ast_log(LOG_WARNING, "Destination %s requires a real destination\n", idest); + return -1; + } + i = c->tech_pvt; + i->doB3 = AST_CAPI_B3_DONT; // DOH + + // always B3 + if (((char *)dest)[0] == 'b') { + i->doB3 = AST_CAPI_B3_ALWAYS; + } + // only do B3 on successfull calls + if (((char *)dest)[0] == 'B') { + i->doB3 = AST_CAPI_B3_ON_SUCCESS; + } + + if (i->doB3 != AST_CAPI_B3_DONT) { + dest++; + } + + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_2 "CAPI Call %s %s", c->name, i->doB3?"with B3":""); + } + switch (c->cid.cid_pres) { + case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: + case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: + case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: + case PRES_ALLOWED_NETWORK_NUMBER: + case PRES_NUMBER_NOT_AVAILABLE: + i->CLIR = 0; + break; + case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + case PRES_PROHIB_NETWORK_NUMBER: + i->CLIR = 1; + break; + default: + i->CLIR = 0; + } + + if (pipe(fds) == 0) { + ast_mutex_lock(&pipelock); + i->fd = fds[0]; + flags = fcntl(i->fd,F_GETFL); + fcntl(i->fd,F_SETFL,flags | O_SYNC | O_DIRECT); + p = malloc(sizeof(struct capi_pipe)); + memset(p, 0, sizeof(struct capi_pipe)); + p->fd = fds[1]; + flags = fcntl(i->fd,F_GETFL); + fcntl(p->fd,F_SETFL,flags | O_SYNC | O_DIRECT); + c->fds[0] = i->fd; + p->PLCI = -1; + p->i = i; + p->c = c; + i->mypipe = p; + p->next = pipelist; + pipelist = p; + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_3 "creating pipe for PLCI=-1\n"); + } + ast_mutex_unlock(&pipelock); + } + i->outgoing = 1; + + i->MessageNumber = ast_capi_MessageNumber++; + CONNECT_REQ_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, i->controller); + CONNECT_REQ_CONTROLLER(&CMSG) = i->controller; + CONNECT_REQ_CIPVALUE(&CMSG) = 0x10; // Telephony, could also use 0x04 (3.1Khz audio) + called[0] = strlen(dest)+1; + called[1] = 0x80; + strncpy(&called[2],dest,sizeof(called)-2); + CONNECT_REQ_CALLEDPARTYNUMBER(&CMSG) = (unsigned char *)called; + CONNECT_REQ_CALLEDPARTYSUBADDRESS(&CMSG) = NULL; + + if (c->cid.cid_num) { + calling[0] = strlen(c->cid.cid_num)+2; + calling[1] = 0x0; + } else { + calling[0] = 0x0; + calling[1] = 0x0; + } + + if (i->CLIR == 1) { + calling[2] = 0xA0; // CLIR + } else { + calling[2] = 0x80; // CLIP + } + + if (c->cid.cid_num) { + strncpy(&calling[3],c->cid.cid_num,sizeof(calling)-3); + } + CONNECT_REQ_CALLINGPARTYNUMBER(&CMSG) = (unsigned char *)calling; + CONNECT_REQ_CALLINGPARTYSUBADDRESS(&CMSG) = NULL; + + CONNECT_REQ_B1PROTOCOL(&CMSG) = 1; + CONNECT_REQ_B2PROTOCOL(&CMSG) = 1; // 1 + CONNECT_REQ_B3PROTOCOL(&CMSG) = 0; + + bchaninfo[0] = 2; + bchaninfo[1] = 0x0; + bchaninfo[2] = 0x0; + CONNECT_REQ_BCHANNELINFORMATION(&CMSG) = (unsigned char *)bchaninfo; // 0 + + if ((error = _capi_put_cmsg(&CMSG))) { + ast_log(LOG_ERROR,"error sending CONNECT_REQ (error=%#x)\n",error); + return error; + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_REQ MN =%#x\n",CMSG.Messagenumber); + } + } + + i->state = CAPI_STATE_CONNECTPENDING; + + ast_setstate(c, AST_STATE_DIALING); + + // XXX fixme, not nice: +/* if (i->controller > 0) { + capi_controllers[i->controller]->nfreebchannels--; + } */ + + // now we shall return .... the rest has to be done by handle_msg + return 0; +} + + +static int capi_answer(struct ast_channel *c) { + struct ast_capi_pvt *i = c->tech_pvt; + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG; + char buf[AST_MAX_EXTENSION]; + char *dnid; + + if (i->isdnmode && (strlen(i->incomingmsn)dnid))) + dnid = i->dnid + strlen(i->incomingmsn); + else + dnid = i->dnid; + + CONNECT_RESP_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0); + CONNECT_RESP_PLCI(&CMSG) = i->PLCI; + CONNECT_RESP_REJECT(&CMSG) = 0; + buf[0] = strlen(dnid)+2; + buf[1] = 0x0; + buf[2] = 0x80; + strncpy(&buf[3],dnid,sizeof(buf)-4); + CONNECT_RESP_CONNECTEDNUMBER(&CMSG) = (unsigned char *)buf; + CONNECT_RESP_CONNECTEDSUBADDRESS(&CMSG) = NULL; + CONNECT_RESP_LLC(&CMSG) = NULL; + CONNECT_RESP_B1PROTOCOL(&CMSG) = 1; + CONNECT_RESP_B2PROTOCOL(&CMSG) = 1; + CONNECT_RESP_B3PROTOCOL(&CMSG) = 0; + + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "CAPI Answering for MSN %s\n", dnid); + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + return -1; + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP PLCI = %#x DNID = %s\n",i->PLCI,i->dnid); + } + } + + i->state = CAPI_STATE_ANSWERING; + i->doB3 = AST_CAPI_B3_DONT; + i->outgoing = 0; + i->earlyB3 = -1; + + return 0; +} + +struct ast_frame *capi_read(struct ast_channel *c) { + struct ast_capi_pvt *i = c->tech_pvt; + int readsize = 0; + + if ((i->state == CAPI_STATE_REMOTE_HANGUP)) { + ast_log(LOG_ERROR,"this channel is not connected\n"); + return NULL; + } + if (i->state == CAPI_STATE_ONHOLD) { + i->fr.frametype = AST_FRAME_NULL; + return &i->fr; + } + + if (i == NULL) { + ast_log(LOG_ERROR,"channel has no interface\n"); + return NULL; + } + i->fr.frametype = AST_FRAME_NULL; + i->fr.subclass = 0; + i->fr.delivery.tv_sec = 0; + i->fr.delivery.tv_usec = 0; + readsize = read(i->fd,&i->fr,sizeof(struct ast_frame)); + if (readsize != sizeof(struct ast_frame)) { + ast_log(LOG_ERROR,"did not read a whole frame\n"); + } + if (i->fr.frametype == AST_FRAME_VOICE) { + readsize = read(i->fd,i->fr.data,i->fr.datalen); + if (readsize != i->fr.datalen) { + ast_log(LOG_ERROR,"did not read whole frame data\n"); + } + } + i->fr.mallocd = 0; + if (i->fr.frametype == AST_FRAME_NULL) { + return NULL; + } + if ((i->fr.frametype == AST_FRAME_DTMF) && (i->fr.subclass == 'f')) { + if (strcmp(c->exten, "fax")) { + if (ast_exists_extension(c, ast_strlen_zero(c->macrocontext) ? c->context : c->macrocontext, "fax", 1, c->cid.cid_num)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", c->name); + /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ + pbx_builtin_setvar_helper(c,"FAXEXTEN",c->exten); + if (ast_async_goto(c, c->context, "fax", 1)) + ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", c->name, c->context); + } else { + ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } + } else { + ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); + } + } + return &i->fr; +} + +int capi_write(struct ast_channel *c, struct ast_frame *f) { + struct ast_capi_pvt *i = c->tech_pvt; + _cmsg CMSG; + MESSAGE_EXCHANGE_ERROR error; + int j=0; + char buf[1000]; + struct ast_frame *fsmooth; +#ifdef CAPI_ES + int txavg=0; +#endif + +#ifndef CAPI_NEVER_EVER_EARLY_B3_CONNECTS + // dont send audio to the local exchange! + if (i->earlyB3 == 1 || !i->NCCI) { + return 0; + } +#endif + + if (!i) { + ast_log(LOG_ERROR,"channel has no interface\n"); + return -1; + } + + if (f->frametype == AST_FRAME_NULL) { + return 0; + } + if (f->frametype == AST_FRAME_DTMF) { + ast_log(LOG_ERROR,"dtmf frame should be written\n"); + return 0; + } + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_ERROR,"not a voice frame\n"); + return -1; + } + if (f->subclass != capi_capability) { + ast_log(LOG_ERROR,"dont know how to write subclass %d\n",f->subclass); + return -1; + } +// ast_log(LOG_NOTICE,"writing frame %d %d\n",f->frametype,f->subclass); + + if (ast_smoother_feed(i->smoother, f)!=0) { + ast_log(LOG_ERROR,"failed to fill smoother\n"); + return -1; + } + + fsmooth=ast_smoother_read(i->smoother); + while(fsmooth != NULL) { + DATA_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DATA_B3_REQ_NCCI(&CMSG) = i->NCCI; + DATA_B3_REQ_DATALENGTH(&CMSG) = fsmooth->datalen; + DATA_B3_REQ_FLAGS(&CMSG) = 0; + + if (ast_mutex_lock(&capi_send_buffer_lock)) { + ast_log(LOG_WARNING,"Unable to lock B3 send buffer!\n"); + return -1; + } +#ifndef CAPI_ES +#ifdef CAPI_GAIN + for (j=0;jdatalen;j++) { + buf[j] = i->g.txgains[reversebits[((unsigned char *)fsmooth->data)[j]]]; + } +#else + for (j=0;jdatalen;j++) { + buf[j] = reversebits[ ((unsigned char *)fsmooth->data)[j] ]; + } +#endif +#else + if ((i->doES == 1)) { + for (j=0;jdatalen;j++) { + buf[j] = reversebits[ ((unsigned char *)fsmooth->data)[j] ]; + txavg += abs( capiXLAW2INT(reversebits[ ((unsigned char*)fsmooth->data)[j]]) ); + } + txavg = txavg/j; + for(j=0;jtxavg[j] = i->txavg[j+1]; + } + i->txavg[ECHO_TX_COUNT-1] = txavg; + +// ast_log(LOG_NOTICE,"txavg = %d\n",txavg); + } else { +#ifdef CAPI_GAIN + for (j=0;jdatalen;j++) { + buf[j] = i->g.txgains[reversebits[((unsigned char *)fsmooth->data)[j]]]; + } +#else + for (j=0;jdatalen;j++) { + buf[j] = reversebits[ ((unsigned char *)fsmooth->data)[j] ]; + } +#endif + } +#endif + + DATA_B3_REQ_DATAHANDLE(&CMSG) = capi_send_buffer_handle; + memcpy((char *)&capi_send_buffer[(capi_send_buffer_handle % AST_CAPI_MAX_B3_BLOCKS) * AST_CAPI_MAX_B3_BLOCK_SIZE],&buf,fsmooth->datalen); + DATA_B3_REQ_DATA(&CMSG) = (unsigned char *)&capi_send_buffer[(capi_send_buffer_handle % AST_CAPI_MAX_B3_BLOCKS) * AST_CAPI_MAX_B3_BLOCK_SIZE]; + capi_send_buffer_handle++; + + if (ast_mutex_unlock(&capi_send_buffer_lock)) { + ast_log(LOG_WARNING,"Unable to unlock B3 send buffer!\n"); + return -1; + } + + +#ifdef CAPI_SYNC + ast_mutex_lock(&i->lockB3in); + if ((i->B3in >= 1) && (i->B3in <= AST_CAPI_MAX_B3_BLOCKS)) { + i->B3in--; + ast_mutex_unlock(&i->lockB3in); + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending DATA_B3_REQ (error=%#x, datalen=%d) B3in=%d\n",error,fsmooth->datalen,i->B3in); +// ast_log(LOG_NOTICE,"f: timelen %d b = %d MN = %d \n",fsmooth->timelen,b,CMSG.Messagenumber); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DATA_B3_REQ (NCCI=%#x) (%d bytes)\n",i->NCCI,fsmooth->datalen); + } + } + } else { + if (i->B3in > 0) i->B3in--; + ast_mutex_unlock(&i->lockB3in); + } +#else + if ((error = _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending DATA_B3_REQ (error=%#x, datalen=%d)\n",error,fsmooth->datalen); +// ast_log(LOG_NOTICE,"f: timelen %d b = %d MN = %d \n",fsmooth->timelen,b,CMSG.Messagenumber); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DATA_B3_REQ (NCCI=%#x) (%d bytes)\n",i->NCCI,fsmooth->datalen); + } + } +#endif + +// ast_frfree(fsmooth); + + fsmooth=ast_smoother_read(i->smoother); + } + return 0; +} + +static int capi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { + struct ast_capi_pvt *p = newchan->tech_pvt; + p->owner = newchan; + return 0; +} + +int capi_indicate(struct ast_channel *c,int condition) { + return -1; +} + +int capi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc) { + return -1; +} + + +struct ast_channel *capi_new(struct ast_capi_pvt *i,int state) { + struct ast_channel *tmp; + int fmt; + + tmp = ast_channel_alloc(1); + if (tmp != NULL) { + snprintf(tmp->name,sizeof(tmp->name),"CAPI/contr%d/%s-%d",i->controller,i->dnid,capi_counter++); + tmp->type = type; + tmp->tech = &capi_tech; + tmp->nativeformats = capi_capability; + ast_setstate(tmp,state); + tmp->fds[0] = i->fd; + i->smoother = ast_smoother_new(AST_CAPI_MAX_B3_BLOCK_SIZE); + if (i->smoother == NULL) { + ast_log(LOG_ERROR, "smoother NULL!\n"); + } + i->fr.frametype = 0; + i->fr.subclass = 0; + i->fr.delivery.tv_sec = 0; + i->fr.delivery.tv_usec = 0; + i->state = CAPI_STATE_DISCONNECTED; + i->CLIR = 0; + i->calledPartyIsISDN = 0; // let's be pessimistic + i->earlyB3 = -1; + i->doB3 = AST_CAPI_B3_DONT; + i->outgoing = 0; + i->onholdPLCI = 0; +#ifdef CAPI_SYNC + i->B3in = 0; + ast_mutex_init(&i->lockB3in); +#endif +#ifdef CAPI_ES + memset(i->txavg,0,ECHO_TX_COUNT); +#endif + +#ifndef CAPI_FORCE_SOFTWARE_DTMF + if (i->doDTMF == 1) { +#endif + i->vad = ast_dsp_new(); + ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT); +#ifndef CAPI_FORCE_SOFTWARE_DTMF + } +#endif + + tmp->tech_pvt = i; + tmp->callgroup = i->callgroup; + tmp->nativeformats = capi_capability; + fmt = ast_best_codec(tmp->nativeformats); +// fmt = capi_capability; + tmp->readformat = fmt; + tmp->writeformat = fmt; + tmp->rawreadformat = fmt; + tmp->rawwriteformat = fmt; + strncpy(tmp->context,i->context,sizeof(tmp->context)-1); + tmp->cid.cid_num = strdup(i->cid); + tmp->cid.cid_dnid = strdup(i->dnid); + strncpy(tmp->exten,i->dnid,sizeof(tmp->exten)-1); + strncpy(tmp->accountcode,i->accountcode,sizeof(tmp->accountcode)-1); + i->owner = tmp; + ast_mutex_lock(&usecnt_lock); + usecnt++; + ast_mutex_unlock(&usecnt_lock); + ast_update_use_count(); + if (state != AST_STATE_DOWN) { + // we are alerting (phones ringing) + if (state == AST_STATE_RING) + capi_alert(tmp); + if (ast_pbx_start(tmp)) { + ast_log(LOG_ERROR,"Unable to start pbx on channel!\n"); + ast_hangup(tmp); + tmp = NULL; + } else { + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "started pbx on channel (callgroup=%d)!\n",tmp->callgroup); + } + } + } + } else { + ast_log(LOG_ERROR,"Unable to allocate channel!\n"); + } + return tmp; +} + + +struct ast_channel *capi_request(const char *type, int format, void *data, int *cause) +{ + struct ast_capi_pvt *i; + struct ast_channel *tmp = NULL; + char *dest,*interface; + char buffer[AST_MAX_EXTENSION]; + unsigned int capigroup=0, controller=0; + int notfound = 1; + + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "data = %s\n",(char *)data); + } + strncpy(buffer,(char *)data,sizeof(buffer)-1); + + interface = strtok(buffer, "/"); + dest = strtok(NULL, "/"); + + + if (((char *)interface)[0] == 'g') { + interface++; + capigroup = atoi(interface); + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "capi request group = %d\n",capigroup); + } + } else if (!strncmp(interface,"contr",5)) { + interface += 5; + controller = atoi(interface); + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "capi request controller = %d\n",controller); + } + } else { + ast_log(LOG_ERROR,"Syntax error in dialstring. read the docs!\n"); + } + + ast_mutex_lock(&iflock); + i = iflist; + while (i && notfound) { + // unused channel + if (!i->owner) { + if (controller && (i->controllers & (1 << controller))) { + // DIAL(CAPI/contrX/...) + ast_mutex_lock(&contrlock); + if (capi_controllers[controller]->nfreebchannels > 0) { + strncpy(i->dnid,dest,sizeof(i->dnid)-1); + i->controller = controller; + tmp = capi_new(i, AST_STATE_DOWN); + i->PLCI = -1; + i->datahandle = 0; + i->outgoing = 1; // this is an outgoing line + i->earlyB3 = -1; + // capi_detect_dtmf(tmp,1); + ast_mutex_unlock(&contrlock); + ast_mutex_unlock(&iflock); + return tmp; + } else { + // keep on running! + ast_mutex_unlock(&contrlock); + } + } else if (capigroup && (i->group & (1 << capigroup))) { + int c; + // DIAL(CAPI/gX/...) + ast_mutex_lock(&contrlock); + for (c=1;c<=capi_num_controllers;c++) { + if (i->controllers & (1 << c)) { + if (capi_controllers[c]->nfreebchannels > 0) { + strncpy(i->dnid,dest,sizeof(i->dnid)-1); + i->controller = c; + tmp = capi_new(i, AST_STATE_DOWN); + i->PLCI = -1; + i->datahandle = 0; + i->outgoing = 1; // this is an outgoing line + i->earlyB3 = -1; + // capi_detect_dtmf(tmp,1); + ast_mutex_unlock(&contrlock); + ast_mutex_unlock(&iflock); + return tmp; + } else { + // keep on running! + } + } + } + ast_mutex_unlock(&contrlock); + } + } +// ast_log(LOG_NOTICE,"not contr %d group %d\n",i->controllers, i->group); + i = i->next; + } + ast_mutex_unlock(&iflock); + ast_log(LOG_NOTICE,"didn't find capi device with controller = %d or group = %d.\n",controller, capigroup); + return NULL; +} + + +struct capi_pipe *find_pipe(int PLCI,int MN) { + struct capi_pipe *p; + // find a pipe by PLCI or by MessageNumber (in case this is a CONNECT_CONF) + ast_mutex_lock(&pipelock); + p = pipelist; + if ((p == NULL) && (capi_last_plci != PLCI)){ + if (capidebug) { + ast_log(LOG_NOTICE,"PLCI doesnt match last pipe (PLCI = %#x)\n",PLCI); + } + ast_mutex_unlock(&pipelock); + return NULL; + } + while(p != NULL) { + if ((p->PLCI == PLCI) || ( (p->PLCI == -1) && (p->i->MessageNumber == MN) ) ){ + ast_mutex_unlock(&pipelock); + return p; + } + p = p->next; + } + if (capidebug) { + ast_log(LOG_ERROR,"unable to find a pipe for PLCI = %#x MN = %#x\n",PLCI,MN); + } + ast_mutex_unlock(&pipelock); + return NULL; +} + +int pipe_frame(struct capi_pipe *p,struct ast_frame *f) { + fd_set wfds; + int written=0; + struct timeval tv; + FD_ZERO(&wfds); + FD_SET(p->fd,&wfds); + tv.tv_sec = 0; + tv.tv_usec = 10; + if ((f->frametype == AST_FRAME_VOICE) && (p->i->doDTMF == 1) && (p->i->vad != NULL)) { + f = ast_dsp_process(p->c,p->i->vad,f); + if (f->frametype == AST_FRAME_NULL) { + return 0; + } + } + // we dont want the monitor thread to block + if (select(p->fd + 1,NULL,&wfds,NULL,&tv) == 1) { + written = write(p->fd,f,sizeof(struct ast_frame)); + if (written < (signed int) sizeof(struct ast_frame)) { + ast_log(LOG_ERROR,"wrote %d bytes instead of %d\n", written, (int)sizeof(struct ast_frame)); + return -1; + } + if (f->frametype == AST_FRAME_VOICE) { + written = write(p->fd,f->data,f->datalen); + if (written < f->datalen) { + ast_log(LOG_ERROR,"wrote %d bytes instead of %d\n",written,f->datalen); + return -1; + } + } + } else { + return 0; + } + return -1; +} + +static int search_did(struct ast_channel *c) +{ + // Returns + // -1 = Failure + // 0 = Match + // 1 = possible match + struct ast_capi_pvt *i = c->tech_pvt; + char *exten; + + if (strlen(i->dnid)incomingmsn)) + return -1; + +// exten = i->dnid + strlen(i->incomingmsn); + exten = i->dnid; + + if (ast_exists_extension(NULL, c->context, exten, 1, NULL)) { + c->priority = 1; + strncpy(c->exten, exten, sizeof(c->exten) - 1); + return 0; + } + + if (ast_canmatch_extension(NULL, c->context, exten, 1, NULL)) { + return 1; + } + + + return -1; +} + +int pipe_msg(int PLCI,_cmsg *CMSG) { + struct capi_pipe *p; + _cmsg CMSG2; + MESSAGE_EXCHANGE_ERROR error; + struct ast_frame fr; + char b3buf[1024]; + int j; + int b3len=0; + char dtmf; + unsigned dtmflen; +#ifdef CAPI_ES + int rxavg = 0; + int txavg = 0; +#endif + + p = find_pipe(PLCI,CMSG->Messagenumber); + if (p == NULL) { + if (IS_DISCONNECT_IND(CMSG)) { + DISCONNECT_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber , 0); + DISCONNECT_RESP_PLCI(&CMSG2) = PLCI; + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_RESP PLCI=%#x\n",PLCI); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_RESP PLCI=%#x\n",PLCI); + } + } + return 0; + } + if (capidebug) { + ast_log(LOG_NOTICE,"%s",capi_cmsg2str(CMSG)); + } + return -1; + } + + if (CMSG != NULL) { + switch (CMSG->Subcommand) { + case CAPI_IND: + switch (CMSG->Command) { + case CAPI_DISCONNECT_B3: +// ast_log(LOG_NOTICE,"DISCONNECT_B3_IND\n"); + + DISCONNECT_B3_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + DISCONNECT_B3_RESP_NCCI(&CMSG2) = DISCONNECT_B3_IND_NCCI(CMSG); + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_B3_RESP NCCI=%#x\n",(int)DISCONNECT_B3_IND_NCCI(CMSG)); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_B3_RESP NCCI=%#x\n",(int)DISCONNECT_B3_IND_NCCI(CMSG)); + } + } + if (p->i->state == CAPI_STATE_BCONNECTED) { + // passive disconnect + p->i->state = CAPI_STATE_CONNECTED; + } else + if (p->i->state == CAPI_STATE_DISCONNECTING) { + // active disconnect + memset(&CMSG2,0,sizeof(_cmsg)); + DISCONNECT_REQ_HEADER(&CMSG2, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG2) = PLCI; + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_REQ PLCI=%#x\n",PLCI); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_REQ PLCI=%#x\n",PLCI); + } + } + } else + if (p->i->state == CAPI_STATE_ONHOLD) { + // no hangup + } + ast_mutex_lock(&contrlock); + if (p->i->controller > 0) { + capi_controllers[p->i->controller]->nfreebchannels++; + } + ast_mutex_unlock(&contrlock); + break; + case CAPI_DISCONNECT: +// ast_log(LOG_NOTICE,"DISCONNECT_IND\n"); + DISCONNECT_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber , 0); + DISCONNECT_RESP_PLCI(&CMSG2) = PLCI; +/* if (p->i->controller > 0) { + capi_controllers[p->i->controller]->nfreebchannels++; + } */ + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_RESP PLCI=%#x\n",PLCI); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_RESP PLCI=%#x\n",PLCI); + } + } + if (p->c) { + p->c->hangupcause = DISCONNECT_IND_REASON(CMSG) - 0x3480; + } + + if (PLCI == p->i->onholdPLCI) { + // the caller onhold hung up (or ECTed away) + p->i->onholdPLCI = 0; + remove_pipe(PLCI); + return 0; + } + + if (p->i->state == CAPI_STATE_DID) { + if ((p->c) != NULL) { + ast_hangup(p->c); + } else { + ast_log(LOG_WARNING, "unable to hangup channel on DID. Channel is NULL.\n"); + } + return 0; + } + + p->i->state = CAPI_STATE_DISCONNECTED; + + fr.frametype = AST_FRAME_CONTROL; + if (DISCONNECT_IND_REASON(CMSG) == 0x34a2) { + fr.subclass = AST_CONTROL_BUSY; + } else { + fr.frametype = AST_FRAME_NULL; + } + fr.datalen = 0; + if (pipe_frame(p,(struct ast_frame *)&fr) == -1) { + // printf("STATE = %#x\n",p->i->state); + // in this case * did not read our hangup control frame + // so we must hangup the channel! + if ( (p->i->state != CAPI_STATE_DISCONNECTED) && (ast_check_hangup(p->c) == 0)) { + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_3 "soft hangup by capi\n"); + } + ast_softhangup(p->c,AST_SOFTHANGUP_DEV); + } else { + // dont ever hangup while hanging up! +// ast_log(LOG_NOTICE,"no soft hangup by capi\n"); + } + return -1; + } else { + return 0; + } + +/* fr.frametype = AST_FRAME_NULL; + fr.datalen = 0; + pipe_frame(p,(struct ast_frame *)&fr); */ + break; + case CAPI_DATA_B3: + + memcpy(&b3buf[AST_FRIENDLY_OFFSET],(char *)DATA_B3_IND_DATA(CMSG),DATA_B3_IND_DATALENGTH(CMSG)); + b3len = DATA_B3_IND_DATALENGTH(CMSG); + + // send a DATA_B3_RESP very quickly to free the buffer in capi + DATA_B3_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber,0); + DATA_B3_RESP_NCCI(&CMSG2) = DATA_B3_IND_NCCI(CMSG); + DATA_B3_RESP_DATAHANDLE(&CMSG2) = DATA_B3_IND_DATAHANDLE(CMSG); + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending DATA_B3_RESP (error=%#x)\n",error); + } else { + if (option_verbose > 6) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DATA_B3_RESP (NCCI=%#x)\n",(int)DATA_B3_IND_NCCI(CMSG)); + } + } +#ifdef CAPI_SYNC + ast_mutex_lock(&p->i->lockB3in); + p->i->B3in++; + if (p->i->B3in > AST_CAPI_MAX_B3_BLOCKS) p->i->B3in = AST_CAPI_MAX_B3_BLOCKS; + ast_mutex_unlock(&p->i->lockB3in); +#endif +#ifdef CAPI_ES + if ((p->i->doES == 1)) { + for (j=0;ji->txavg[j]; + } + txavg = txavg/j; + + if( (txavg/ECHO_TXRX_RATIO) > rxavg) { +#ifdef CAPI_ULAW + memset(&b3buf[AST_FRIENDLY_OFFSET],255,b3len); +#else + memset(&b3buf[AST_FRIENDLY_OFFSET],84,b3len); +#endif + if (capidebug) { + ast_log(LOG_NOTICE,"SUPPRESSING ECHOrx=%d, tx=%d\n",rxavg,txavg); + } + } + } else { +#ifdef CAPI_GAIN + for (j=0;ji->g.rxgains[(unsigned char)b3buf[AST_FRIENDLY_OFFSET + j]]]; + } +#else + for (j=0;ji->g.rxgains[(unsigned char)b3buf[AST_FRIENDLY_OFFSET + j]]]; + } +#else + for (j=0;jc->_state != AST_STATE_UP) { + ast_setstate(p->c,AST_STATE_UP); + } */ + + fr.frametype = AST_FRAME_VOICE; + fr.subclass = capi_capability; + fr.data = (char *)&b3buf[AST_FRIENDLY_OFFSET]; + fr.datalen = b3len; + fr.samples = b3len; + fr.offset = AST_FRIENDLY_OFFSET; + fr.mallocd = 0; + fr.delivery.tv_sec = 0; + fr.delivery.tv_usec = 0; + fr.src = NULL; + // ast_verbose(VERBOSE_PREFIX_3 "DATA_B3_IND (len=%d) fr.datalen=%d fr.subclass=%d\n",(int)DATA_B3_IND_DATALENGTH(CMSG),fr.datalen,fr.subclass); + return pipe_frame(p,(struct ast_frame *)&fr); + break; + case CAPI_FACILITY: + if (FACILITY_IND_FACILITYSELECTOR(CMSG) == 0x0001) { + // DTMF received + if (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[0] != (0xff)) { + dtmflen = FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[0]; + FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG) += 1; + } else { + dtmflen = ((__u16 *) (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG) + 1))[0]; + FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG) += 3; + } + if (dtmflen == 1) { + dtmf = (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG))[0]; + fr.frametype = AST_FRAME_DTMF; + fr.subclass = dtmf; + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "c_dtmf = %c\n",dtmf); + } + pipe_frame(p,(struct ast_frame *)&fr); + } + } + if (FACILITY_IND_FACILITYSELECTOR(CMSG) == 0x0003) { + // sservices + /* ast_log(LOG_NOTICE,"FACILITY_IND PLCI = %#x\n",(int)FACILITY_IND_PLCI(CMSG)); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[0]); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[1]); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[2]); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[3]); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[4]); + ast_log(LOG_NOTICE,"%#x\n",FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[5]); */ + // RETRIEVE + if ( (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[1] == 0x3) && (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[3] == 0x2)) { + p->i->state = CAPI_STATE_CONNECTED; + p->i->PLCI = p->i->onholdPLCI; + p->i->onholdPLCI = 0; + } + if ( (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[1] == 0x2) && (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[3] == 0x2)) { + if ((FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[5] != 0) && (FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[4] != 0)) { + // reason != 0x0000 == problem + p->i->onholdPLCI = 0; + p->i->state = CAPI_STATE_ONHOLD; + ast_log(LOG_WARNING, "unable to put PLCI=%#x onhold, REASON = %#x%#x, maybe you need to subscribe for this...\n",(int)FACILITY_IND_PLCI(CMSG),FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[5],FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[4]); + } else { + // reason = 0x0000 == call on hold + p->i->state = CAPI_STATE_ONHOLD; + if (capidebug) + ast_log(LOG_NOTICE, "PLCI=%#x put onhold\n",(int)FACILITY_IND_PLCI(CMSG)); + } + } + } + + error = FACILITY_RESP(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber,FACILITY_IND_PLCI(CMSG),FACILITY_IND_FACILITYSELECTOR(CMSG),FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)); + + if (error != 0) { + ast_log(LOG_ERROR,"error sending FACILITY_RESP (error=%#x)\n",error); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent FACILITY_RESP (PLCI=%#x)\n",(int)FACILITY_IND_PLCI(CMSG)); + } + } + break; + case CAPI_INFO: + // ast_log(LOG_ERROR,"INFO_IND PLCI=%#x INFO# = %#x\n",PLCI,INFO_IND_INFONUMBER(CMSG)); + + memset(&CMSG2,0,sizeof(_cmsg)); + error = INFO_RESP(&CMSG2,ast_capi_ApplID,CMSG->Messagenumber,PLCI); + if (error != 0) { + ast_log(LOG_ERROR,"error sending INFO_RESP (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent INFO_RESP (PLCI=%#x)\n",PLCI); + } + } +/* if ((INFO_IND_INFONUMBER(CMSG) >> 8) == 0x00) { + ast_log(LOG_ERROR,"%#x\n",INFO_IND_INFOELEMENT(CMSG)[0]); + ast_log(LOG_ERROR,"%#x\n",INFO_IND_INFOELEMENT(CMSG)[1]); + ast_log(LOG_ERROR,"%#x\n",INFO_IND_INFOELEMENT(CMSG)[2]); + ast_log(LOG_ERROR,"%#x\n",INFO_IND_INFOELEMENT(CMSG)[3]); + } */ +#ifndef CAPI_NEVER_EVER_EARLY_B3_CONNECTS + if ((INFO_IND_INFONUMBER(CMSG) == 0x001e) && (p->i->doB3 != AST_CAPI_B3_DONT) && (p->i->earlyB3 == -1) && (p->i->state != CAPI_STATE_BCONNECTED)){ + // ETSI 300 102-1 Progress Indicator + // we do early B3 Connect + if(INFO_IND_INFOELEMENT(CMSG)[0] >= 2) { + if(INFO_IND_INFOELEMENT(CMSG)[2] & 0x2) { + p->i->calledPartyIsISDN = 0; + // ast_log(LOG_NOTICE,"A N A L O G \n"); + } else { + p->i->calledPartyIsISDN = 1; + // ast_log(LOG_NOTICE,"I S D N\n"); + } + if(INFO_IND_INFOELEMENT(CMSG)[2] & 0x88) { + // in-band info available + p->i->earlyB3 = 1; + memset(&CMSG2,0,sizeof(_cmsg)); + CONNECT_B3_REQ_HEADER(&CMSG2, ast_capi_ApplID, ast_capi_MessageNumber++,0); + CONNECT_B3_REQ_PLCI(&CMSG2) = PLCI; + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending early CONNECT_B3_REQ (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent early CONNECT_B3_REQ (PLCI=%#x)\n",PLCI); + } + } + } + } + } + // DISCONNECT + if ((INFO_IND_INFONUMBER(CMSG) == 0x8045) && (PLCI == p->i->onholdPLCI)) { + // the caller onhold hung up (or ECTed away) + // send a disconnect_req , we cannot hangup the channel here!!! + memset(&CMSG2,0,sizeof(_cmsg)); + DISCONNECT_REQ_HEADER(&CMSG2, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + DISCONNECT_REQ_PLCI(&CMSG2) = p->i->onholdPLCI; + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_NOTICE, "error sending DISCONNECT_REQ PLCI=%#x\n",PLCI); + } else { + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_REQ for onholdPLCI=%#x\n",PLCI); + } + } + return 0; + } + + // case 1: B3 on success or no B3 at all + if ((INFO_IND_INFONUMBER(CMSG) == 0x8045) && (p->i->doB3 != AST_CAPI_B3_ALWAYS) && (p->i->outgoing == 1)) { + p->i->earlyB3 = 0; // !!! + fr.frametype = AST_FRAME_NULL; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + } + // case 2: we are doing B3, and receive the 0x8045 after a successful call + if ((INFO_IND_INFONUMBER(CMSG) == 0x8045) && (p->i->doB3 != AST_CAPI_B3_DONT) && (p->i->earlyB3 == 0) && (p->i->outgoing == 1)) { + fr.frametype = AST_FRAME_NULL; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + } + // case 3: this channel is an incoming channel! the user hung up! + // it is much better to hangup now instead of waiting for a timeout and + // network caused DISCONNECT_IND! + if ((INFO_IND_INFONUMBER(CMSG) == 0x8045) && (p->i->outgoing == 0)) { + // ast_log(LOG_NOTICE,"case 3\n"); + fr.frametype = AST_FRAME_NULL; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + } + // case 4 (a.k.a. the italian case): B3 always. call is unsuccessful + if ((INFO_IND_INFONUMBER(CMSG) == 0x8045) && (p->i->doB3 == AST_CAPI_B3_ALWAYS) && (p->i->earlyB3 == -1) && (p->i->outgoing == 1)) { + // wait for the 0x001e (PROGRESS), play audio and wait for a timeout from the network + return 0; + } +#endif + // Handle DID digits + if ((INFO_IND_INFONUMBER(CMSG) == 0x0070) && p->i->isdnmode && (p->c != NULL)) { + int search = -1; + char name[AST_CHANNEL_NAME] = ""; + char *did; + + did = capi_number((char *)INFO_IND_INFOELEMENT(CMSG),1); + if (strcasecmp(p->i->dnid, did)) { + strncat(p->i->dnid, did, sizeof(p->i->dnid)-1); + } + + snprintf(name,sizeof(name),"CAPI/contr%d/%s/-%d",p->i->controller,p->i->dnid,capi_counter++); + ast_change_name(p->c, name); + + search = search_did(p->c); + if (search != -1) { + if (!search) { + ast_setstate(p->c, AST_STATE_RING); + // we are alerting (phones ringing) + capi_alert(p->c); // Do this here after pbx_start the Channel can be destroyed + if (ast_pbx_start(p->c)) { + ast_log(LOG_ERROR,"Unable to start pbx on channel!\n"); + ast_hangup(p->c); + } else { + if (option_verbose > 2) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "started pbx on channel!\n"); + } + } + } + } else { + ast_log(LOG_ERROR,"did not find device for msn = %s\n",p->i->dnid); + CONNECT_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + CONNECT_RESP_PLCI(&CMSG2) = PLCI; + CONNECT_RESP_REJECT(&CMSG2) = 1; // ignore + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } + } + + return 0; + } + } + if (INFO_IND_INFONUMBER(CMSG) == 0x8001) { + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_RINGING; + return pipe_frame(p,(struct ast_frame *)&fr); + } + if (INFO_IND_INFONUMBER(CMSG) == 0x800d) { + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_PROGRESS; + return pipe_frame(p,(struct ast_frame *)&fr); + } + if (INFO_IND_INFONUMBER(CMSG) == 0x74) { + strncpy(p->i->owner->exten,capi_number((char *)INFO_IND_INFOELEMENT(CMSG),3),sizeof(p->i->owner->exten)-1); + ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(CMSG)); + } + if (INFO_IND_INFONUMBER(CMSG) == 0x28) { + // ast_sendtext(p->i->owner,capi_number(INFO_IND_INFOELEMENT(CMSG),0)); + // struct ast_frame ft = { AST_FRAME_TEXT, capi_number(INFO_IND_INFOELEMENT(CMSG),0), }; + // ast_queue_frame(p->i->owner, &ft); + // ast_log(LOG_NOTICE,"%s\n",capi_number(INFO_IND_INFOELEMENT(CMSG),0)); + } + break; + case CAPI_CONNECT_ACTIVE: +// ast_log(LOG_NOTICE,"CONNECT_ACTIVE_IND PLCI=%#x\n",(int)CONNECT_ACTIVE_IND_PLCI(CMSG)); + CONNECT_ACTIVE_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber,0); + CONNECT_ACTIVE_RESP_PLCI(&CMSG2) = PLCI; + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_ACTIVE_RESP (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_ACTIVE_RESP (PLCI=%#x)\n",PLCI); + } + } + // normal processing + if (p->i->earlyB3 != 1) { + p->i->state = CAPI_STATE_CONNECTED; + + // send a CONNECT_B3_REQ + if (p->i->outgoing == 1) { + // outgoing call + memset(&CMSG2,0,sizeof(_cmsg)); + CONNECT_B3_REQ_HEADER(&CMSG2, ast_capi_ApplID, ast_capi_MessageNumber++,0); + CONNECT_B3_REQ_PLCI(&CMSG2) = PLCI; + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_B3_REQ (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "sent CONNECT_B3_REQ (PLCI=%#x)\n",PLCI); + } + } + } else { + // incoming call + // RESP already sent ... wait for CONNECT_B3_IND +// ast_log(LOG_NOTICE,"waiting for CONNECT_B3_IND\n"); + } + } else { + // special treatment for early B3 connects + p->i->state = CAPI_STATE_BCONNECTED; + if (p->c->_state != AST_STATE_UP) { + ast_setstate(p->c,AST_STATE_UP); + } + p->i->earlyB3 = 0; // not early anymore + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_ANSWER; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + + } + break; + case CAPI_CONNECT_B3: + // then send a CONNECT_B3_RESP + memset(&CMSG2,0,sizeof(_cmsg)); + CONNECT_B3_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + CONNECT_B3_RESP_NCCI(&CMSG2) = CONNECT_B3_IND_NCCI(CMSG); + p->NCCI = CONNECT_B3_IND_NCCI(CMSG); + p->i->NCCI = p->NCCI; + CONNECT_B3_RESP_REJECT(&CMSG2) = 0; + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_B3_RESP (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_B3_RESP (NCCI=%#x)\n",p->i->NCCI); + } + } + /* if (p->i->controller > 0) { + capi_controllers[p->i->controller]->nfreebchannels--; + } */ + break; + case CAPI_CONNECT_B3_ACTIVE: +// ast_log(LOG_NOTICE,"CONNECT_B3_ACTIVE_IND NCCI=%#x\n",p->i->NCCI); + // then send a CONNECT_B3__ACTIVERESP + + CONNECT_B3_ACTIVE_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + CONNECT_B3_ACTIVE_RESP_NCCI(&CMSG2) = p->i->NCCI; + + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_B3_ACTIVE_RESP (error=%#x)\n",error); + return -1; + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_B3_ACTIVE_RESP (NCCI=%#x)\n",p->i->NCCI); + } + } + + ast_mutex_lock(&contrlock); + if (p->i->controller > 0) { + capi_controllers[p->i->controller]->nfreebchannels--; + } + ast_mutex_unlock(&contrlock); + + p->i->state = CAPI_STATE_BCONNECTED; + capi_echo_canceller(p->c,EC_FUNCTION_ENABLE); + capi_detect_dtmf(p->c,1); + + if (p->i->earlyB3 != 1) { + ast_setstate(p->c,AST_STATE_UP); + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_ANSWER; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + } + return 0; + break; + } + break; + + case CAPI_CONF: + switch (CMSG->Command) { + case CAPI_FACILITY: + if (FACILITY_CONF_FACILITYSELECTOR(CMSG) == 0x3) { + if ((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[1] == 0x2) && (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[2] == 0x0)) { + if ((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[4] == 0x0) && (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[5] == 0x0)) { + } else { + p->i->state = CAPI_STATE_BCONNECTED; + if (capidebug) + ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(CMSG)); + } + } + } + break; + case CAPI_DATA_B3: +// ast_log(LOG_NOTICE,"DATA_B3_CONF (NCCI %#x) for DATAHANDLE %#x\n",DATA_B3_CONF_NCCI(CMSG),DATA_B3_CONF_DATAHANDLE(CMSG)); + break; + case CAPI_ALERT: +// ast_log(LOG_NOTICE,"ALERT_CONF (PLCI=%#x)\n",(int)ALERT_CONF_PLCI(CMSG)); + p->i->state = CAPI_STATE_ALERTING; + if (p->c->_state == AST_STATE_RING) { + p->c->rings = 1; + } + break; + case CAPI_CONNECT: + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_2 "received CONNECT_CONF PLCI = %#x INFO = %#x\n",(int)CONNECT_CONF_PLCI(CMSG),CONNECT_CONF_INFO(CMSG)); + } + if (CONNECT_CONF_INFO(CMSG) == 0) { + p->i->PLCI = CONNECT_CONF_PLCI(CMSG); + p->PLCI = p->i->PLCI; + ast_setstate(p->c,AST_STATE_DIALING); + } else { + // here, something has to be done --> + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_BUSY; + fr.datalen = 0; + return pipe_frame(p,(struct ast_frame *)&fr); + } + break; + case CAPI_CONNECT_B3: +// ast_log(LOG_NOTICE,"received CONNECT_B3_CONF NCCI = %#x INFO = %#x\n",(int)CONNECT_B3_CONF_NCCI(CMSG),CONNECT_B3_CONF_INFO(CMSG)); + if (CONNECT_B3_CONF_INFO(CMSG) == 0) { + p->i->NCCI = CONNECT_B3_CONF_NCCI(CMSG); + } else { + p->i->earlyB3 = -1; + p->i->doB3 = AST_CAPI_B3_DONT; + } + break; + } + break; + } + } +// ast_log(LOG_NOTICE,"returning\n"); + return 0; +} + +static void capi_handle_msg(_cmsg *CMSG) { + struct ast_capi_pvt *i; + char *DNID; + char *CID; + char *msn; + _cmsg CMSG2; + MESSAGE_EXCHANGE_ERROR error; + int PLCI=0,NCCI; + int NPLAN=0; + int fds[2]; + int controller=0; + char buffer[AST_MAX_EXTENSION]; + struct capi_pipe *p; + char *magicmsn = "*\0"; + char *emptyid = "\0"; + char *emptydnid = "s\0"; + long flags; +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY + int deflect=0; +#endif + + switch (CMSG->Subcommand) { + // indication msgs + case CAPI_IND: + + switch (CMSG->Command) { + case CAPI_CONNECT: // only connect_ind are global (not channel specific) + if (capidebug) + ast_log(LOG_NOTICE,"%s\n",capi_cmsg2str(CMSG)); + DNID = capi_number((char *)CONNECT_IND_CALLEDPARTYNUMBER(CMSG),1); + if ((DNID && *DNID == 0) || !DNID) { + DNID = emptydnid; + } + NPLAN = (CONNECT_IND_CALLINGPARTYNUMBER(CMSG)[1] & 0x70); + CID = capi_number((char *)CONNECT_IND_CALLINGPARTYNUMBER(CMSG),2); + PLCI = CONNECT_IND_PLCI(CMSG); + controller = PLCI & 0xff; + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_2 "CONNECT_IND (PLCI=%#x,DID=%s,CID=%s,CIP=%#x,CONTROLLER=%#x)\n",PLCI,DNID,CID,CONNECT_IND_CIPVALUE(CMSG),controller); + } + if(CONNECT_IND_BCHANNELINFORMATION(CMSG)) + if ((CONNECT_IND_BCHANNELINFORMATION(CMSG)[1] == 0x02) && (!capi_controllers[controller]->isdnmode)) { + // this is a call waiting CONNECT_IND with BChannelinformation[1] == 0x02 + // meaning "no B or D channel for this call", since we can't do anything with call waiting now + // just reject it with "user busy" + // however...if we are a p2p BRI then the telco switch will allow us to choose the b channel + // so it will look like a callwaiting connect_ind to us + + ast_log(LOG_ERROR,"received a call waiting CONNECT_IND\n"); +#ifndef CAPI_DEFLECT_ON_CIRCUITBUSY + CONNECT_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + CONNECT_RESP_PLCI(&CMSG2) = CONNECT_IND_PLCI(CMSG); + CONNECT_RESP_REJECT(&CMSG2) = 3; // user is busy + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } + } + // no need to pipe this + PLCI = 0; + break; +#else + deflect = 1; +#endif + } + // well...somebody is calling us. let's set up a channel + ast_mutex_lock(&iflock); + i = iflist; + while(i) { + //XXX test this! + // has no owner + if ((!i->owner) && (i->incomingmsn != NULL)){ + strncpy(buffer,i->incomingmsn,sizeof(buffer)-1); + msn = strtok(buffer,","); + while (msn != NULL) { +// ast_log(LOG_NOTICE,"msn=%s\n",msn); + if (DNID && ((!strcasecmp(msn,DNID)) || + (i->isdnmode && (strlen(msn)controllers & (1 << controller))) { + if (CID != NULL) { + if(NPLAN == CAPI_ETSI_NPLAN_NATIONAL) + snprintf(i->cid, (sizeof(i->cid)-1), "%s%s%s", i->prefix, capi_national_prefix, CID); + else if(NPLAN == CAPI_ETSI_NPLAN_INTERNAT) + snprintf(i->cid, (sizeof(i->cid)-1), "%s%s%s", i->prefix, capi_international_prefix, CID); + else + snprintf(i->cid, (sizeof(i->cid)-1), "%s%s", i->prefix, CID); + } else + strncpy(i->cid,emptyid,sizeof(i->cid)-1); + + if (DNID != NULL) + strncpy(i->dnid,DNID,sizeof(i->dnid)-1); + else + strncpy(i->dnid,emptydnid,sizeof(i->dnid)-1); + + i->controller=controller; + i->PLCI = PLCI; + i->MessageNumber = CMSG->Messagenumber; + if (pipe(fds) == 0) { + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_3 "creating pipe for PLCI=%#x msn = %s\n",PLCI,msn); + } + i->fd = fds[0]; + flags = fcntl(i->fd,F_GETFL); + fcntl(i->fd,F_SETFL,flags | O_SYNC | O_DIRECT); +// ast_log(LOG_NOTICE,"i->fd = %d\n",i->fd); + p = malloc(sizeof(struct capi_pipe)); + memset(p, 0, sizeof(struct capi_pipe)); + p->fd = fds[1]; + flags = fcntl(i->fd,F_GETFL); + fcntl(p->fd,F_SETFL,flags | O_SYNC | O_DIRECT); +// ast_log(LOG_NOTICE,"p->fd = %d\n",p->fd); + p->PLCI = PLCI; + p->i = i; + ast_mutex_init(&(p->lock)); + i->mypipe = p; + if (i->isdnmode) { + p->c = capi_new(i,AST_STATE_DOWN); + i->state = CAPI_STATE_DID; + } else { + p->c = capi_new(i,AST_STATE_RING); + } + p->next = pipelist; + pipelist = p; + // hmmm.... + ast_mutex_unlock(&iflock); +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY + if ((deflect == 1) && (i->deflect2)) { + capi_deflect(p->c,i->deflect2); + } +#endif + return; + } else { + ast_log(LOG_ERROR,"creating pipe for PLCI=%#x failed\n",PLCI); + } + break; + } // if strcasecmp + msn = strtok(NULL,","); + } // while strtok + } // if + i = i->next; + } // while interface list + ast_mutex_unlock(&iflock); // obviously we are not called...so tell capi to ignore this call + if (capidebug) { + ast_log(LOG_ERROR,"did not find device for msn = %s\n",DNID); + } + CONNECT_RESP_HEADER(&CMSG2, ast_capi_ApplID, CMSG->Messagenumber, 0); + CONNECT_RESP_PLCI(&CMSG2) = CONNECT_IND_PLCI(CMSG); + CONNECT_RESP_REJECT(&CMSG2) = 1; // ignore + if ((error = _capi_put_cmsg(&CMSG2)) != 0) { + ast_log(LOG_ERROR,"error sending CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } else { + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP for PLCI = %#x\n",(int)CONNECT_IND_PLCI(CMSG)); + } + } + ast_mutex_lock(&pipelock); + if (pipelist == NULL) { + capi_last_plci = PLCI; + } + ast_mutex_unlock(&pipelock); + // no need to pipe this + PLCI = 0; +// ast_mutex_unlock(&iflock); +// return; + break; + case CAPI_FACILITY: + PLCI = FACILITY_IND_PLCI(CMSG) & 0xffff; // this is for you eicon + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"FACILITY_IND PLCI=%#x\n",PLCI); + break; + case CAPI_INFO: + PLCI = INFO_IND_PLCI(CMSG); + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"INFO_IND PLCI=%#x INFO# = %#x\n",PLCI,INFO_IND_INFONUMBER(CMSG)); + break; + case CAPI_CONNECT_ACTIVE: + PLCI = CONNECT_ACTIVE_IND_PLCI(CMSG); + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"CONNECT_ACTIVE_IND PLCI=%#x\n",PLCI); + break; + case CAPI_CONNECT_B3: + NCCI = CONNECT_B3_IND_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"CONNECT_B3_IND NCCI=%#x PLCI=%#x\n",NCCI,PLCI); + break; + case CAPI_CONNECT_B3_ACTIVE: + NCCI = CONNECT_B3_IND_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"CONNECT_B3_ACTIVE_IND NCCI=%#x PLCI=%#x\n",NCCI,PLCI); + break; + case CAPI_DATA_B3: + NCCI = DATA_B3_IND_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; +// ast_log(LOG_ERROR,"DATA_B3_IND NCCI=%#x PLCI=%#x\n",NCCI,PLCI); + break; + case CAPI_DISCONNECT_B3: + NCCI = DISCONNECT_B3_IND_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_2 "DISCONNECT_B3_IND NCCI=%#x\n",NCCI); + } + break; + case CAPI_DISCONNECT: + PLCI = DISCONNECT_IND_PLCI(CMSG); + if (option_verbose > 1) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_2 "DISCONNECT_IND PLCI=%#x REASON=%#x\n",PLCI,DISCONNECT_IND_REASON(CMSG)); + } + break; + default: + ast_log(LOG_ERROR,"Command.Subcommand = %#x.%#x\n",CMSG->Command,CMSG->Subcommand); + } + break; + // confirmation msgs + case CAPI_CONF: + switch (CMSG->Command) { + case CAPI_FACILITY: + NCCI = FACILITY_CONF_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 2) { + if (FACILITY_CONF_FACILITYSELECTOR(CMSG) == 6) { + if (FACILITY_CONF_INFO(CMSG)) + ast_verbose (VERBOSE_PREFIX_3 "Error setting up echo canceller (PLCI=%#x, Info=%#04x)\n", PLCI, FACILITY_CONF_INFO(CMSG)); + else + ast_verbose (VERBOSE_PREFIX_3 "Echo canceller successfully set up (PLCI=%#x)\n",PLCI); + } + } + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"FACILITY_CONF NCCI=%#x INFO=%#x\n",(int)FACILITY_CONF_NCCI(CMSG),FACILITY_CONF_INFO(CMSG)); + break; + case CAPI_INFO: + PLCI = INFO_CONF_PLCI(CMSG); +// ast_log(LOG_ERROR,"INFO_CONF PLCI=%#x INFO=%#x\n",PLCI,INFO_CONF_INFO(CMSG)); + break; + case CAPI_CONNECT: + PLCI = CONNECT_CONF_PLCI(CMSG); + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"CONNECT_CONF PLCI=%#x INFO=%#x MN=%#x\n",PLCI,CONNECT_CONF_INFO(CMSG),CMSG->Messagenumber); + break; + case CAPI_DISCONNECT: + PLCI = DISCONNECT_CONF_PLCI(CMSG); + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"DISCONNECT_CONF PLCI=%#x INFO=%#x MN=%#x\n",PLCI,DISCONNECT_CONF_INFO(CMSG),CMSG->Messagenumber); + break; + case CAPI_DISCONNECT_B3: + NCCI = DISCONNECT_B3_CONF_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"DISCONNECT_B3_CONF NCCI=%#x INFO=%#x MN=%#x\n",NCCI,DISCONNECT_B3_CONF_INFO(CMSG),CMSG->Messagenumber); + break; + case CAPI_CONNECT_B3: + NCCI = CONNECT_B3_CONF_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"CONNECT_B3_CONF PLCI=%#x INFO=%#x MN=%#x\n",PLCI,CONNECT_B3_CONF_INFO(CMSG),CMSG->Messagenumber); + break; + case CAPI_ALERT: + PLCI = ALERT_CONF_PLCI(CMSG); + if (option_verbose > 3) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"ALERT_CONF PLCI=%#x\n",PLCI); + break; + case CAPI_DATA_B3: + NCCI = DATA_B3_CONF_NCCI(CMSG); + PLCI = (NCCI << 16) >> 16; + if (option_verbose > 5) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(CMSG)); + } +// ast_log(LOG_ERROR,"DATA_B3_CONF NCCI=%#x PLCI=%#x\n",NCCI,PLCI); + break; + default: + ast_log(LOG_ERROR,"Command.Subcommand = %#x.%#x\n",CMSG->Command,CMSG->Subcommand); + } + break; + } + if (PLCI > 0) { + pipe_msg(PLCI,CMSG); + } + +} + +// module stuff, monitor... + +static void *do_monitor(void *data) { + unsigned int Info; + _cmsg *monCMSG; + for (;;) { +/* + if (ast_mutex_lock(&monlock)) { + ast_log(LOG_ERROR,"Unable to get monitor lock!\n"); + return NULL; + } + // do some nifty stuff + ast_mutex_unlock(&monlock); +*/ + monCMSG = malloc(sizeof(_cmsg)); + memset(monCMSG,0,sizeof(_cmsg)); + switch(Info = check_wait_get_cmsg(monCMSG)) { + case 0x0000: + if (option_verbose > 8) { + if (capidebug) + ast_verbose(VERBOSE_PREFIX_3 "%s\n",capi_cmsg2str(monCMSG)); + } + capi_handle_msg(monCMSG); + break; + case 0x1104: + // CAPI queue is empty + break; + default: + // something is wrong! + break; + } //switch + free(monCMSG); + } // for + // never reached + return NULL; +} + +#ifdef CAPI_GAIN +static void capi_gains(struct ast_capi_gains *g,float rxgain,float txgain) { + int i=0; + int x=0; + if (rxgain != 1.0) { + for (i=0;i<256;i++) { + x = (int)(((float)capiXLAW2INT(i)) * rxgain); + if (x > 32767) x = 32767; + if (x < -32767) x = -32767; + g->rxgains[i] = capiINT2XLAW(x); + } + } else { + for (i=0;i<256;i++) { + g->rxgains[i] = i; + } + } + if (txgain != 1.0) { + for (i=0;i<256;i++) { + x = (int)(((float)capiXLAW2INT(i)) * txgain); + if (x > 32767) x = 32767; + if (x < -32767) x = -32767; + g->txgains[i] = capiINT2XLAW(x); + } + } else { + for (i=0;i<256;i++) { + g->txgains[i] = i; + } + } + +} +#endif +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY +int mkif(char *incomingmsn,char *context,char *controllerstr,int devices,int softdtmf,int echocancel,int ecoption,int ectail, char *prefix, int isdnmode, int es,float rxgain,float txgain, char *deflect2, char *accountcode, unsigned int callgroup, unsigned int group) { +#else +int mkif(char *incomingmsn,char *context,char *controllerstr,int devices,int softdtmf,int echocancel,int ecoption,int ectail, char *prefix, int isdnmode, int es,float rxgain,float txgain, char *accountcode, unsigned int callgroup, unsigned int group) { +#endif + struct ast_capi_pvt *tmp; + int i=0; + char buffer[100]; + char *contr; + unsigned long contrmap=0; + + for (i=0;ilock)); + strncpy(tmp->context, context, sizeof(tmp->context)-1); + strncpy(tmp->incomingmsn, incomingmsn, sizeof(tmp->incomingmsn)-1); + strncpy(tmp->prefix, prefix, sizeof(tmp->prefix)-1); + strncpy(tmp->accountcode, accountcode, sizeof(tmp->accountcode)-1); + + strncpy(buffer,controllerstr,sizeof(buffer)-1); + contr = strtok(buffer,","); + while (contr != NULL) { + contrmap |= (1 << atoi(contr)); + if (capi_controllers[atoi(contr)]) { + capi_controllers[atoi(contr)]->isdnmode = isdnmode; + // ast_log(LOG_NOTICE, "contr %d isdnmode %d\n",atoi(contr),isdnmode); + } + contr = strtok(NULL,","); + } + tmp->controllers = contrmap; + capi_used_controllers |= contrmap; + tmp->controller = 0; + tmp->CLIR = 0; + tmp->earlyB3 = -1; + tmp->onholdPLCI = 0; + tmp->doEC = echocancel; + tmp->ecOption = ecoption; + tmp->ecTail = ectail; + tmp->isdnmode = isdnmode; + tmp->doES = es; + tmp->callgroup = callgroup; + tmp->group = group; +#ifdef CAPI_ES +#endif +#ifdef CAPI_GAIN + tmp->rxgain = rxgain; + tmp->txgain = txgain; + capi_gains(&tmp->g,rxgain,txgain); +#endif +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY + strncpy(tmp->deflect2, deflect2, sizeof(tmp->deflect2)-1); +#endif +#ifndef CAPI_FORCE_SOFTWARE_DTMF + if (softdtmf == 1) { +#endif + tmp->doDTMF = 1; +#ifndef CAPI_FORCE_SOFTWARE_DTMF + } else { + tmp->doDTMF = 0; + } +#endif + tmp->next = iflist; // prepend + iflist = tmp; + // ast_log(LOG_NOTICE, "ast_capi_pvt(%s,%s,%#x,%d) (%d,%d,%d) (%d)(%f/%f) %d\n",tmp->incomingmsn,tmp->context,(int)tmp->controllers,devices,tmp->doEC,tmp->ecOption,tmp->ecTail,tmp->doES,tmp->rxgain,tmp->txgain,callgroup); + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_2 "ast_capi_pvt(%s,%s,%d,%d) (%d,%d,%d)\n",tmp->incomingmsn,tmp->context,tmp->controller,devices,tmp->doEC,tmp->ecOption,tmp->ecTail); + } + + } else { + return -1; + } + } + return 0; +} + +void supported_sservices(struct ast_capi_controller *cp) { + MESSAGE_EXCHANGE_ERROR error; + _cmsg CMSG,CMSG2; + struct timeval tv; + char fac[20]; + + FACILITY_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0); + FACILITY_REQ_CONTROLLER(&CMSG) = cp->controller; + FACILITY_REQ_FACILITYSELECTOR(&CMSG) = 0x0003; // sservices + fac[0] = 3; + fac[1] = 0; + fac[2] = 0; + fac[3] = 0; + FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = (unsigned char *)&fac; + if ((error= _capi_put_cmsg(&CMSG)) != 0) { + ast_log(LOG_ERROR,"error sending FACILITY_REQ (error=%#x)\n",error); + } else { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "sent FACILITY_REQ (CONTROLLER=%#x)\n",cp->controller); + } + } + + tv.tv_sec = 1; + tv.tv_usec = 0; + for (;;){ + error = capi20_waitformessage(ast_capi_ApplID,&tv); + error = capi_get_cmsg(&CMSG2,ast_capi_ApplID); +// error = check_wait_get_cmsg(&CMSG2); + if (error == 0) { + if (IS_FACILITY_CONF(&CMSG2)) { + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_4 "FACILITY_CONF INFO = %#x\n",FACILITY_CONF_INFO(&CMSG2)); + } + break; + } + } + } + // parse supported sservices + if (FACILITY_CONF_FACILITYSELECTOR(&CMSG2) == 0x0003) { + // success + if (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[4] == 0) { + if ((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 1) == 1) { + cp->holdretrieve = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "HOLD/RETRIEVE\n"); + } else { + cp->holdretrieve = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 2) >> 1) == 1) { + cp->terminalportability = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "TERMINAL PORTABILITY\n"); + } else { + cp->terminalportability = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 4) >> 2) == 1) { + cp->ECT = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "ECT\n"); + } else { + cp->ECT = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 8) >> 3) == 1) { + cp->threePTY = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "3PTY\n"); + } else { + cp->threePTY = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 16) >> 4) == 1) { + cp->CF = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "CF\n"); + } else { + cp->CF = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 32) >> 5) == 1) { + cp->CD = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "CD\n"); + } else { + cp->CD = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 64) >> 6) == 1) { + cp->MCID = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "MCID\n"); + } else { + cp->MCID = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[6] & 128) >> 7) == 1) { + cp->CCBS = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "CCBS\n"); + } else { + cp->CCBS = 0; + } + if ((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[7] & 1) == 1) { + cp->MWI = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "MWI\n"); + } else { + cp->MWI = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[7] & 2) >> 1) == 1) { + cp->CCNR = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "CCNR\n"); + } else { + cp->CCNR = 0; + } + if (((FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[7] & 4) >> 2) == 1) { + cp->CONF = 1; + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "CONF\n"); + } else { + cp->CONF = 0; + } + } else { + ast_log(LOG_NOTICE,"supplementary services info = %#x\n",(short)FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&CMSG2)[1]); + } + } else { + ast_log(LOG_NOTICE,"unexpected FACILITY_SELECTOR = %#x\n",FACILITY_CONF_FACILITYSELECTOR(&CMSG2)); + } +} + +static int capi_info(int fd, int argc, char *argv[]) +{ + int i=0; + if (argc != 2) + return RESULT_SHOWUSAGE; + for (i=1;i<=capi_num_controllers;i++) { + ast_mutex_lock(&contrlock); + if (capi_controllers[i] != NULL) { + ast_cli(fd,"Contr%d: %d B channels total, %d B channels free.\n",i,capi_controllers[i]->nbchannels,capi_controllers[i]->nfreebchannels); + } + ast_mutex_unlock(&contrlock); + } + return RESULT_SUCCESS; +} + +static int capi_do_debug(int fd, int argc, char *argv[]) +{ + if (argc != 2) + return RESULT_SHOWUSAGE; + capidebug = 1; + ast_cli(fd, "CAPI Debugging Enabled\n"); + return RESULT_SUCCESS; +} + +static int capi_no_debug(int fd, int argc, char *argv[]) +{ + if (argc != 3) + return RESULT_SHOWUSAGE; + capidebug = 0; + ast_cli(fd, "CAPI Debugging Disabled\n"); + return RESULT_SUCCESS; +} + +static char info_usage[] = +"Usage: capi info\n" +" Show info about B channels.\n"; + +static char debug_usage[] = +"Usage: capi debug\n" +" Enables dumping of CAPI packets for debugging purposes\n"; + +static char no_debug_usage[] = +"Usage: capi no debug\n" +" Disables dumping of CAPI packets for debugging purposes\n"; + +static struct ast_cli_entry cli_info = + { { "capi", "info", NULL }, capi_info, "Show CAPI info", info_usage }; +static struct ast_cli_entry cli_debug = + { { "capi", "debug", NULL }, capi_do_debug, "Enable CAPI debugging", debug_usage }; +static struct ast_cli_entry cli_no_debug = + { { "capi", "no", "debug", NULL }, capi_no_debug, "Disable CAPI debugging", no_debug_usage }; + +static const struct ast_channel_tech capi_tech = { + .type = type, + .description = tdesc, +#ifdef CAPI_ULAW + .capabilities = AST_FORMAT_ULAW, +#else + .capabilities = AST_FORMAT_ALAW, +#endif + .requester = capi_request, + .send_digit = capi_send_digit, + .send_text = NULL, + .call = capi_call, + .hangup = capi_hangup, + .answer = capi_answer, + .read = capi_read, + .write = capi_write, + .bridge = NULL, + .exception = NULL, + .indicate = capi_indicate, + .fixup = capi_fixup, + .setoption = NULL, +}; + +int load_module(void) +{ + struct ast_config *cfg; + struct ast_variable *v; + char *config = "capi.conf"; + char incomingmsn[AST_MAX_EXTENSION]=""; + char context[AST_MAX_EXTENSION]=""; + char prefix[AST_MAX_EXTENSION]=""; + char accountcode[20]=""; + char *empty = "\0"; + char deflect2[AST_MAX_EXTENSION]=""; + char controllerstr[AST_MAX_EXTENSION]=""; + int res = 0; + int controller=0; + int softdtmf=0; + int echocancel=1; + int ecoption=EC_OPTION_DISABLE_G165; + int ectail=EC_DEFAULT_TAIL; + int es=0; + float rxgain = 1.0; + float txgain = 1.0; + int isdnmode = 0; + unsigned int callgroup=0; + unsigned int group=0; + struct ast_capi_controller *cp; + + cfg = ast_config_load(config); + + /* We *must* have a config file otherwise stop immediately, well no... */ + if (!cfg) { + ast_log(LOG_ERROR, "Unable to load config %s, CAPI disabled\n", config); + return 0; + } + if (ast_mutex_lock(&iflock)) { + ast_log(LOG_ERROR, "Unable to lock interface list???\n"); + return -1; + } + + strncpy(capi_national_prefix, AST_CAPI_NATIONAL_PREF, sizeof(capi_national_prefix)-1); + strncpy(capi_international_prefix, AST_CAPI_NATIONAL_PREF, sizeof(capi_national_prefix)-1); + v = ast_variable_browse(cfg, "general"); + while(v) { + if (!strcasecmp(v->name, "nationalprefix")) { + strncpy(capi_national_prefix, v->value, sizeof(capi_national_prefix)-1); + } else if (!strcasecmp(v->name, "internationalprefix")) { + strncpy(capi_international_prefix, v->value, sizeof(capi_international_prefix)-1); + } else if (!strcasecmp(v->name, "rxgain")) { + if (sscanf(v->value,"%f",&rxgain) != 1) { + ast_log(LOG_ERROR,"invalid rxgain\n"); + } + } else if (!strcasecmp(v->name, "txgain")) { + if (sscanf(v->value,"%f",&txgain) != 1) { + ast_log(LOG_ERROR,"invalid txgain\n"); + } + } + v = v->next; + } + + + + + if (capi20_isinstalled() != 0) { + ast_log(LOG_WARNING,"CAPI not installed, CAPI disabled!\n"); + return 0; + } + + if (capi20_register(AST_CAPI_BCHANS,AST_CAPI_MAX_B3_BLOCKS,AST_CAPI_MAX_B3_BLOCK_SIZE,&ast_capi_ApplID) != 0) { + ast_log(LOG_NOTICE,"unable to register application at CAPI!\n"); + return -1; + } + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + if (capi20_get_profile(0,&profile) != 0) { +#else + if (capi20_get_profile(0,(unsigned char *)&profile) != 0) { +#endif + ast_log(LOG_NOTICE,"unable to get CAPI profile!\n"); + return -1; + } else { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + capi_num_controllers = profile.wCtlr; +#else + capi_num_controllers = profile.ncontrollers; +#endif + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "This box has %d capi controller(s).\n",capi_num_controllers); + for (controller=1;controller<=capi_num_controllers;controller++) { + + memset(&profile,0,sizeof(profile)); +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + capi20_get_profile(controller,&profile); +#else + capi20_get_profile(controller,(unsigned char *)&profile); +#endif + cp = malloc(sizeof(struct ast_capi_controller)); + cp->controller = controller; +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + cp->nbchannels = profile.wNumBChannels; + cp->nfreebchannels = profile.wNumBChannels; + if (profile.dwGlobalOptions & CAPI_PROFILE_DTMF_SUPPORT) { +#else + cp->nbchannels = profile.nbchannels; + cp->nfreebchannels = profile.nbchannels; + if ((profile.globaloptions & 8) >> 3 == 1) { +#endif + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "CAPI/contr%d supports DTMF\n",controller); + cp->dtmf = 1; + } else { + cp->dtmf = 0; + } +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + if (profile.dwGlobalOptions & CAPI_PROFILE_ECHO_CANCELLATION) { +#else + if (profile.globaloptions2 & 1) { +#endif + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "CAPI/contr%d supports echo cancellation\n",controller); + cp->echocancel = 1; + } else { + cp->echocancel = 0; + } +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) + if (profile.dwGlobalOptions & CAPI_PROFILE_SUPPLEMENTARY_SERVICES) { +#else + if ((profile.globaloptions & 16) >> 4 == 1) { +#endif + cp->sservices = 1; + } else { + cp->sservices = 0; + } + capi_controllers[controller] = cp; + if (cp->sservices == 1) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "CAPI/contr%d supports supplementary services\n",controller); + supported_sservices(cp); + } + } + } + + v = ast_variable_browse(cfg, "interfaces"); + while(v) { + /* Create the interface list */ + if (!strcasecmp(v->name, "devices")) { +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY + if (mkif(incomingmsn,context,controllerstr,atoi(v->value),softdtmf,echocancel,ecoption,ectail, prefix, isdnmode, es,rxgain,txgain,deflect2,accountcode,callgroup, group)) { +#else + if (mkif(incomingmsn,context,controllerstr,atoi(v->value),softdtmf,echocancel,ecoption,ectail, prefix, isdnmode, es,rxgain,txgain,accountcode,callgroup, group)) { +#endif + ast_log(LOG_ERROR,"Error creating interface list\n"); + return -1; + } + es=0; + strncpy(deflect2, empty, sizeof(deflect2)-1); + } else if (!strcasecmp(v->name, "context")) { + strncpy(context, v->value, sizeof(context)-1); + } else if (!strcasecmp(v->name, "incomingmsn")) { + strncpy(incomingmsn, v->value, sizeof(incomingmsn)-1); + } else if (!strcasecmp(v->name, "controller")) { + strncpy(controllerstr, v->value, sizeof(controllerstr)-1); + } else if (!strcasecmp(v->name, "softdtmf")) { + softdtmf = atoi(v->value); + } else if (!strcasecmp(v->name, "echosquelch")) { + es = atoi(v->value); + } else if (!strcasecmp(v->name, "callgroup")) { + callgroup = ast_get_group(v->value); + } else if (!strcasecmp(v->name, "group")) { + group = ast_get_group(v->value); + } else if (!strcasecmp(v->name, "deflect")) { + strncpy(deflect2, v->value, sizeof(deflect2)-1); + } else if (!strcasecmp(v->name, "rxgain")) { + if (sscanf(v->value,"%f",&rxgain) != 1) { + ast_log(LOG_ERROR,"invalid rxgain\n"); + } + } else if (!strcasecmp(v->name, "txgain")) { + if (sscanf(v->value,"%f",&txgain) != 1) { + ast_log(LOG_ERROR,"invalid txgain\n"); + } + } else if (!strcasecmp(v->name, "echocancel")) { + if (!strcasecmp(v->value, "yes") || !strcasecmp(v->value, "1") || !strcasecmp(v->value, "on")) { + echocancel=1; + ecoption=EC_OPTION_DISABLE_G165; + } + else if (!strcasecmp(v->value, "no") || !strcasecmp(v->value, "0") || !strcasecmp(v->value, "off")) { + echocancel=0; + ecoption=0; + } + else if (!strcasecmp(v->value, "g165") || !strcasecmp(v->value, "g.165")) { + echocancel=1; + ecoption=EC_OPTION_DISABLE_G165; + } + else if (!strcasecmp(v->value, "g164") || !strcasecmp(v->value, "g.164")) { + echocancel=1; + ecoption=EC_OPTION_DISABLE_G164_OR_G165; + } + else if (!strcasecmp(v->value, "force")) { + echocancel=1; + ecoption=EC_OPTION_DISABLE_NEVER; + } + else { + ast_log(LOG_ERROR,"Unknown echocancel parameter \"%s\" -- ignoring\n",v->value); + } + } else if (!strcasecmp(v->name, "echotail")) { + ectail = atoi(v->value); + if (ectail > 255) + ectail = 255; + } else if (!strcasecmp(v->name, "prefix")) { + strncpy(prefix, v->value, sizeof(prefix)-1); + } else if (!strcasecmp(v->name, "accountcode")) { + strncpy(accountcode, v->value, sizeof(accountcode)-1); + } else if (!strcasecmp(v->name, "isdnmode")) { + if (!strcasecmp(v->value, "ptp") || !strcasecmp(v->value, "1")) + isdnmode = 1; + else if (!strcasecmp(v->value, "ptm") || !strcasecmp(v->value, "0") || !strcasecmp(v->value, "ptmp")) + isdnmode = 0; + else + ast_log(LOG_ERROR,"Unknown isdnmode parameter \"%s\" -- ignoring\n",v->value); + + } + + v = v->next; + } + ast_config_destroy(cfg); + + for (controller=1;controller<=capi_num_controllers;controller++) { + if (capi_used_controllers & (1 << controller)) { + if (ListenOnController(ALL_SERVICES,controller) != 0) { + ast_log(LOG_ERROR,"Unable to listen on contr%d\n",controller); + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "listening on contr%d CIPmask = %#x\n",controller,ALL_SERVICES); + } + } else { + ast_log(LOG_WARNING,"Unused contr%d\n",controller); + } + } + + + ast_mutex_unlock(&iflock); + + if (ast_channel_register(&capi_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); + unload_module(); + return -1; + } + + ast_cli_register(&cli_info); + ast_cli_register(&cli_debug); + ast_cli_register(&cli_no_debug); + + if (ast_mutex_lock(&monlock)) { + ast_log(LOG_WARNING,"Unable to get monitor lock!\n"); + return -1; + } + if (monitor_thread == pthread_self()) { + ast_mutex_unlock(&monlock); + ast_log(LOG_WARNING,"Unable to kill myself!\n"); + return -1; + } + + if (ast_pthread_create(&monitor_thread,NULL,do_monitor,NULL) < 0) { + ast_mutex_unlock(&monlock); + ast_log(LOG_ERROR,"Unable to start monitor thread!\n"); + return -1; + } + + return res; +} + + +int unload_module() +{ + if (capi20_release(ast_capi_ApplID) != 0) + ast_log(LOG_WARNING,"Unable to unregister from CAPI!\n"); + ast_channel_unregister(&capi_tech); + return 0; +} + +int usecount() +{ + int res; + ast_mutex_lock(&usecnt_lock); + res = usecnt; + ast_mutex_unlock(&usecnt_lock); + return res; +} + +char *description() +{ + return desc; +} + + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/channels/chan_features.c asterisk-1.2.10/channels/chan_features.c --- asterisk-1.2.10.orig/channels/chan_features.c 2006-07-03 06:19:09.000000000 +0200 +++ asterisk-1.2.10/channels/chan_features.c 2006-07-31 14:13:08.000000000 +0200 @@ -427,7 +427,7 @@ } ast_mutex_unlock(&featurelock); if (!tmp) { - chan = ast_request(tech, format, dest, &status); + chan = ast_request(tech, format, dest, &status, NULL); if (!chan) { ast_log(LOG_NOTICE, "Unable to allocate subchannel '%s/%s'\n", tech, dest); return NULL; diff -urN asterisk-1.2.10.orig/channels/chan_iax2.c asterisk-1.2.10/channels/chan_iax2.c --- asterisk-1.2.10.orig/channels/chan_iax2.c 2006-07-12 17:23:59.000000000 +0200 +++ asterisk-1.2.10/channels/chan_iax2.c 2006-07-31 14:13:08.000000000 +0200 @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Hangup cause signalling implementation by + * Levent Guendogdu + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -3089,7 +3092,7 @@ memset(&ied, 0, sizeof(ied)); ast_mutex_lock(&iaxsl[callno]); if (callno && iaxs[callno]) { - ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name); + ast_log(LOG_DEBUG, "We're hanging up %s with cause %i now...\n", c->name, c->hangupcause); alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE); /* Send the hangup unless we have had a transmission error or are already gone */ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause); @@ -3141,7 +3144,8 @@ static struct ast_frame *iax2_read(struct ast_channel *c) { static struct ast_frame f = { AST_FRAME_NULL, }; - ast_log(LOG_NOTICE, "I should never be called!\n"); + if (option_verbose > 3) + ast_log(LOG_NOTICE, "I should never be called!\n"); return &f; } diff -urN asterisk-1.2.10.orig/channels/chan_sip.c asterisk-1.2.10/channels/chan_sip.c --- asterisk-1.2.10.orig/channels/chan_sip.c 2006-07-13 18:44:23.000000000 +0200 +++ asterisk-1.2.10/channels/chan_sip.c 2006-07-31 14:53:09.000000000 +0200 @@ -603,6 +603,7 @@ unsigned int flags; /*!< SIP_ flags */ int timer_t1; /*!< SIP timer T1, ms rtt */ unsigned int sipoptions; /*!< Supported SIP sipoptions on the other end */ + int dialog_established; /*!< SIP dialog established */ int capability; /*!< Special capability (codec) */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; /*!< Supported peer capability */ @@ -626,6 +627,7 @@ char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */ char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ char refer_contact[SIP_LEN_CONTACT]; /*!< Place to store Contact info from a REFER extension */ + char refer_replaces[AST_MAX_EXTENSION]; /*!< Place to store Replaces header of REFER-TO header */ struct sip_pvt *refer_call; /*!< Call we are referring */ struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ int route_persistant; /*!< Is this the "real" route? */ @@ -645,6 +647,7 @@ char peername[256]; /*!< [peer] name, not set if [user] */ char authname[256]; /*!< Who we use for authentication */ char uri[256]; /*!< Original requested URI */ + char origuri[SIP_LEN_CONTACT]; /*!< REAL! Original requested URI */ char okcontacturi[SIP_LEN_CONTACT]; /*!< URI from the 200 OK on INVITE */ char peersecret[256]; /*!< Password */ char peermd5secret[256]; @@ -768,6 +771,9 @@ int callingpres; /*!< Calling id presentation */ int inUse; /*!< Number of calls in use */ int call_limit; /*!< Limit of concurrent calls */ + int max_regs; /*!< Limit of concurrent registrations */ + int subpeer; /*!< Peer entry used for multiple registrations */ + char reg_callid[80]; /*!< Call-ID used for registration */ char vmexten[AST_MAX_EXTENSION]; /*!< Dialplan extension for MWI notify message*/ char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox setting for MWI checks */ char language[MAX_LANGUAGE]; /*!< Default language for prompts */ @@ -928,7 +934,7 @@ static int determine_firstline_parts(struct sip_request *req); static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); -static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate); +static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate, char *cid_num, char *cid_name); static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize); /*! \brief Definition of this channel for PBX channel registration */ @@ -1311,7 +1317,7 @@ /* If this is a subscription, tell the phone that we got a timeout */ if (p->subscribed) { p->subscribed = TIMEOUT; - transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, 1); /* Send first notification */ + transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, 1, NULL, NULL); /* Send first notification */ p->subscribed = NONE; append_history(p, "Subscribestatus", "timeout"); return 10000; /* Reschedule this destruction so that we know that it's gone */ @@ -3144,16 +3150,30 @@ /*! \brief find_call: Connect incoming SIP message to current dialog or create new dialog structure */ /* Called by handle_request, sipsock_read */ -static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method) +static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method, const int replaces_callid) { struct sip_pvt *p = NULL; char *callid; char *tag = ""; + char *replaces; char totag[128]; char fromtag[128]; + char *c; callid = get_header(req, "Call-ID"); + if (replaces_callid) { + replaces = get_header(req, "Replaces"); + c = strchr(replaces, ';'); + if (c) + *c = '\0'; + if (!ast_strlen_zero(replaces)) { + callid = replaces; + } else { + return NULL; + } + } + if (pedanticsipchecking) { /* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy we need more to identify a branch - so we have to check branch, from @@ -4135,6 +4155,7 @@ if (sipmethod == SIP_CANCEL) { c = p->initreq.rlPart2; /* Use original URI */ } else if (sipmethod == SIP_ACK) { +// XXX+ } else if (!strcasecmp(msg, "ACK") && !p->dialog_established) { /* Use URI from Contact: in 200 OK (if INVITE) (we only have the contacturi on INVITEs) */ if (!ast_strlen_zero(p->okcontacturi)) @@ -4901,13 +4922,15 @@ ast_build_string(&invite, &invite_max, ";%s", p->options->uri_options); ast_copy_string(p->uri, invite_buf, sizeof(p->uri)); + ast_copy_string(p->origuri, invite, sizeof(p->origuri)); if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) { /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */ snprintf(to, sizeof(to), ";tag=%s", p->uri, p->theirtag); } else if (p->options && p->options->vxml_url) { /* If there is a VXML URL append it to the SIP URL */ - snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url); +// snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url); + snprintf(to, sizeof(to), "<%s;%s>", p->uri, p->options->vxml_url); } else { snprintf(to, sizeof(to), "<%s>", p->uri); } @@ -4964,6 +4987,11 @@ if (!ast_strlen_zero(p->referred_by)) add_header(&req, "Referred-By", p->referred_by); } + if (sipmethod == SIP_INVITE) { + if (!ast_strlen_zero(p->refer_replaces)) { + add_header(&req, "Replaces", p->refer_replaces); + } + } #ifdef OSP_SUPPORT if ((req.method != SIP_OPTIONS) && p->options && !ast_strlen_zero(p->options->osptoken)) { ast_log(LOG_DEBUG,"Adding OSP Token: %s\n", p->options->osptoken); @@ -5038,8 +5066,7 @@ } /*! \brief transmit_state_notify: Used in the SUBSCRIBE notification subsystem ----*/ -static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate) -{ +static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate, char *cid_num, char *cid_name) { char tmp[4000], from[256], to[256]; char *t = tmp, *c, *a, *mfrom, *mto; size_t maxbytes = sizeof(tmp); @@ -5183,10 +5210,19 @@ case DIALOG_INFO_XML: /* SNOM subscribes in this format */ ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); - if ((state & AST_EXTENSION_RINGING) && global_notifyringing) - ast_build_string(&t, &maxbytes, "\n", p->exten); - else + if ((state & AST_EXTENSION_RINGING) && global_notifyringing) { + ast_build_string(&t, &maxbytes, "\n", p->exten); + if (cid_num) { + ast_build_string(&t, &maxbytes, "%s\n", p->exten, p->exten, mfrom); + if (cid_name && !ast_strlen_zero(cid_name)) { + ast_build_string(&t, &maxbytes, "sip:%s@%s\n", cid_name, cid_num, p->fromdomain, ast_pickup_ext(), p->exten, p->fromdomain); + } else { + ast_build_string(&t, &maxbytes, "sip:%s@%s\n", cid_num, cid_num, p->fromdomain, ast_pickup_ext(), p->exten, p->fromdomain); + } + } + } else { ast_build_string(&t, &maxbytes, "\n", p->exten); + } ast_build_string(&t, &maxbytes, "%s\n", statestring); ast_build_string(&t, &maxbytes, "\n\n"); break; @@ -6013,8 +6049,10 @@ p->expire = -1; pvt->expiry = expiry; snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry, p->username, p->fullcontact); - if (!ast_test_flag((&p->flags_page2), SIP_PAGE2_RT_FROMCONTACT)) + if (!ast_test_flag((&p->flags_page2), SIP_PAGE2_RT_FROMCONTACT)) { + // ast_log(LOG_NOTICE, "updating SIP/Registry for peer %s with data %s\n", p->name, data); ast_db_put("SIP/Registry", p->name, data); + } manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", p->name); if (inaddrcmp(&p->addr, &oldsin)) { sip_poke_peer(p); @@ -6405,7 +6443,7 @@ /*! \brief cb_extensionstate: Callback for the devicestate notification (SUBSCRIBE) support subsystem ---*/ /* If you add an "hint" priority to the extension in the dial plan, you will get notifications on device state changes */ -static int cb_extensionstate(char *context, char* exten, int state, void *data) +static int cb_extensionstate(char *context, char* exten, int state, void *data, char *cid_num, char *cid_name) { struct sip_pvt *p = data; @@ -6424,7 +6462,7 @@ p->laststate = state; break; } - transmit_state_notify(p, state, 1, 1); + transmit_state_notify(p, state, 1, 1, cid_num, cid_name); if (option_debug > 1) ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username); @@ -6450,7 +6488,13 @@ char *name, *c; char *t; char *domain; - + char *callid; + struct sip_peer *clone; + char clone_name[256]; + int found = 0; + struct sip_peer *recycle_peer = NULL; + char peer_name[256]; + /* Terminate URI */ t = uri; while(*t && (*t > 32) && (*t != ';')) @@ -6499,9 +6543,68 @@ if (!ast_test_flag(&peer->flags_page2, SIP_PAGE2_DYNAMIC)) { ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); } else { + callid = get_header(req, "Call-ID"); + ast_copy_string(peer_name, peer->name, sizeof(peer_name)); + if (peer->max_regs > 1) { + int i = 0; + /* check if peer matches callid */ + if ((peer->expire > -1) && (!strncmp(peer->reg_callid, callid, strlen(callid)))) { + // ast_log(LOG_NOTICE, "peer->reg_callid %s, req->callid %s found in peer\n", peer->reg_callid, callid); + found++; + } else { + /* otherwise check subpeers for callid */ + for (i=0; imax_regs - 1; i++) { + snprintf(clone_name, sizeof(clone_name), "%s@%d", peer->name, i); + // ast_log(LOG_NOTICE, "checking subpeer %s\n", clone_name); + clone = find_peer(clone_name, NULL, 1); + if (clone && (clone->expire > -1)) { + if (!strncmp(clone->reg_callid, callid, strlen(callid))) { + // ast_log(LOG_NOTICE, "clone->reg_callid %s, req->callid %s found in subpeer\n", clone->reg_callid, callid); + found++; + peer = clone; + break; + } + } + } + } + if (!found) { + // ast_log(LOG_NOTICE, "did not find callid in peer or subpeer\n"); + /* choose the next best peer or subpeer (that means: find the peer with the smalles expiry time */ + if (peer->expire == -1) { + recycle_peer = peer; + // ast_log(LOG_NOTICE, "peer %s expiry %d\n", peer->name, peer->expire); + } else { + for (i=0; imax_regs - 1; i++) { + snprintf(clone_name, sizeof(clone_name), "%s@%d", peer->name, i); + clone = find_peer(clone_name, NULL, 1); + if (clone) { + if (clone->expire == -1) { + recycle_peer = clone; + break; + } + // ast_log(LOG_NOTICE, "clone %s expiry %d\n", clone->name, clone->expire); + } + } + } + if (recycle_peer) { + peer = recycle_peer; + ast_copy_string(peer_name, peer->name, sizeof(peer_name)); + // ast_log(LOG_NOTICE, "recycling peer %s\n", peer->name); + if (peer->subpeer) { + i = strchr(peer_name, '@') - peer_name; + if (i < sizeof(peer_name)) + peer_name[i] = '\0'; + // ast_log(LOG_NOTICE, "i = %d\n", i); + } + } else { + /* deny registration */ + peer_name[0] = '\0'; + } + } + } ast_copy_flags(p, peer, SIP_NAT); transmit_response(p, "100 Trying", req); - if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, 0, ignore))) { + if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), peer_name, peer->secret, peer->md5secret, SIP_REGISTER, uri, 0, ignore))) { sip_cancel_destroy(p); switch (parse_register_contact(p, peer, req)) { case PARSE_REGISTER_FAILED: @@ -6521,6 +6624,7 @@ transmit_response_with_date(p, "200 OK", req); peer->lastmsgssent = -1; res = 0; + ast_copy_string(peer->reg_callid, callid, sizeof(peer->reg_callid)); break; } } @@ -6866,6 +6970,11 @@ /* XXX The refer_to could contain a call on an entirely different machine, requiring an INVITE with a replaces header -anthm XXX */ /* The only way to find out is to use the dialplan - oej */ + ast_copy_string(sip_pvt->refer_to, refer_to, sizeof(sip_pvt->refer_to)); + ast_copy_string(sip_pvt->referred_by, referred_by, sizeof(sip_pvt->referred_by)); + ast_copy_string(sip_pvt->refer_contact, h_contact, sizeof(sip_pvt->refer_contact)); + ast_copy_string(sip_pvt->refer_replaces, replace_callid, sizeof(sip_pvt->referred_by)); + return 2; } } else if (ast_exists_extension(NULL, transfercontext, refer_to, 1, NULL) || !strcmp(refer_to, ast_parking_ext())) { /* This is an unsupervised transfer (blind transfer) */ @@ -7586,6 +7695,8 @@ int peers_offline = 0; char *id; char idtext[256] = ""; + char *tmp; + int i = 0; if (s) { /* Manager - get ActionID */ id = astman_get_header(m,"ActionID"); @@ -7628,6 +7739,7 @@ else ast_copy_string(name, iterator->name, sizeof(name)); + if ((iterator->expire != -1) || (iterator->subpeer != 1)) { pstatus = peer_status(iterator, status, sizeof(status)); if (pstatus) peers_online++; @@ -7644,14 +7756,24 @@ } } - snprintf(srch, sizeof(srch), FORMAT, name, + } + /* multiple registration, peer used ? */ + if ((iterator->expire != -1) || (iterator->subpeer != 1)) { + snprintf(srch, sizeof(srch), FORMAT, name, iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), iterator->addr.sin_addr) : "(Unspecified)", ast_test_flag(&iterator->flags_page2, SIP_PAGE2_DYNAMIC) ? " D " : " ", /* Dynamic or not? */ (ast_test_flag(iterator, SIP_NAT) & SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */ iterator->ha ? " A " : " ", /* permit/deny */ ntohs(iterator->addr.sin_port), status); + } if (!s) {/* Normal CLI list */ + if ((iterator->expire != -1) || (iterator->subpeer != 1)) { + if (iterator->subpeer == 1) { + tmp = strchr(name, '@'); + i = tmp - name; + name[i] = '\0'; + } ast_cli(fd, FORMAT, name, iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), iterator->addr.sin_addr) : "(Unspecified)", ast_test_flag(&iterator->flags_page2, SIP_PAGE2_DYNAMIC) ? " D " : " ", /* Dynamic or not? */ @@ -7659,6 +7781,7 @@ iterator->ha ? " A " : " ", /* permit/deny */ ntohs(iterator->addr.sin_port), status); + } } else { /* Manager format */ /* The names here need to be the same as other channels */ ast_cli(fd, @@ -7684,7 +7807,9 @@ ASTOBJ_UNLOCK(iterator); - total_peers++; + if ((iterator->expire != -1) || (iterator->subpeer != 1)) { + total_peers++; + } } while(0) ); if (!s) { @@ -8719,6 +8844,7 @@ char buf[1024]; unsigned int event; char *c; + struct ast_call_feature *feature; /* Need to check the media/type */ if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay") || @@ -8782,6 +8908,19 @@ ast_queue_control(p->owner, AST_CONTROL_VIDUPDATE); transmit_response(p, "200 OK", req); return; + } else if ((c = get_header(req, "Record"))) { + feature = ast_find_builtin_feature("automon"); + if (feature && (!ast_strlen_zero(feature->exten))) { + int i = 0; +// ast_log(LOG_NOTICE, "feature exten %s\n", feature->exten); + for (i=0; iexten); i++) { + struct ast_frame f = { AST_FRAME_DTMF, feature->exten[i] }; + ast_queue_frame(p->owner, &f); + } + } else { + ast_log(LOG_NOTICE, "Feature \"One Touch Monitor\" not configured in features.conf.\n"); + } + return; } else if ((c = get_header(req, "X-ClientCode"))) { /* Client code (from SNOM phone) */ if (ast_test_flag(p, SIP_USECLIENTCODE)) { @@ -8881,12 +9020,63 @@ return RESULT_SUCCESS; } + +/*! \brief sip_notify: Send SIP notify to peer */ +static int sip_send_notify(int fd, char *notify_type, char *peer) +{ + struct ast_variable *varlist; + struct sip_pvt *p; + struct sip_request req; + struct ast_variable *var; + + varlist = ast_variable_browse(notify_types, notify_type); + + if (!varlist) { + if (fd > 0) + ast_cli(fd, "Unable to find notify type '%s'\n", notify_type); + return RESULT_FAILURE; + } + + p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY); + if (!p) { + ast_log(LOG_WARNING, "Unable to build sip pvt data for notify\n"); + return RESULT_FAILURE; + } + + if (create_addr(p, peer)) { + /* Maybe they're not registered, etc. */ + sip_destroy(p); + if (fd > 0) + ast_cli(fd, "Could not create address for '%s'\n", peer); + return RESULT_FAILURE; + } + + initreqprep(&req, p, SIP_NOTIFY); + + for (var = varlist; var; var = var->next) + add_header(&req, var->name, var->value); + + add_blank_header(&req); + /* Recalculate our side, and recalculate Call ID */ + if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) + memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); + build_via(p, p->via, sizeof(p->via)); + build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); + if (fd > 0) + ast_cli(fd, "Sending NOTIFY of type '%s' to '%s'\n", notify_type, peer); + transmit_sip_request(p, &req); + sip_scheddestroy(p, 15000); + + return RESULT_SUCCESS; +} + /*! \brief sip_notify: Send SIP notify to peer */ static int sip_notify(int fd, int argc, char *argv[]) { struct ast_variable *varlist; int i; - + int res = RESULT_SUCCESS; + if (argc < 4) return RESULT_SHOWUSAGE; @@ -8903,41 +9093,13 @@ } for (i = 3; i < argc; i++) { - struct sip_pvt *p; - struct sip_request req; - struct ast_variable *var; - - p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY); - if (!p) { - ast_log(LOG_WARNING, "Unable to build sip pvt data for notify\n"); - return RESULT_FAILURE; - } - - if (create_addr(p, argv[i])) { - /* Maybe they're not registered, etc. */ - sip_destroy(p); - ast_cli(fd, "Could not create address for '%s'\n", argv[i]); - continue; - } - - initreqprep(&req, p, SIP_NOTIFY); + if (sip_send_notify(fd, argv[2], argv[i]) == RESULT_FAILURE) + res = RESULT_FAILURE; + } + return res; +} - for (var = varlist; var; var = var->next) - add_header(&req, var->name, var->value); - add_blank_header(&req); - /* Recalculate our side, and recalculate Call ID */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); - build_via(p, p->via, sizeof(p->via)); - build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); - ast_cli(fd, "Sending NOTIFY of type '%s' to '%s'\n", argv[2], argv[i]); - transmit_sip_request(p, &req); - sip_scheddestroy(p, 15000); - } - - return RESULT_SUCCESS; -} /*! \brief sip_do_history: Enable SIP History logging (CLI) ---*/ static int sip_do_history(int fd, int argc, char *argv[]) { @@ -9598,7 +9760,7 @@ if (!ignore && p->owner) { ast_queue_control(p->owner, AST_CONTROL_RINGING); if (p->owner->_state != AST_STATE_UP) - ast_setstate(p->owner, AST_STATE_RINGING); + ast_setstate_and_cid(p->owner, AST_STATE_RINGING, p->owner->cid.cid_num, p->owner->cid.cid_name); } if (find_sdp(req)) { process_sdp(p, req); @@ -10423,9 +10585,18 @@ /* This is a call to ourself. Send ourselves an error code and stop processing immediately, as SIP really has no good mechanism for being able to call yourself */ - transmit_response(p, "482 Loop Detected", req); - /* We do NOT destroy p here, so that our response will be accepted */ - return 0; +/* char tmp[256] = "", *uri; + if (req->rlPart2) + ast_copy_string(tmp, req->rlPart2, sizeof(tmp)); + uri = get_in_brackets(tmp); + if (strcmp(p->uri, uri)) { + ast_log(LOG_NOTICE, "SPIRAL DETECTED p->uri: %s uri: %s\n", p->uri, uri); + } else { + ast_log(LOG_NOTICE, "LOOP DETECTED p->uri: %s uri: %s\n", p->uri, uri);*/ + transmit_response(p, "482 Loop Detected", req); + /* We do NOT destroy p here, so that our response will be accepted */ + return 0; +/* }*/ } if (!ignore) { /* Use this as the basis */ @@ -10657,7 +10828,8 @@ struct ast_channel *c=NULL; int res; struct ast_channel *transfer_to; char *transfercontext = NULL; + struct sip_pvt *rp; /* replace call */ if (option_debug > 2) ast_log(LOG_DEBUG, "SIP call transfer received for call %s (REFER)!\n", p->callid); @@ -10665,9 +10837,73 @@ res = get_refer_info(p, req); if (res < 0) transmit_response(p, "603 Declined", req); - else if (res > 0) + else if (res == 1) transmit_response(p, "484 Address Incomplete", req); - else { + else if (res == 2) { + transmit_response(p, "202 Accepted", req); + rp = sip_alloc(NULL, NULL, 0, SIP_INVITE); + if (!rp) { + return -1; + } + rp->capability = global_capability; + + build_route(rp, req, 0); + if (option_verbose > 3) + ast_log(LOG_NOTICE, "got REFER for callid %s TO %s CONTACT %s replacing callid %s (tohost %s, p->route %s, rp->route %s)\n", p->callid, p->refer_to, p->refer_contact, p->refer_replaces, p->tohost, p->route->hop, rp->route->hop); + if (create_addr(rp, p->tohost)) { + sip_destroy(rp); + return -1; + } + + if (ast_sip_ouraddrfor(&rp->sa.sin_addr,&rp->ourip)) { + memcpy(&rp->ourip, &__ourip, sizeof(rp->ourip)); + } + build_via(rp, rp->via, sizeof(rp->via)); + build_callid(rp->callid, sizeof(rp->callid) - 1, rp->ourip, rp->fromdomain); + + ast_log(LOG_NOTICE, "1\n"); + rp->prefcodec = p->prefcodec; + rp->jointcapability = rp->capability; + rp->rtp = p->rtp; + p->rtp = NULL; + if (!ast_strlen_zero(p->refer_to)) { + ast_copy_string(rp->username, p->refer_to, sizeof(rp->username)); + rp->fullcontact[0] = '\0'; + } + if (!ast_strlen_zero(p->refer_replaces)) { + ast_copy_string(rp->refer_replaces, p->refer_replaces, sizeof(rp->refer_replaces)); + } + ast_log(LOG_NOTICE, "2\n"); + ast_set_flag(rp, SIP_OUTGOING); + + ast_log(LOG_NOTICE, "3\n"); + + if (p->owner) { + c = p->owner; +// ast_copy_string(rp->cid_num, c->cid.cid_num, sizeof(rp->cid_num)); +// ast_copy_string(rp->cid_name, c->cid.cid_name, sizeof(rp->cid_name)); + ast_log(LOG_NOTICE, "4\n"); + c->tech_pvt = rp; + rp->owner = c; + ast_log(LOG_NOTICE, "5\n"); + ast_mutex_unlock(&c->lock); + } + p->owner = NULL; + ast_log(LOG_NOTICE, "6\n"); + + transmit_invite(rp, SIP_INVITE, 1, 2); + if (rp->maxtime) { + /* Initialize auto-congest time */ + rp->initid = ast_sched_add(sched, rp->maxtime * 4, auto_congest, rp); + } + + transmit_notify_with_sipfrag(p, seqno); + + /* Always increment on a BYE */ + transmit_request_with_auth(p, SIP_BYE, 0, 1, 1); + ast_set_flag(p, SIP_ALREADYGONE); + return 0; + } else { /* res == 0 */ int nobye = 0; if (!ignore) { if (p->refer_call) { @@ -10995,7 +11231,7 @@ struct sip_pvt *p_old; transmit_response(p, "200 OK", req); - transmit_state_notify(p, firststate, 1, 1); /* Send first notification */ + transmit_state_notify(p, firststate, 1, 1, NULL, NULL); /* Send first notification */ append_history(p, "Subscribestatus", ast_extension_state2str(firststate)); /* remove any old subscription from this peer for the same exten/context, @@ -11189,6 +11425,8 @@ res = handle_request_options(p, req, debug); break; case SIP_INVITE: + /* XXX quick fix for xfers */ + ast_copy_string(p->tohost, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), sizeof(p->tohost)); res = handle_request_invite(p, req, debug, ignore, seqno, sin, recount, e); break; case SIP_REFER: @@ -11309,7 +11547,7 @@ /* Process request, with netlock held */ retrylock: ast_mutex_lock(&netlock); - p = find_call(&req, &sin, req.method); + p = find_call(&req, &sin, req.method, 0); if (p) { /* Go ahead and lock the owner if it has one -- we may need it */ if (p->owner && ast_mutex_trylock(&p->owner->lock)) { @@ -11636,6 +11874,52 @@ return 0; } +static char mandescr_sip_notify[] = +"Description: Send a NOTIFY message to one or more SIP peers.\n" +"Variables: \n" +" Peer: The peer name you want to send a NOTIFY to.\n" +" Type: The notify type (see sip_notify.conf).\n" +" ActionID: Optional action ID for this AMI transaction.\n"; + +/*! \brief manager_sip_notify: Send a notify (see sip_notify.conf) to a peer ---*/ +static int manager_sip_notify(struct mansession *s, struct message *m) +{ + char *id = astman_get_header(m,"ActionID"); + char *peer; + char *notify_type; + int res = 0; + + peer = astman_get_header(m,"Peer"); + if (ast_strlen_zero(peer)) { + astman_send_error(s, m, "Peer: missing.\n"); + return 0; + } + notify_type = astman_get_header(m,"Type"); + if (ast_strlen_zero(notify_type)) { + astman_send_error(s, m, "Type: missing.\n"); + return 0; + } + + res = sip_send_notify(-1, notify_type, peer); + if (res != RESULT_SUCCESS) { + ast_cli(s->fd, "Response: SIPNotify Failure\r\n" + "Peer: %s\r\n" + "Type: %s\r\n" + "ActionID: %s\r\n" + "\r\n", + peer, notify_type, id); + } else { + ast_cli(s->fd, "Response: SIPNotify Success\r\n" + "Peer: %s\r\n" + "Type: %s\r\n" + "ActionID: %s\r\n" + "\r\n", + peer, notify_type, id); + } + return res; +} + + /*! \brief sip_devicestate: Part of PBX channel interface ---*/ /* Return values:--- @@ -12173,6 +12457,7 @@ peer->expire = -1; peer->pokeexpire = -1; + peer->max_regs = 1; ast_copy_string(peer->name, name, sizeof(peer->name)); ast_copy_flags(peer, &global_flags, SIP_FLAGS_TO_COPY); strcpy(peer->context, default_context); @@ -12218,7 +12503,9 @@ if (peer) { /* Already in the list, remove it and it will be added back (or FREE'd) */ - found++; + if (peer->max_regs == 1) { + found++; + } } else { peer = malloc(sizeof(*peer)); if (peer) { @@ -12230,6 +12517,7 @@ ASTOBJ_INIT(peer); peer->expire = -1; peer->pokeexpire = -1; + peer->max_regs = 1; } else { ast_log(LOG_WARNING, "Can't allocate SIP peer memory\n"); } @@ -12375,6 +12663,10 @@ peer->call_limit = atoi(v->value); if (peer->call_limit < 0) peer->call_limit = 0; + } else if (!strcasecmp(v->name, "registrations")) { + peer->max_regs = atoi(v->value); + if (peer->max_regs < 0) + peer->max_regs = 0; } else if (!strcasecmp(v->name, "amaflags")) { format = ast_cdr_amaflags2int(v->value); if (format < 0) { @@ -12770,8 +13062,24 @@ if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) { peer = build_peer(cat, ast_variable_browse(cfg, cat), 0); if (peer) { - ASTOBJ_CONTAINER_LINK(&peerl,peer); - ASTOBJ_UNREF(peer, sip_destroy_peer); + if (peer->max_regs > 1) { + int i = 0; + int clones = peer->max_regs - 1; + struct sip_peer *clone = NULL; + char clone_name[sizeof(clone->name)]; + /* clone clone clone */ + for (i=0;isubpeer = 1; + ASTOBJ_CONTAINER_LINK(&peerl,clone); + ASTOBJ_UNREF(clone, sip_destroy_peer); + } + } + } + ASTOBJ_CONTAINER_LINK(&peerl,peer); + ASTOBJ_UNREF(peer, sip_destroy_peer); } } else if (strcasecmp(utype, "user")) { ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf"); @@ -13361,6 +13669,8 @@ "List SIP peers (text format)", mandescr_show_peers); ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM, manager_sip_show_peer, "Show SIP peer (text format)", mandescr_show_peer); + ast_manager_register2("SIPNotify", EVENT_FLAG_SYSTEM, manager_sip_notify, + "Send NOTIFY to peer", mandescr_sip_notify); sip_poke_all_peers(); sip_send_all_registers(); @@ -13391,6 +13701,7 @@ ast_rtp_proto_unregister(&sip_rtp); + ast_manager_unregister("SIPNotify"); ast_manager_unregister("SIPpeers"); ast_manager_unregister("SIPshowpeer"); diff -urN asterisk-1.2.10.orig/channels/chan_zap.c asterisk-1.2.10/channels/chan_zap.c --- asterisk-1.2.10.orig/channels/chan_zap.c 2006-07-12 15:54:10.000000000 +0200 +++ asterisk-1.2.10/channels/chan_zap.c 2006-08-09 16:15:04.000000000 +0200 @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2003-2006 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -65,6 +69,9 @@ #ifdef ZAPATA_R2 #include #endif +#ifdef ZAPATA_GSM +#include +#endif #include "asterisk.h" @@ -96,6 +103,7 @@ #include "asterisk/term.h" #include "asterisk/utils.h" #include "asterisk/transcap.h" +#include "asterisk/devicestate.h" #ifndef ZT_SIG_EM_E1 #error "Your zaptel is too old. please cvs update" @@ -173,6 +181,7 @@ #define SIG_FXOGS ZT_SIG_FXOGS #define SIG_FXOKS ZT_SIG_FXOKS #define SIG_PRI ZT_SIG_CLEAR +#define SIG_GSM (0x100000 | ZT_SIG_CLEAR) #define SIG_R2 ZT_SIG_CAS #define SIG_SF ZT_SIG_SF #define SIG_SFWINK (0x0100000 | ZT_SIG_SF) @@ -183,7 +192,7 @@ #define SIG_GR303FXOKS (0x0100000 | ZT_SIG_FXOKS) #define SIG_GR303FXSKS (0x0100000 | ZT_SIG_FXSKS) -#define NUM_SPANS 32 +#define NUM_SPANS 128 /*!<"32 spans", muahahaha, us alaws like to have some more... */ #define NUM_DCHANS 4 /*!< No more than 4 d-channels */ #define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */ @@ -201,6 +210,11 @@ static char defaultcic[64] = ""; static char defaultozz[64] = ""; +static char nocid[256] = "No CID available"; +static char withheldcid[256] = "CID withheld"; +static char gsm_modem_pin[20]; +static char gsm_modem_exten[AST_MAX_EXTENSION]; + static char language[MAX_LANGUAGE] = ""; static char musicclass[MAX_MUSICCLASS] = ""; static char progzone[10]= ""; @@ -287,6 +301,7 @@ static int cur_priexclusive = 0; static int priindication_oob = 0; +static int pritransfer = 0; #ifdef ZAPATA_PRI static int minunused = 2; @@ -294,6 +309,7 @@ static char idleext[AST_MAX_EXTENSION]; static char idledial[AST_MAX_EXTENSION]; static int overlapdial = 0; +static int usercid = 0; static int facilityenable = 0; static char internationalprefix[10] = ""; static char nationalprefix[10] = ""; @@ -305,8 +321,6 @@ #ifdef PRI_GETSET_TIMERS static int pritimers[PRI_MAX_TIMERS]; #endif -static int pridebugfd = -1; -static char pridebugfilename[1024]=""; #endif /*! \brief Wait up to 16 seconds for first digit (FXO logic) */ @@ -327,10 +341,6 @@ static int ifcount = 0; -#ifdef ZAPATA_PRI -AST_MUTEX_DEFINE_STATIC(pridebugfdlock); -#endif - /*! \brief Whether we answer on a Polarity Switch event */ static int answeronpolarityswitch = 0; @@ -389,6 +399,18 @@ struct zt_pvt; +#ifdef ZAPATA_GSM +struct zt_gsm { + pthread_t master; + ast_mutex_t lock; /* Mutex */ + int fd; + int span; + struct gsm_modul *modul; + char pin[256]; + char exten[AST_MAX_EXTENSION]; /* Where to idle extra calls */ + struct zt_pvt *pvt; +}; +#endif #ifdef ZAPATA_R2 static int r2prot = -1; @@ -403,6 +425,28 @@ #define PRI_SPAN(p) (((p) >> 8) & 0xff) #define PRI_EXPLICIT(p) (((p) >> 16) & 0x01) +struct zt_suspended_call { + ast_mutex_t lock; /* Mutex */ + char msn[AST_MAX_EXTENSION]; /* the MSN to which this parked call belongs */ + char callid[10]; /* the callID provided by the user */ + int parked_at; /* extension in the call parking context */ + struct zt_suspended_call *next; +}; + +struct zt_holded_call { + ast_mutex_t lock; /* Mutex */ + char msn[AST_MAX_EXTENSION]; /* the MSN to which this parked call belongs */ + char uniqueid[AST_MAX_EXTENSION]; /* unique id of the onhold channel */ + int tei; + int cref; + int alreadyhungup; + struct ast_channel *channel; + struct ast_channel *bridge; + q931_call *call; /* this also covers tei mumbojumbo */ + struct zt_holded_call *next; +}; + + struct zt_pri { pthread_t master; /*!< Thread of master */ ast_mutex_t lock; /*!< Mutex */ @@ -416,6 +460,8 @@ int nsf; /*!< Network-Specific Facilities */ int dialplan; /*!< Dialing plan */ int localdialplan; /*!< Local dialing plan */ + char nocid[256]; + char withheldcid[256]; char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */ char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */ char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */ @@ -435,6 +481,7 @@ int fds[NUM_DCHANS]; /*!< FD's for d-channels */ int offset; int span; + int usercid; /* trust user provided callerid (callerani) ?? */ int resetting; int resetpos; time_t lastreset; /*!< time when unused channels were last reset */ @@ -442,6 +489,9 @@ struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */ struct zt_pvt *crvs; /*!< Member CRV structs */ struct zt_pvt *crvend; /*!< Pointer to end of CRV structs */ + struct zt_suspended_call *suspended_calls; /* Calls parked with SUSPEND messages */ + struct zt_holded_call *holded_calls; /* Calls on hold */ + int debugfd; }; @@ -561,6 +611,8 @@ unsigned int echocanbridged:1; unsigned int echocanon:1; unsigned int faxhandled:1; /*!< Has a fax tone already been handled? */ + /*!< KPJ: i will abuse this flag to implement a zapata option for dialing out + on a zap channel with EC to be off no matter what happens. */ unsigned int firstradio:1; unsigned int hanguponpolarityswitch:1; unsigned int hardwaredtmf:1; @@ -573,7 +625,8 @@ unsigned int overlapdial:1; unsigned int permcallwaiting:1; unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ - unsigned int priindication_oob:1; + unsigned int priindication_oob:2; + unsigned int pritransfer:2; unsigned int priexclusive:1; unsigned int pulse:1; unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */ @@ -612,6 +665,7 @@ #endif char cid_num[AST_MAX_EXTENSION]; int cid_ton; /*!< Type Of Number (TON) */ + int cid_pres; /*!< Calling Presentation */ char cid_name[AST_MAX_EXTENSION]; char lastcid_num[AST_MAX_EXTENSION]; char lastcid_name[AST_MAX_EXTENSION]; @@ -672,10 +726,15 @@ int polarityonanswerdelay; struct timeval polaritydelaytv; int sendcalleridafter; +#ifdef ZAPATA_GSM + struct zt_gsm gsm; +#endif #ifdef ZAPATA_PRI struct zt_pri *pri; struct zt_pvt *bearer; struct zt_pvt *realcall; + int tei; /* channel in use by this tei */ + q931_call *holdedcall; q931_call *call; int prioffset; int logicalspan; @@ -701,11 +760,14 @@ static int zt_indicate(struct ast_channel *chan, int condition); static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen); +static int zt_devicestate(void *data); +static void disable_dtmf_detect(struct zt_pvt *p); +static void enable_dtmf_detect(struct zt_pvt *p); static const struct ast_channel_tech zap_tech = { .type = type, .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW, + .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW, .requester = zt_request, .send_digit = zt_digit, .send_text = zt_sendtext, @@ -719,6 +781,7 @@ .indicate = zt_indicate, .fixup = zt_fixup, .setoption = zt_setoption, + .devicestate = zt_devicestate }; #ifdef ZAPATA_PRI @@ -730,6 +793,13 @@ struct zt_pvt *round_robin[32]; #ifdef ZAPATA_PRI +struct app_tmp { + char app[256]; + char data[256]; + struct ast_channel *chan; + pthread_t t; +}; + static inline int pri_grab(struct zt_pvt *pvt, struct zt_pri *pri) { int res; @@ -779,6 +849,112 @@ #define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */) #define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */) +static int zt_devicestate(void *data) +{ + int groupmatch = 0; + int channelmatch = 0; + struct zt_pvt *p; + char *dest=NULL; + int x,d; + char *s; + char opt=0; + int res, y=0; + struct zt_pvt *exit, *start, *end; + ast_mutex_t *lock; + +// ast_log(LOG_NOTICE, "data = %s\n", (char *)data); + return AST_DEVICE_UNKNOWN; + + /* Assume we're locking the iflock */ + lock = &iflock; + start = iflist; + end = ifend; + + if (data) { + dest = ast_strdupa((char *)data); + } else { + ast_log(LOG_WARNING, "Channel requested with no data\n"); + return AST_DEVICE_INVALID; + } + if (toupper(dest[0]) == 'G' || toupper(dest[0])=='R') { + /* Retrieve the group number */ + char *stringp=NULL; + stringp=dest + 1; + s = strsep(&stringp, "/"); + if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { + ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data); + return AST_DEVICE_INVALID; + } + groupmatch = 1 << x; + } else { + char *stringp=NULL; + stringp=dest; + s = strsep(&stringp, "/"); + p = iflist; + if (!strcasecmp(s, "pseudo")) { + /* Special case for pseudo */ + x = CHAN_PSEUDO; + channelmatch = x; + /* bail out */ + return AST_DEVICE_INVALID; + } + + else if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { + ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data); + return AST_DEVICE_INVALID; + } else { + channelmatch = x; + ast_log(LOG_NOTICE, "channelmatch = %d\n", channelmatch); + } + } + /* Search for an unowned channel */ + if (ast_mutex_lock(lock)) { + ast_log(LOG_ERROR, "Unable to lock interface list???\n"); + return AST_DEVICE_INVALID; + } + p = iflist; + exit = iflist; + res = AST_DEVICE_INVALID; /* start pessimistic */ + while(p) { + if (p) { + ast_mutex_lock(&p->lock); + if ((groupmatch && ((p->group & groupmatch) != 0)) || (channelmatch && (p->channel == channelmatch))) { +#ifdef ZAPATA_PRI + if (p->pri) { + for(d=0;dpri->dchanavail[d] & DCHAN_UP) { + res = AST_DEVICE_UNKNOWN; + } + } + } +#endif + if ((!ast_strlen_zero(p->cid_num) && (strncasecmp(p->cid_num, dest, strlen(p->cid_num)))) || (!ast_strlen_zero(p->dnid) && (strncasecmp(p->dnid, dest, strlen(p->dnid))))) { + res = AST_DEVICE_UNKNOWN; + if (p->owner) { + if ((p->owner->_state == AST_STATE_RINGING) && (p->outgoing)) { + res = AST_DEVICE_RINGING; + } + if (((p->owner->_state == AST_STATE_RINGING) && (!p->outgoing)) || (p->owner->_state == AST_STATE_UP) || (p->owner->_state == AST_STATE_DIALING) || (p->owner->_state == AST_STATE_RESERVED) || (p->owner->_state == AST_STATE_RING)){ + res = AST_DEVICE_INUSE; + } + } + if ((res == AST_DEVICE_INUSE) || (res == AST_DEVICE_RINGING)) { + /* stop searching now, one non-idle channel is sufficient */ + ast_mutex_unlock(&p->lock); + break; + } + } + } + ast_mutex_unlock(&p->lock); + } + p = p->next; + } + ast_mutex_unlock(lock); + + return res; + +} + static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok) { int res; @@ -1181,6 +1357,8 @@ return "GR-303 Signalling with FXOKS"; case SIG_GR303FXSKS: return "GR-303 Signalling with FXSKS"; + case SIG_GSM: + return "GSM Signalling"; case 0: return "Pseudo Signalling"; default: @@ -1381,12 +1559,16 @@ int res; if (!p) return; + if (p->faxhandled) { + ast_log(LOG_DEBUG, "Not enabling echo cancellation on a fax/modem call\n"); + return; + } if (p->echocanon) { ast_log(LOG_DEBUG, "Echo cancellation already on\n"); return; } if (p->digital) { - ast_log(LOG_DEBUG, "Echo cancellation isn't required on digital connection\n"); + ast_log(LOG_DEBUG, "Echo cancellation does not make any sense on digital connections!\n"); return; } if (p->echocancel) { @@ -1412,7 +1594,7 @@ { int x; int res; - if (p && p->echocancel && p->echotraining) { + if (p && p->echocancel && p->echotraining && (!p->digital) && (!p->faxhandled)) { x = p->echotraining; res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOTRAIN, &x); if (res) @@ -1592,7 +1774,7 @@ { int x, y, res; x = muted; - if (p->sig == SIG_PRI) { + if ((p->sig == SIG_PRI) || (p->sig == SIG_GSM)) { y = 1; res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &y); if (res) @@ -1774,7 +1956,12 @@ ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", p->channel); p->outgoing = 1; - set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law); + if (IS_DIGITAL(ast->transfercapability)) { + set_actual_gain(p->subs[SUB_REAL].zfd, 0, 0, 0, p->law); + } else { + set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law); + } + switch(p->sig) { case SIG_FXOLS: @@ -1998,6 +2185,26 @@ case SIG_PRI: /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */ p->dialdest[0] = '\0'; + disable_dtmf_detect(p); + break; + case SIG_GSM: +#ifdef ZAPATA_GSM + if (p->gsm.modul) { + c = strchr(dest, '/'); + if (c) + c++; + else + c = dest; + ast_mutex_lock(&p->gsm.lock); + if (gsm_dial(p->gsm.modul, p->use_callingpres ? ast->cid.cid_pres : 0, c)) { + ast_log(LOG_WARNING, "dialing failed on channel %d\n", p->channel); + ast_mutex_unlock(&p->gsm.lock); + ast_mutex_unlock(&p->lock); + return -1; + } + ast_mutex_unlock(&p->gsm.lock); + } +#endif break; default: ast_log(LOG_DEBUG, "not yet implemented\n"); @@ -2016,6 +2223,12 @@ int ldp_strip; int exclusive; + if ((p->pri->nodetype == BRI_NETWORK_PTMP) || (p->pri->nodetype == BRI_NETWORK)) { + // pass NO audio when ringing an isdn phone + p->dialing = 1; + // maybe we could allow passing audio when calling a p2p PBX, but well... ;-) + } + c = strchr(dest, '/'); if (c) c++; @@ -2033,6 +2246,7 @@ ast_mutex_unlock(&p->lock); return -1; } + strncpy(p->dnid, (c + p->stripmsd), sizeof(p->dnid)-1); if (p->sig != SIG_FXSKS) { p->dop.op = ZT_DIAL_OP_REPLACE; s = strchr(c + p->stripmsd, 'w'); @@ -2056,6 +2270,8 @@ pri_rel(p->pri); ast_mutex_unlock(&p->lock); return -1; + } else { + // ast_log(LOG_NOTICE, "call %d\n", p->call); } if (!(sr = pri_sr_new())) { ast_log(LOG_WARNING, "Failed to allocate setup request channel %d\n", p->channel); @@ -2085,7 +2301,7 @@ pri_sr_set_channel(sr, p->bearer ? PVT_TO_CHANNEL(p->bearer) : PVT_TO_CHANNEL(p), exclusive, 1); pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability, (p->digital ? -1 : - ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW))); + ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW)), ast->lowlayercompat); if (p->pri->facilityenable) pri_facility_enable(p->pri->pri); @@ -2286,8 +2502,10 @@ } if (newslot < 0) { newslot = 0; - ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n", + if (pri->nodetype != BRI_CPE_PTMP) { + ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n", pri->dchannels[newslot]); + } } if (old && (oldslot != newslot)) ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n", @@ -2343,8 +2561,7 @@ ast_log(LOG_DEBUG, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n", p->channel, index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd); - p->ignoredtmf = 0; - + if (index > -1) { /* Real channel, do some fixup */ p->subs[index].owner = NULL; @@ -2441,6 +2658,7 @@ if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) { + int outgoing = p->outgoing; p->owner = NULL; p->ringt = 0; p->distinctivering = 0; @@ -2477,19 +2695,61 @@ if (p->call && (!p->bearer || (p->bearer->call == p->call))) { if (!pri_grab(p, p->pri)) { if (p->alreadyhungup) { +/* char *aoc = pbx_builtin_getvar_helper(ast,"AOCEUNITS"); + int iaoc = aoc ? atoi(aoc) : -1; + char *aocpm = pbx_builtin_getvar_helper(ast,"AOCEUNITSPERMIN"); + int iaocpm = aocpm ? atoi(aocpm) : -1; + + if (iaocpm > -1) { + if (ast->cdr) { + long bill_sec = ast->cdr->billsec; + long bill_min = 0; + if (bill_sec > 0) { + bill_min = bill_sec / 60; + if (bill_min < 1) bill_min = 1; + } + iaoc = bill_min * iaocpm; + } else { + ast_log(LOG_NOTICE, "no cdr \n"); + } + } else { + ast_log(LOG_NOTICE, "iaocpm %d \n", iaocpm); + } +*/ ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n"); #ifdef SUPPORT_USERUSER pri_call_set_useruser(p->call, useruser); #endif - pri_hangup(p->pri->pri, p->call, -1); + pri_hangup(p->pri->pri, p->call, -1, -1); p->call = NULL; if (p->bearer) p->bearer->call = NULL; } else { char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); int icause = ast->hangupcause ? ast->hangupcause : -1; +/* char *aoc = pbx_builtin_getvar_helper(ast,"AOCEUNITS"); + int iaoc = aoc ? atoi(aoc) : -1; + char *aocpm = pbx_builtin_getvar_helper(ast,"AOCEUNITSPERMIN"); + int iaocpm = aocpm ? atoi(aocpm) : -1; + + if (iaocpm > -1) { + if (ast->cdr) { + long bill_sec = ast->cdr->billsec; + long bill_min = 0; + if (bill_sec > 0) { + bill_min = bill_sec / 60; + if (bill_min < 1) bill_min = 1; + } + iaoc = bill_min * iaocpm; + } else { + ast_log(LOG_NOTICE, "no cdr \n"); + } + } else { + ast_log(LOG_NOTICE, "iaocpm %d \n", iaocpm); + } +*/ ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); #ifdef SUPPORT_USERUSER @@ -2503,7 +2763,28 @@ if (atoi(cause)) icause = atoi(cause); } - pri_hangup(p->pri->pri, p->call, icause); + + pri_hangup(p->pri->pri, p->call, icause, -1); + + /* if we send a release complete we wont ge no hangup event, so clear the call here */ + if (icause == 34 || icause == 44 || icause == 82 || icause == 1 || icause == 81 || icause == 17) { + if ((ast->_state == AST_STATE_RING) || (ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING)) { + p->call = NULL; + } else { + ast_log(LOG_ERROR, "What is wrong with you? You cannot use cause %d number when in state %d!\n", icause, ast->_state); + icause = 16; /* Note, in pri_hangup() libpri will already override the cause */ + } + } + + if (p->pri->nodetype == BRI_NETWORK_PTMP) { + if ((icause == 16 || icause == -1) && (ast->_state != AST_STATE_UP)) { + if (outgoing) { + p->call = NULL; + } + } + } + + } if (res < 0) ast_log(LOG_WARNING, "pri_disconnect failed\n"); @@ -2531,7 +2812,13 @@ } #endif - if (p->sig && (p->sig != SIG_PRI) && (p->sig != SIG_R2)) +#ifdef ZAPATA_GSM + if (p->gsm.modul) { + if (!p->alreadyhungup) + gsm_hangup(p->gsm.modul); + } +#endif + if (p->sig && (p->sig != SIG_PRI) && (p->sig != SIG_R2) && (p->sig != SIG_GSM)) res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK); if (res < 0) { ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name); @@ -2700,10 +2987,14 @@ p->proceeding = 1; res = pri_answer(p->pri->pri, p->call, 0, !p->digital); pri_rel(p->pri); + /* stop ignoring inband dtmf */ + enable_dtmf_detect(p); } else { ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); res= -1; } + /* the audio path is complete now, train the echo canceler */ + zt_train_ec(p); break; #endif #ifdef ZAPATA_R2 @@ -2713,6 +3004,13 @@ ast_log(LOG_WARNING, "R2 Answer call failed :( on %s\n", ast->name); break; #endif +#ifdef ZAPATA_GSM + case SIG_GSM: + if (p->gsm.modul) { + gsm_answer(p->gsm.modul); + } + break; +#endif case 0: ast_mutex_unlock(&p->lock); return 0; @@ -3273,6 +3571,15 @@ { struct zt_pvt *p = newchan->tech_pvt; int x; + if (newchan && newchan->tech_pvt) { + p = newchan->tech_pvt; + } + if (!p) { + if (newchan) { + ast_log(LOG_ERROR, "channel %s has no tech_pvt structure\n", newchan->name); + } + return 0; + } ast_mutex_lock(&p->lock); ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name); if (p->owner == oldchan) { @@ -3633,7 +3940,7 @@ if (p->call) { if (p->pri && p->pri->pri) { if (!pri_grab(p, p->pri)) { - pri_hangup(p->pri->pri, p->call, -1); + pri_hangup(p->pri->pri, p->call, -1, -1); pri_destroycall(p->pri->pri, p->call); p->call = NULL; pri_rel(p->pri); @@ -4596,7 +4903,7 @@ p->subs[index].f.data = NULL; p->subs[index].f.datalen= 0; } - if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) { + if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) { /* Perform busy detection. etc on the zap line */ f = ast_dsp_process(ast, p->dsp, &p->subs[index].f); if (f) { @@ -4608,8 +4915,9 @@ } } else if (f->frametype == AST_FRAME_DTMF) { #ifdef ZAPATA_PRI - if (!p->proceeding && p->sig==SIG_PRI && p->pri && p->pri->overlapdial) { - /* Don't accept in-band DTMF when in overlap dial mode */ + if (p->sig==SIG_PRI && p->pri && p->pri->overlapdial && p->ignoredtmf) { + /* Don't accept in-band DTMF when in overlap dial mode + or when in non-overlap overlapdialing mode ... */ f->frametype = AST_FRAME_NULL; f->subclass = 0; } @@ -4657,8 +4965,10 @@ pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); if (ast_async_goto(ast, target_context, "fax", 1)) ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); - } else - ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Fax detected, but no fax extension\n"); + } } else ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); } else @@ -4744,7 +5054,9 @@ #endif /* Write a frame of (presumably voice) data */ if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype != AST_FRAME_IMAGE) + if (frame->frametype == AST_FRAME_TEXT) { + ast_log(LOG_NOTICE, "text\n"); + } else if (frame->frametype != AST_FRAME_IMAGE) ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); return 0; } @@ -4815,7 +5127,7 @@ switch(condition) { case AST_CONTROL_BUSY: #ifdef ZAPATA_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { + if ((p->priindication_oob == 1) && p->sig == SIG_PRI) { chan->hangupcause = AST_CAUSE_USER_BUSY; chan->_softhangup |= AST_SOFTHANGUP_DEV; res = 0; @@ -4897,7 +5209,7 @@ case AST_CONTROL_CONGESTION: chan->hangupcause = AST_CAUSE_CONGESTION; #ifdef ZAPATA_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { + if ((p->priindication_oob == 1) && p->sig == SIG_PRI) { chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION; chan->_softhangup |= AST_SOFTHANGUP_DEV; res = 0; @@ -5082,8 +5394,12 @@ if (state == AST_STATE_RING) tmp->rings = 1; tmp->tech_pvt = i; - if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) { - /* Only FXO signalled stuff can be picked up */ +#ifdef ZAPATA_PRI + if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS) || (i->sig == SIG_PRI)) { +#else + if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) { +#endif + /* Only FXO signalled stuff can be picked up */ /* i dont think so, mr. ulaw! we alaws like to pick up BRIs/PRIs */ tmp->callgroup = i->callgroup; tmp->pickupgroup = i->pickupgroup; } @@ -5213,6 +5529,7 @@ int len = 0; int res; int index; + int network; if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s'\n", chan->name); index = zt_get_index(chan, p, 1); @@ -5231,10 +5548,17 @@ len = strlen(exten); res = 0; while((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { - if (len && !ast_ignore_pattern(chan->context, exten)) + if (len && !ast_ignore_pattern(chan->context, exten)) { tone_zone_play_tone(p->subs[index].zfd, -1); - else - tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE); + } else { + network = p->pri->nodetype == PRI_NETWORK || p->pri->nodetype == BRI_NETWORK || p->pri->nodetype == BRI_NETWORK_PTMP; + if (network) { + tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE); + } else { + /* cpe be quiet */ + tone_zone_play_tone(p->subs[index].zfd, -1); + } + } if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) timeout = matchdigittimeout; else @@ -6327,18 +6651,44 @@ break; case ZT_EVENT_NOALARM: i->inalarm = 0; +#ifdef ZAPATA_PRI + if (i->pri) { + if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE_PTMP)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + } else { + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", i->channel); + } + } +#else ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", "Channel: %d\r\n", i->channel); +#endif break; case ZT_EVENT_ALARM: i->inalarm = 1; res = get_alarms(i); +#ifdef ZAPATA_PRI + if (i->pri) { + if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE_PTMP)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + } else { + ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", i->channel, alarm2str(res)); + manager_event(EVENT_FLAG_SYSTEM, "Alarm", + "Alarm: %s\r\n" + "Channel: %d\r\n", + alarm2str(res), i->channel); + } + } +#else ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", i->channel, alarm2str(res)); manager_event(EVENT_FLAG_SYSTEM, "Alarm", "Alarm: %s\r\n" "Channel: %d\r\n", alarm2str(res), i->channel); +#endif /* fall thru intentionally */ case ZT_EVENT_ONHOOK: if (i->radio) break; @@ -6378,8 +6728,10 @@ zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK); break; case SIG_PRI: - zt_disable_ec(i); - res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1); + if (event != ZT_EVENT_ALARM) { + zt_disable_ec(i); + res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1); + } break; default: ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel); @@ -6702,6 +7054,8 @@ } else { if (si->totalchans == 31) { /* if it's an E1 */ pris[*span].dchannels[0] = 16 + offset; + } else if (si->totalchans == 3) { /* if it's an S0 ZAPBRI */ + pris[*span].dchannels[0] = 3 + offset; } else { pris[*span].dchannels[0] = 24 + offset; } @@ -6789,6 +7143,10 @@ #endif +#ifdef ZAPATA_GSM +static void *gsm_dchannel(void *vgsm); +#endif + static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_pri *pri, int reloading) { /* Make a zt_pvt structure for this interface (or CRV if "pri" is specified) */ @@ -6947,6 +7305,11 @@ destroy_zt_pvt(&tmp); return NULL; } + if ((pris[span].localdialplan) && (pris[span].localdialplan != localdialplan)) { + ast_log(LOG_ERROR, "Span %d is already a %s local dialing plan\n", span + 1, dialplan2str(pris[span].localdialplan)); + destroy_zt_pvt(&tmp); + return NULL; + } if (!ast_strlen_zero(pris[span].idledial) && strcmp(pris[span].idledial, idledial)) { ast_log(LOG_ERROR, "Span %d already has idledial '%s'.\n", span + 1, idledial); destroy_zt_pvt(&tmp); @@ -6974,6 +7337,17 @@ return NULL; } pris[span].nodetype = pritype; +// XXX + if (pritype == BRI_NETWORK_PTMP) { + pris[span].dchanavail[0] = DCHAN_AVAILABLE; + pri_find_dchan(&pris[span]); + } +// XXX tuev + +// if ((pritype == BRI_CPE) || (pritype == BRI_CPE_PTMP)) { +// pris[span].dchanavail[0] = DCHAN_AVAILABLE; +// pri_find_dchan(&pris[span]); +// } pris[span].switchtype = myswitchtype; pris[span].nsf = nsf; pris[span].dialplan = dialplan; @@ -6982,9 +7356,14 @@ pris[span].minunused = minunused; pris[span].minidle = minidle; pris[span].overlapdial = overlapdial; + pris[span].usercid = usercid; + pris[span].suspended_calls = NULL; + pris[span].holded_calls = NULL; pris[span].facilityenable = facilityenable; ast_copy_string(pris[span].idledial, idledial, sizeof(pris[span].idledial)); ast_copy_string(pris[span].idleext, idleext, sizeof(pris[span].idleext)); + ast_copy_string(pris[span].nocid, nocid, sizeof(pris[span].nocid) - 1); + ast_copy_string(pris[span].withheldcid, withheldcid, sizeof(pris[span].withheldcid) - 1); ast_copy_string(pris[span].internationalprefix, internationalprefix, sizeof(pris[span].internationalprefix)); ast_copy_string(pris[span].nationalprefix, nationalprefix, sizeof(pris[span].nationalprefix)); ast_copy_string(pris[span].localprefix, localprefix, sizeof(pris[span].localprefix)); @@ -7005,6 +7384,36 @@ tmp->prioffset = 0; } #endif +#ifdef ZAPATA_GSM + if (signalling == SIG_GSM) { + struct zt_bufferinfo bi; + ast_mutex_init(&tmp->gsm.lock); + strncpy(tmp->gsm.pin, gsm_modem_pin, sizeof(tmp->gsm.pin) - 1); + strncpy(tmp->gsm.exten, gsm_modem_exten, sizeof(tmp->gsm.exten) - 1); + snprintf(fn, sizeof(fn), "%d", channel + 1); + /* Open non-blocking */ + tmp->gsm.fd = zt_open(fn); + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 16; + bi.bufsize = 1024; + if (ioctl(tmp->gsm.fd, ZT_SET_BUFINFO, &bi)) { + ast_log(LOG_ERROR, "Unable to set buffer info on channel '%s': %s\n", fn, strerror(errno)); + return NULL; + } + tmp->gsm.pvt = tmp; + tmp->gsm.span = tmp->span; + tmp->gsm.modul = gsm_new(tmp->gsm.fd, 0, tmp->gsm.pin, tmp->span, tmp->channel); + if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, tmp->channel)) { + ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d: %s\n", tmp->channel, strerror(errno)); + destroy_zt_pvt(&tmp); + return NULL; + } + if (ast_pthread_create(&tmp->gsm.master, NULL, gsm_dchannel, &tmp->gsm)) { + zt_close(tmp->gsm.fd); + } + } +#endif #ifdef ZAPATA_R2 if (signalling == SIG_R2) { if (r2prot < 0) { @@ -7138,6 +7547,7 @@ tmp->restrictcid = restrictcid; tmp->use_callingpres = use_callingpres; tmp->priindication_oob = priindication_oob; + tmp->pritransfer = pritransfer; tmp->priexclusive = cur_priexclusive; if (tmp->usedistinctiveringdetection) { if (!tmp->use_callerid) { @@ -7411,7 +7821,7 @@ break; if (!backwards && (x >= pri->numchans)) break; - if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) { + if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner && !pri->pvts[x]->call) { ast_log(LOG_DEBUG, "Found empty available channel %d/%d\n", pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset); return x; @@ -7458,7 +7868,7 @@ end = ifend; /* We do signed linear */ oldformat = format; - format &= (AST_FORMAT_SLINEAR | AST_FORMAT_ULAW); + format &= (AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW); if (!format) { ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat); return NULL; @@ -7618,6 +8028,11 @@ p->digital = 1; if (tmp) tmp->transfercapability = AST_TRANS_CAP_DIGITAL; + } else if (opt == 'm') { + /* If this is a modem/fax call, pretend to have the fax handled and dont do EC */ + p->faxhandled = 1; + if (tmp) + tmp->transfercapability = AST_TRANS_CAP_3_1K_AUDIO; } else { ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data); } @@ -7651,12 +8066,174 @@ *cause = AST_CAUSE_BUSY; } else if (groupmatched) { *cause = AST_CAUSE_CONGESTION; + } else { + *cause = AST_CAUSE_CONGESTION; } } return tmp; } +#ifdef ZAPATA_GSM +static void handle_gsm_event(struct zt_gsm *gsm, gsm_event *e) +{ + struct ast_channel *c = NULL; + int law = ZT_LAW_ALAW; + int res = 0; + + switch(e->e) { + case GSM_EVENT_DCHAN_UP: + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d registered to network!\n", gsm->span); + break; + case GSM_EVENT_DCHAN_DOWN: + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d unregistered from network!\n", gsm->span); + break; + case GSM_EVENT_RING: + ast_mutex_lock(&gsm->pvt->lock); + if (!ast_strlen_zero(e->ring.callingnum)) { + strncpy(gsm->pvt->cid_num, e->ring.callingnum, sizeof(gsm->pvt->cid_num) - 1); + } else { + strncpy(gsm->pvt->cid_name, withheldcid, sizeof(gsm->pvt->cid_name)); + } + if (!ast_strlen_zero(gsm->exten)) { + strncpy(gsm->pvt->exten, gsm->exten, sizeof(gsm->pvt->exten) - 1); + } else { + gsm->pvt->exten[0] = 's'; + gsm->pvt->exten[1] = '\0'; + } + c = zt_new(gsm->pvt, AST_STATE_RING, 1, SUB_REAL, ZT_LAW_ALAW, AST_TRANS_CAP_SPEECH); + if (c) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Ring on channel %d (from %s to %s)\n", e->ring.channel, e->ring.callingnum, gsm->exten); + gsm->pvt->owner = c; + if (ioctl(gsm->pvt->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", gsm->pvt->channel, law); + res = zt_setlaw(gsm->pvt->subs[SUB_REAL].zfd, law); + res = set_actual_gain(gsm->pvt->subs[SUB_REAL].zfd, 0, gsm->pvt->rxgain, gsm->pvt->txgain, law); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", gsm->pvt->channel); +// } else { +// ast_log(LOG_NOTICE, "tx gain %f rx gain %f law %d pvt->law %d\n", gsm->pvt->txgain, gsm->pvt->rxgain, law, gsm->pvt->law); + } + } + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_HANGUP: + ast_verbose(VERBOSE_PREFIX_3 "Got hang up on channel %d\n", e->hangup.channel); + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->alreadyhungup = 1; + if (gsm->pvt->owner) { + gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_ALERTING: + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->subs[SUB_REAL].needringing =1; + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_ANSWER: + ast_mutex_lock(&gsm->pvt->lock); + gsm->pvt->dialing = 0; + gsm->pvt->subs[SUB_REAL].needanswer =1; + gsm->pvt->ignoredtmf = 0; + ast_mutex_unlock(&gsm->pvt->lock); + break; + case GSM_EVENT_PIN_REQUIRED: + gsm_send_pin(gsm->modul, gsm->pin); + break; + case GSM_EVENT_SM_RECEIVED: + ast_verbose(VERBOSE_PREFIX_3 "SMS received on span %d. PDU: %s\n", gsm->span, e->sm_received.pdu); + break; + default: + ast_log(LOG_WARNING,"!! Unknown GSM event %d !!\n", e->e); + } +} + +static void *gsm_dchannel(void *vgsm) +{ + struct zt_gsm *gsm = vgsm; + gsm_event *e; + struct timeval tv = {0,0}, *next; + fd_set rfds, efds; + int res,x; + + if (!gsm) return NULL; + + if (!gsm->modul) { + fprintf(stderr, "No gsm_mod\n"); + return NULL; + } + gsm_set_debug(gsm->modul, GSM_DEBUG_NONE); + for (;;) { + + /* Run the D-Channel */ + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(gsm->fd, &rfds); + FD_SET(gsm->fd, &efds); + + if ((next = gsm_schedule_next(gsm->modul))) { + gettimeofday(&tv, NULL); + tv.tv_sec = next->tv_sec - tv.tv_sec; + tv.tv_usec = next->tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + if (tv.tv_sec < 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + res = select(gsm->fd + 1, &rfds, NULL, &efds, next ? &tv : NULL); + e = NULL; + + ast_mutex_lock(&gsm->lock); + if (!res) { + e = gsm_schedule_run(gsm->modul); + } else if (res > 0) { + e = gsm_check_event(gsm->modul, 1); + } else if (errno == ELAST) { + res = ioctl(gsm->fd, ZT_GETEVENT, &x); + printf("Got Zaptel event: %d\n", x); + } else if (errno != EINTR) + fprintf(stderr, "Error (%d) on select: %s\n", ELAST, strerror(errno)); + + if (!e) { + e = gsm_check_event(gsm->modul, 0); + } + + if (e) { + handle_gsm_event(gsm, e); + } + ast_mutex_unlock(&gsm->lock); + + res = ioctl(gsm->fd, ZT_GETEVENT, &x); + + if (!res && x) { + switch (x) { + case ZT_EVENT_NOALARM: + ast_log(LOG_NOTICE, "Alarm cleared on span %d\n", gsm->span); + usleep(1000); + gsm_restart(gsm->modul); + break; + case ZT_EVENT_ALARM: + ast_log(LOG_NOTICE, "Alarm detected on span %d\n", gsm->span); + break; + default: + fprintf(stderr, "Got event on GSM interface: %d\n", x); + } + } + + + } + return NULL; +} + +#endif #ifdef ZAPATA_PRI static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv) @@ -7671,6 +8248,57 @@ return NULL; } +static int pri_find_tei(struct zt_pri *pri, q931_call *c, int tei) +{ + int x=0; + for (x=0;xnumchans;x++) { + if (!pri->pvts[x]) continue; + if ((pri->pvts[x]->tei == tei) && (pri->pvts[x]-> call != c)) { + return x; + } + } + return -1; +} + +static struct zt_holded_call *pri_get_callonhold(struct zt_pri *pri, int cref, int tei) { + struct zt_holded_call *zhc = pri->holded_calls; + struct zt_holded_call *zhctemp = NULL; + + while (zhc) { + if ((zhc->tei == tei) && ((zhc->cref == cref) || (cref == -1))) { + return zhc; + } + zhctemp = zhc; + if (zhc) zhc = zhc->next; + } + return NULL; +} + +static int pri_destroy_callonhold(struct zt_pri *pri, struct zt_holded_call *onhold) { + struct zt_holded_call *zhc = pri->holded_calls; + struct zt_holded_call *zhctemp = NULL; + + while (zhc) { + if (zhc == onhold) { + if (zhctemp) { + zhctemp->next = zhc->next; + zhc = zhctemp; + } else { + pri->holded_calls = zhc->next; + zhc = pri->holded_calls; + zhctemp = NULL; + } + } + zhctemp = zhc; + if (zhc) zhc = zhc->next; + } + if (onhold) { + free(onhold); + onhold = NULL; + return 1; + } + return 0; +} static int pri_find_principle(struct zt_pri *pri, int channel) { @@ -7703,7 +8331,9 @@ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c) { int x; + int res = 0; struct zt_pvt *crv; + char tmpname[256]; if (!c) { if (principle < 0) return -1; @@ -7717,6 +8347,7 @@ /* First, check for other bearers */ for (x=0;xnumchans;x++) { if (!pri->pvts[x]) continue; +// ast_log(LOG_NOTICE, "principle %d channel %d call %d channel[x]->call %d\n",principle, x, c, pri->pvts[x]->call); if (pri->pvts[x]->call == c) { /* Found our call */ if (principle != x) { @@ -7730,19 +8361,56 @@ } /* Fix it all up now */ pri->pvts[principle]->owner = pri->pvts[x]->owner; + pri->pvts[principle]->outgoing = pri->pvts[x]->outgoing; if (pri->pvts[principle]->owner) { snprintf(pri->pvts[principle]->owner->name, sizeof(pri->pvts[principle]->owner->name), "Zap/%d:%d-%d", pri->trunkgroup, pri->pvts[principle]->channel, 1); pri->pvts[principle]->owner->tech_pvt = pri->pvts[principle]; pri->pvts[principle]->owner->fds[0] = pri->pvts[principle]->subs[SUB_REAL].zfd; pri->pvts[principle]->subs[SUB_REAL].owner = pri->pvts[x]->subs[SUB_REAL].owner; - } else + } else { ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", pri->pvts[x]->channel, pri->pvts[principle]->channel); + } pri->pvts[principle]->call = pri->pvts[x]->call; + pri->pvts[principle]->dsp = pri->pvts[x]->dsp; + pri->pvts[principle]->alreadyhungup = pri->pvts[x]->alreadyhungup; + pri->pvts[principle]->digital = pri->pvts[x]->digital; + pri->pvts[principle]->faxhandled = pri->pvts[x]->faxhandled; + + if ((pri->nodetype == BRI_CPE_PTMP) || (pri->nodetype == BRI_CPE)) { + /* this might also apply for other pri types! */ + pri->pvts[principle]->law = pri->pvts[x]->law; + if (ioctl(pri->pvts[principle]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &pri->pvts[principle]->law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", pri->pvts[principle]->channel, pri->pvts[principle]->law); + res = zt_setlaw(pri->pvts[principle]->subs[SUB_REAL].zfd, pri->pvts[principle]->law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[principle]->channel); + if (!pri->pvts[principle]->digital) { + res = set_actual_gain(pri->pvts[principle]->subs[SUB_REAL].zfd, 0, pri->pvts[principle]->rxgain, pri->pvts[principle]->txgain, pri->pvts[principle]->law); + } else { + res = set_actual_gain(pri->pvts[principle]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[principle]->law); + } + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[principle]->channel); + zt_confmute(pri->pvts[x], 0); + update_conf(pri->pvts[x]); + reset_conf(pri->pvts[x]); + restore_gains(pri->pvts[x]); + zt_disable_ec(pri->pvts[x]); + zt_setlinear(pri->pvts[x]->subs[SUB_REAL].zfd, 0); + } + + if (pri->pvts[principle]->owner) { + snprintf(tmpname, sizeof(tmpname), "Zap/%d-1", pri->pvts[principle]->channel); + ast_change_name(pri->pvts[principle]->owner, tmpname); + } + + /* Free up the old channel, now not in use */ pri->pvts[x]->subs[SUB_REAL].owner = NULL; pri->pvts[x]->owner = NULL; pri->pvts[x]->call = NULL; + pri->pvts[x]->dsp = NULL; } return principle; } @@ -7771,7 +8439,9 @@ } crv = crv->next; } - ast_log(LOG_WARNING, "Call specified, but not found?\n"); + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + ast_log(LOG_WARNING, "Call specified, but not found?\n"); + } return -1; } @@ -7830,86 +8500,33 @@ #ifndef PRI_RESTART #error "Upgrade your libpri" #endif -static void zt_pri_message(struct pri *pri, char *s) +static void zt_pri_message(char *s, int span) { - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; - - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s); - else - ast_verbose("%s", s); - } else - ast_verbose("PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_verbose("%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - write(pridebugfd, s, strlen(s)); - - ast_mutex_unlock(&pridebugfdlock); + ast_verbose("%d %s", span, s); } -static void zt_pri_error(struct pri *pri, char *s) +static void zt_pri_error(char *s, int span) { - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; + ast_log(LOG_WARNING, "%d %s", span, s); +} - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_log(LOG_WARNING, "[Span %d D-Channel %d] PRI: %s", span, dchan, s); - else - ast_verbose("%s", s); - } else - ast_verbose("PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_log(LOG_WARNING, "%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - write(pridebugfd, s, strlen(s)); +#ifdef ZAPATA_GSM +static void zt_gsm_message(char *s, int channel) +{ + ast_verbose("GSM %d: %s", channel, s); +} - ast_mutex_unlock(&pridebugfdlock); +static void zt_gsm_error(char *s, int channel) +{ + ast_log(LOG_WARNING, "GSM %d: %s", channel, s); } +#endif static int pri_check_restart(struct zt_pri *pri) { + if ((pri->nodetype != PRI_NETWORK) && (pri->nodetype != PRI_CPE)) { + return 0; + } do { pri->resetpos++; } while((pri->resetpos < pri->numchans) && @@ -7992,6 +8609,32 @@ } } +static void pri_make_callerid(struct zt_pri *pri, char *callerid, int callerid_len, char *callingnum, int callingnum_len, int callingplan, int callingpres, int stripmsd) { + if (callingnum && (callingnum_len > stripmsd)) { + callingnum += stripmsd; + } + switch (callingplan) { + case PRI_INTERNATIONAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->internationalprefix, callingnum); + break; + case PRI_NATIONAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->nationalprefix, callingnum); + break; + case PRI_LOCAL_ISDN: + snprintf(callerid, callerid_len, "%s%s", pri->localprefix, callingnum); + break; + case PRI_PRIVATE: + snprintf(callerid, callerid_len, "%s%s", pri->privateprefix, callingnum); + break; + case PRI_UNKNOWN: + snprintf(callerid, callerid_len, "%s%s", pri->unknownprefix, callingnum); + break; + default: + snprintf(callerid, callerid_len, "%s", callingnum); + break; + } +} + static void *pri_dchannel(void *vpri) { struct zt_pri *pri = vpri; @@ -8172,15 +8815,44 @@ /* Check for an event */ x = 0; res = ioctl(pri->fds[which], ZT_GETEVENT, &x); - if (x) + if ((pri->nodetype != BRI_CPE) && (pri->nodetype != BRI_CPE_PTMP)) { + /* dont annoy BRI TE mode users with layer2layer alarms */ + if (x) ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span); + } /* Keep track of alarm state */ if (x == ZT_EVENT_ALARM) { pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP); pri_find_dchan(pri); + if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) { + if (pri->pri) { + for (i=0; inumchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { + if (p->call) { + if (p->pri && p->pri->pri) { + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + p->tei = -1; + } else + ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); + } + if (p->owner) + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->inalarm = 1; + } + } + pri_shutdown(pri->pri); + } + } } else if (x == ZT_EVENT_NOALARM) { - pri->dchanavail[which] |= DCHAN_NOTINALARM; - pri_restart(pri->dchans[which]); + if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) { + pri->dchanavail[which] |= DCHAN_NOTINALARM; + // pri->dchanavail[which] |= DCHAN_UP; + } else { + pri->dchanavail[which] |= DCHAN_NOTINALARM; + pri_restart(pri->dchans[which]); + } } if (option_debug) @@ -8192,8 +8864,7 @@ break; } } else if (errno != EINTR) - ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno)); - + ast_log(LOG_WARNING, "pri_event returned error %d (%s) on span %d\n", errno, strerror(errno), pri->span); if (e) { if (pri->debug) pri_dump_event(pri->dchans[which], e); @@ -8201,32 +8872,102 @@ pri->dchanavail[which] |= DCHAN_UP; switch(e->e) { case PRI_EVENT_DCHAN_UP: - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); - pri->dchanavail[which] |= DCHAN_UP; - if (!pri->pri) pri_find_dchan(pri); - - /* Note presense of D-channel */ - time(&pri->lastreset); - - /* Restart in 5 seconds */ - if (pri->resetinterval > -1) { - pri->lastreset -= pri->resetinterval; - pri->lastreset += 5; - } - pri->resetting = 0; - /* Take the channels from inalarm condition */ - for (i=0; inumchans; i++) - if (pri->pvts[i]) { - pri->pvts[i]->inalarm = 0; - } + if (pri->nodetype == BRI_NETWORK_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up for TEI %d\n", pri_order(which), pri->span, e->gen.tei); + pri->dchanavail[which] |= (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP); + pri_find_dchan(pri); + + /* Note presense of D-channel */ + time(&pri->lastreset); + + pri->resetting = 0; + /* Take the channels from inalarm condition */ + for (i=0; inumchans; i++) + if (pri->pvts[i]) { + pri->pvts[i]->inalarm = 0; + } + } else { + if (pri->nodetype == BRI_CPE_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); + } else { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); + } + pri->dchanavail[which] |= (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP); + pri_find_dchan(pri); + + /* Note presense of D-channel */ + time(&pri->lastreset); + + /* Restart in 5 seconds */ + pri->lastreset -= pri->resetinterval; + pri->lastreset += 5; + pri->resetting = 0; + /* Take the channels from inalarm condition */ + for (i=0; inumchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { + p->inalarm = 0; + /* hang up calls that are not bridged yet, dont touch bridged calls */ + if (p->call) { + if (p->pri && p->pri->pri) { + if (p->owner) { + if (p->owner->_state != AST_STATE_UP) { + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + } + } else { + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + } + } + } + } + } + } break; case PRI_EVENT_DCHAN_DOWN: - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); - pri->dchanavail[which] &= ~DCHAN_UP; - pri_find_dchan(pri); - if (!pri_is_up(pri)) { + if (pri->nodetype == BRI_NETWORK_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down for TEI %d\n", pri_order(which), pri->span, e->gen.tei); + // PTMP BRIs have N dchans, handled by libpri + if (e->gen.tei == 0) break; + /* Hangup active channels */ + for (i=0; inumchans; i++) { + struct zt_pvt *p = pri->pvts[i]; + if (p) { + // ast_log(LOG_NOTICE, "chan %d tei %d\n",i,p->tei); + if (p->tei == e->gen.tei) { + if (p->call) { + if (p->pri && p->pri->pri) { + // pri_hangup(p->pri->pri, p->call, -1); + pri_destroycall(p->pri->pri, p->call); + p->tei = -1; + p->call = NULL; + } else + ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); + } + if (p->owner) + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->inalarm = 1; + p->tei = -1; + } + } + } + } else { + if (pri->nodetype == BRI_CPE_PTMP) { + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); + } else { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); + } + pri->dchanavail[which] &= ~DCHAN_UP; + pri_find_dchan(pri); + if (!pri_is_up(pri)) { pri->resetting = 0; /* Hangup active channels and put them in alarm mode */ for (i=0; inumchans; i++) { @@ -8234,19 +8975,29 @@ if (p) { if (p->call) { if (p->pri && p->pri->pri) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; + if (p->owner) { + if (p->owner->_state != AST_STATE_UP) { + // pri_hangup(p->pri->pri, p->call, -1); + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->inalarm = 1; + } + } else { + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + p->inalarm = 1; + } } else ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); } if (p->realcall) { - pri_hangup_all(p->realcall, pri); - } else if (p->owner) - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; - p->inalarm = 1; + pri_hangup_all(p->realcall, pri); + p->inalarm = 1; + } } } + } } break; case PRI_EVENT_RESTART: @@ -8281,8 +9032,8 @@ pri_destroycall(pri->pri, pri->pvts[x]->call); pri->pvts[x]->call = NULL; } - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); + if (pri->pvts[x]->realcall) + pri_hangup_all(pri->pvts[x]->realcall, pri); else if (pri->pvts[x]->owner) pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV; ast_mutex_unlock(&pri->pvts[x]->lock); @@ -8316,7 +9067,6 @@ } } break; - case PRI_EVENT_INFO_RECEIVED: chanpos = pri_find_principle(pri, e->ring.channel); if (chanpos < 0) { @@ -8325,9 +9075,11 @@ } else { chanpos = pri_fixup_principle(pri, chanpos, e->ring.call); if (chanpos > -1) { +// ast_log(LOG_NOTICE, "INFO received on channel %d/%d span %d\n", +// PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); ast_mutex_lock(&pri->pvts[chanpos]->lock); /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */ - if (pri->overlapdial && pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) { + if (pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) { /* how to do that */ int digitlen = strlen(e->ring.callednum); char digit; @@ -8339,6 +9091,14 @@ zap_queue_frame(pri->pvts[chanpos], &f, pri); } } + if (!pri->overlapdial) { + strncat(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten)); + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } } ast_mutex_unlock(&pri->pvts[chanpos]->lock); } @@ -8346,39 +9106,58 @@ break; case PRI_EVENT_RING: crv = NULL; - if (e->ring.channel == -1) + if (e->ring.channel == -1) { + /* if no channel specified find one empty */ chanpos = pri_find_empty_chan(pri, 1); - else + } else { chanpos = pri_find_principle(pri, e->ring.channel); - /* if no channel specified find one empty */ + } if (chanpos < 0) { - ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); + /* no channel specified and no free channel. this is a callwating SETUP */ + if (e->ring.channel == -1) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Ignoring callwaiting SETUP on channel %d/%d span %d %d\n", PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span, e->ring.channel); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_USER_BUSY, -1); + break; + } } else { + /* ok, we got a b channel for this call, lock it */ ast_mutex_lock(&pri->pvts[chanpos]->lock); if (pri->pvts[chanpos]->owner) { - if (pri->pvts[chanpos]->call == e->ring.call) { - ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - break; - } else { - ast_log(LOG_WARNING, "Ring requested on channel %d/%d already in use on span %d. Hanging up owner.\n", + /* safety check, for messed up retransmissions? */ + if (pri->pvts[chanpos]->call == e->ring.call) { + ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - chanpos = -1; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + chanpos = -1; + break; + } else { + ast_log(LOG_WARNING, "Ring requested on channel %d/%d already in use on span %d. Hanging up owner.\n", + PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); + if (pri->pvts[chanpos]->realcall) { + pri_hangup_all(pri->pvts[chanpos]->realcall, pri); + } else { + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + /* XXX destroy the call here, so we can accept the retransmission as a new call */ + pri_destroycall(pri->pri, e->ring.call); } - } - if (chanpos > -1) ast_mutex_unlock(&pri->pvts[chanpos]->lock); + chanpos = -1; + break; + } + } + if (chanpos > -1) { + /* everything is ok with the b channel */ + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } } - if ((chanpos < 0) && (e->ring.flexible)) - chanpos = pri_find_empty_chan(pri, 1); + /* actually, we already got a valid channel by now */ if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* dont detect dtmfs before the signalling is done */ + disable_dtmf_detect(pri->pvts[chanpos]); + /* this channel is owned by this TEI */ + pri->pvts[chanpos]->tei = e->ring.tei; if (pri->switchtype == PRI_SWITCH_GR303_TMC) { /* Should be safe to lock CRV AFAIK while bearer is still locked */ crv = pri_find_crv(pri, pri_get_crv(pri->pri, e->ring.call, NULL)); @@ -8392,13 +9171,14 @@ ast_log(LOG_WARNING, "Call received for busy CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); } else ast_log(LOG_NOTICE, "Call received for unconfigured CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE, -1); if (crv) ast_mutex_unlock(&crv->lock); ast_mutex_unlock(&pri->pvts[chanpos]->lock); break; } } + /* assign call to b channel */ pri->pvts[chanpos]->call = e->ring.call; apply_plan_to_number(plancallingnum, sizeof(plancallingnum), pri, e->ring.callingnum, e->ring.callingplan); if (pri->pvts[chanpos]->use_callerid) { @@ -8423,29 +9203,78 @@ } apply_plan_to_number(pri->pvts[chanpos]->rdnis, sizeof(pri->pvts[chanpos]->rdnis), pri, e->ring.redirectingnum, e->ring.callingplanrdnis); + /* get callingpres */ + pri->pvts[chanpos]->cid_pres = e->ring.callingpres; + switch (e->ring.callingpres) { + case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + case PRES_PROHIB_NETWORK_NUMBER: + strncpy(pri->pvts[chanpos]->cid_name, pri->withheldcid, sizeof(pri->pvts[chanpos]->cid_name)); + break; + case PRES_NUMBER_NOT_AVAILABLE: + strncpy(pri->pvts[chanpos]->cid_name, pri->nocid, sizeof(pri->pvts[chanpos]->cid_name)); + break; + } /* If immediate=yes go to s|1 */ if (pri->pvts[chanpos]->immediate) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of immediate=yes\n"); pri->pvts[chanpos]->exten[0] = 's'; pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Get called number */ - else if (!ast_strlen_zero(e->ring.callednum)) { - ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten)); - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - } else - pri->pvts[chanpos]->exten[0] = '\0'; - /* Set DNID on all incoming calls -- even immediate */ - if (!ast_strlen_zero(e->ring.callednum)) - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - /* No number yet, but received "sending complete"? */ - if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) { + } else if (ast_strlen_zero(e->ring.callednum)) { + /* called party number is empty */ + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + if (!pri->overlapdial) { + // be able to set digittimeout for BRI phones + pri->pvts[chanpos]->exten[0] = 's'; + pri->pvts[chanpos]->exten[1] = '\0'; + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } else { + pri->pvts[chanpos]->exten[0] = '\0'; + } + } else { + if (pri->nodetype == BRI_CPE) { + /* fix for .at p2p bri lines */ + pri->pvts[chanpos]->exten[0] = 's'; + pri->pvts[chanpos]->exten[1] = '\0'; + } else { + pri->pvts[chanpos]->exten[0] = '\0'; + } + } + /* No number yet, but received "sending complete"? */ + if (e->ring.complete) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of Complete received\n"); pri->pvts[chanpos]->exten[0] = 's'; pri->pvts[chanpos]->exten[1] = '\0'; - } + } + } else { + /* Get called number */ + pri_make_callerid(pri, pri->pvts[chanpos]->dnid, sizeof(pri->pvts[chanpos]->dnid), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd); + pri_make_callerid(pri, pri->pvts[chanpos]->exten, sizeof(pri->pvts[chanpos]->exten), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd); + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + /* if we get the next digit we should stop the dialtone */ + if (!pri->overlapdial) { + // with overlapdial=no the exten is always prefixed by "s" + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } else { + if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten)) { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1); + } else { + tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE); + } + } + } + } + /* Part 3: create channel, setup audio... */ + /* Set DNID on all incoming calls -- even immediate */ + if (!ast_strlen_zero(e->ring.callednum)) + strncpy(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid) - 1); /* Make sure extension exists (or in overlap dial mode, can exist) */ if ((pri->overlapdial && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) || ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) { @@ -8464,23 +9293,39 @@ res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); if (res < 0) ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[chanpos]->channel); - res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + if (IS_DIGITAL(e->ring.ctype)) { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law); + } else { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + } if (res < 0) ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[chanpos]->channel); - if (e->ring.complete || !pri->overlapdial) { + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + if (e->ring.complete || !pri->overlapdial) { /* Just announce proceeding */ pri->pvts[chanpos]->proceeding = 1; pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0); - } else { + // pri->pvts[chanpos]->ignoredtmf = 0; + } else { if (pri->switchtype != PRI_SWITCH_GR303_TMC) pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); else pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + } + } else { + /* BRI_NETWORK | BRI_NETWORK_PTMP */ + if (pri->overlapdial || (!strcasecmp(pri->pvts[chanpos]->exten, "s"))) { + /* send a SETUP_ACKNOWLEDGE */ + pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + } else { + /* send an ALERTING ??? wtf */ + // pri_acknowledge(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); + pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0); + } } - /* Get the use_callingpres state */ - pri->pvts[chanpos]->callingpres = e->ring.callingpres; - - /* Start PBX */ + + /* overlapdial = yes and the extension can be valid */ + if (pri->overlapdial && ast_matchmore_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) { /* Release the PRI lock while we create the channel */ ast_mutex_unlock(&pri->lock); @@ -8487,14 +9332,31 @@ /* Set bearer and such */ pri_assign_bearer(crv, pri, pri->pvts[chanpos]); c = zt_new(crv, AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } pri->pvts[chanpos]->owner = &inuse; ast_log(LOG_DEBUG, "Started up crv %d:%d on bearer channel %d\n", pri->trunkgroup, crv->channel, crv->bearer->channel); } else { c = zt_new(pri->pvts[chanpos], AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } + zt_enable_ec(pri->pvts[chanpos]); } if (!ast_strlen_zero(e->ring.callingsubaddr)) { pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr); } + if (!ast_strlen_zero(e->ring.callingnum)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0); + pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr); + } + if (!ast_strlen_zero(e->ring.callingani)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0); + pbx_builtin_setvar_helper(c, "PRI_USER_CID", tmpstr); + } if(e->ring.ani2 >= 0) { snprintf(ani2str, 5, "%.2d", e->ring.ani2); pbx_builtin_setvar_helper(c, "ANI2", ani2str); @@ -8514,8 +9376,8 @@ ast_mutex_lock(&pri->lock); if (c && !ast_pthread_create(&threadid, &attr, ss_thread, c)) { if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, !ast_strlen_zero(pri->pvts[chanpos]->exten) ? pri->pvts[chanpos]->exten : "", + ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap %s call from '%s' to '%s' on channel %d/%d, span %d\n", + pri->pvts[chanpos]->digital ? "data" : "voice", plancallingnum, !ast_strlen_zero(pri->pvts[chanpos]->exten) ? pri->pvts[chanpos]->exten : "", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); } else { ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", @@ -8523,14 +9385,18 @@ if (c) ast_hangup(c); else { - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1); pri->pvts[chanpos]->call = NULL; } } } else { + /* overlapdial = no */ ast_mutex_unlock(&pri->lock); /* Release PRI lock while we create the channel */ c = zt_new(pri->pvts[chanpos], AST_STATE_RING, 1, SUB_REAL, law, e->ring.ctype); + if (c && (e->ring.lowlayercompat[0] > 0)) { + memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat)); + } ast_mutex_lock(&pri->lock); if (c) { char calledtonstr[10]; @@ -8551,23 +9417,40 @@ snprintf(calledtonstr, sizeof(calledtonstr)-1, "%d", e->ring.calledplan); pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr); if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, pri->pvts[chanpos]->exten, + ast_verbose(VERBOSE_PREFIX_3 "Accepting %s call from '%s' to '%s' on channel %d/%d, span %d\n", + pri->pvts[chanpos]->digital ? "data" : "voice", e->ring.callingnum, pri->pvts[chanpos]->exten, pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); zt_enable_ec(pri->pvts[chanpos]); + if(!ast_strlen_zero(e->ring.callingsubaddr)) { + pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr); + } + if (!ast_strlen_zero(e->ring.callingnum)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0); + pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr); + } + if (!ast_strlen_zero(e->ring.callingani)) { + char tmpstr[256]; + pri_make_callerid(pri, tmpstr,sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0); + pbx_builtin_setvar_helper(c, "PRI_USER_CID", e->ring.callednum); + } + if (!ast_strlen_zero(e->ring.useruserinfo)) { + pbx_builtin_setvar_helper(c, "UUI", e->ring.useruserinfo); + } } else { ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1); pri->pvts[chanpos]->call = NULL; } } } else { + /* invalid extension */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n", pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED, -1); pri->pvts[chanpos]->call = NULL; pri->pvts[chanpos]->exten[0] = '\0'; } @@ -8575,7 +9458,7 @@ ast_mutex_unlock(&crv->lock); ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); + pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, -1); break; case PRI_EVENT_RINGING: chanpos = pri_find_principle(pri, e->ringing.channel); @@ -8593,7 +9476,7 @@ } else { ast_mutex_lock(&pri->pvts[chanpos]->lock); if (ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) { - zt_enable_ec(pri->pvts[chanpos]); + // XXX zt_enable_ec(pri->pvts[chanpos]); pri->pvts[chanpos]->subs[SUB_REAL].needringing = 1; pri->pvts[chanpos]->alerting = 1; } else @@ -8622,9 +9505,16 @@ } break; case PRI_EVENT_PROGRESS: - /* Get chan value if e->e is not PRI_EVNT_RINGING */ + /* Get chan value if e->e is not PRI_EVENT_RINGING */ chanpos = pri_find_principle(pri, e->proceeding.channel); if (chanpos > -1) { + if ((pri->pvts[chanpos]->priindication_oob == 2) && (e->proceeding.cause == PRI_CAUSE_USER_BUSY)) { + /* received PROGRESS with cause BUSY, no inband callprogress wanted => hang up! */ + if (pri->pvts[chanpos]->owner) { + pri->pvts[chanpos]->owner->hangupcause = AST_CAUSE_USER_BUSY; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } + } else { #ifdef PRI_PROGRESS_MASK if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)) { #else @@ -8671,6 +9561,12 @@ case PRI_EVENT_PROCEEDING: chanpos = pri_find_principle(pri, e->proceeding.channel); if (chanpos > -1) { + chanpos = pri_fixup_principle(pri, chanpos, e->proceeding.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Received PROCEEDING on channel %d/%d not in use on span %d\n", + PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), pri->span); + chanpos = -1; + } else { if (!pri->pvts[chanpos]->proceeding) { struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, }; @@ -8721,6 +9617,295 @@ } } break; + case PRI_EVENT_SUSPEND_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_suspend_reject(pri->pri, e->suspend_req.call, ""); + break; + } + chanpos = pri_find_principle(pri, e->suspend_req.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Suspend requested on unconfigured channel %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + + if (chanpos > -1) { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->owner) { + if (ast_bridged_channel(pri->pvts[chanpos]->owner)) { + struct zt_suspended_call *zpc; + char tmpstr[256]; + zpc = malloc(sizeof(struct zt_suspended_call)); + if (!zpc) { + ast_log(LOG_ERROR, "unable to malloc zt_suspended_call\n"); + break; + } + strncpy(zpc->msn, pri->pvts[chanpos]->cid_num, sizeof(zpc->msn)); + strncpy(zpc->callid, e->suspend_req.callid, sizeof(zpc->callid)); + ast_masq_park_call(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL, 0, &zpc->parked_at); + zpc->next = pri->suspended_calls; + pri->suspended_calls = zpc; + snprintf(tmpstr, sizeof(tmpstr), "Parked at %d", zpc->parked_at); + pri_suspend_acknowledge(pri->pri, e->suspend_req.call,tmpstr); + pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; + } else { + pri_suspend_reject(pri->pri, e->suspend_req.call, "cant park a non-bridge"); + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + break; + } + } else { + pri_suspend_reject(pri->pri, e->suspend_req.call, ""); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + break; + case PRI_EVENT_RESUME_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + break; + } + chanpos = pri_find_empty_chan(pri, 1); + if (chanpos < 0) { + pri_resume_reject(pri->pri, e->resume_req.call,"All channels busy"); + ast_log(LOG_WARNING, "Resume requested on odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + } else if (!pri->pvts[chanpos]) { + pri_resume_reject(pri->pri, e->resume_req.call,"General protection fault in module 0x0BRI"); + chanpos = -1; + } + + if (chanpos > -1) { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (!pri->pvts[chanpos]->owner) { + struct zt_suspended_call *zpc, *zpcl; + int unparked=0; + char extenstr[255], temp[255]; + zpc = NULL; + zpcl = pri->suspended_calls; + while (zpcl) { + // ast_log(LOG_NOTICE, "zpc->parked_at %d zpcl->callid %s\n",zpcl->parked_at, zpcl->callid); + if (((strlen(zpcl->callid) == 0) && (strlen(e->resume_req.callid)==0)) || (!strcmp(zpcl->callid,e->resume_req.callid))) { + int law; + // found a parked call + snprintf(extenstr, sizeof(extenstr), "%d", zpcl->parked_at); + strncpy(pri->pvts[chanpos]->exten, extenstr, sizeof(pri->pvts[chanpos]->exten)); + // strncpy(pri->pvts[chanpos]->context, ast_parking_con(), sizeof(pri->pvts[chanpos]->context)); + pri->pvts[chanpos]->call = e->resume_req.call; + law = 1; + if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]), law); + // uhh ohh...what shall we do without the bearer cap??? + law = ZT_LAW_ALAW; + res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + if (!pri->pvts[chanpos]->digital) { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + } else { + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law); + } + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + /* Start PBX */ + c = zt_new(pri->pvts[chanpos], AST_STATE_UP, 1, SUB_REAL, law, PRI_TRANS_CAP_SPEECH); + if (c) { + pri->pvts[chanpos]->owner = c; + pri->pvts[chanpos]->call = e->resume_req.call; + zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + } else { + ast_log(LOG_ERROR, "unable to start pbx\n"); + } + + if (zpc) { + zpc->next = zpcl->next; + free(zpcl); + zpcl = zpc->next; + } else { + // remove head + pri->suspended_calls = zpcl->next; + free(zpcl); + zpcl = pri->suspended_calls; + zpc = NULL; + } + unparked = 1; + snprintf(temp, sizeof(temp), "Unparked %s", extenstr); + pri_resume_acknowledge(pri->pri, e->resume_req.call, chanpos + 1, temp); + break; + } + zpc = zpcl; + if (zpcl) zpcl = zpcl->next; + } + if (!unparked) + pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!"); + } else { + pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!"); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + break; + case PRI_EVENT_HOLD_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_hold_reject(pri->pri, e->hold_req.call); + break; + } + chanpos = pri_find_principle(pri, e->hold_req.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Hold requested on unconfigured channel %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + if (chanpos > -1) { + // ast_log(LOG_NOTICE, "Hold request for channel number %d span %d\n", chanpos, pri->span); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->owner) { + struct zt_pvt *p = pri->pvts[chanpos]; + struct zt_holded_call *zhc; + int holdacked=0; + +// ast_log(LOG_NOTICE,"HOLD request from channel %s tei %d\n",p->owner->name, e->hold_req.tei); + if (ast_bridged_channel(p->owner)) { + zhc = malloc(sizeof(struct zt_holded_call)); + if (!zhc) { + ast_log(LOG_ERROR, "unable to malloc zt_holded_call\n"); + break; + } + memset(zhc, 0, sizeof(zhc)); + strncpy(zhc->msn, pri->pvts[chanpos]->cid_num, sizeof(zhc->msn)); + strncpy(zhc->uniqueid, ast_bridged_channel(p->owner)->uniqueid, sizeof(zhc->uniqueid)); + zhc->tei = e->hold_req.tei; + zhc->cref = e->hold_req.cref; + zhc->call = e->hold_req.call; + zhc->channel = p->owner; + zhc->alreadyhungup = 0; + zhc->bridge = ast_bridged_channel(p->owner); + zhc->next = pri->holded_calls; + pri->holded_calls = zhc; + + /* put channel on hold */ + ast_masq_hold_call(ast_bridged_channel(p->owner), p->owner); + + pri_hold_acknowledge(pri->pri, e->hold_req.call); + holdacked = 1; + p->call = NULL; // free the bchannel withouth destroying the call + p->tei = -1; + } else { + // cant hold a non-bridge,...yet + + // make a fake channel + + // masquerade + + // put on hold + pri_hold_reject(pri->pri, e->hold_req.call); + } + } else { + pri_hold_reject(pri->pri, e->hold_req.call); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } else { + pri_hold_reject(pri->pri, e->hold_req.call); + } + break; + case PRI_EVENT_RETRIEVE_REQ: + if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + break; + } + chanpos = pri_find_empty_chan(pri, 1); + if (chanpos < 0) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + ast_log(LOG_WARNING, "Retrieve requested on odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + break; + } else if (!pri->pvts[chanpos]) { + ast_log(LOG_WARNING, "Retrieve requested on unconfigured channel number %d span %d\n", chanpos, pri->span); + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + chanpos = -1; + break; + } + if (chanpos > -1) { + struct zt_holded_call *onhold = NULL; + int retrieved = 0; + int res = -1; + struct app_tmp *tmp; + pthread_attr_t attr; + int law; + + onhold = pri_get_callonhold(pri, e->retrieve_req.cref, e->retrieve_req.tei); + + if (!onhold) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + break; + } + ast_mutex_lock(&pri->pvts[chanpos]->lock); + // found a parked call + law = 1; + if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1) + ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]), law); + // uhh ohh...what shall we do without the bearer cap??? + law = ZT_LAW_ALAW; + res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos])); + /* Start PBX */ + c = zt_new(pri->pvts[chanpos], AST_STATE_UP, 0, SUB_REAL, law, PRI_TRANS_CAP_SPEECH); + if (c) { + pri->pvts[chanpos]->owner = c; + pri->pvts[chanpos]->outgoing = 1; /* for not sending proceedings... */ + pri->pvts[chanpos]->call = e->retrieve_req.call; + pri->pvts[chanpos]->tei = e->retrieve_req.tei; + zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + } else { + ast_log(LOG_ERROR, "unable to start pbx\n"); + } + + retrieved = 1; + // ast_log(LOG_NOTICE, "sending RETRIEVE ACK on channel %d, span %d for tei %d cref %d\n",chanpos,pri->span, e->retrieve_req.tei, e->retrieve_req.cref); + pri_retrieve_acknowledge(pri->pri, e->retrieve_req.call, chanpos + 1); + + // the magic begins here: .... + tmp = malloc(sizeof(struct app_tmp)); + if (tmp) { + memset(tmp, 0, sizeof(struct app_tmp)); + strncpy(tmp->app, "holdedcall", sizeof(tmp->app) - 1); + strncpy(tmp->data, onhold->uniqueid, sizeof(tmp->data) - 1); + tmp->chan = c; + } + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) { + ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", c->name, strerror(errno)); + free(tmp); + ast_hangup(c); + retrieved = 0; + } + + if (!retrieved) { + pri_retrieve_reject(pri->pri, e->retrieve_req.call); + } + } + break; + case PRI_EVENT_DISPLAY_RECEIVED: + ast_log(LOG_NOTICE, "DISPLAY IE: [ %s ] received\n",e->display.text); + chanpos = pri_find_principle(pri, e->display.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "odd channel number %d span %d\n", chanpos, pri->span); + chanpos = -1; + } + if (chanpos > -1) { + if (pri->pvts[chanpos]->owner) { + // ast_sendtext(pri->pvt[chanpos]->owner, e->display.text); + } + } + break; case PRI_EVENT_ANSWER: chanpos = pri_find_principle(pri, e->answer.channel); if (chanpos < 0) { @@ -8736,6 +9921,7 @@ chanpos = -1; } else { ast_mutex_lock(&pri->pvts[chanpos]->lock); + pri->pvts[chanpos]->tei = e->answer.tei; /* Now we can do call progress detection */ /* We changed this so it turns on the DSP no matter what... progress or no progress. @@ -8765,11 +9951,16 @@ ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", pri->pvts[chanpos]->dop.dialstr); pri->pvts[chanpos]->dop.dialstr[0] = '\0'; } else if (pri->pvts[chanpos]->confirmanswer) { - ast_log(LOG_DEBUG, "Waiting on answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel); + ast_log(LOG_DEBUG, "Waiting for answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel); + enable_dtmf_detect(pri->pvts[chanpos]); } else { + pri->pvts[chanpos]->dialing = 0; pri->pvts[chanpos]->subs[SUB_REAL].needanswer =1; /* Enable echo cancellation if it's not on already */ zt_enable_ec(pri->pvts[chanpos]); + zt_train_ec(pri->pvts[chanpos]); + /* stop ignoring inband dtmf */ + enable_dtmf_detect(pri->pvts[chanpos]); } #ifdef SUPPORT_USERUSER @@ -8818,23 +10009,32 @@ } } if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup, cause %d\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause); } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; } if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) + if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) { + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d on span %d since channel reported in use\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; + pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); + pri->pvts[chanpos]->resetting = 1; + } } - if (e->hangup.aoc_units > -1) + if (e->hangup.aoc_units > -1) { + if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } #ifdef SUPPORT_USERUSER if (!ast_strlen_zero(e->hangup.useruserinfo)) { @@ -8844,8 +10044,20 @@ ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else { - ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + struct zt_holded_call *onhold = NULL; + /* check calls on hold */ + onhold = pri_get_callonhold(pri, e->hangup.cref, e->hangup.tei); + + if (onhold) { + // ast_log(LOG_NOTICE, "hangup, found cref %d, tei %d\n",e->hangup.cref, e->hangup.tei); + pri_hangup(pri->pri, onhold->call, e->hangup.cause, -1); + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + } else { + ast_log(LOG_NOTICE, "Hangup, did not find cref %d, tei %d\n",e->hangup.cref, e->hangup.tei); + ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n", + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } } } break; @@ -8855,17 +10067,25 @@ case PRI_EVENT_HANGUP_REQ: chanpos = pri_find_principle(pri, e->hangup.channel); if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + if (pri->nodetype == BRI_NETWORK_PTMP) { + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause, -1); + } else { + ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } chanpos = -1; } - if (chanpos > -1) { + /* dont hang up if we want to hear inband call progress */ + if ((chanpos > -1) && ((pri->pvts[chanpos]->priindication_oob != 2) || (!e->hangup.inband_progress) || (!pri->pvts[chanpos]->outgoing))){ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); if (pri->pvts[chanpos]->realcall) pri_hangup_all(pri->pvts[chanpos]->realcall, pri); else if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", e->hangup.cause); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "PRI_CAUSE", tmpstr); pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; switch(e->hangup.cause) { case PRI_CAUSE_USER_BUSY: @@ -8884,20 +10104,87 @@ } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup request\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - if (e->hangup.aoc_units > -1) - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + if (e->hangup.aoc_units > -1) { + if (pri->pvts[chanpos]->owner) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + } + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } + if (pri->nodetype == BRI_NETWORK_PTMP) { + // check for bri transfers, not everybody uses ECT... + if (pri->pvts[chanpos]->owner) { + // find on hold call + struct zt_holded_call *onhold = NULL; + struct ast_channel *transferee = NULL; + int transfer_ok = 0; + + onhold = pri_get_callonhold(pri, -1, e->hangup.tei); + + if (onhold) { + if (pri->pvts[chanpos]->pritransfer == 2) { + if (((pri->pvts[chanpos]->owner->_state != AST_STATE_RING) && (pri->pvts[chanpos]->owner->_state != AST_STATE_RESERVED)) || ((!ast_strlen_zero(pri->pvts[chanpos]->exten)) && (strncasecmp(pri->pvts[chanpos]->exten, "s", sizeof(pri->pvts[chanpos]->exten))))) { + transferee = ast_get_holded_call(onhold->uniqueid); + + if (transferee) { + if (pri->pvts[chanpos]->owner->_state == AST_STATE_RINGING) { + ast_indicate(transferee, AST_CONTROL_RINGING); + } + + pri->pvts[chanpos]->owner->_softhangup &= ~AST_SOFTHANGUP_DEV; + + ast_mutex_unlock(&transferee->lock); + if (ast_channel_masquerade(pri->pvts[chanpos]->owner, transferee)) { + ast_log(LOG_WARNING, "unable to masquerade\n"); + } else { + /* beware of zombies!!! */ + ast_set_flag(transferee, AST_FLAG_ZOMBIE); + pri->pvts[chanpos]->owner = NULL; + pri->pvts[chanpos]->tei = -1; + transfer_ok = 1; + } + } + } + } else if (pri->pvts[chanpos]->pritransfer == 0) { + ast_log(LOG_NOTICE, "killing channel %s \n", onhold->uniqueid); + ast_retrieve_call_to_death(onhold->uniqueid); + transfer_ok = 1; + } else if (pri->pvts[chanpos]->pritransfer == 1) { + /* we use ECT transfers, so just ignore this */ + transfer_ok = 0; + } + + if (transfer_ok) { + onhold->alreadyhungup = 1; + pri_hangup(pri->pri, onhold->call, e->hangup.cause, -1); + onhold = NULL; + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + break; + } else { + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); + pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; + } + } + } } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; } if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; + if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d span %d since channel reported in use\n", + PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); + pri->pvts[chanpos]->resetting = 1; + } + } #ifdef SUPPORT_USERUSER @@ -8908,9 +10195,37 @@ ast_mutex_unlock(&pri->pvts[chanpos]->lock); } else { - ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + if (pri->nodetype != BRI_NETWORK_PTMP) { + ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } else { + // check holded_calls!!! + struct zt_holded_call *onhold = NULL; + + onhold = pri_get_callonhold(pri, e->hangup.cref, e->hangup.tei); + + if (onhold) { + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause, -1); + ast_retrieve_call_to_death(onhold->uniqueid); + pri_destroy_callonhold(pri, onhold); + onhold = NULL; + } else { + ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); + } + } } } + if ((chanpos > -1) && (pri->pvts[chanpos]->owner) && (pri->pvts[chanpos]->priindication_oob == 2) && (e->hangup.inband_progress) && (pri->pvts[chanpos]->outgoing)) { + if (e->hangup.aoc_units > -1) { + char tmpstr[256]; + snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units); + pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); + } + pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; + ast_channel_setwhentohangup(pri->pvts[chanpos]->owner, 5); + } break; case PRI_EVENT_HANGUP_ACK: chanpos = pri_find_principle(pri, e->hangup.channel); @@ -8924,6 +10239,7 @@ if (chanpos > -1) { ast_mutex_lock(&pri->pvts[chanpos]->lock); pri->pvts[chanpos]->call = NULL; + pri->pvts[chanpos]->tei = -1; pri->pvts[chanpos]->resetting = 0; if (pri->pvts[chanpos]->owner) { if (option_verbose > 2) @@ -8937,7 +10253,9 @@ #endif ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } } + } } break; case PRI_EVENT_CONFIG_ERR: @@ -9029,10 +10347,22 @@ ast_mutex_lock(&pri->pvts[chanpos]->lock); switch(e->notify.info) { case PRI_NOTIFY_REMOTE_HOLD: + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on NETWORK channel. Starting MoH\n"); + ast_moh_start(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL); + } else { + ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on CPE channel. Not Starting MoH\n"); + } f.subclass = AST_CONTROL_HOLD; zap_queue_frame(pri->pvts[chanpos], &f, pri); break; case PRI_NOTIFY_REMOTE_RETRIEVAL: + if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) { + ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on NETWORK channel. Stopping MoH\n"); + ast_moh_stop(ast_bridged_channel(pri->pvts[chanpos]->owner)); + } else { + ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on CPE channel.\n"); + } f.subclass = AST_CONTROL_UNHOLD; zap_queue_frame(pri->pvts[chanpos], &f, pri); break; @@ -9040,6 +10370,77 @@ ast_mutex_unlock(&pri->pvts[chanpos]->lock); } break; + case PRI_EVENT_FACILITY: + if (e->facility.operation == 0x06) { + struct ast_channel *chan = NULL; + struct zt_holded_call *onhold = NULL; + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "ECT requested by TEI %d for cref %d\n", e->facility.tei, e->facility.cref); + } + /* search for cref/tei in held calls */ + onhold = pri_get_callonhold(pri, e->facility.cref, e->facility.tei); + if (onhold) { + chan = ast_get_holded_call(onhold->uniqueid); + onhold->alreadyhungup = 1; + onhold = NULL; + if (!chan) { + /* hang up */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + break; + } + } else { + /* unknown cref/tei */ + ast_log(LOG_WARNING, "did not find call on hold for cref %d tei %d\n", e->facility.tei, e->facility.cref); + /* hang up */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + break; + } + + /* find an active call for the same tei */ + chanpos = pri_find_tei(pri, e->facility.call, e->facility.tei); + if (chanpos < 0) { + /* did not find active call, hangup call on hold */ + if (chan) { + ast_hangup(chan); + chan = NULL; + } + } else { + ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* transfer */ + if (pri->pvts[chanpos]->owner) { + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "ECT: found %s on channel %d for tei %d\n", pri->pvts[chanpos]->owner->name ,chanpos, e->facility.tei); + } + /* pass callprogress if the channel is not up yet */ + if (pri->pvts[chanpos]->owner->_state == AST_STATE_RINGING) { + ast_indicate(chan, AST_CONTROL_RINGING); + } + /* unlock the channel we removed from hold */ + ast_mutex_unlock(&chan->lock); + if (ast_channel_masquerade(pri->pvts[chanpos]->owner, chan)) { + ast_log(LOG_WARNING, "unable to masquerade\n"); + } else { + /* beware of zombies !!! */ + ast_set_flag(chan, AST_FLAG_ZOMBIE); + // chan->zombie = 1; + } + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } + /* disconnect */ + pri_hangup(pri->pri, e->facility.call, 16, -1); + } else if (e->facility.operation == 0x0D) { + ast_log(LOG_NOTICE, "call deflection to %s requested.\n", e->facility.forwardnum); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + /* transfer */ + if (pri->pvts[chanpos]->owner) { + snprintf(pri->pvts[chanpos]->owner->call_forward, sizeof(pri->pvts[chanpos]->owner->call_forward), "Local/%s@%s", e->facility.forwardnum, pri->pvts[chanpos]->owner->context); + } + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + } else { + ast_log(LOG_WARNING, "Unknown facility operation %#x requested.\n", e->facility.operation); + } + break; default: ast_log(LOG_DEBUG, "Event: %d\n", e->e); } @@ -9101,7 +10502,7 @@ pri->fds[i] = -1; return -1; } - pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype); + pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype, pri->span); /* Force overlap dial if we're doing GR-303! */ if (pri->switchtype == PRI_SWITCH_GR303_TMC) pri->overlapdial = 1; @@ -9170,39 +10571,77 @@ static int handle_pri_set_debug_file(int fd, int argc, char **argv) { - int myfd; + int myfd, x, d; + int span; + + if (argc < 6) + return RESULT_SHOWUSAGE; if (!strncasecmp(argv[1], "set", 3)) { - if (argc < 5) + if (argc < 7) return RESULT_SHOWUSAGE; - if (ast_strlen_zero(argv[4])) + if (!argv[4] || ast_strlen_zero(argv[4])) return RESULT_SHOWUSAGE; + if (!argv[5]) + return RESULT_SHOWUSAGE; + + if (!argv[6] || ast_strlen_zero(argv[6])) + return RESULT_SHOWUSAGE; + + span = atoi(argv[6]); + if ((span < 1) && (span > NUM_SPANS)) { + return RESULT_SUCCESS; + } + + myfd = open(argv[4], O_CREAT|O_WRONLY); if (myfd < 0) { - ast_cli(fd, "Unable to open '%s' for writing\n", argv[4]); - return RESULT_SUCCESS; + ast_cli(fd, "Unable to open '%s' for writing\n", argv[4]); + return RESULT_SUCCESS; } - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - close(pridebugfd); - - pridebugfd = myfd; - ast_copy_string(pridebugfilename,argv[4],sizeof(pridebugfilename)); - - ast_mutex_unlock(&pridebugfdlock); - - ast_cli(fd, "PRI debug output will be sent to '%s'\n", argv[4]); + for (x=0; x < NUM_SPANS; x++) { + ast_mutex_lock(&pris[x].lock); + + if (pris[x].span == span) { + if (pris[x].debugfd >= 0) + close(pris[x].debugfd); + pris[x].debugfd = myfd; + for (d=0; d < NUM_DCHANS; d++) { + if (pris[x].dchans[d]) + pri_set_debug_fd(pris[x].dchans[d], myfd); + } + } + ast_mutex_unlock(&pris[x].lock); + } + + ast_cli(fd, "PRI debug output for span %d will be sent to '%s'\n", span, argv[4]); } else { + if (!argv[5] || ast_strlen_zero(argv[5])) + return RESULT_SHOWUSAGE; /* Assume it is unset */ - ast_mutex_lock(&pridebugfdlock); - close(pridebugfd); - pridebugfd = -1; - ast_cli(fd, "PRI debug output to file disabled\n"); - ast_mutex_unlock(&pridebugfdlock); + span = atoi(argv[5]); + if ((span < 1) && (span > NUM_SPANS)) { + return RESULT_SUCCESS; + } + + for (x=0; x < NUM_SPANS; x++) { + ast_mutex_lock(&pris[x].lock); + + if (pris[x].span == span) { + if (pris[x].debugfd >= 0) + close(pris[x].debugfd); + pris[x].debugfd = -1; + for (d=0; d < NUM_DCHANS; d++) { + if (pris[x].dchans[d]) + pri_set_debug_fd(pris[x].dchans[d], -1); + } + } + ast_mutex_unlock(&pris[x].lock); + } + + ast_cli(fd, "PRI debug output to file for span %d disabled\n", span); } return RESULT_SUCCESS; @@ -9234,6 +10673,7 @@ + static int handle_pri_no_debug(int fd, int argc, char *argv[]) { int span; @@ -9340,36 +10780,6 @@ return RESULT_SUCCESS; } -static int handle_pri_show_debug(int fd, int argc, char *argv[]) -{ - int x; - int span; - int count=0; - int debug=0; - - for(span=0;span= 0) - ast_cli(fd, "Logging PRI debug to file %s\n", pridebugfilename); - ast_mutex_unlock(&pridebugfdlock); - - if (!count) - ast_cli(fd, "No debug set or no PRI running\n"); - return RESULT_SUCCESS; -} - static char pri_debug_help[] = "Usage: pri debug span \n" " Enables debugging on a given PRI span\n"; @@ -9386,6 +10796,18 @@ "Usage: pri show span \n" " Displays PRI Information\n"; +static char bri_debug_help[] = + "Usage: bri debug span \n" + " Enables debugging on a given BRI span\n"; + +static char bri_no_debug_help[] = + "Usage: bri no debug span \n" + " Disables debugging on a given BRI span\n"; + +static char bri_really_debug_help[] = + "Usage: bri intensive debug span \n" + " Enables debugging down to the Q.921 level\n"; + static struct ast_cli_entry zap_pri_cli[] = { { { "pri", "debug", "span", NULL }, handle_pri_debug, "Enables PRI debugging on a span", pri_debug_help, complete_span_4 }, @@ -9393,19 +10815,282 @@ "Disables PRI debugging on a span", pri_no_debug_help, complete_span_5 }, { { "pri", "intense", "debug", "span", NULL }, handle_pri_really_debug, "Enables REALLY INTENSE PRI debugging", pri_really_debug_help, complete_span_5 }, + { { "bri", "debug", "span", NULL }, handle_pri_debug, + "Enables BRI debugging on a span", bri_debug_help, complete_span_4 }, + { { "bri", "no", "debug", "span", NULL }, handle_pri_no_debug, + "Disables BRI debugging on a span", bri_no_debug_help, complete_span_5 }, + { { "bri", "intense", "debug", "span", NULL }, handle_pri_really_debug, + "Enables REALLY INTENSE BRI debugging", bri_really_debug_help, complete_span_5 }, { { "pri", "show", "span", NULL }, handle_pri_show_span, "Displays PRI Information", pri_show_span_help, complete_span_4 }, - { { "pri", "show", "debug", NULL }, handle_pri_show_debug, - "Displays current PRI debug settings" }, { { "pri", "set", "debug", "file", NULL }, handle_pri_set_debug_file, "Sends PRI debug output to the specified file" }, - { { "pri", "unset", "debug", "file", NULL }, handle_pri_set_debug_file, + { { "pri", "unset", "debug", "file", "span", NULL }, handle_pri_set_debug_file, "Ends PRI debug output to file" }, }; +static char *zapCD_tdesc = "Call Deflection"; +static char *zapCD_app = "zapCD"; +static char *zapCD_synopsis = "Call Deflection"; + +static int app_zapCD(struct ast_channel *chan, void *data) +{ + struct zt_pvt *p = chan->tech_pvt; + + if(!data) { + ast_log(LOG_WARNING, "zapCD wants a number to deflect to\n"); + return -1; + } + return pri_deflect(p->pri->pri, p->call, data); +} + +static char *zapInband_tdesc = "Inband Call Progress (pre-answer)"; +static char *zapInband_app = "zapInband"; +static char *zapInband_synopsis = "Inband Call Progress"; + +static int app_zapInband(struct ast_channel *chan, void *data) +{ + struct zt_pvt *p = chan->tech_pvt; + + return pri_acknowledge(p->pri->pri, p->call, PVT_TO_CHANNEL(p), 1); +} #endif /* ZAPATA_PRI */ +#ifdef ZAPATA_GSM +static int handle_gsm_debug_helper(int fd, int channel, int debug) +{ +/* gsm debug channel */ + struct zt_pvt *pvt = NULL; + if (channel < 1) { + ast_cli(fd, "Invalid channel %d. Should be a number.\n", channel); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + ast_mutex_lock(&pvt->lock); + gsm_set_debug(pvt->gsm.modul, debug); + ast_mutex_unlock(&pvt->lock); + ast_cli(fd, "%s debugging on channel %d\n", debug ? "Enabled":"Disabled", channel); + return RESULT_SUCCESS; + } + pvt = pvt->next; + } + + ast_cli(fd, "No GSM running on channel %d\n", channel); + return RESULT_SUCCESS; +} + + + +static int handle_gsm_debug(int fd, int argc, char *argv[]) +{ +/* gsm debug channel */ + int channel; + if (argc < 4) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_AT); +} + +static int handle_gsm_no_debug(int fd, int argc, char *argv[]) +{ +/* gsm no debug channel */ + int channel; + if (argc < 5) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[4]); + return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_NONE); +} + +static char gsm_debug_help[] = + "Usage: gsm debug channel \n" + " Enables debugging on a given GSM channel\n"; + +static char gsm_no_debug_help[] = + "Usage: gsm no debug channel \n" + " Disables debugging on a given GSM channel\n"; + +static struct ast_cli_entry zap_gsm_cli[] = { + { { "gsm", "debug", "channel", NULL }, handle_gsm_debug, + "Enables GSM debugging on a channel", gsm_debug_help }, + { { "gsm", "no", "debug", "channel", NULL }, handle_gsm_no_debug, + "Disables GSM debugging on a channel", gsm_no_debug_help}, +}; + + + +static char gsm_send_pdu_help[] = + "Usage: gsm send pdu \n" + " Sends a PDU on a GSM channel\n"; + + +static int handle_gsm_send_pdu(int fd, int argc, char *argv[]) +{ +/* gsm send sms */ + int channel; + int len; + struct zt_pvt *pvt = NULL; + if (argc < 6) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + len = atoi(argv[4]); + if (len < 1) { + ast_cli(fd, "Invalid length %s. Should be a number.\n", argv[4]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_sms_send_pdu(pvt->gsm.modul, argv[5], len); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry gsm_send_pdu = { + { "gsm", "send", "pdu", NULL }, handle_gsm_send_pdu, "Sends a SM on a GSM channel", gsm_send_pdu_help, complete_span_4 }; + + +static char gsm_send_sms_help[] = + "Usage: gsm send sms \n" + " Sends a SM on a GSM channel\n"; + + +static int handle_gsm_send_sms(int fd, int argc, char *argv[]) +{ +/* gsm send sms */ + int channel; + struct zt_pvt *pvt = NULL; + if (argc < 6) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_sms_send_text(pvt->gsm.modul, argv[4], argv[5]); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry gsm_send_sms = { + { "gsm", "send", "sms", NULL }, handle_gsm_send_sms, "Sends a SM on a GSM channel", gsm_send_sms_help, complete_span_4 }; + +static char gsm_show_status_help[] = + "Usage: gsm show status >\n" + " Displays status information about the GSM channel.\n"; + + +static int handle_gsm_show_status(int fd, int argc, char *argv[]) +{ + int channel; + struct zt_pvt *pvt = NULL; + if (argc < 4) { + return RESULT_SHOWUSAGE; + } + channel = atoi(argv[3]); + if (channel < 1) { + ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]); + return RESULT_SUCCESS; + } + pvt = iflist; + while (pvt) { + if (pvt->channel == channel) { + if (pvt->owner) { + ast_cli(fd, "Channel in use.\n"); + return RESULT_FAILURE; + } else { + ast_mutex_lock(&pvt->lock); + gsm_request_status(pvt->gsm.modul); + ast_mutex_unlock(&pvt->lock); + return RESULT_SUCCESS; + } + } + pvt = pvt->next; + } + + return RESULT_SUCCESS; +} + +static struct ast_cli_entry gsm_show_status = { + { "gsm", "show", "status", NULL }, handle_gsm_show_status, "Displays status information about the GSM channel.", gsm_show_status_help, complete_span_4 }; + +#endif /* ZAPATA_GSM */ + +static int app_zapEC(struct ast_channel *chan, void *data) +{ + int res=-1; + struct zt_pvt *p = NULL; + + if (!data) { + ast_log(LOG_WARNING, "zapEC requires one argument (on | off)\n"); + } + if (chan && !strcasecmp("ZAP",chan->type)) { + p = chan->tech_pvt; + if (!p) return res; + if (!strcasecmp("on",(char *)data)) { + zt_enable_ec(p); + res = 0; + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "Enabled echo cancelation on channel %s.\n", chan->name); + } + } else if (!strcasecmp("off",(char *)data)) { + zt_disable_ec(p); + res = 0; + if (option_verbose > 3) { + ast_verbose(VERBOSE_PREFIX_3 "Disabled echo cancelation on channel %s.\n", chan->name); + } + } else { + ast_log(LOG_WARNING, "Unknown argument %s to zapEC\n", (char *)data); + } + } else { + ast_log(LOG_WARNING, "zapNoEC only works on ZAP channels, check your extensions.conf!\n"); + res = 0; + } + + return res; +} + +static char *zapEC_tdesc = "Enable/disable Echo cancelation"; +static char *zapEC_app = "zapEC"; +static char *zapEC_synopsis = "Enable/Disable Echo Cancelation on a Zap channel"; + + + #ifdef ZAPATA_R2 static int handle_r2_no_debug(int fd, int argc, char *argv[]) { @@ -10017,6 +11702,14 @@ pthread_cancel(pris[i].master); } ast_cli_unregister_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(zap_pri_cli[0])); + ast_unregister_application(zapCD_app); + ast_unregister_application(zapInband_app); +#endif +#ifdef ZAPATA_GSM + ast_cli_unregister_multiple(zap_gsm_cli, sizeof(zap_gsm_cli) / sizeof(zap_gsm_cli[0])); + ast_cli_unregister(&gsm_send_sms); + ast_cli_unregister(&gsm_send_pdu); + ast_cli_unregister(&gsm_show_status); #endif #ifdef ZAPATA_R2 ast_cli_unregister_multiple(zap_r2_cli, sizeof(zap_r2_cli) / sizeof(zap_r2_cli[0])); @@ -10028,6 +11721,7 @@ ast_manager_unregister( "ZapDNDoff" ); ast_manager_unregister( "ZapDNDon" ); ast_manager_unregister("ZapShowChannels"); + ast_unregister_application(zapEC_app); ast_channel_unregister(&zap_tech); if (!ast_mutex_lock(&iflock)) { /* Hangup all interfaces if they have an owner */ @@ -10386,8 +12080,8 @@ } } else if (!strcasecmp(v->name, "echotraining")) { if (sscanf(v->value, "%d", &y) == 1) { - if ((y < 10) || (y > 4000)) { - ast_log(LOG_WARNING, "Echo training time must be within the range of 10 to 2000 ms at line %d\n", v->lineno); + if ((y < 10) || (y > 1000)) { + ast_log(LOG_WARNING, "Echo training time must be within the range of 10 to 1000 ms at line %d\n", v->lineno); } else { echotraining = y; } @@ -10573,12 +12267,33 @@ cur_signalling = SIG_GR303FXSKS; cur_radio = 0; pritype = PRI_CPE; + } else if (!strcasecmp(v->value, "bri_net_ptmp")) { + cur_radio = 0; + cur_signalling = SIG_PRI; + pritype = BRI_NETWORK_PTMP; + } else if (!strcasecmp(v->value, "bri_cpe_ptmp")) { + cur_signalling = SIG_PRI; + cur_radio = 0; + pritype = BRI_CPE_PTMP; + } else if (!strcasecmp(v->value, "bri_net")) { + cur_radio = 0; + cur_signalling = SIG_PRI; + pritype = BRI_NETWORK; + } else if (!strcasecmp(v->value, "bri_cpe")) { + cur_signalling = SIG_PRI; + cur_radio = 0; + pritype = BRI_CPE; #endif #ifdef ZAPATA_R2 } else if (!strcasecmp(v->value, "r2")) { cur_signalling = SIG_R2; cur_radio = 0; #endif +#ifdef ZAPATA_GSM + } else if (!strcasecmp(v->value, "gsm")) { + cur_signalling = SIG_GSM; + cur_radio = 0; +#endif } else { ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value); } @@ -10661,8 +12376,20 @@ priindication_oob = 1; else if (!strcasecmp(v->value, "inband")) priindication_oob = 0; + else if (!strcasecmp(v->value, "passthrough")) + priindication_oob = 2; + else + ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' , 'outofband' or 'passthrough' at line %d\n", + v->value, v->lineno); + } else if (!strcasecmp(v->name, "pritransfer")) { + if (!strcasecmp(v->value, "no")) + pritransfer = 0; + else if (!strcasecmp(v->value, "ect")) + pritransfer = 1; + else if (!strcasecmp(v->value, "hangup")) + pritransfer = 2; else - ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' or 'outofband' at line %d\n", + ast_log(LOG_WARNING, "'%s' is not a valid pri transfer value, should be 'no' , 'ect' or 'hangup' at line %d\n", v->value, v->lineno); } else if (!strcasecmp(v->name, "priexclusive")) { cur_priexclusive = ast_true(v->value); @@ -10676,6 +12403,14 @@ ast_copy_string(privateprefix, v->value, sizeof(privateprefix)); } else if (!strcasecmp(v->name, "unknownprefix")) { ast_copy_string(unknownprefix, v->value, sizeof(unknownprefix)); + } else if (!strcasecmp(v->name, "nocid")) { + ast_copy_string(nocid, v->value, sizeof(nocid) - 1); + } else if (!strcasecmp(v->name, "withheldcid")) { + ast_copy_string(withheldcid, v->value, sizeof(withheldcid) - 1); + } else if (!strcasecmp(v->name, "pin")) { + ast_copy_string(gsm_modem_pin, v->value, sizeof(gsm_modem_pin) - 1); + } else if (!strcasecmp(v->name, "exten")) { + ast_copy_string(gsm_modem_exten, v->value, sizeof(gsm_modem_exten) - 1); } else if (!strcasecmp(v->name, "resetinterval")) { if (!strcasecmp(v->value, "never")) resetinterval = -1; @@ -10692,6 +12427,8 @@ ast_copy_string(idleext, v->value, sizeof(idleext)); } else if (!strcasecmp(v->name, "idledial")) { ast_copy_string(idledial, v->value, sizeof(idledial)); + } else if (!strcasecmp(v->name, "pritrustusercid")) { + usercid = ast_true(v->value); } else if (!strcasecmp(v->name, "overlapdial")) { overlapdial = ast_true(v->value); } else if (!strcasecmp(v->name, "pritimer")) { @@ -10877,6 +12614,7 @@ #ifdef ZAPATA_PRI if (!reload) { for (x=0;xtech_pvt; + if (!p) return -1; + if (!p->pri) return -1; + if (strlen(text)) { + if (p->pri) { + if (!pri_grab(p, p->pri)) { + // ast_log(LOG_NOTICE, "Sending Display IE '%s'\n", text); + pri_information_display(p->pri->pri,p->call,(char *)text); + pri_rel(p->pri); + } else ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); + } + } + return 0; +} + +static int zt_sendtext(struct ast_channel *c, const char *text) { + struct zt_pvt *p = c->tech_pvt; + if (!p) return -1; + if (p->sig == SIG_PRI) { + return zt_pri_sendtext(c, text); + } else { + return zt_tdd_sendtext(c, text); + } +} + +static int zt_tdd_sendtext(struct ast_channel *c, const char *text) +#else static int zt_sendtext(struct ast_channel *c, const char *text) +#endif { #define END_SILENCE_LEN 400 #define HEADER_MS 50 @@ -10957,6 +12741,7 @@ float scont = 0.0; int index; + index = zt_get_index(c, p, 0); if (index < 0) { ast_log(LOG_WARNING, "Huh? I don't exist?\n"); diff -urN asterisk-1.2.10.orig/codecs/codec_ilbc.c asterisk-1.2.10/codecs/codec_ilbc.c --- asterisk-1.2.10.orig/codecs/codec_ilbc.c 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/codecs/codec_ilbc.c 2006-07-31 14:13:08.000000000 +0200 @@ -49,7 +49,7 @@ #include "slin_ilbc_ex.h" #include "ilbc_slin_ex.h" -#define USE_ILBC_ENHANCER 0 +#define USE_ILBC_ENHANCER 1 #define ILBC_MS 30 /* #define ILBC_MS 20 */ diff -urN asterisk-1.2.10.orig/configs/capi.conf.sample asterisk-1.2.10/configs/capi.conf.sample --- asterisk-1.2.10.orig/configs/capi.conf.sample 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/configs/capi.conf.sample 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,44 @@ +; +; CAPI config +; +; +[general] +nationalprefix=0 +internationalprefix=00 +rxgain=0.8 +txgain=0.8 + +[interfaces] + +; mode: ptmp (point-to-multipoint) or ptp (point-to-point) +isdnmode=ptmp +; allow incoming calls to this list of MSNs, * == any +incomingmsn=* +; capi controller number +controller=1 +; dialout group +group=1 +; enable/disable software dtmf detection, recommended for AVM cards +softdtmf=1 +; accountcode to use in CDRs +accountcode= +; context for incoming calls +context=capi-in +; _VERY_PRIMITIVE_ echo suppression +;echosquelch=1 +; EICON DIVA SERVER echo cancelation +;echocancel=yes +;echotail=64 +; call group +;callgroup=1 +; deflect incoming calls to 12345678 if all B channels are busy +;deflect=12345678 +; number of concurrent calls on this controller (2 makes sense for single BRI) +devices => 2 + + +;PointToPoint (55512-0) +;isdnmode=ptp +;msn=55512 +;controller=2 +;devices => 30 diff -urN asterisk-1.2.10.orig/configs/modules.conf.sample asterisk-1.2.10/configs/modules.conf.sample --- asterisk-1.2.10.orig/configs/modules.conf.sample 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/configs/modules.conf.sample 2006-07-31 14:13:08.000000000 +0200 @@ -51,3 +51,4 @@ ; exported to modules loaded after them. ; [global] +chan_capi.so=yes diff -urN asterisk-1.2.10.orig/configs/watchdog.conf.sample asterisk-1.2.10/configs/watchdog.conf.sample --- asterisk-1.2.10.orig/configs/watchdog.conf.sample 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/configs/watchdog.conf.sample 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,22 @@ +; +; Configuration file for res_watchdog +; +; type = isdnguard | watchdog +; device = /dev/... +; interval = interval to trigger the watchdog in ms + +;[ISDNguard-direct] +;type = isdnguard +;device = /dev/ttyS0 +;interval = 200 + +;[ISDNguard-with-daemon] +;type = isdnguard +;device = /var/run/guard.ctl +;interval = 200 + +;[kernel_watchdog] +;type = watchdog +;device = /dev/watchdog +;interval = 100 + diff -urN asterisk-1.2.10.orig/configs/zapata.conf.sample asterisk-1.2.10/configs/zapata.conf.sample --- asterisk-1.2.10.orig/configs/zapata.conf.sample 2006-04-28 18:40:32.000000000 +0200 +++ asterisk-1.2.10/configs/zapata.conf.sample 2006-07-31 14:13:08.000000000 +0200 @@ -121,9 +121,20 @@ ; ; outofband: Signal Busy/Congestion out of band with RELEASE/DISCONNECT ; inband: Signal Busy/Congestion using in-band tones +; passthrough: Listen to the telco ; ; priindication = outofband ; +; PRI/BRI transfers (HOLD -> SETUP -> ECT/Hangup) +; +; Configure how transfers are initiated. ECT should be preferred +; +; no: no transfers allowed (results in hangup) +; ect: use ECT (facility) +: hangup: transfer on hangup (if your phones dont support ECT) +; +; pritransfer = ect +; ; If you need to override the existing channels selection routine and force all ; PRI channels to be marked as exclusively selected, set this to yes. ; priexclusive = yes diff -urN asterisk-1.2.10.orig/db.c asterisk-1.2.10/db.c --- asterisk-1.2.10.orig/db.c 2006-01-09 19:09:53.000000000 +0100 +++ asterisk-1.2.10/db.c 2006-07-31 14:13:08.000000000 +0200 @@ -516,11 +516,18 @@ struct ast_cli_entry cli_database_deltree = { { "database", "deltree", NULL }, database_deltree, "Removes database keytree/values", database_deltree_usage }; +static char mandescr_dbput[] = +"Description: Put a value into astdb\n" +"Variables: \n" +" Family: ...\n" +" Key: ...\n" +" Value: ...\n"; + static int manager_dbput(struct mansession *s, struct message *m) { char *family = astman_get_header(m, "Family"); char *key = astman_get_header(m, "Key"); - char *val = astman_get_header(m, "Val"); + char *val = astman_get_header(m, "Value"); int res; if (!strlen(family)) { @@ -545,6 +552,12 @@ return 0; } +static char mandescr_dbget[] = +"Description: Get a value from astdb\n" +"Variables: \n" +" Family: ...\n" +" Key: ...\n"; + static int manager_dbget(struct mansession *s, struct message *m) { char *id = astman_get_header(m,"ActionID"); @@ -574,7 +587,7 @@ ast_cli(s->fd, "Event: DBGetResponse\r\n" "Family: %s\r\n" "Key: %s\r\n" - "Val: %s\r\n" + "Value: %s\r\n" "%s" "\r\n", family, key, tmp, idText); @@ -582,6 +595,39 @@ return 0; } +static char mandescr_dbdel[] = +"Description: remove value from astdb\n" +"Variables: \n" +" Family: ...\n" +" Key: ...\n"; + +static int manager_dbdel(struct mansession *s, struct message *m) +{ + char *family = astman_get_header(m, "Family"); + char *key = astman_get_header(m, "Key"); + char *id = astman_get_header(m,"ActionID"); + + if (!strlen(family)) { + astman_send_error(s, m, "No family specified"); + return 0; + } + if (!strlen(key)) { + astman_send_error(s, m, "No key specified"); + return 0; + } + + if (ast_db_del(family, key)) { + ast_cli(s->fd, "Response: Failed\r\n"); + } else { + ast_cli(s->fd, "Response: Success\r\n"); + } + if (id && !ast_strlen_zero(id)) + ast_cli(s->fd, "ActionID: %s\r\n",id); + ast_cli(s->fd, "\r\n"); + + return 0; +} + int astdb_init(void) { dbinit(); @@ -591,7 +637,8 @@ ast_cli_register(&cli_database_put); ast_cli_register(&cli_database_del); ast_cli_register(&cli_database_deltree); - ast_manager_register("DBGet", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry"); - ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry"); + ast_manager_register("DBget", EVENT_FLAG_SYSTEM, manager_dbget, mandescr_dbget); + ast_manager_register("DBput", EVENT_FLAG_SYSTEM, manager_dbput, mandescr_dbput); + ast_manager_register("DBdel", EVENT_FLAG_SYSTEM, manager_dbdel, mandescr_dbdel); return 0; } diff -urN asterisk-1.2.10.orig/devicestate.c asterisk-1.2.10/devicestate.c --- asterisk-1.2.10.orig/devicestate.c 2006-02-10 21:38:59.000000000 +0100 +++ asterisk-1.2.10/devicestate.c 2006-07-31 14:13:08.000000000 +0200 @@ -62,6 +62,8 @@ struct state_change { AST_LIST_ENTRY(state_change) list; + char cid_num[AST_MAX_EXTENSION]; + char cid_name[AST_MAX_EXTENSION]; char device[1]; }; @@ -177,7 +179,7 @@ } /*--- do_state_change: Notify callback watchers of change, and notify PBX core for hint updates */ -static void do_state_change(const char *device) +static void do_state_change(const char *device, char *cid_num, char *cid_name) { int state; struct devstate_cb *devcb; @@ -188,13 +190,13 @@ AST_LIST_LOCK(&devstate_cbs); AST_LIST_TRAVERSE(&devstate_cbs, devcb, list) - devcb->callback(device, state, devcb->data); + devcb->callback(device, state, devcb->data, cid_num, cid_name); AST_LIST_UNLOCK(&devstate_cbs); - ast_hint_state_changed(device); + ast_hint_state_changed(device, cid_num, cid_name); } -static int __ast_device_state_changed_literal(char *buf) +static int __ast_device_state_changed_literal(char *buf, char *cid_num, char *cid_name) { char *device, *tmp; struct state_change *change = NULL; @@ -209,10 +211,16 @@ if (!change) { /* we could not allocate a change struct, or */ /* there is no background thread, so process the change now */ - do_state_change(device); + do_state_change(device, cid_num, cid_name); } else { /* queue the change */ strcpy(change->device, device); + if (cid_num && (!ast_strlen_zero(cid_num))) { + strncpy(change->cid_num, cid_num, sizeof(change->cid_num) - 1); + } + if (cid_name && (!ast_strlen_zero(cid_name))) { + strncpy(change->cid_name, cid_name, sizeof(change->cid_name) - 1); + } AST_LIST_LOCK(&state_changes); AST_LIST_INSERT_TAIL(&state_changes, change, list); if (AST_LIST_FIRST(&state_changes) == change) @@ -224,11 +232,17 @@ return 1; } -int ast_device_state_changed_literal(const char *dev) +int ast_device_state_changed_literal(const char *dev, const char *cid_num, const char *cid_name) { char *buf; + char *buf2 = NULL; + char *buf3 = NULL; buf = ast_strdupa(dev); - return __ast_device_state_changed_literal(buf); + if (cid_num) + buf2 = ast_strdupa(cid_num); + if (cid_name) + buf3 = ast_strdupa(cid_name); + return __ast_device_state_changed_literal(buf, buf2, buf3); } /*--- ast_device_state_changed: Accept change notification, add it to change queue */ @@ -240,7 +254,7 @@ va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - return __ast_device_state_changed_literal(buf); + return __ast_device_state_changed_literal(buf, NULL, NULL); } /*--- do_devstate_changes: Go through the dev state change queue and update changes in the dev state thread */ @@ -255,7 +269,7 @@ if (cur) { /* we got an entry, so unlock the list while we process it */ AST_LIST_UNLOCK(&state_changes); - do_state_change(cur->device); + do_state_change(cur->device, cur->cid_num, cur->cid_name); free(cur); AST_LIST_LOCK(&state_changes); } else { diff -urN asterisk-1.2.10.orig/doc/README.asterisk.conf asterisk-1.2.10/doc/README.asterisk.conf --- asterisk-1.2.10.orig/doc/README.asterisk.conf 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/doc/README.asterisk.conf 2006-07-31 14:13:08.000000000 +0200 @@ -62,6 +62,7 @@ maxcalls = 255 ; The maximum number of concurrent calls you want to allow execincludes = yes | no ; Allow #exec entries in configuration files dontwarn = yes | no ; Don't over-inform the Asterisk sysadm, he's a guru +uniquename = asterisk ; host name part to be included in the uniqueid [files] ; Changing the following lines may compromise your security diff -urN asterisk-1.2.10.orig/editline/cygdef.h asterisk-1.2.10/editline/cygdef.h --- asterisk-1.2.10.orig/editline/cygdef.h 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/editline/cygdef.h 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,11 @@ +/* cygdef.h. Generated automatically by configure. */ +#ifndef _CYGDEF_H_ +#define _CYGDEF_H_ 1 +#include +#define __linux__ 1 + + +typedef void (*sig_t)(int); + + +#endif /* _CYGDEF_H_ */ diff -urN asterisk-1.2.10.orig/include/asterisk/agi.h asterisk-1.2.10/include/asterisk/agi.h --- asterisk-1.2.10.orig/include/asterisk/agi.h 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/agi.h 2006-07-31 14:13:08.000000000 +0200 @@ -29,7 +29,8 @@ typedef struct agi_state { int fd; /* FD for general output */ - int audio; /* FD for audio output */ + int audio_out; /* FD for audio output */ + int audio_in; /* FD for audio output */ int ctrl; /* FD for input control */ } AGI; diff -urN asterisk-1.2.10.orig/include/asterisk/chan_capi.h asterisk-1.2.10/include/asterisk/chan_capi.h --- asterisk-1.2.10.orig/include/asterisk/chan_capi.h 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/chan_capi.h 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,276 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#ifndef _ASTERISK_CAPI_H +#define _ASTERISK_CAPI_H + +#define AST_CAPI_MAX_CONTROLLERS 16 +#define AST_CAPI_MAX_DEVICES 30 +#define AST_CAPI_MAX_BUF 160 + +#define AST_CAPI_MAX_B3_BLOCKS 7 + +/* was : 130 bytes Alaw = 16.25 ms audio not suitable for VoIP */ +/* now : 160 bytes Alaw = 20 ms audio */ +/* you can tune this to your need. higher value == more latency */ +#define AST_CAPI_MAX_B3_BLOCK_SIZE 160 + +#define AST_CAPI_BCHANS 120 +#define ALL_SERVICES 0x1FFF03FF + +/* duration in ms for sending and detecting dtmfs */ +#define AST_CAPI_DTMF_DURATION 0x40 + +#define AST_CAPI_NATIONAL_PREF "0" +#define AST_CAPI_INTERNAT_PREF "00" + +#ifdef CAPI_ES +#define ECHO_TX_COUNT 5 // 5 x 20ms = 100ms +#define ECHO_EFFECTIVE_TX_COUNT 3 // 2 x 20ms = 40ms == 40-100ms ... ignore first 40ms +#define ECHO_TXRX_RATIO 2.3 // if( rx < (txavg/ECHO_TXRX_RATIO) ) rx=0; +#endif + +/* + * state combination for a normal incoming call: + * DIS -> ALERT -> CON -> BCON -> CON -> DIS + * + * outgoing call: + * DIS -> CONP -> BCONNECTED -> CON -> DIS + */ + +#define CAPI_STATE_ALERTING 1 +#define CAPI_STATE_CONNECTED 2 +#define CAPI_STATE_BCONNECTED 3 + +#define CAPI_STATE_DISCONNECTING 4 +#define CAPI_STATE_DISCONNECTED 5 +#define CAPI_STATE_REMOTE_HANGUP 6 + +#define CAPI_STATE_CONNECTPENDING 7 +#define CAPI_STATE_ONHOLD 8 +#define CAPI_STATE_NETWORKHANGUP 9 +#define CAPI_STATE_ANSWERING 10 +#define CAPI_STATE_PUTTINGONHOLD 11 +#define CAPI_STATE_RETRIEVING 12 + +#define CAPI_STATE_DID 13 + +#define AST_CAPI_B3_DONT 0 +#define AST_CAPI_B3_ALWAYS 1 +#define AST_CAPI_B3_ON_SUCCESS 2 + +#ifdef CAPI_GAIN +struct ast_capi_gains { + unsigned char txgains[256]; + unsigned char rxgains[256]; +}; +#endif + +#define PRES_ALLOWED_USER_NUMBER_NOT_SCREENED 0x00 +#define PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN 0x01 +#define PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN 0x02 +#define PRES_ALLOWED_NETWORK_NUMBER 0x03 +#define PRES_PROHIB_USER_NUMBER_NOT_SCREENED 0x20 +#define PRES_PROHIB_USER_NUMBER_PASSED_SCREEN 0x21 +#define PRES_PROHIB_USER_NUMBER_FAILED_SCREEN 0x22 +#define PRES_PROHIB_NETWORK_NUMBER 0x23 +#define PRES_NUMBER_NOT_AVAILABLE 0x43 + + +//! Private data for a capi device +struct ast_capi_pvt { + ast_mutex_t lock; + int fd; + + /*! Channel we belong to, possibly NULL */ + struct ast_channel *owner; + /*! Frame */ + struct ast_frame fr; + + char offset[AST_FRIENDLY_OFFSET]; + + // capi message number + _cword MessageNumber; + int NCCI; + int PLCI; + /* on which controller we do live */ + int controller; + + /* we could live on those */ + unsigned long controllers; + + int datahandle; + + short buf[AST_CAPI_MAX_BUF]; + int buflen; + /*! Immediate, or wait for an answer */ + int mode; + /*! State of modem in miniature */ + int state; + /*! Digits to strip on outgoing numbers */ + int stripmsd; + /*! ringer timeout */ + int ringt; + /*! actual time of last ring */ + time_t lastring; + /*! dtmf receive state/data */ + char dtmfrx; + + char context[AST_MAX_EXTENSION]; + /*! Multiple Subscriber Number we listen to (, seperated list) */ + char incomingmsn[AST_MAX_EXTENSION]; + /*! Prefix to Build CID */ + char prefix[AST_MAX_EXTENSION]; + /*! Caller ID if available */ + char cid[AST_MAX_EXTENSION]; + /*! Dialed Number if available */ + char dnid[AST_MAX_EXTENSION]; + + char accountcode[20]; + + unsigned int callgroup; + unsigned int group; + + /*! default language */ + char language[MAX_LANGUAGE]; + /*! Static response buffer */ + char response[256]; + + int calledPartyIsISDN; + // this is an outgoing channel + int outgoing; + // use CLIR + int CLIR; + // are we doing early B3 connect on this interface? + int earlyB3; + // should we do early B3 on this interface? + int doB3; + // store plci here for the call that is onhold + int onholdPLCI; + // do software dtmf detection + int doDTMF; + // CAPI echo cancellation + int doEC; + int ecOption; + int ecTail; + // isdnmode ptp or ptm + int isdnmode; +#ifdef CAPI_DEFLECT_ON_CIRCUITBUSY + // deflect on circuitbusy + char deflect2[AST_MAX_EXTENSION]; +#endif + + // not all codecs supply frames in nice 320 byte chunks + struct ast_smoother *smoother; + // ok, we stop to be nice and give them the lowest possible latency 130 samples * 2 = 260 bytes */ +#ifdef CAPI_SYNC + int B3in; + ast_mutex_t lockB3in; +#endif + + // do ECHO SURPRESSION + int doES; +#ifdef CAPI_ES + short txavg[ECHO_TX_COUNT]; + float rxmin; + float txmin; +#endif +#ifdef CAPI_GAIN + struct ast_capi_gains g; +#endif + float txgain; + float rxgain; + struct ast_dsp *vad; + + + struct capi_pipe *mypipe; + /*! Next channel in list */ + struct ast_capi_pvt *next; +}; + + +struct ast_capi_profile { + unsigned short ncontrollers; + unsigned short nbchannels; + unsigned char globaloptions; + unsigned char globaloptions2; + unsigned char globaloptions3; + unsigned char globaloptions4; + unsigned int b1protocols; + unsigned int b2protocols; + unsigned int b3protocols; + unsigned int reserved3[6]; + unsigned int manufacturer[5]; +}; + +struct capi_pipe { + // lock + ast_mutex_t lock; + + // fd for writing to the channel + int fd; + + // PLCI and NCCI of the B3 CON + int PLCI; + int NCCI; + // pointer to the interface + struct ast_capi_pvt *i; + // pointer to the channel + struct ast_channel *c; + // next pipe + struct capi_pipe *next; +}; + +struct ast_capi_controller { + // which controller is this? + int controller; + // how many bchans? + int nbchannels; + // free bchans + int nfreebchannels; + // DID + int isdnmode; + // features: + int dtmf; + int echocancel; + int sservices; // supplementray services + // supported sservices: + int holdretrieve; + int terminalportability; + int ECT; + int threePTY; + int CF; + int CD; + int MCID; + int CCBS; + int MWI; + int CCNR; + int CONF; +}; + + +// ETSI 300 102-1 information element identifiers +#define CAPI_ETSI_IE_CAUSE 0x08; +#define CAPI_ETSI_IE_PROGRESS_INDICATOR 0x1e; +#define CAPI_ETSI_IE_CALLED_PARTY_NUMBER 0x70; + +// ETIS 300 102-1 message types +#define CAPI_ETSI_ALERTING 0x01; +#define CAPI_ETSI_SETUP_ACKKNOWLEDGE 0x0d; +#define CAPI_ETSI_DISCONNECT 0x45; + +// ETSI 300 102-1 Numbering Plans +#define CAPI_ETSI_NPLAN_NATIONAL 0x20 +#define CAPI_ETSI_NPLAN_INTERNAT 0x10 + +#endif diff -urN asterisk-1.2.10.orig/include/asterisk/chan_capi_app.h asterisk-1.2.10/include/asterisk/chan_capi_app.h --- asterisk-1.2.10.orig/include/asterisk/chan_capi_app.h 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/chan_capi_app.h 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,30 @@ +/* + * (CAPI*) + * + * An implementation of Common ISDN API 2.0 for Asterisk + * + * include file for helper applications + * + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#ifndef _ASTERISK_CAPI_IF_H +#define _ASTERISK_CAPI_IF_H + +// exported symbols from chan_capi + +// important things we need +extern unsigned ast_capi_ApplID; +extern unsigned ast_capi_MessageNumber; +extern int capidebug; + +extern int capi_call(struct ast_channel *c, char *idest, int timeout); +extern int capi_detect_dtmf(struct ast_channel *c, int flag); +extern MESSAGE_EXCHANGE_ERROR _capi_put_cmsg(_cmsg *CMSG); + +#endif diff -urN asterisk-1.2.10.orig/include/asterisk/channel.h asterisk-1.2.10/include/asterisk/channel.h --- asterisk-1.2.10.orig/include/asterisk/channel.h 2006-06-01 22:27:50.000000000 +0200 +++ asterisk-1.2.10/include/asterisk/channel.h 2006-07-31 14:13:08.000000000 +0200 @@ -86,6 +86,9 @@ #ifndef _ASTERISK_CHANNEL_H #define _ASTERISK_CHANNEL_H +/* Max length of the uniqueid */ +#define AST_MAX_UNIQUEID 64 + #include #include #ifdef POLLCOMPAT @@ -381,7 +384,7 @@ unsigned int fout; /* Unique Channel Identifier */ - char uniqueid[32]; + char uniqueid[AST_MAX_UNIQUEID]; /* Why is the channel hanged up */ int hangupcause; @@ -398,6 +401,12 @@ /*! ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */ unsigned short transfercapability; + /*! ISDN Low Layer Compatibility */ + char lowlayercompat[16]; + + /*! ISDN High Layer Compatibility */ + char highlayercompat[4]; + struct ast_frame *readq; int alertpipe[2]; /*! Write translation path */ @@ -534,6 +543,11 @@ #define AST_STATE_MUTE (1 << 16) /*! @} */ +extern ast_mutex_t uniquelock; + +/*! \brief Change the state of a channel and the callerid of the calling channel*/ +int ast_setstate_and_cid(struct ast_channel *chan, int state, char *cid_num, char *cid_name); + /*! \brief Change the state of a channel */ int ast_setstate(struct ast_channel *chan, int state); @@ -570,7 +584,7 @@ * by the low level module * \return Returns an ast_channel on success, NULL on failure. */ -struct ast_channel *ast_request(const char *type, int format, void *data, int *status); +struct ast_channel *ast_request(const char *type, int format, void *data, int *status, char *uniqueid); /*! * \brief Request a channel of a given type, with data as optional information used @@ -585,9 +599,9 @@ * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state * to know if the call was answered or not. */ -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname); +struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, char *uniqueid); -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname, struct outgoing_helper *oh); +struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, struct outgoing_helper *oh, char *uniqueid); /*!\brief Register a channel technology (a new channel driver) * Called by a channel module to register the kind of channels it supports. @@ -840,6 +854,10 @@ /*--- ast_get_channel_by_exten_locked: Get channel by exten (and optionally context) and lock it */ struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context); +/*! Get channel by uniqueid (locks channel) */ +struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid); + + /*! Waits for a digit */ /*! * \param c channel to wait for a digit on @@ -910,6 +928,9 @@ p->owner pointer) that is affected by the change. The physical layer of the original channel is hung up. */ int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone); +int ast_channel_masquerade_locked(struct ast_channel *original, struct ast_channel *clone); + +char *ast_alloc_uniqueid(void); /*! Gives the string form of a given cause code */ /*! diff -urN asterisk-1.2.10.orig/include/asterisk/devicestate.h asterisk-1.2.10/include/asterisk/devicestate.h --- asterisk-1.2.10.orig/include/asterisk/devicestate.h 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/devicestate.h 2006-07-31 14:13:08.000000000 +0200 @@ -42,7 +42,7 @@ /*! Device is ringing */ #define AST_DEVICE_RINGING 6 -typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); +typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data, char *cid_num, char *cid_name); /*! \brief Convert device state to text string for output * \param devstate Current device state @@ -84,7 +84,7 @@ * callbacks for the changed extensions * Returns 0 on success, -1 on failure */ -int ast_device_state_changed_literal(const char *device); +int ast_device_state_changed_literal(const char *device, const char *cid_num, const char *cid_name); /*! \brief Registers a device state change callback * \param callback Callback diff -urN asterisk-1.2.10.orig/include/asterisk/features.h asterisk-1.2.10/include/asterisk/features.h --- asterisk-1.2.10.orig/include/asterisk/features.h 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/features.h 2006-07-31 14:13:08.000000000 +0200 @@ -45,6 +45,8 @@ }; +extern int ast_autoanswer_login(struct ast_channel *chan, void *data); +extern int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data); /*! \brief Park a call and read back parked location * \param chan the channel to actually be parked @@ -68,11 +70,19 @@ */ extern int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout); +extern int ast_hold_call(struct ast_channel *chan, struct ast_channel *host); +extern int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *host); +extern int ast_retrieve_call(struct ast_channel *chan, char *uniqueid); +extern int ast_retrieve_call_to_death(char *uniqueid); +extern struct ast_channel *ast_get_holded_call(char *uniqueid); + /*! \brief Determine system parking extension * Returns the call parking extension for drivers that provide special call parking help */ extern char *ast_parking_ext(void); +extern char *ast_parking_con(void); + /*! \brief Determine system call pickup extension */ extern char *ast_pickup_ext(void); @@ -92,4 +102,12 @@ \param feature the ast_call_feature object which was registered before*/ extern void ast_unregister_feature(struct ast_call_feature *feature); +/*! \brief find a feature by name + \param name of the feature to be returned */ +extern struct ast_call_feature *ast_find_feature(char *name); + +/*! \brief find a builtin feature by name + \param name of the feature to be returned */ +extern struct ast_call_feature *ast_find_builtin_feature(char *name); + #endif /* _AST_FEATURES_H */ diff -urN asterisk-1.2.10.orig/include/asterisk/manager.h asterisk-1.2.10/include/asterisk/manager.h --- asterisk-1.2.10.orig/include/asterisk/manager.h 2006-02-11 19:15:00.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/manager.h 2006-07-31 14:13:08.000000000 +0200 @@ -54,6 +54,7 @@ #define EVENT_FLAG_COMMAND (1 << 4) /* Ability to read/set commands */ #define EVENT_FLAG_AGENT (1 << 5) /* Ability to read/set agent info */ #define EVENT_FLAG_USER (1 << 6) /* Ability to read/set user info */ +#define EVENT_FLAG_EXTENSIONSTATUS (1 << 7) /* ExtensionStatus events */ /* Export manager structures */ #define AST_MAX_MANHEADERS 80 diff -urN asterisk-1.2.10.orig/include/asterisk/monitor.h asterisk-1.2.10/include/asterisk/monitor.h --- asterisk-1.2.10.orig/include/asterisk/monitor.h 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/monitor.h 2006-07-31 14:13:08.000000000 +0200 @@ -35,6 +35,8 @@ char write_filename[FILENAME_MAX]; char filename_base[FILENAME_MAX]; int filename_changed; + char target_url[FILENAME_MAX]; + char target_script[FILENAME_MAX]; char *format; int joinfiles; int (*stop)(struct ast_channel *chan, int need_lock); @@ -42,7 +44,7 @@ /* Start monitoring a channel */ int ast_monitor_start(struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock ); + const char *fname_base, const char *target_url, const char *target_script, int need_lock ); /* Stop monitoring a channel */ int ast_monitor_stop(struct ast_channel *chan, int need_lock); diff -urN asterisk-1.2.10.orig/include/asterisk/pbx.h asterisk-1.2.10/include/asterisk/pbx.h --- asterisk-1.2.10.orig/include/asterisk/pbx.h 2006-03-29 21:11:18.000000000 +0200 +++ asterisk-1.2.10/include/asterisk/pbx.h 2006-07-31 14:13:08.000000000 +0200 @@ -57,7 +57,7 @@ AST_EXTENSION_BUSY = 1 << 1, /*! All devices UNAVAILABLE/UNREGISTERED */ AST_EXTENSION_UNAVAILABLE = 1 << 2, - /*! All devices RINGING */ + /*! One or more devices RINGING */ AST_EXTENSION_RINGING = 1 << 3, }; @@ -80,7 +80,7 @@ struct ast_ignorepat; struct ast_sw; -typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data); +typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data, char *cid_num, char *cid_name); /*! Data structure associated with a custom function */ struct ast_custom_function { @@ -156,6 +156,8 @@ */ extern struct ast_app *pbx_findapp(const char *app); +void *ast_pbx_run_app(void *data); + /*! executes an application */ /*! * \param c channel to execute on @@ -563,11 +565,11 @@ /* Synchronously or asynchronously make an outbound call and send it to a particular extension */ -int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel); +int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid); /* Synchronously or asynchronously make an outbound call and send it to a particular application with given extension */ -int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel); +int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid); /* Evaluate a condition for non-falseness and return a boolean */ int pbx_checkcondition(char *condition); @@ -659,7 +661,7 @@ */ void ast_func_write(struct ast_channel *chan, const char *in, const char *value); -void ast_hint_state_changed(const char *device); +void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name); #if defined(__cplusplus) || defined(c_plusplus) } diff -urN asterisk-1.2.10.orig/include/asterisk/xlaw.h asterisk-1.2.10/include/asterisk/xlaw.h --- asterisk-1.2.10.orig/include/asterisk/xlaw.h 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/include/asterisk/xlaw.h 2006-07-31 14:13:08.000000000 +0200 @@ -0,0 +1,1665 @@ +#ifndef _ASTERISK_XLAW_H +#define _ASTERISK_XLAW_H + +#ifdef CAPI_ULAW +#define capiXLAW2INT(x) capiULAW2INT[x] +#define capiINT2XLAW(x) capiINT2ULAW[((unsigned short)x) >> 2] +#else +#define capiXLAW2INT(x) capiALAW2INT[x] +#define capiINT2XLAW(x) capiINT2ALAW[(x>>4)+4096] +#endif + +static unsigned char reversebits[256] = +{ +0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, +0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, +0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, +0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, +0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, +0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, +0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, +0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, +0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, +0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, +0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, +0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, +0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, +0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, +0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, +0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, +0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, +0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, +0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, +0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, +0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, +0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, +0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, +0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, +0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, +0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, +0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, +0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, +0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, +0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, +0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, +0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +#ifdef CAPI_ULAW +static short capiULAW2INT[] = +{ +0x8284, 0x7d7c, 0xf8a4, 0x075c, 0xe104, 0x1efc, 0xfe8c, 0x0174, +0xc184, 0x3e7c, 0xfc94, 0x036c, 0xf0c4, 0x0f3c, 0xff88, 0x0078, +0xa284, 0x5d7c, 0xfaa4, 0x055c, 0xe904, 0x16fc, 0xff0c, 0x00f4, +0xd184, 0x2e7c, 0xfd94, 0x026c, 0xf4c4, 0x0b3c, 0xffc8, 0x0038, +0x9284, 0x6d7c, 0xf9a4, 0x065c, 0xe504, 0x1afc, 0xfecc, 0x0134, +0xc984, 0x367c, 0xfd14, 0x02ec, 0xf2c4, 0x0d3c, 0xffa8, 0x0058, +0xb284, 0x4d7c, 0xfba4, 0x045c, 0xed04, 0x12fc, 0xff4c, 0x00b4, +0xd984, 0x267c, 0xfe14, 0x01ec, 0xf6c4, 0x093c, 0xffe8, 0x0018, +0x8a84, 0x757c, 0xf924, 0x06dc, 0xe304, 0x1cfc, 0xfeac, 0x0154, +0xc584, 0x3a7c, 0xfcd4, 0x032c, 0xf1c4, 0x0e3c, 0xff98, 0x0068, +0xaa84, 0x557c, 0xfb24, 0x04dc, 0xeb04, 0x14fc, 0xff2c, 0x00d4, +0xd584, 0x2a7c, 0xfdd4, 0x022c, 0xf5c4, 0x0a3c, 0xffd8, 0x0028, +0x9a84, 0x657c, 0xfa24, 0x05dc, 0xe704, 0x18fc, 0xfeec, 0x0114, +0xcd84, 0x327c, 0xfd54, 0x02ac, 0xf3c4, 0x0c3c, 0xffb8, 0x0048, +0xba84, 0x457c, 0xfc24, 0x03dc, 0xef04, 0x10fc, 0xff6c, 0x0094, +0xdd84, 0x227c, 0xfe54, 0x01ac, 0xf7c4, 0x083c, 0xfff8, 0x0008, +0x8684, 0x797c, 0xf8e4, 0x071c, 0xe204, 0x1dfc, 0xfe9c, 0x0164, +0xc384, 0x3c7c, 0xfcb4, 0x034c, 0xf144, 0x0ebc, 0xff90, 0x0070, +0xa684, 0x597c, 0xfae4, 0x051c, 0xea04, 0x15fc, 0xff1c, 0x00e4, +0xd384, 0x2c7c, 0xfdb4, 0x024c, 0xf544, 0x0abc, 0xffd0, 0x0030, +0x9684, 0x697c, 0xf9e4, 0x061c, 0xe604, 0x19fc, 0xfedc, 0x0124, +0xcb84, 0x347c, 0xfd34, 0x02cc, 0xf344, 0x0cbc, 0xffb0, 0x0050, +0xb684, 0x497c, 0xfbe4, 0x041c, 0xee04, 0x11fc, 0xff5c, 0x00a4, +0xdb84, 0x247c, 0xfe34, 0x01cc, 0xf744, 0x08bc, 0xfff0, 0x0010, +0x8e84, 0x717c, 0xf964, 0x069c, 0xe404, 0x1bfc, 0xfebc, 0x0144, +0xc784, 0x387c, 0xfcf4, 0x030c, 0xf244, 0x0dbc, 0xffa0, 0x0060, +0xae84, 0x517c, 0xfb64, 0x049c, 0xec04, 0x13fc, 0xff3c, 0x00c4, +0xd784, 0x287c, 0xfdf4, 0x020c, 0xf644, 0x09bc, 0xffe0, 0x0020, +0x9e84, 0x617c, 0xfa64, 0x059c, 0xe804, 0x17fc, 0xfefc, 0x0104, +0xcf84, 0x307c, 0xfd74, 0x028c, 0xf444, 0x0bbc, 0xffc0, 0x0040, +0xbe84, 0x417c, 0xfc64, 0x039c, 0xf004, 0x0ffc, 0xff7c, 0x0084, +0xdf84, 0x207c, 0xfe74, 0x018c, 0xf844, 0x07bc, 0x0000, 0x0000 +}; + +const unsigned char capiINT2ULAW[16384] = { +255,127,127,191,191,63,63,223,223,95,95,159,159,31,31,239, +239,111,111,175,175,47,47,207,207,79,79,143,143,15,15,247, +247,247,247,119,119,119,119,183,183,183,183,55,55,55,55,215, +215,215,215,87,87,87,87,151,151,151,151,23,23,23,23,231, +231,231,231,103,103,103,103,167,167,167,167,39,39,39,39,199, +199,199,199,71,71,71,71,135,135,135,135,7,7,7,7,251, +251,251,251,251,251,251,251,123,123,123,123,123,123,123,123,187, +187,187,187,187,187,187,187,59,59,59,59,59,59,59,59,219, +219,219,219,219,219,219,219,91,91,91,91,91,91,91,91,155, +155,155,155,155,155,155,155,27,27,27,27,27,27,27,27,235, +235,235,235,235,235,235,235,107,107,107,107,107,107,107,107,171, +171,171,171,171,171,171,171,43,43,43,43,43,43,43,43,203, +203,203,203,203,203,203,203,75,75,75,75,75,75,75,75,139, +139,139,139,139,139,139,139,11,11,11,11,11,11,11,11,243, +243,243,243,243,243,243,243,243,243,243,243,243,243,243,243,115, +115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,179, +179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,51, +51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,211, +211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,83, +83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,147, +147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,227, +227,227,227,227,227,227,227,227,227,227,227,227,227,227,227,99, +99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,163, +163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,35, +35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,195, +195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,67, +67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,131, +131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,125, +125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, +125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,189, +189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, +189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,61, +61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, +61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,221, +221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, +221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,93, +93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, +93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,157, +157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, +157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,29, +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,237, +237,237,237,237,237,237,237,237,237,237,237,237,237,237,237,237, +237,237,237,237,237,237,237,237,237,237,237,237,237,237,237,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, +109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,173, +173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, +173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,45, +45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, +45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,205, +205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205, +205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,77, +77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, +77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,141, +141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, +141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,245, +245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, +245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, +245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, +245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,117, +117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, +117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, +117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, +117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, +181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,53, +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, +53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,213, +213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, +213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, +213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, +213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,85, +85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, +85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, +85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, +85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,149, +149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, +149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, +149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, +149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,21, +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,229, +229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, +229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, +229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, +229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, +101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,165, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,197, +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, +197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,69, +69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, +69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, +69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, +69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,133, +133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, +133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, +133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, +133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,249, +249,249,249,249,249,249,249,249,249,249,249,249,249,249,249,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, +121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, +185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, +57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, +89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, +153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,233, +233,233,233,233,233,233,233,233,233,233,233,233,233,233,233,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, +105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, +41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,201, +201,201,201,201,201,201,201,201,201,201,201,201,201,201,201,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73, +73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, +241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, +177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, +49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, +209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, +81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, +145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, +225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, +161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, +193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, +65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, +129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +128,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, +64,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, +192,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, +96,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, +224,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, +144,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, +80,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, +208,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, +48,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, +176,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, +112,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +240,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, +136,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, +72,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, +200,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, +40,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, +168,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, +104,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232, +232,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, +152,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88, +88,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, +216,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56, +56,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, +184,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120, +120,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,248,248,248,248,248,248,248,248,248,248,248,248,248,248,248, +248,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, +132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, +132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, +132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, +132,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, +196,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, +164,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, +100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, +100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, +100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, +100,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, +228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, +228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, +228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, +228,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, +148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, +148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, +148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, +148,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, +84,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, +212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, +212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, +212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, +212,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, +52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, +52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, +52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, +52,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, +180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, +180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, +180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, +180,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, +116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, +116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, +116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, +116,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, +140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, +140,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76, +76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76, +76,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204, +204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204, +204,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44, +44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44, +44,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, +172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, +172,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, +108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, +108,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, +236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, +236,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, +28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, +28,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, +156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, +156,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, +92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, +92,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220, +220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220, +220,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, +60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, +60,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, +188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, +188,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, +124,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252, +252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252, +252,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, +130,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, +66,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194, +194,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, +162,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98, +98,226,226,226,226,226,226,226,226,226,226,226,226,226,226,226, +226,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, +146,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82, +82,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210, +210,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, +50,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, +178,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, +114,242,242,242,242,242,242,242,242,242,242,242,242,242,242,242, +242,10,10,10,10,10,10,10,10,138,138,138,138,138,138,138, +138,74,74,74,74,74,74,74,74,202,202,202,202,202,202,202, +202,42,42,42,42,42,42,42,42,170,170,170,170,170,170,170, +170,106,106,106,106,106,106,106,106,234,234,234,234,234,234,234, +234,26,26,26,26,26,26,26,26,154,154,154,154,154,154,154, +154,90,90,90,90,90,90,90,90,218,218,218,218,218,218,218, +218,58,58,58,58,58,58,58,58,186,186,186,186,186,186,186, +186,122,122,122,122,122,122,122,122,250,250,250,250,250,250,250, +250,6,6,6,6,134,134,134,134,70,70,70,70,198,198,198, +198,38,38,38,38,166,166,166,166,102,102,102,102,230,230,230, +230,22,22,22,22,150,150,150,150,86,86,86,86,214,214,214, +214,54,54,54,54,182,182,182,182,118,118,118,118,246,246,246, +246,14,14,142,142,78,78,206,206,46,46,174,174,110,110,238, +238,30,30,158,158,94,94,222,222,62,62,190,190,126,126,254, +}; +#else +static short capiALAW2INT[] = +{ + 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, + 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, + 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, + 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, + 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, + 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, + 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, + 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, + 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, + 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, + 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, + 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, + 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, + 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, + 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, + 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, + 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, + 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, + 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, + 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, + 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, + 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, + 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, + 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, + 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, + 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, + 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, + 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, + 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, + 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, + 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, + 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 +}; + +const unsigned char capiINT2ALAW[8192] = { + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, + 212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, + 212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, + 212,212,212,212,212,212,212,212,212,212,212,212,212,212,212,212, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, + 244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, + 244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, + 244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, + 52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, + 52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, + 52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, + 52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, + 196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, + 196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, + 196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, + 228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, + 228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, + 228,228,228,228,228,228,228,228,228,228,228,228,228,228,228,228, + 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, + 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, + 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, + 220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220, + 220,220,220,220,220,220,220,220,220,220,220,220,220,220,220,220, + 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, + 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, + 124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, + 124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, + 252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252, + 252,252,252,252,252,252,252,252,252,252,252,252,252,252,252,252, + 60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, + 60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, + 76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76, + 76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76, + 204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204, + 204,204,204,204,204,204,204,204,204,204,204,204,204,204,204,204, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, + 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, + 236,236,236,236,236,236,236,236,236,236,236,236,236,236,236,236, + 44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44, + 44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, + 80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, + 208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144, + 112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, + 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96, + 224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, + 88,88,88,88,88,88,88,88,216,216,216,216,216,216,216,216, + 24,24,24,24,24,24,24,24,152,152,152,152,152,152,152,152, + 120,120,120,120,120,120,120,120,248,248,248,248,248,248,248,248, + 56,56,56,56,56,56,56,56,184,184,184,184,184,184,184,184, + 72,72,72,72,72,72,72,72,200,200,200,200,200,200,200,200, + 8,8,8,8,8,8,8,8,136,136,136,136,136,136,136,136, + 104,104,104,104,104,104,104,104,232,232,232,232,232,232,232,232, + 40,40,40,40,40,40,40,40,168,168,168,168,168,168,168,168, + 86,86,86,86,214,214,214,214,22,22,22,22,150,150,150,150, + 118,118,118,118,246,246,246,246,54,54,54,54,182,182,182,182, + 70,70,70,70,198,198,198,198,6,6,6,6,134,134,134,134, + 102,102,102,102,230,230,230,230,38,38,38,38,166,166,166,166, + 94,94,222,222,30,30,158,158,126,126,254,254,62,62,190,190, + 78,78,206,206,14,14,142,142,110,110,238,238,46,46,174,174, + 82,210,18,146,114,242,50,178,66,194,2,130,98,226,34,162, + 90,218,26,154,122,250,58,186,74,202,10,138,106,234,42,170, + 171,43,235,107,139,11,203,75,187,59,251,123,155,27,219,91, + 163,35,227,99,131,3,195,67,179,51,243,115,147,19,211,83, + 175,175,47,47,239,239,111,111,143,143,15,15,207,207,79,79, + 191,191,63,63,255,255,127,127,159,159,31,31,223,223,95,95, + 167,167,167,167,39,39,39,39,231,231,231,231,103,103,103,103, + 135,135,135,135,7,7,7,7,199,199,199,199,71,71,71,71, + 183,183,183,183,55,55,55,55,247,247,247,247,119,119,119,119, + 151,151,151,151,23,23,23,23,215,215,215,215,87,87,87,87, + 169,169,169,169,169,169,169,169,41,41,41,41,41,41,41,41, + 233,233,233,233,233,233,233,233,105,105,105,105,105,105,105,105, + 137,137,137,137,137,137,137,137,9,9,9,9,9,9,9,9, + 201,201,201,201,201,201,201,201,73,73,73,73,73,73,73,73, + 185,185,185,185,185,185,185,185,57,57,57,57,57,57,57,57, + 249,249,249,249,249,249,249,249,121,121,121,121,121,121,121,121, + 153,153,153,153,153,153,153,153,25,25,25,25,25,25,25,25, + 217,217,217,217,217,217,217,217,89,89,89,89,89,89,89,89, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, + 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, + 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, + 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, + 49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49, + 241,241,241,241,241,241,241,241,241,241,241,241,241,241,241,241, + 113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, + 145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 209,209,209,209,209,209,209,209,209,209,209,209,209,209,209,209, + 81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, + 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, + 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, + 237,237,237,237,237,237,237,237,237,237,237,237,237,237,237,237, + 237,237,237,237,237,237,237,237,237,237,237,237,237,237,237,237, + 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205, + 205,205,205,205,205,205,205,205,205,205,205,205,205,205,205,205, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, + 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, + 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, + 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, + 157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, + 157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, + 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, + 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, + 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, + 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, + 93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, + 93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, + 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, + 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, + 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, + 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, + 229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, + 229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, + 229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, + 229,229,229,229,229,229,229,229,229,229,229,229,229,229,229,229, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, + 197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, + 197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, + 197,197,197,197,197,197,197,197,197,197,197,197,197,197,197,197, + 69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, + 69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, + 69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, + 69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, + 53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, + 53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, + 53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, + 53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53, + 245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, + 245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, + 245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, + 245,245,245,245,245,245,245,245,245,245,245,245,245,245,245,245, + 117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, + 117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, + 117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, + 117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, + 149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, + 213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, + 213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, + 213,213,213,213,213,213,213,213,213,213,213,213,213,213,213,213, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, + 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85 +}; + +#endif // CAPI_ULAW +#endif + diff -urN asterisk-1.2.10.orig/include/asterisk.h asterisk-1.2.10/include/asterisk.h --- asterisk-1.2.10.orig/include/asterisk.h 2005-11-30 04:37:37.000000000 +0100 +++ asterisk-1.2.10/include/asterisk.h 2006-07-31 14:13:08.000000000 +0200 @@ -36,6 +36,7 @@ extern char ast_config_AST_PID[AST_CONFIG_MAX_PATH]; extern char ast_config_AST_SOCKET[AST_CONFIG_MAX_PATH]; extern char ast_config_AST_RUN_DIR[AST_CONFIG_MAX_PATH]; +extern char ast_config_AST_SYMBOLIC_NAME[20]; extern char ast_config_AST_CTL_PERMISSIONS[AST_CONFIG_MAX_PATH]; extern char ast_config_AST_CTL_OWNER[AST_CONFIG_MAX_PATH]; extern char ast_config_AST_CTL_GROUP[AST_CONFIG_MAX_PATH]; diff -urN asterisk-1.2.10.orig/manager.c asterisk-1.2.10/manager.c --- asterisk-1.2.10.orig/manager.c 2006-02-11 19:15:00.000000000 +0100 +++ asterisk-1.2.10/manager.c 2006-07-31 14:13:08.000000000 +0200 @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2003-2004, Junghanns.NET Gmbh + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -62,6 +65,7 @@ #include "asterisk/md5.h" #include "asterisk/acl.h" #include "asterisk/utils.h" +#include "asterisk/astdb.h" struct fast_originate_helper { char tech[AST_MAX_MANHEADER_LEN]; @@ -76,6 +80,8 @@ char idtext[AST_MAX_MANHEADER_LEN]; char account[AST_MAX_ACCOUNT_CODE]; int priority; + int callingpres; + char uniqueid[64]; struct ast_variable *vars; }; @@ -99,6 +105,7 @@ { EVENT_FLAG_COMMAND, "command" }, { EVENT_FLAG_AGENT, "agent" }, { EVENT_FLAG_USER, "user" }, + { EVENT_FLAG_EXTENSIONSTATUS, "extensionstatus" }, { -1, "all" }, { 0, "none" }, }; @@ -657,11 +664,17 @@ { struct ast_channel *c = NULL; char *name = astman_get_header(m, "Channel"); - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "No channel specified"); + char *uniqueid = astman_get_header(m, "Uniqueid"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel or uniqueid specified"); return 0; } - c = ast_get_channel_by_name_locked(name); + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + } else { + if (!ast_strlen_zero(name)) + c = ast_get_channel_by_name_locked(name); + } if (!c) { astman_send_error(s, m, "No such channel"); return 0; @@ -760,6 +773,7 @@ } + /*! \brief action_status: Manager "status" command to show channels */ /* Needs documentation... */ static int action_status(struct mansession *s, struct message *m) @@ -866,32 +880,50 @@ char *exten = astman_get_header(m, "Exten"); char *context = astman_get_header(m, "Context"); char *priority = astman_get_header(m, "Priority"); + char *uniqueid = astman_get_header(m, "Uniqueid"); + char *uniqueid2 = astman_get_header(m, "ExtraUniqueid"); + char *exten2 = astman_get_header(m, "ExtraExten"); + char *context2 = astman_get_header(m, "ExtraContext"); + char *priority2 = astman_get_header(m, "ExtraPriority"); struct ast_channel *chan, *chan2 = NULL; int pi = 0; + int pi2 = 0; int res; - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "Channel not specified"); + if ((!name || ast_strlen_zero(name)) && (!uniqueid || ast_strlen_zero(uniqueid))) { + astman_send_error(s, m, "Channel or Uniqueid not specified"); return 0; } if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) { astman_send_error(s, m, "Invalid priority\n"); return 0; } - chan = ast_get_channel_by_name_locked(name); + if (uniqueid && (!ast_strlen_zero(uniqueid))) { + chan = ast_get_channel_by_uniqueid_locked(uniqueid); + } else { + chan = ast_get_channel_by_name_locked(name); + } if (!chan) { char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "Channel does not exist: %s", name); astman_send_error(s, m, buf); return 0; } - if (!ast_strlen_zero(name2)) + if (!ast_strlen_zero(uniqueid2)) { + chan2 = ast_get_channel_by_uniqueid_locked(uniqueid2); + if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%d", &pi2) != 1)) { + astman_send_error(s, m, "Invalid priority2\n"); + return 0; + } + } else { + if (!ast_strlen_zero(name2)) chan2 = ast_get_channel_by_name_locked(name2); + } res = ast_async_goto(chan, context, exten, pi); if (!res) { - if (!ast_strlen_zero(name2)) { + if ((!ast_strlen_zero(name2)) || (!ast_strlen_zero(uniqueid2))){ if (chan2) - res = ast_async_goto(chan2, context, exten, pi); + res = ast_async_goto(chan2, context2, exten2, pi2); else res = -1; if (!res) @@ -937,15 +969,15 @@ struct ast_channel *chan = NULL; if (!ast_strlen_zero(in->app)) { - res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, + res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, in->callingpres, !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL, - in->vars, in->account, &chan); + in->vars, in->account, &chan, in->uniqueid); } else { - res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, + res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, in->callingpres, !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL, - in->vars, in->account, &chan); + in->vars, in->account, &chan, in->uniqueid); } if (!res) manager_event(EVENT_FLAG_CALL, @@ -956,7 +988,7 @@ "Exten: %s\r\n" "Reason: %d\r\n" "Uniqueid: %s\r\n", - in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : ""); + in->idtext, in->tech, in->data, in->context, in->exten, reason, in->uniqueid ? in->uniqueid : (chan ? chan->uniqueid : "")); else manager_event(EVENT_FLAG_CALL, "OriginateFailure", @@ -966,7 +998,7 @@ "Exten: %s\r\n" "Reason: %d\r\n" "Uniqueid: %s\r\n", - in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : ""); + in->idtext, in->tech, in->data, in->context, in->exten, reason, in->uniqueid ? in->uniqueid : (chan ? chan->uniqueid : "")); /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */ if (chan) @@ -999,6 +1031,7 @@ char *priority = astman_get_header(m, "Priority"); char *timeout = astman_get_header(m, "Timeout"); char *callerid = astman_get_header(m, "CallerID"); + char *callingpres = astman_get_header(m, "CallingPres"); char *account = astman_get_header(m, "Account"); char *app = astman_get_header(m, "Application"); char *appdata = astman_get_header(m, "Data"); @@ -1007,12 +1040,15 @@ struct ast_variable *vars = astman_get_variables(m); char *tech, *data; char *l=NULL, *n=NULL; + char *uniqueid; int pi = 0; + int cpresi = 0; int res; int to = 30000; int reason = 0; char tmp[256]; char tmp2[256]; + char idText[256] = ""; pthread_t th; pthread_attr_t attr; @@ -1028,6 +1064,10 @@ astman_send_error(s, m, "Invalid timeout\n"); return 0; } + if (!ast_strlen_zero(callingpres) && (sscanf(callingpres, "%d", &cpresi) != 1)) { + astman_send_error(s, m, "Invalid CallingPres\n"); + return 0; + } ast_copy_string(tmp, name, sizeof(tmp)); tech = tmp; data = strchr(tmp, '/'); @@ -1048,6 +1088,7 @@ if (ast_strlen_zero(l)) l = NULL; } + uniqueid = ast_alloc_uniqueid(); if (ast_true(async)) { struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper)); if (!fast) { @@ -1068,8 +1109,10 @@ ast_copy_string(fast->context, context, sizeof(fast->context)); ast_copy_string(fast->exten, exten, sizeof(fast->exten)); ast_copy_string(fast->account, account, sizeof(fast->account)); + ast_copy_string(fast->uniqueid, uniqueid, sizeof(fast->uniqueid)); fast->timeout = to; fast->priority = pi; + fast->callingpres = cpresi; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ast_pthread_create(&th, &attr, fast_originate, fast)) { @@ -1079,19 +1122,28 @@ } } } else if (!ast_strlen_zero(app)) { - res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid); } else { if (exten && context && pi) - res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid); else { astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'"); return 0; } } - if (!res) - astman_send_ack(s, m, "Originate successfully queued"); - else + if (!res) { + if (id && !ast_strlen_zero(id)) { + snprintf(idText,256,"ActionID: %s\r\n",id); + } + ast_cli(s->fd, "Response: Success\r\n" + "%s" + "Message: Originate successfully queued\r\n" + "Uniqueid: %s\r\n" + "\r\n", + idText, uniqueid); + } else { astman_send_error(s, m, "Originate failed"); + } return 0; } @@ -1565,10 +1617,12 @@ return 0; } -static int manager_state_cb(char *context, char *exten, int state, void *data) +static int manager_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) { + char hint[256] = ""; + ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten); /* Notify managers of change */ - manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state); + manager_event(EVENT_FLAG_EXTENSIONSTATUS, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\nCallerID: \"%s\" <%s>\r\nHint: %s\r\n", exten, context, state, cid_num, cid_name, hint); return 0; } diff -urN asterisk-1.2.10.orig/pbx/pbx_spool.c asterisk-1.2.10/pbx/pbx_spool.c --- asterisk-1.2.10.orig/pbx/pbx_spool.c 2006-02-11 19:15:00.000000000 +0100 +++ asterisk-1.2.10/pbx/pbx_spool.c 2006-07-31 14:13:08.000000000 +0200 @@ -259,11 +259,11 @@ if (!ast_strlen_zero(o->app)) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries); - res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL); + res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, 0, o->cid_num, o->cid_name, o->vars, o->account, NULL, NULL); } else { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries); - res = ast_pbx_outgoing_exten(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL); + res = ast_pbx_outgoing_exten(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, 0, o->cid_num, o->cid_name, o->vars, o->account, NULL, NULL); } if (res) { ast_log(LOG_NOTICE, "Call failed to go through, reason %d\n", reason); diff -urN asterisk-1.2.10.orig/pbx.c asterisk-1.2.10/pbx.c --- asterisk-1.2.10.orig/pbx.c 2006-06-18 23:03:58.000000000 +0200 +++ asterisk-1.2.10/pbx.c 2006-07-31 14:13:08.000000000 +0200 @@ -353,7 +353,8 @@ { "Hangup", pbx_builtin_hangup, "Hang up the calling channel", - " Hangup(): This application will hang up the calling channel.\n" + " Hangup(Cause): Unconditionally hangs up a given channel by returning -1 always.\n" + " If cause is given, it will set the hangup cause accordingly.\n" }, { "NoOp", pbx_builtin_noop, @@ -1883,7 +1884,7 @@ return ast_extension_state2(e); /* Check all devices in the hint */ } -void ast_hint_state_changed(const char *device) +void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name) { struct ast_hint *hint; struct ast_state_cb *cblist; @@ -1911,11 +1912,11 @@ /* For general callbacks */ for (cblist = statecbs; cblist; cblist = cblist->next) - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name); /* For extension callbacks */ for (cblist = hint->callbacks; cblist; cblist = cblist->next) - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name); hint->laststate = state; break; @@ -2156,7 +2157,7 @@ /* Notify with -1 and remove all callbacks */ cbprev = cblist; cblist = cblist->next; - cbprev->callback(list->exten->parent->name, list->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data); + cbprev->callback(list->exten->parent->name, list->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data, NULL, NULL); free(cbprev); } list->callbacks = NULL; @@ -3777,7 +3778,7 @@ while (thiscb) { prevcb = thiscb; thiscb = thiscb->next; - prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data); + prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data, NULL, NULL); free(prevcb); } } else { @@ -4981,7 +4982,7 @@ return 0; /* success */ } -int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel) +int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, char *uniqueid) { struct ast_channel *chan; struct async_stat *as; @@ -4991,7 +4992,7 @@ if (sync) { LOAD_OH(oh); - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); if (channel) { *channel = chan; if (chan) @@ -5093,7 +5094,7 @@ goto outgoing_exten_cleanup; } memset(as, 0, sizeof(struct async_stat)); - chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name); + chan = ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, uniqueid); if (channel) { *channel = chan; if (chan) @@ -5139,7 +5140,7 @@ pthread_t t; }; -static void *ast_pbx_run_app(void *data) +void *ast_pbx_run_app(void *data) { struct app_tmp *tmp = data; struct ast_app *app; @@ -5155,7 +5156,7 @@ return NULL; } -int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel) +int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid) { struct ast_channel *chan; struct async_stat *as; @@ -5175,7 +5176,7 @@ goto outgoing_app_cleanup; } if (sync) { - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); if (chan) { if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */ ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name); @@ -5262,7 +5263,8 @@ goto outgoing_app_cleanup; } memset(as, 0, sizeof(struct async_stat)); - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid); +// chan = ast_request_and_dial(type, format, data, timeout, reason, callingpres, cid_num, cid_name, uniqueid); if (!chan) { free(as); res = -1; @@ -5551,6 +5553,9 @@ */ static int pbx_builtin_hangup(struct ast_channel *chan, void *data) { + /* Copy the hangup cause as specified */ + if (data) + chan->hangupcause = atoi(data); /* Just return non-zero and it will hang up */ if (!chan->hangupcause) chan->hangupcause = AST_CAUSE_NORMAL_CLEARING; @@ -6207,6 +6212,9 @@ return -1; } } + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } return res = ast_say_number(chan, atoi((char *) tmp), "", chan->language, options); } @@ -6214,8 +6222,12 @@ { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_digit_str(chan, (char *)data, "", chan->language); + } return res; } @@ -6223,8 +6235,12 @@ { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_character_str(chan, (char *)data, "", chan->language); + } return res; } @@ -6232,8 +6248,12 @@ { int res = 0; - if (data) + if (data) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } res = ast_say_phonetic_str(chan, (char *)data, "", chan->language); + } return res; } diff -urN asterisk-1.2.10.orig/res/Makefile asterisk-1.2.10/res/Makefile --- asterisk-1.2.10.orig/res/Makefile 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.10/res/Makefile 2006-07-31 14:13:08.000000000 +0200 @@ -11,7 +11,7 @@ # the GNU General Public License # -MODS=res_indications.so res_monitor.so res_adsi.so res_agi.so res_features.so +MODS=res_indications.so res_monitor.so res_adsi.so res_agi.so res_features.so res_watchdog.so ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/odbcinst.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/odbcinst.h),) ifneq (${OSARCH},FreeBSD) diff -urN asterisk-1.2.10.orig/res/res_agi.c asterisk-1.2.10/res/res_agi.c --- asterisk-1.2.10.orig/res/res_agi.c 2006-07-12 15:54:10.000000000 +0200 +++ asterisk-1.2.10/res/res_agi.c 2006-07-31 14:13:08.000000000 +0200 @@ -11,6 +11,9 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2005 Junghanns.NET GmbH + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -74,16 +77,19 @@ static char *app = "AGI"; +static char *xapp = "XAGI"; + static char *eapp = "EAGI"; static char *deadapp = "DeadAGI"; static char *synopsis = "Executes an AGI compliant application"; +static char *xsynopsis = "Executes an XAGI compliant application"; static char *esynopsis = "Executes an EAGI compliant application"; static char *deadsynopsis = "Executes AGI on a hungup channel"; static char *descrip = -" [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" +" [E|Dead|X]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" "program on a channel. AGI allows Asterisk to launch external programs\n" "written in any language to control a telephony channel, play audio,\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" @@ -92,6 +98,8 @@ " hangup, or 0 on non-hangup exit. \n" "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n" "on file descriptor 3\n\n" +"Using 'XAGI' provides enhanced AGI, with incoming audio available out of band" +" on file descriptor 3 and outgoing audio available out of band on file descriptor 4\n\n" "Use the CLI command 'show agi' to list available agi commands\n"; static int agidebug = 0; @@ -225,13 +233,14 @@ return 0; } -static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) +static int launch_script(char *script, char *argv[], int *fds, int *efd, int *efd2, int *opid) { char tmp[256]; int pid; int toast[2]; int fromast[2]; int audio[2]; + int audio2[2]; int x; int res; sigset_t signal_set; @@ -276,6 +285,33 @@ return -1; } } + if (efd2) { + if (pipe(audio2)) { + ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno)); + close(fromast[0]); + close(fromast[1]); + close(toast[0]); + close(toast[1]); + close(audio[0]); + close(audio[1]); + return -1; + } + res = fcntl(audio2[0], F_GETFL); + if (res > -1) + res = fcntl(audio2[0], F_SETFL, res | O_NONBLOCK); + if (res < 0) { + ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno)); + close(fromast[0]); + close(fromast[1]); + close(toast[0]); + close(toast[1]); + close(audio[0]); + close(audio[1]); + close(audio2[0]); + close(audio2[1]); + return -1; + } + } pid = fork(); if (pid < 0) { ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); @@ -293,15 +329,19 @@ } else { close(STDERR_FILENO + 1); } + if (efd2) { + dup2(audio2[1], STDERR_FILENO + 2); + } else { + close(STDERR_FILENO + 2); + } /* unblock important signal handlers */ if (sigfillset(&signal_set) || pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) { ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno)); exit(1); } - /* Close everything but stdin/out/error */ - for (x=STDERR_FILENO + 2;x<1024;x++) + for (x=STDERR_FILENO + 3;x<1024;x++) close(x); /* Execute script */ @@ -317,6 +357,9 @@ if (efd) { *efd = audio[1]; } + if (efd2) { + *efd2 = audio2[0]; + } /* close what we're not using in the parent */ close(toast[1]); close(fromast[0]); @@ -325,6 +368,9 @@ /* [PHM 12/18/03] */ close(audio[0]); } + if (efd2) { + close(audio2[1]); + } *opid = pid; return 0; @@ -355,7 +401,7 @@ fdprintf(fd, "agi_context: %s\n", chan->context); fdprintf(fd, "agi_extension: %s\n", chan->exten); fdprintf(fd, "agi_priority: %d\n", chan->priority); - fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0"); + fdprintf(fd, "agi_enhanced: %d%s\n", enhanced, ".0"); /* User information */ fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : ""); @@ -387,7 +433,7 @@ return RESULT_SHOWUSAGE; if (sscanf(argv[3], "%d", &to) != 1) return RESULT_SHOWUSAGE; - res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); + res = ast_waitfordigit_full(chan, to, agi->audio_out, agi->ctrl); fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; @@ -563,7 +609,7 @@ else return RESULT_FAILURE; } - res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); + res = ast_waitstream_full(chan, argv[3], agi->audio_out, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length; @@ -623,7 +669,7 @@ else return RESULT_FAILURE; } - res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); + res = ast_waitstream_full(chan, argv[3], agi->audio_out, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream)?ast_tellstream(fs):max_length; @@ -635,7 +681,7 @@ /* If the user didnt press a key, wait for digitTimeout*/ if (res == 0 ) { - res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl); + res = ast_waitfordigit_full(chan, timeout, agi->audio_out, agi->ctrl); /* Make sure the new result is in the escape digits of the GET OPTION */ if ( !strchr(edigits,res) ) res=0; @@ -662,7 +708,7 @@ return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl); + res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio_out, agi->ctrl); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -682,7 +728,7 @@ if (sscanf(argv[2], "%d", &num) != 1) return RESULT_SHOWUSAGE; - res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -699,7 +745,7 @@ if (argc != 4) return RESULT_SHOWUSAGE; - res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -789,7 +835,7 @@ if (argc != 4) return RESULT_SHOWUSAGE; - res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); + res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio_out, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); @@ -816,7 +862,7 @@ max = atoi(argv[4]); else max = 1024; - res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); + res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio_out, agi->ctrl); if (res == 2) /* New command */ return RESULT_SUCCESS; else if (res == 1) @@ -1854,7 +1900,12 @@ int ms; int returnstatus = 0; struct ast_frame *f; + struct ast_frame fr; char buf[2048]; + char audiobuf[2048]; + int audiobytes; + int fds[2]; + int enhanced = 0; FILE *readf; /* how many times we'll retry if ast_waitfor_nandfs will return without either channel or file descriptor in case select is interrupted by a system call (EINTR) */ @@ -1868,10 +1919,22 @@ return -1; } setlinebuf(readf); - setup_env(chan, request, agi->fd, (agi->audio > -1)); + if (agi->audio_out > -1) { + enhanced = 1; + } + if (agi->audio_in > -1) { + enhanced++; + } + setup_env(chan, request, agi->fd, enhanced); + fds[0] = agi->ctrl; + fds[1] = agi->audio_in; for (;;) { ms = -1; - c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); + if (agi->audio_in > -1) { + c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, fds, 2, NULL, &outfd, &ms); + } else { + c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); + } if (c) { retry = RETRY; /* Idle the channel until we get a command */ @@ -1882,13 +1945,24 @@ break; } else { /* If it's voice, write it to the audio pipe */ - if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) { + if ((agi->audio_out > -1) && (f->frametype == AST_FRAME_VOICE)) { /* Write, ignoring errors */ - write(agi->audio, f->data, f->datalen); + write(agi->audio_out, f->data, f->datalen); } ast_frfree(f); } } else if (outfd > -1) { + if ((agi->audio_in > -1) && (outfd == agi->audio_in)) { + audiobytes = read(agi->audio_in, audiobuf, sizeof(audiobuf)); + if (audiobytes > 0) { + // ast_log(LOG_NOTICE, "read %d bytes of audio\n", audiobytes); + fr.frametype = AST_FRAME_VOICE; + fr.subclass = AST_FORMAT_SLINEAR; + fr.datalen = audiobytes; + fr.data = audiobuf; + ast_write(chan, &fr); + } + } else { retry = RETRY; if (!fgets(buf, sizeof(buf), readf)) { /* Program terminated */ @@ -1910,6 +1984,7 @@ if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) { break; } + } } else { if (--retry <= 0) { ast_log(LOG_WARNING, "No channel, no fd?\n"); @@ -2016,6 +2091,7 @@ int argc = 0; int fds[2]; int efd = -1; + int efd2 = -1; int pid; char *stringp; AGI agi; @@ -2041,16 +2117,19 @@ } } #endif - res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid); + res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, (enhanced == 2) ? &efd2 : NULL, &pid); if (!res) { agi.fd = fds[1]; agi.ctrl = fds[0]; - agi.audio = efd; + agi.audio_out = efd; + agi.audio_in = efd2; res = run_agi(chan, argv[0], &agi, pid, dead); if (fds[1] != fds[0]) close(fds[1]); if (efd > -1) close(efd); + if (efd2 > -1) + close(efd2); } LOCAL_USER_REMOVE(u); return res; @@ -2083,6 +2162,35 @@ return res; } +static int xagi_exec(struct ast_channel *chan, void *data) +{ + int readformat, writeformat; + int res; + + if (chan->_softhangup) + ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); + readformat = chan->readformat; + if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { + ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name); + return -1; + } + writeformat = chan->writeformat; + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name); + return -1; + } + res = agi_exec_full(chan, data, 2, 0); + if (!res) { + if (ast_set_read_format(chan, readformat)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat)); + } + if (ast_set_write_format(chan, writeformat)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(writeformat)); + } + } + return res; +} + static int deadagi_exec(struct ast_channel *chan, void *data) { return agi_exec_full(chan, data, 0, 1); @@ -2112,6 +2220,7 @@ ast_cli_unregister(&dumpagihtml); ast_cli_unregister(&cli_debug); ast_cli_unregister(&cli_no_debug); + ast_unregister_application(xapp); ast_unregister_application(eapp); ast_unregister_application(deadapp); return ast_unregister_application(app); @@ -2125,6 +2234,7 @@ ast_cli_register(&cli_no_debug); ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip); ast_register_application(eapp, eagi_exec, esynopsis, descrip); + ast_register_application(xapp, xagi_exec, xsynopsis, descrip); return ast_register_application(app, agi_exec, synopsis, descrip); } diff -urN asterisk-1.2.10.orig/res/res_features.c asterisk-1.2.10/res/res_features.c --- asterisk-1.2.10.orig/res/res_features.c 2006-05-23 19:15:23.000000000 +0200 +++ asterisk-1.2.10/res/res_features.c 2006-08-02 09:55:40.000000000 +0200 @@ -11,6 +11,10 @@ * the project provides a web site, mailing lists and IRC * channels for your use. * + * Copyright (C) 2004, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. @@ -56,6 +60,7 @@ #include "asterisk/utils.h" #include "asterisk/adsi.h" #include "asterisk/monitor.h" +#include "asterisk/indications.h" #ifdef __AST_DEBUG_MALLOC static void FREE(void *ptr) @@ -73,6 +78,7 @@ #define AST_MAX_WATCHERS 256 static char *parkedcall = "ParkedCall"; +static char *holdedcall = "HoldedCall"; /* No more than 45 seconds parked before you do something with them */ static int parkingtime = DEFAULT_PARK_TIME; @@ -132,6 +138,20 @@ "into the dialplan, although you should include the 'parkedcalls'\n" "context.\n"; +static char *autoanswerlogin = "AutoanswerLogin"; + +static char *synopsis3 = "Log in for autoanswer"; + +static char *descrip3 = "AutoanswerLogin(exten):" +"Used to login to the autoanswer application for an extension.\n"; + +static char *autoanswer = "Autoanswer"; + +static char *synopsis4 = "Autoanswer a call"; + +static char *descrip4 = "Autoanswer(exten):" +"Used to autoanswer a call for an extension.\n"; + static struct ast_app *monitor_app=NULL; static int monitor_ok=1; @@ -150,12 +170,51 @@ struct parkeduser *next; }; +struct holdeduser { + struct ast_channel *chan; + struct timeval start; + int parkingnum; + int cref; + int tei; + /* Where to go if our parking time expires */ + char context[AST_MAX_EXTENSION]; + char exten[AST_MAX_EXTENSION]; + int priority; + int parkingtime; + char uniqueid[AST_MAX_UNIQUEID]; + char uniqueidpeer[AST_MAX_UNIQUEID]; + struct holdeduser *next; +}; + +/* auto answer user */ +struct aauser { + struct ast_channel *chan; + struct timeval start; + /* waiting on this extension/context */ + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_EXTENSION]; + int priority; + int notquiteyet; + struct aauser *next; +}; + + +static struct aauser *aalot; +AST_MUTEX_DEFINE_STATIC(autoanswer_lock); +static pthread_t autoanswer_thread; + static struct parkeduser *parkinglot; +static struct holdeduser *holdlist; + AST_MUTEX_DEFINE_STATIC(parking_lock); +AST_MUTEX_DEFINE_STATIC(holding_lock); + static pthread_t parking_thread; +static pthread_t holding_thread; + STANDARD_LOCAL_USER; LOCAL_USER_DECL; @@ -165,6 +224,12 @@ return parking_ext; } +char *ast_parking_con(void) +{ + return parking_con; +} + + char *ast_pickup_ext(void) { return pickup_ext; @@ -362,10 +427,11 @@ "Timeout: %ld\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Unqiueid: %s\r\n\r\n" ,pu->parkingnum, pu->chan->name, peer ? peer->name : "" ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL) ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "") - ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "") + ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : ""), pu->chan->uniqueid ); if (peer) { @@ -418,7 +484,8 @@ ast_copy_string(chan->context, rchan->context, sizeof(chan->context)); ast_copy_string(chan->exten, rchan->exten, sizeof(chan->exten)); chan->priority = rchan->priority; - + /* might be dirty but we want trackable channels */ + strncpy(chan->uniqueid, rchan->uniqueid, sizeof(chan->uniqueid) - 1); /* Make the masq execute */ f = ast_read(chan); if (f) @@ -906,7 +973,7 @@ } /* find a feature by name */ -static struct ast_call_feature *find_feature(char *name) +struct ast_call_feature *ast_find_feature(char *name) { struct ast_call_feature *tmp; @@ -916,10 +983,21 @@ break; } AST_LIST_UNLOCK(&feature_list); - return tmp; } +struct ast_call_feature *ast_find_builtin_feature(char *name) +{ + int x = 0; + + for (x = 0; x < FEATURES_COUNT; x++) { + if (!strcasecmp(name, builtin_features[x].sname)) { + return &builtin_features[x]; + } + } + return NULL; +} + /* exec an app by feature */ static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) { @@ -1018,7 +1096,7 @@ return res; while ((tok = strsep(&tmp, "#")) != NULL) { - feature = find_feature(tok); + feature = ast_find_feature(tok); if (feature) { /* Feature is up for consideration */ @@ -1071,7 +1149,7 @@ /* while we have a feature */ while (NULL != (tok = strsep(&tmp, "#"))) { - if ((feature = find_feature(tok))) { + if ((feature = ast_find_feature(tok))) { if (ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) { if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLER)) ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0); @@ -1096,7 +1174,7 @@ struct ast_frame *f = NULL; int res = 0, ready = 0; - if ((chan = ast_request(type, format, data, &cause))) { + if ((chan = ast_request(type, format, data, &cause, NULL))) { ast_set_callerid(chan, cid_num, cid_name, cid_num); ast_channel_inherit_variables(caller, chan); if (!ast_call(chan, data, timeout)) { @@ -1549,9 +1627,10 @@ "Channel: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n\r\n" ,pu->parkingnum, pu->chan->name ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "") - ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "") + ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : ""), pu->chan->uniqueid ); if (option_verbose > 1) @@ -1594,9 +1673,10 @@ "Channel: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n\r\n" ,pu->parkingnum, pu->chan->name ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "") - ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "") + ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : ""), pu->chan->uniqueid ); /* There's a problem, hang them up*/ @@ -1683,6 +1763,282 @@ return res; } +int ast_hold_call(struct ast_channel *chan, struct ast_channel *peer) +{ + /* We put the user in the parking list, then wake up the parking thread to be sure it looks + after these channels too */ + struct holdeduser *pu; + pu = malloc(sizeof(struct holdeduser)); + if (pu) { + memset(pu, 0, sizeof(pu)); + ast_mutex_lock(&holding_lock); + chan->appl = "Holded Call"; + chan->data = NULL; + + pu->chan = chan; + strncpy(pu->uniqueid, chan->uniqueid, sizeof(pu->uniqueid)); + strncpy(pu->uniqueidpeer, peer->uniqueid, sizeof(pu->uniqueidpeer)); + /* Start music on hold */ + ast_moh_start(pu->chan, NULL); + gettimeofday(&pu->start, NULL); + pu->next = holdlist; + holdlist = pu; + ast_mutex_unlock(&holding_lock); + /* Wake up the (presumably select()ing) thread */ + pthread_kill(holding_thread, SIGURG); + + manager_event(EVENT_FLAG_CALL, "HoldedCall", + "Channel1: %s\r\n" + "Channel2: %s\r\n" + "Uniqueid1: %s\r\n" + "Uniqueid2: %s\r\n" + ,pu->chan->name, peer->name, pu->chan->uniqueid, peer->uniqueid); + + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + return 0; +} + +int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *peer) +{ + struct ast_channel *chan; + struct ast_frame *f; + /* Make a new, fake channel that we'll use to masquerade in the real one */ + chan = ast_channel_alloc(0); + if (chan) { + /* Let us keep track of the channel name */ + snprintf(chan->name, sizeof (chan->name), "Onhold/%s",rchan->name); + /* Make formats okay */ + chan->readformat = rchan->readformat; + chan->writeformat = rchan->writeformat; + ast_channel_masquerade(chan, rchan); + /* Setup the extensions and such */ + strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); + strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); + chan->priority = rchan->priority; + /* this might be dirty, but we need to preserve the uniqueid */ + strncpy(chan->uniqueid, rchan->uniqueid, sizeof(chan->uniqueid) - 1); + /* Make the masq execute */ + f = ast_read(chan); + if (f) + ast_frfree(f); + ast_hold_call(chan, peer); + return -1; + } else { + ast_log(LOG_WARNING, "Unable to create holded channel\n"); + return -1; + } + return 0; +} + +int ast_retrieve_call(struct ast_channel *chan, char *uniqueid) +{ + int res=-1, dres=-1; + struct ast_channel *peer=NULL; + struct ast_bridge_config config; + + peer = ast_get_holded_call(uniqueid); + + /* JK02: it helps to answer the channel if not already up */ + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (peer) { + ast_mutex_unlock(&peer->lock); + ast_moh_stop(peer); + res = ast_channel_make_compatible(chan, peer); + if (res < 0) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ast_hangup(peer); + return -1; + } + /* This runs sorta backwards, since we give the incoming channel control, as if it + were the person called. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to holded call %s\n", chan->name, peer->name); + + memset(&config,0,sizeof(struct ast_bridge_config)); + ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); + ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); + config.timelimit = 0; + config.play_warning = 0; + config.warning_freq = 0; + config.warning_sound=NULL; + res = ast_bridge_call(chan,peer,&config); + + /* Simulate the PBX hanging up */ + if (res != AST_PBX_NO_HANGUP_PEER) + ast_hangup(peer); + return res; + } else { + /* XXX Play a message XXX */ + dres = ast_streamfile(chan, "pbx-invalidpark", chan->language); + if (!dres) + dres = ast_waitstream(chan, ""); + else { + ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name); + dres = 0; + } + } + return res; +} + +int ast_retrieve_call_to_death(char *uniqueid) +{ + int res=-1; + struct ast_channel *peer=NULL; + + peer = ast_get_holded_call(uniqueid); + + if (peer) { + res=0; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); + ast_mutex_unlock(&peer->lock); + ast_hangup(peer); + } else { + ast_log(LOG_WARNING, "Could not find channel with uniqueid %s to retrieve.\n", uniqueid); + } + return res; +} + +struct ast_channel *ast_get_holded_call(char *uniqueid) +{ + int res=-1; + struct ast_channel *peer=NULL; + struct holdeduser *pu, *pl=NULL; + + ast_mutex_lock(&holding_lock); + pu = holdlist; + while(pu) { + if (!strncmp(uniqueid,pu->uniqueid,sizeof(pu->uniqueid))) { + if (pl) + pl->next = pu->next; + else + holdlist = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&holding_lock); + if (pu) { + peer = ast_get_channel_by_uniqueid_locked(pu->uniqueid); + free(pu); + if (peer) { + res=0; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); + ast_moh_stop(peer); + return peer; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Could not find channel with uniqueid %s.\n", uniqueid); + return NULL; + } + } else { + ast_log(LOG_WARNING, "Could not find held channel with uniqueid %s to retrieve.\n", uniqueid); + } + return NULL; +} + +/* this is our autmagically service thread that keeps channels onhold happy */ +static void *do_holding_thread(void *ignore) +{ + int ms, tms, max; + struct holdeduser *pu, *pl, *pt = NULL; + struct timeval tv; + struct ast_frame *f; + int x; + fd_set rfds, efds; + fd_set nrfds, nefds; + FD_ZERO(&rfds); + FD_ZERO(&efds); + for (;;) { + ms = -1; + max = -1; + ast_mutex_lock(&holding_lock); + pl = NULL; + pu = holdlist; + gettimeofday(&tv, NULL); + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + while(pu) { + tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being onhold\n", pu->chan->name); + ast_hangup(pu->chan); + /* find the corresponding channel and hang them up too! */ + /* but only if it is not bridged yet! */ + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + holdlist = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { +std: for (x=0;xchan->fds[x] > -1) { + FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + } + } + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + pl = pu; + pu = pu->next; + } + } + ast_mutex_unlock(&holding_lock); + rfds = nrfds; + efds = nefds; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + /* Wait for something to happen */ + ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); + pthread_testcancel(); + } + return NULL; /* Never reached */ +} + +static int retrieve_call_exec(struct ast_channel *chan, void *data) { + int res=0; + struct localuser *u; + char *uniqueid = (char *)data; + LOCAL_USER_ADD(u); + res = ast_retrieve_call(chan, uniqueid); + LOCAL_USER_REMOVE(u); + return res; +} + static int park_exec(struct ast_channel *chan, void *data) { int res=0; @@ -1731,9 +2087,10 @@ "From: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n\r\n" ,pu->parkingnum, pu->chan->name, chan->name ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "") - ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "") + ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : ""), pu->chan->uniqueid ); free(pu); @@ -1901,12 +2258,13 @@ "Timeout: %ld\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n" "%s" "\r\n" ,cur->parkingnum, cur->chan->name ,(long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL) ,(cur->chan->cid.cid_num ? cur->chan->cid.cid_num : "") - ,(cur->chan->cid.cid_name ? cur->chan->cid.cid_name : "") + ,(cur->chan->cid.cid_name ? cur->chan->cid.cid_name : ""), cur->chan->uniqueid ,idText); cur = cur->next; @@ -1922,6 +2280,416 @@ return RESULT_SUCCESS; } +static int handle_autoanswer(int fd, int argc, char *argv[]) +{ + struct aauser *cur; + + ast_cli(fd, "%25s %10s %15s \n", "Channel" + , "Extension", "Context"); + + ast_mutex_lock(&autoanswer_lock); + + cur=aalot; + while(cur) { + ast_cli(fd, "%25s %10s %15s\n",cur->chan->name, cur->exten, cur->context); + + cur = cur->next; + } + + ast_mutex_unlock(&autoanswer_lock); + + return RESULT_SUCCESS; +} +static char showautoanswer_help[] = +"Usage: show autoanswer\n" +" Lists currently logged in autoanswr channels.\n"; + +static struct ast_cli_entry showautoanswer = +{ { "show", "autoanswer", NULL }, handle_autoanswer, "Lists autoanswer channels", showautoanswer_help }; + +int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data) +{ + struct ast_channel *chan; + struct ast_frame *f; + /* Make a new, fake channel that we'll use to masquerade in the real one */ + chan = ast_channel_alloc(0); + if (chan) { + /* Let us keep track of the channel name */ + snprintf(chan->name, sizeof (chan->name), "Autoanswer/%s",rchan->name); + /* Make formats okay */ + chan->readformat = rchan->readformat; + chan->writeformat = rchan->writeformat; + ast_channel_masquerade(chan, rchan); + /* Setup the extensions and such */ + strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); + strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); + chan->priority = rchan->priority; + /* Make the masq execute */ + f = ast_read(chan); + if (f) + ast_frfree(f); + ast_autoanswer_login(chan, data); + } else { + ast_log(LOG_WARNING, "Unable to create aa channel\n"); + return -1; + } + return 0; +} + +static int autoanswer_login_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + LOCAL_USER_ADD(u); + if (!data) { + ast_log(LOG_WARNING, "AutoanswerLogin requires an argument (extension number)\n"); + return -1; + } + res = ast_masq_autoanswer_login(chan, data); + LOCAL_USER_REMOVE(u); + return res; +} + +int ast_autoanswer_login(struct ast_channel *chan, void *data) +{ + /* We put the user in the parking list, then wake up the parking thread to be sure it looks + after these channels too */ + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + struct aauser *pu,*pl = NULL; + char *s, *stringp, *aacontext, *aaexten = NULL; + + s = ast_strdupa((void *) data); + stringp=s; + aacontext = strsep(&stringp, "|"); + aaexten = strsep(&stringp, "|"); + if (!aaexten) { + aaexten = aacontext; + aacontext = NULL; + } + if (!aaexten) { + ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); + return -1; + } else { + if (!aacontext) { + aacontext = "default"; + } + } + + ast_mutex_lock(&autoanswer_lock); + pu = aalot; + while(pu) { + if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&autoanswer_lock); + if (pu) { + ast_log(LOG_NOTICE, "Logout old Channel %s for %s@%s.\n",pu->chan->name, pu->exten, pu->context); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + ast_hangup(pu->chan); + free(pu); + } + pu = malloc(sizeof(struct aauser)); + if (pu) { + memset(pu, 0, sizeof(pu)); + ast_mutex_lock(&autoanswer_lock); + chan->appl = "Autoanswer"; + chan->data = NULL; + + pu->chan = chan; + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + /* Start music on hold */ + ast_moh_start(pu->chan, NULL); + gettimeofday(&pu->start, NULL); + strncpy(pu->exten, aaexten, sizeof(pu->exten)-1); + strncpy(pu->context, aacontext, sizeof(pu->exten)-1); + pu->next = aalot; + aalot = pu; + con = ast_context_find(aacontext); + if (!con) { + con = ast_context_create(NULL,aacontext, registrar); + if (!con) { + ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", aacontext); + } + } + if (con) { + snprintf(exten, sizeof(exten), "%s", aaexten); + ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)data), free, registrar); + } + + ast_mutex_unlock(&autoanswer_lock); + /* Wake up the (presumably select()ing) thread */ + pthread_kill(autoanswer_thread, SIGURG); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Autoanswer login from %s for %s@%s.\n", pu->chan->name, pu->exten, pu->context); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogin", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + + return 0; + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + return 0; +} + +static void autoanswer_reregister_extensions(void) +{ + struct aauser *cur; + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + char args[AST_MAX_EXTENSION]; + + ast_mutex_lock(&autoanswer_lock); + + cur=aalot; + while(cur) { + con = ast_context_find(cur->context); + if (!con) { + con = ast_context_create(NULL,cur->context, registrar); + if (!con) { + ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", cur->context); + } + } + if (con) { + snprintf(exten, sizeof(exten), "%s", cur->exten); + snprintf(args, sizeof(args), "%s|%s", cur->context, cur->exten); + ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)args), free, registrar); + } + cur = cur->next; + } + + ast_mutex_unlock(&autoanswer_lock); +} +static void *do_autoanswer_thread(void *ignore) +{ + int ms, tms, max; + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + struct aauser *pu, *pl, *pt = NULL; + struct timeval tv; + struct ast_frame *f; + int x; + fd_set rfds, efds; + fd_set nrfds, nefds; + FD_ZERO(&rfds); + FD_ZERO(&efds); + for (;;) { + ms = -1; + max = -1; + ast_mutex_lock(&autoanswer_lock); + pl = NULL; + pu = aalot; + gettimeofday(&tv, NULL); + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + while(pu) { + tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s logged out of autoanswer app\n", pu->chan->name); + manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); + ast_hangup(pu->chan); + con = ast_context_find(pu->context); + if (con) { + snprintf(exten, sizeof(exten), "%s", pu->exten); + if (ast_context_remove_extension2(con, exten, 1, registrar)) + ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); + } else { + ast_log(LOG_WARNING, "Whoa, no %s context?\n", pu->exten); + } + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { +std: for (x=0;xchan->fds[x] > -1) { + FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + } + } + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + pl = pu; + pu = pu->next; + } + } + ast_mutex_unlock(&autoanswer_lock); + rfds = nrfds; + efds = nefds; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + /* Wait for something to happen */ + ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); + pthread_testcancel(); + } + return NULL; /* Never reached */ +} + +static int autoanswer_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + struct ast_channel *peer=NULL; + struct aauser *pu, *pl=NULL; + struct ast_bridge_config config; + char *s, *stringp, *aacontext, *aaexten = NULL; + char datastring[80]; + + if (!data) { + ast_log(LOG_WARNING, "Autoanswer requires an argument (extension number)\n"); + return -1; + } + s = ast_strdupa((void *) data); + stringp=s; + aacontext = strsep(&stringp, "|"); + aaexten = strsep(&stringp, "|"); + if (!aaexten) { + aaexten = aacontext; + aacontext = NULL; + } + if (!aaexten) { + ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); + return -1; + } else { + if (!aacontext) { + aacontext = "default"; + } + } + + LOCAL_USER_ADD(u); + ast_mutex_lock(&autoanswer_lock); + pu = aalot; + while(pu) { + if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ + if (pl) + pl->next = pu->next; + else + aalot = pu->next; + break; + } + pl = pu; + pu = pu->next; + } + ast_mutex_unlock(&autoanswer_lock); + if (pu) { + peer = pu->chan; + free(pu); + pu = NULL; + } + /* JK02: it helps to answer the channel if not already up */ + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (peer) { + ast_moh_stop(peer); + /* Play a courtesy beep in the callED channel to prefix the bridge connecting */ + if (!ast_strlen_zero(courtesytone)) { + if (!ast_streamfile(peer, courtesytone, peer->language)) { + if (ast_waitstream(peer, "") < 0) { + ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); + ast_hangup(peer); + return -1; + } + } + } + + res = ast_channel_make_compatible(chan, peer); + if (res < 0) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ast_hangup(peer); + return -1; + } + /* This runs sorta backwards, since we give the incoming channel control, as if it + were the person called. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s autoanswered %s\n", peer->name, chan->name); + manager_event(EVENT_FLAG_CALL, "Autoanswer", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Channel2: %s\r\n" + "Uniqueid2: %s\r\n" + "Context: %s\r\n" + "Exten: %s\r\n" + ,chan->name, chan->uniqueid, peer->name, peer->uniqueid, aacontext, aaexten); + + + memset(&config,0,sizeof(struct ast_bridge_config)); + ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); + ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); + config.timelimit = 0; + config.play_warning = 0; + config.warning_freq = 0; + config.warning_sound=NULL; + res = ast_bridge_call(chan,peer,&config); + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "returning from bridge %s\n", peer->name); + /* relogin */ + snprintf(datastring, sizeof(datastring) - 1, "%s|%s", aacontext, aaexten); + ast_autoanswer_login(peer, datastring); + return res; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Nobody logged in for autoanswer %s@%s\n", aaexten, aacontext); + res = -1; + } + LOCAL_USER_REMOVE(u); + return res; +} + int ast_pickup_call(struct ast_channel *chan) { @@ -2076,7 +2844,7 @@ } { - struct ast_call_feature *feature=find_feature(var->name); + struct ast_call_feature *feature = ast_find_feature(var->name); int mallocd=0; if (!feature) { @@ -2138,6 +2906,7 @@ } int reload(void) { + autoanswer_reregister_extensions(); return load_config(); } @@ -2151,14 +2920,22 @@ if ((res = load_config())) return res; ast_cli_register(&showparked); + ast_cli_register(&showautoanswer); ast_cli_register(&showfeatures); ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL); + ast_pthread_create(&holding_thread, NULL, do_holding_thread, NULL); res = ast_register_application(parkedcall, park_exec, synopsis, descrip); if (!res) res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2); if (!res) { ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" ); } + res = ast_register_application(holdedcall, retrieve_call_exec, synopsis, descrip); + ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL); + if (!res) + res = ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3); + if (!res) + res = ast_register_application(autoanswer, autoanswer_exec, synopsis4, descrip4); return res; } @@ -2169,7 +2946,11 @@ ast_manager_unregister("ParkedCalls"); ast_cli_unregister(&showfeatures); + ast_cli_unregister(&showautoanswer); ast_cli_unregister(&showparked); + ast_unregister_application(autoanswer); + ast_unregister_application(autoanswerlogin); + ast_unregister_application(holdedcall); ast_unregister_application(parkcall); return ast_unregister_application(parkedcall); } diff -urN asterisk-1.2.10.orig/res/res_monitor.c asterisk-1.2.10/res/res_monitor.c --- asterisk-1.2.10.orig/res/res_monitor.c 2006-03-02 20:05:40.000000000 +0100 +++ asterisk-1.2.10/res/res_monitor.c 2006-07-31 14:13:08.000000000 +0200 @@ -90,7 +90,7 @@ /* Start monitoring a channel */ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock) + const char *fname_base, const char *target_url, const char *target_script, int need_lock) { int res = 0; char tmp[256]; @@ -122,6 +122,11 @@ } memset(monitor, 0, sizeof(struct ast_channel_monitor)); + if (target_url) + ast_copy_string(monitor->target_url, target_url, sizeof(monitor->target_url)); + if (target_script) + ast_copy_string(monitor->target_script, target_script, sizeof(monitor->target_script)); + /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; @@ -257,6 +262,8 @@ if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) { char tmp[1024]; char tmp2[1024]; + char tmp3[1024]; + int result; char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format; char *name = chan->monitor->filename_base; int directory = strchr(name, '/') ? 1 : 0; @@ -278,9 +285,19 @@ snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */ ast_copy_string(tmp, tmp2, sizeof(tmp)); } - ast_log(LOG_DEBUG,"monitor executing %s\n",tmp); - if (ast_safe_system(tmp) == -1) + if (!ast_strlen_zero(chan->monitor->target_script) && !ast_strlen_zero(chan->monitor->target_url)) { + snprintf(tmp3,sizeof(tmp3), "( %s& nice -19 %s \"%s/%s.%s\" \"%s\" ) &",tmp, chan->monitor->target_script , dir, name, format, chan->monitor->target_url); + ast_copy_string(tmp, tmp3, sizeof(tmp)); + } + ast_log(LOG_NOTICE,"monitor executing %s\n",tmp); + result = ast_safe_system(tmp); + if (result == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); + manager_event(EVENT_FLAG_CALL, "MonitorStopped", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Result: %d\r\n" + ,chan->name, chan->uniqueid, result); } free(chan->monitor->format); @@ -392,7 +409,7 @@ return 0; } - res = ast_monitor_start(chan, format, fname_base, 1); + res = ast_monitor_start(chan, format, fname_base, NULL, NULL, 1); if (res < 0) res = ast_monitor_change_fname(chan, fname_base, 1); ast_monitor_setjoinfiles(chan, joinfiles); @@ -428,19 +445,30 @@ { struct ast_channel *c = NULL; char *name = astman_get_header(m, "Channel"); + char *uniqueid = astman_get_header(m, "Uniqueid"); char *fname = astman_get_header(m, "File"); char *format = astman_get_header(m, "Format"); char *mix = astman_get_header(m, "Mix"); + char *target_url = astman_get_header(m, "TargetURL"); + char *target_script = astman_get_header(m, "TargetScript"); char *d; - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "No channel specified"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel/uniqueid specified"); return 0; } - c = ast_get_channel_by_name_locked(name); - if (!c) { + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + if (!c) { + astman_send_error(s, m, "No such uniqueid"); + return 0; + } + } else { + c = ast_get_channel_by_name_locked(name); + if (!c) { astman_send_error(s, m, "No such channel"); return 0; + } } if (ast_strlen_zero(fname)) { @@ -457,7 +485,7 @@ if ((d=strchr(fname, '/'))) *d='-'; } - if (ast_monitor_start(c, format, fname, 1)) { + if (ast_monitor_start(c, format, fname, target_url, target_script, 1)) { if (ast_monitor_change_fname(c, fname, 1)) { astman_send_error(s, m, "Could not start monitoring channel"); ast_mutex_unlock(&c->lock); @@ -483,16 +511,26 @@ { struct ast_channel *c = NULL; char *name = astman_get_header(m, "Channel"); + char *uniqueid = astman_get_header(m, "Uniqueid"); int res; - if (ast_strlen_zero(name)) { - astman_send_error(s, m, "No channel specified"); + if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) { + astman_send_error(s, m, "No channel/uniqueid specified"); return 0; } - c = ast_get_channel_by_name_locked(name); - if (!c) { + if (!ast_strlen_zero(uniqueid)) { + c = ast_get_channel_by_uniqueid_locked(uniqueid); + if (!c) { + astman_send_error(s, m, "No such uniqueid"); + return 0; + } + } else { + c = ast_get_channel_by_name_locked(name); + if (!c) { astman_send_error(s, m, "No such channel"); return 0; + } } + res = ast_monitor_stop(c, 1); ast_mutex_unlock(&c->lock); if (res) { diff -urN asterisk-1.2.10.orig/res/res_watchdog.c asterisk-1.2.10/res/res_watchdog.c --- asterisk-1.2.10.orig/res/res_watchdog.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.10/res/res_watchdog.c 2006-08-10 14:07:14.000000000 +0200 @@ -0,0 +1,149 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Resource to make watchdogs happy + * + * Copyright (C) 2005, Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct watchdog_pvt *watchdogs = NULL; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +typedef struct watchdog_pvt { + char device[80]; + int fd; + int type; + int interval; + pthread_t watchdog_thread; + struct watchdog_pvt *next; +} watchdog_pvt; + +static void *do_watchdog_thread(void *data) { + struct watchdog_pvt *woof = (struct watchdog_pvt *)data; + for (;;) { + if (woof->fd) { + write(woof->fd, "PING\n", 1); + } + usleep(woof->interval * 1000); + } + return NULL; +} + + +int load_module(void) +{ + int res = 0; + char *cat, *utype, *udevice, *uinterval; + struct ast_config *cfg; + struct watchdog_pvt *woof = NULL; + + cfg = ast_config_load("watchdog.conf"); + if (cfg) { + cat = ast_category_browse(cfg, NULL); + while(cat) { + cat = ast_category_browse(cfg, cat); + utype = ast_variable_retrieve(cfg, cat, "type"); +/* if (utype) { + ast_log(LOG_NOTICE, "type = %s\n", utype); + } */ + udevice = ast_variable_retrieve(cfg, cat, "device"); +/* if (udevice) { + ast_log(LOG_NOTICE, "device = %s\n", udevice); + } */ + uinterval = ast_variable_retrieve(cfg, cat, "interval"); +/* if (uinterval) { + ast_log(LOG_NOTICE, "interval = %s\n", uinterval); + } */ + if (uinterval && udevice && utype) { + woof = malloc(sizeof(struct watchdog_pvt)); + if (!woof) { + ast_log(LOG_ERROR, "unable to malloc!\n"); + return -1; + } + memset(woof, 0x0, sizeof(struct watchdog_pvt)); + strncpy(woof->device, udevice, sizeof(woof->device) - 1); + + woof->interval = atoi(uinterval);; + woof->next = watchdogs; + watchdogs = woof; + woof->fd = open(woof->device, O_WRONLY | O_SYNC); + if (woof->fd) { + if (!strncmp(utype, "isdnguard", sizeof(utype))) { + woof->type = 1; + write(woof->fd, "START\n", 6); + } + ast_pthread_create(&woof->watchdog_thread, NULL, do_watchdog_thread, woof); + } else { + ast_log(LOG_WARNING, "error opening watchdog device %s !\n", woof->device); + } + } + } + ast_config_destroy(cfg); + } + return res; +} + + +int unload_module(void) +{ + struct watchdog_pvt *dogs, *woof; + STANDARD_HANGUP_LOCALUSERS; + dogs = watchdogs; + while (dogs) { + pthread_cancel(dogs->watchdog_thread); + close(dogs->fd); + woof = dogs->next; + free(dogs); + dogs = woof; + } + return 0; +} + +char *description(void) +{ + return "Watchdog Resource"; +} + +int usecount(void) +{ + return 1; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff -urN asterisk-1.2.10.orig/rtp.c asterisk-1.2.10/rtp.c --- asterisk-1.2.10.orig/rtp.c 2006-07-13 20:44:17.000000000 +0200 +++ asterisk-1.2.10/rtp.c 2006-07-31 14:16:56.000000000 +0200 @@ -445,6 +445,11 @@ struct rtpPayloadType rtpPT; len = sizeof(sin); + + /* XXX SYMPTON CURE, DIRTY FIX, CHECK, BEGIN */ + if (!rtp) + return &null_frame; + /* XXX SYMPTON CURE, DIRTY FIX, CHECK, END */ /* Cache where the header will go */ res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,