capisuite/src/backend/connection.cpp

1429 lines
48 KiB
C++

/* @file connection.cpp
@brief Contains Connection - Encapsulates a CAPI connection with all its states and methods.
@author Gernot Hillier <gernot@hillier.de>
$Revision: 1.15 $
*/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <../../config.h>
#include <fstream>
#include <pthread.h>
#include <errno.h> // for errno
#include <iconv.h> // for iconv(), iconv_open(), iconv_close()
#include "capi.h"
#include "callinterface.h"
#include "connection.h"
#define conf_send_buffers 4
using namespace std;
Connection::Connection (_cmsg& message, Capi *capi, unsigned short DDILength, unsigned short DDIBaseLength, std::vector<std::string> DDIStopNumbers):
call_if(NULL),capi(capi),plci_state(P2),ncci_state(N0), buffer_start(0), buffers_used(0),
file_for_reception(NULL), file_to_send(NULL), received_dtmf(""), keepPhysicalConnection(false),
disconnect_cause(0),debug(capi->debug), debug_level(capi->debug_level), error(capi->error),
our_call(false), disconnect_cause_b3(0), fax_info(NULL), DDILength(DDILength),
DDIBaseLength(DDIBaseLength), DDIStopNumbers(DDIStopNumbers)
{
pthread_mutex_init(&send_mutex, NULL);
pthread_mutex_init(&receive_mutex, NULL);
plci=CONNECT_IND_PLCI(&message); // Physical Link Connection Identifier
call_from = getNumber(CONNECT_IND_CALLINGPARTYNUMBER(&message),true);
if (DDILength)
call_to=""; // we enable the CalledParty InfoElement when using DDI and will get the number later again
else
call_to=getNumber(CONNECT_IND_CALLEDPARTYNUMBER(&message),false);
if (debug_level >= 1) {
debug << prefix() << "Connection object created for incoming call PLCI " << plci;
debug << " from " << call_from << " to " << call_to << " CIP 0x" << hex << CONNECT_IND_CIPVALUE(&message) << endl;
}
switch (CONNECT_IND_CIPVALUE(&message)) {
case 1:
case 4:
case 16:
service=VOICE;
break;
case 17:
service=FAXG3;
break;
default:
service=OTHER;
break;
}
connect_ind_msg_nr=message.Messagenumber; // this is needed as connect_resp is given later
}
Connection::Connection (Capi* capi, _cdword controller, string call_from, bool clir, string call_to, service_t service, string faxStationID, string faxHeadline) throw (CapiExternalError, CapiMsgError)
:call_if(NULL),capi(capi),plci_state(P01),ncci_state(N0),plci(0),service(service),
buffer_start(0), buffers_used(0), file_for_reception(NULL), file_to_send(NULL),
call_from(call_from), call_to(call_to), connect_ind_msg_nr(0), disconnect_cause(0),
debug(capi->debug), debug_level(capi->debug_level), error(capi->error), keepPhysicalConnection(false),
our_call(true), disconnect_cause_b3(0), fax_info(NULL), DDILength(0), DDIBaseLength(0)
{
pthread_mutex_init(&send_mutex, NULL);
pthread_mutex_init(&receive_mutex, NULL);
if (debug_level >= 1) {
debug << prefix() << "Connection object created for outgoing call from " << call_from << " to " << call_to
<< " service " << dec << service << endl;
}
if (debug_level >= 2) {
debug << prefix() << "using faxStationID " << faxStationID << " faxHeadline " << faxHeadline << " CLIR " << clir << endl;
}
_cstruct B1config=NULL, B2config=NULL, B3config=NULL, calledPartyNumber=NULL, callingPartyNumber=NULL;
_cword B1proto,B2proto,B3proto;
try {
_cword CIPvalue;
switch (service) {
case VOICE:
CIPvalue=16;
break;
case FAXG3:
CIPvalue=17;
break;
default:
throw CapiExternalError("unsupported service given","Connection::Connection()");
break;
}
buildBconfiguration(controller, service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config);
if (!call_to.size())
throw CapiExternalError("calledPartyNumber is required","Connection::Connection()");
calledPartyNumber=new unsigned char [1+1+call_to.size()]; //struct length, number type/number plan, number
calledPartyNumber[0]=1+call_to.size(); // length
calledPartyNumber[1]=0x80; // as suggested by CAPI spec (unknown number type, unknown number plan, see ETS 300 102-1)
for (unsigned j=0;j<call_to.size();j++)
calledPartyNumber[j+2]=call_to[j];
callingPartyNumber=new unsigned char [1+2+call_from.size()];
callingPartyNumber[0]=2+call_from.size(); // length
callingPartyNumber[1]=0x00; // as suggested by CAPI spec (unknown number type, unknown number plan, see ETS 300 102-1)
if (clir)
callingPartyNumber[2]=0xA0; // suppress calling id presentation (CLIR)
else
callingPartyNumber[2]=0x80; // allow calling id presentation (CLIP)
for (unsigned j=0;j<call_from.size();j++) // TODO: does this really work when no number is given?!
callingPartyNumber[j+3]=call_from[j];
plci_state=P01;
capi->connect_req(this,controller,CIPvalue, calledPartyNumber, callingPartyNumber,B1proto,B2proto,B3proto,B1config, B2config, B3config);
} catch (...) {
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
if (calledPartyNumber)
delete[] calledPartyNumber;
if (callingPartyNumber)
delete[] callingPartyNumber;
throw;
}
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
if (calledPartyNumber)
delete[] calledPartyNumber;
if (callingPartyNumber)
delete[] callingPartyNumber;
}
Connection::~Connection()
{
stop_file_transmission();
stop_file_reception();
if (getState()!=DOWN) {
error << prefix() << "WARNING: please disconnect yourself before deleting connection object!!" << endl;
disconnectCall(PHYSICAL_ONLY);
while (getState()!=DOWN)
;
}
plci_state=P0;
pthread_mutex_lock(&send_mutex); // assure the lock is free before destroying it
pthread_mutex_unlock(&send_mutex);
pthread_mutex_destroy(&send_mutex);
pthread_mutex_lock(&receive_mutex); // assure the lock is free before destroying it
pthread_mutex_unlock(&receive_mutex);
pthread_mutex_destroy(&receive_mutex);
if (fax_info)
delete fax_info;
if (debug_level >= 1) {
debug << prefix() << "Connection object deleted" << endl;
}
}
void
Connection::registerCallInterface(CallInterface *call_if_in)
{
call_if=call_if_in;
}
void
Connection::changeProtocol(service_t desired_service, string faxStationID, string faxHeadline) throw (CapiMsgError, CapiExternalError, CapiWrongState)
{
if (debug_level >= 2) {
debug << prefix() << "Protocol change to " << desired_service << " requested" << endl;
}
if (ncci_state!=N0 || plci_state!=PACT)
throw CapiWrongState("wrong state for changeProtocol","Connection::changeProtocol()");
if (desired_service!=service) {
_cstruct B1config=NULL, B2config=NULL, B3config=NULL;
_cword B1proto,B2proto,B3proto;
try {
buildBconfiguration(plci & 0xff, desired_service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config);
capi->select_b_protocol_req(plci,B1proto,B2proto,B3proto,B1config, B2config, B3config);
} catch (...) {
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
throw;
}
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
service=desired_service;
}
}
void
Connection::connectWaiting(service_t desired_service, string faxStationID, string faxHeadline) throw (CapiWrongState, CapiExternalError, CapiMsgError)
{
if (debug_level >= 1) {
debug << prefix() << "accepting with service " << desired_service << endl;
}
if (debug_level >= 2) {
debug << prefix() << "using faxStationID " << faxStationID << " faxHeadline " << faxHeadline << endl;
}
if (plci_state!=P2)
throw CapiWrongState("wrong state for connectWaiting","Connection::connectWaiting()");
if (our_call)
throw (CapiError("can't accept an outgoing call","Connection::connectWaiting()"));
_cstruct B1config=NULL, B2config=NULL, B3config=NULL;
_cword B1proto,B2proto,B3proto;
try {
buildBconfiguration(plci & 0xff, desired_service, faxStationID, faxHeadline, B1proto, B2proto, B3proto, B1config, B2config, B3config);
plci_state=P4;
capi->connect_resp(connect_ind_msg_nr,plci,0,B1proto,B2proto,B3proto,B1config, B2config, B3config);
} catch (...) {
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
throw;
}
if (B1config)
delete[] B1config;
if (B2config)
delete[] B2config;
if (B3config)
delete[] B3config;
service=desired_service;
}
void
Connection::rejectWaiting(_cword reject) throw (CapiWrongState, CapiMsgError, CapiExternalError)
{
if (debug_level >= 1) {
debug << prefix() << "rejecting with cause " << reject << endl;
}
if (plci_state!=P2)
throw CapiWrongState("wrong state for reject","Connection::reject()");
if (our_call)
throw (CapiError("can't accept an outgoing call","Connection::connectWaiting()"));
if (!reject)
throw CapiExternalError("reject cause must not be zero","Connection::reject()");
plci_state=P5;
capi->connect_resp(connect_ind_msg_nr,plci,reject,0,0,0,NULL,NULL,NULL); // can throw CapiMsgError. Propagate
}
void
Connection::acceptWaiting() throw (CapiMsgError, CapiWrongState)
{
if (plci_state!=P2)
throw CapiWrongState("wrong state for acceptWaiting","Connection::acceptWaiting()");
capi->alert_req(plci);
}
string
Connection::getCalledPartyNumber()
{
return call_to;
}
string
Connection::getCallingPartyNumber()
{
return call_from;
}
string
Connection::prefix()
{
stringstream s;
time_t t=time(NULL);
char* ct=ctime(&t);
ct[24]='\0';
s << ct << " Connection " << hex << this << ": ";
return (s.str());
}
void
Connection::debugMessage(string message, unsigned short level)
{
if (debug_level >= level)
debug << prefix() << message << endl;
}
void
Connection::errorMessage(string message)
{
error << prefix() << message << endl;
}
Connection::service_t
Connection::getService()
{
return service;
}
Connection::fax_info_t*
Connection::getFaxInfo()
{
return fax_info;
}
Connection::connection_state_t
Connection::getState()
{
if (plci_state==PACT && ncci_state==NACT)
return UP;
else if (plci_state==P2 && ncci_state==N0)
return WAITING;
else if (plci_state==P0 && ncci_state==N0)
return DOWN;
else
return OTHER_STATE;
}
_cword
Connection::getCause()
{
return disconnect_cause;
}
_cword
Connection::getCauseB3()
{
return disconnect_cause_b3;
}
void
Connection::connect_active_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=P4 && plci_state!=P1) {
throw CapiWrongState("CONNECT_ACTIVE_IND received in wrong state","Connection::connect_active_ind()");
} else {
try {
capi->connect_active_resp(message.Messagenumber,plci);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: error detected when trying to send connect_active_resp. Message was:" << e << endl;
}
if (plci_state==P1) { // this is an outgoing call, so we have to initiate B3 connection
ncci_state=N01;
try {
capi->connect_b3_req(plci);
}
catch (CapiMsgError) {
plci_state=PACT;
ncci_state=N0;
throw; // this is critical, so propagate
}
}
plci_state=PACT;
}
}
void
Connection::connect_b3_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (ncci_state!=N0) {
throw CapiWrongState("CONNECT_B3_IND received in wrong state","Connection::connect_b3_ind()");
} else {
ncci=CONNECT_B3_IND_NCCI(&message);
// 0 = we'll accept any call, NULL=no NCPI necessary
// this can throw CapiMsgError. Propagate.
ncci_state=N2;
capi->connect_b3_resp(message.Messagenumber,ncci,0,NULL);
}
}
void
Connection::connect_b3_active_ind(_cmsg& message) throw (CapiWrongState, CapiExternalError)
{
if (ncci_state!=N2) {
throw CapiWrongState("CONNECT_B3_ACTIVE_IND received in wrong state","Connection::connect_b3_active_ind()");
} else {
if (ncci!=CONNECT_B3_ACTIVE_IND_NCCI(&message))
throw CapiError("CONNECT_B3_ACTIVE_IND received with wrong NCCI","Connection::connect_b3_active_ind()");
try {
capi->connect_b3_active_resp(message.Messagenumber,ncci);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Error deteced when sending connect_b3_active_resp. Message was: " << e << endl;
}
ncci_state=NACT;
if (service==FAXG3 && CONNECT_B3_ACTIVE_IND_NCPI(&message)[0]>=9) {
_cstruct ncpi=CONNECT_B3_ACTIVE_IND_NCPI(&message);
if (!fax_info)
fax_info=new fax_info_t;
fax_info->rate=ncpi[1]+(ncpi[2]<<8);
fax_info->hiRes=((ncpi[3] & 0x01) == 0x01);
fax_info->format=((ncpi[4] & 0x04) == 0x04);
fax_info->pages=ncpi[7]+(ncpi[8]<<8);
fax_info->stationID.assign(reinterpret_cast<char*>(&ncpi[10]),static_cast<int>(ncpi[9])); // indx 9 helds the length, string starts at 10
if (debug_level >= 2) {
debug << prefix() << "fax connected with rate " << dec << fax_info->rate
<< (fax_info->hiRes ? ", hiRes" : ", lowRes") << (fax_info->format ? ", JPEG" : "")
<< ", ID: " << fax_info->stationID << endl;
}
}
if (call_if)
call_if->callConnected();
else
throw CapiExternalError("no call control interface registered!","Connection::connect_b3_active_ind()");
}
}
void
Connection::disconnect_b3_ind(_cmsg& message) throw (CapiWrongState)
{
if (ncci_state!=NACT && ncci_state!=N1 && ncci_state!=N2 && ncci_state!=N3 && ncci_state!=N4) {
throw CapiWrongState("DISCONNECT_B3_IND received in wrong state","Connection::disconnect_b3_ind()");
} else {
if (ncci!=DISCONNECT_B3_IND_NCCI(&message))
throw CapiError("DISCONNECT_B3_IND received with wrong NCCI","Connection::disconnect_b3_ind()");
disconnect_cause_b3=DISCONNECT_B3_IND_REASON_B3(&message);
if (service==FAXG3 && CONNECT_B3_ACTIVE_IND_NCPI(&message)[0]>=9) {
_cstruct ncpi=CONNECT_B3_ACTIVE_IND_NCPI(&message);
if (!fax_info)
fax_info=new fax_info_t;
fax_info->rate=ncpi[1]+(ncpi[2]<<8);
fax_info->hiRes=((ncpi[3] & 0x01) == 0x01);
fax_info->format=((ncpi[4] & 0x04) == 0x04);
fax_info->pages=ncpi[7]+(ncpi[8]<<8);
fax_info->stationID.assign(reinterpret_cast<char*>(&ncpi[10]),static_cast<int>(ncpi[9])); // indx 9 helds the length, string starts at 10
if (debug_level >= 2) {
debug << prefix() << "fax finished with rate " << dec << fax_info->rate
<< (fax_info->hiRes ? ", hiRes" : ", lowRes") << (fax_info->format ? ", JPEG" : "")
<< ", ID: " << fax_info->stationID << ", " << fax_info->pages << " pages" << endl;
}
}
pthread_mutex_lock(&send_mutex);
buffers_used=0; // we'll get no DATA_B3_CONF's after DISCONNECT_B3_IND, see Capi 2.0 spec, 5.18, note for DATA_B3_CONF
pthread_mutex_unlock(&send_mutex);
stop_file_transmission();
stop_file_reception();
bool our_disconnect_req= (ncci_state==N4) ? true : false;
ncci_state=N5;
if (call_if)
call_if->callDisconnectedLogical();
try {
ncci_state=N0;
capi->disconnect_b3_resp(message.Messagenumber,ncci);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Can't send disconnect_b3_resp. Message was: " << e << endl;
}
if (our_disconnect_req && !keepPhysicalConnection) { // this means *we* initiated disconnect of logical connection with DISCONNECT_B3_REQ before
try {
plci_state=P5;
capi->disconnect_req(plci); // so we'll continue with the disconnect of physical connection
}
catch (CapiMsgError e) {
// in this application this is fatal. Panic please.
throw CapiError("Can't disconnect. Please file a bug report. Error message: "+e.message(),"Connection::disconnect_b3_ind()");
}
} else
keepPhysicalConnection=false;
}
}
void
Connection::disconnect_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (ncci_state!=N0 || (plci_state!=P1 && plci_state!=P2 && plci_state!=P3 && plci_state!=P4 && plci_state!=P5 && plci_state!=PACT)) {
throw CapiWrongState("DISCONNECT_IND received in wrong state","Connection::disconnect_ind()");
} else {
if (plci!=DISCONNECT_IND_PLCI(&message))
throw CapiError("DISCONNECT_IND received with wrong PLCI","Connection::disconnect_ind()");
disconnect_cause=DISCONNECT_IND_REASON(&message);
plci_state=P0;
capi->disconnect_resp(message.Messagenumber,plci);
capi->unregisterConnection(plci);
if (call_if)
call_if->callDisconnectedPhysical();
}
}
void
Connection::data_b3_ind(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (ncci_state!=NACT && ncci_state!=N4)
throw CapiWrongState("DATA_B3_IND received in wrong state","Connection::data_b3_ind()");
if (ncci!=CONNECT_B3_IND_NCCI(&message))
throw CapiError("DATA_B3_IND received with wrong NCCI","Connection::data_b3_ind()");
pthread_mutex_lock(&receive_mutex);
if (file_for_reception) {
for (int i=0;i<DATA_B3_IND_DATALENGTH(&message);i++)
(*file_for_reception) << DATA_B3_IND_DATA(&message)[i];
}
pthread_mutex_unlock(&receive_mutex);
if (call_if)
call_if->dataIn(DATA_B3_IND_DATA(&message),DATA_B3_IND_DATALENGTH(&message));
capi->data_b3_resp(message.Messagenumber,ncci,DATA_B3_IND_DATAHANDLE(&message));
}
void
Connection::facility_ind_DTMF(_cmsg &message) throw (CapiWrongState)
{
if (plci_state!=PACT)
throw CapiWrongState("FACILITY_IND received in wrong state","Connection::facility_ind_DTMF()");
if (plci!=(FACILITY_IND_PLCI(&message) & 0xFFFF) ) // this *should* be PLCI, but who knows - so mask NCCI part if it's there...
throw CapiError("FACILITY_IND received with wrong PLCI","Connection::facility_ind_DTMF()");
try {
capi->facility_resp(message.Messagenumber,plci,1);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Can't send facility_resp. Message was: " << e << endl;
}
_cstruct facilityIndParam=FACILITY_IND_FACILITYINDICATIONPARAMETER(&message);
received_dtmf.append(reinterpret_cast<char*>(facilityIndParam+1),static_cast<size_t>(facilityIndParam[0])); //string, length
if (debug_level >= 2) {
debug << prefix() << "received DTMF buffer " << received_dtmf << endl;
}
if (call_if)
call_if->gotDTMF();
}
void
Connection::info_ind_alerting(_cmsg &message) throw (CapiWrongState)
{
if (plci_state!=P01 && plci_state!=P1)
throw CapiWrongState("INFO_IND for ALERTING received in wrong state","Connection::info_ind_alerting()");
if (plci!=INFO_IND_PLCI(&message))
throw CapiError("INFO_IND received with wrong PLCI","Connection::info_ind_alerting()");
try {
capi->info_resp(message.Messagenumber,plci);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Can't send info_resp. Message was: " << e << endl;
}
if (call_if)
call_if->alerting();
}
bool
Connection::info_ind_called_party_nr(_cmsg &message) throw (CapiWrongState)
{
if (plci_state!=P2)
throw CapiWrongState("INFO_IND for CalledPartyNr received in wrong state","Connection::info_ind_called_party_nr()");
if (plci!=INFO_IND_PLCI(&message))
throw CapiError("INFO_IND received with wrong PLCI","Connection::info_ind_called_party_nr()");
try {
capi->info_resp(message.Messagenumber,plci);
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Can't send info_resp. Message was: " << e << endl;
}
call_to+=getNumber(INFO_IND_INFOELEMENT(&message),false);
string currDDI=call_to.substr(DDIBaseLength);
for (int i=0;i<DDIStopNumbers.size();i++)
if (DDIStopNumbers[i]==currDDI) {
if (debug_level >= 1)
debug << prefix() << "got DDI, nr is now " << call_to << " (complete,stop_nr)" << endl;
return true;
}
if (call_to.length()>=DDIBaseLength+DDILength) {
if (debug_level >=1)
debug << prefix() << "got DDI, nr is now " << call_to << " (complete)" << endl;
return true;
} else {
if (debug_level >=1)
debug << prefix() << "got DDI, nr is now " << call_to << " (incomplete)" << endl;
return false;
}
}
void
Connection::connect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=P01)
throw CapiWrongState("CONNECT_CONF received in wrong state","Connection::connect_conf()");
if (CONNECT_CONF_INFO(&message))
throw CapiMsgError(CONNECT_CONF_INFO(&message),"CONNECT_CONF received with Error (Info)","Connection::connect_conf()");
// TODO: do we have to delete Connection here if Info!=0 or is a DISCONNECT_IND initiated then (think not ...)
plci=CONNECT_CONF_PLCI(&message);
if (debug_level >= 2) {
debug << prefix() << "got PLCI " << plci << endl;
}
plci_state=P1;
}
void
Connection::connect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (ncci_state!=N01)
throw CapiWrongState("CONNECT_B3_CONF received in wrong state","Connection::connect_b3_conf()");
if (CONNECT_B3_CONF_INFO(&message)) {
ncci_state=N0;
throw CapiMsgError(CONNECT_B3_CONF_INFO(&message),"CONNECT_B3_CONF received with Error (Info)","Connection::connect_b3_conf()");
}
ncci=CONNECT_B3_CONF_NCCI(&message);
ncci_state=N2;
}
void
Connection::select_b_protocol_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=PACT || ncci_state!=N0)
throw CapiWrongState("SELECT_B_PROTOCOL_CONF received in wrong state","Connection::select_b_protocol_conf()");
if (plci!=SELECT_B_PROTOCOL_CONF_PLCI(&message))
throw CapiError("SELECT_B_PROTOCOL_CONF received with wrong PLCI","Connection::select_b_protocol_conf()");
if (SELECT_B_PROTOCOL_CONF_INFO(&message))
throw CapiMsgError(SELECT_B_PROTOCOL_CONF_INFO(&message),"SELECT_B_PROTOCOL_CONF received with Error (Info)","Connection::select_b_protocol_conf()");
if (our_call) {
try {
ncci_state=N01;
capi->connect_b3_req(plci);
}
catch (CapiMsgError) {
ncci_state=N0;
throw; // this is critical, so propagate
}
}
}
void
Connection::alert_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=P2 && plci_state!=P5)
throw CapiWrongState("ALERT_CONF received in wrong state","Connection::alert_conf()");
if (plci!=ALERT_CONF_PLCI(&message))
throw CapiError("ALERT_CONF received with wrong PLCI","Connection::alert_conf()");
if (ALERT_CONF_INFO(&message) && ALERT_CONF_INFO(&message)!=0x0003) // 0x0003 = another application sent ALERT_REQ earlier -> no problem for us
throw CapiMsgError(ALERT_CONF_INFO(&message),"ALERT_CONF received with Error (Info)","Connection::alert_conf()");
}
void
Connection::data_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError, CapiExternalError)
{
if (ncci_state!=NACT)
throw CapiWrongState("DATA_B3_CONF received in wrong state","Connection::data_b3_conf()");
if (ncci!=DATA_B3_CONF_NCCI(&message))
throw CapiError("DATA_B3_CONF received with wrong NCCI","Connection::data_b3_conf()");
if (DATA_B3_CONF_INFO(&message))
throw CapiMsgError(DATA_B3_CONF_INFO(&message),"DATA_B3_CONF received with Error (Info)","Connection::data_b3_conf()");
if ( (!buffers_used) || (DATA_B3_CONF_DATAHANDLE(&message)!=buffer_start) )
throw CapiError("DATA_B3_CONF received with invalid data handle","Connection::data_b3_conf()");
pthread_mutex_lock(&send_mutex);
// free one buffer
buffers_used--;
buffer_start=(buffer_start+1)%7;
try {
while (file_to_send && (buffers_used < conf_send_buffers) )
send_block();
}
catch (...) {
pthread_mutex_unlock(&send_mutex);
throw;
}
pthread_mutex_unlock(&send_mutex);
}
void
Connection::facility_conf_DTMF(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=PACT)
throw CapiWrongState("FACILITY_CONF for DTMF received in wrong state","Connection::facility_conf_DTMF()");
if (plci!=(FACILITY_CONF_PLCI(&message) & 0xFFFF)) // this *should* be the PLCI but to be sure we mask out NCCI part
throw CapiError("FACILITY_CONF received with wrong PLCI","Connection::facility_conf_DTMF()");
if (FACILITY_CONF_INFO(&message))
throw CapiMsgError(FACILITY_CONF_INFO(&message),"FACILITY_CONF received with Error (Info)","Connection::facility_conf_DTMF()");
_cstruct facilityConfParameter=FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(&message);
if ((facilityConfParameter[0]==2) && facilityConfParameter[1])
throw CapiMsgError(FACILITY_CONF_INFO(&message),"FACILITY_CONF received with DTMF Error (DTMF information)","Connection::facility_conf_DTMF()");
}
void
Connection::disconnect_b3_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (ncci_state!=N4)
throw CapiWrongState("DISCONNECT_B3_CONF received in wrong state","Connection::disconnect_b3_conf()");
if (ncci!=DISCONNECT_B3_CONF_NCCI(&message))
throw CapiError("DISCONNECT_B3_CONF received with wrong NCCI","Connection::disconnect_b3_conf()");
if (DISCONNECT_B3_CONF_INFO(&message))
throw CapiMsgError(DISCONNECT_B3_CONF_INFO(&message),"DISCONNECT_B3_CONF received with Error (Info)","Connection::disconnect_b3_conf()");
}
void
Connection::disconnect_conf(_cmsg& message) throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=P5)
throw CapiWrongState("DISCONNECT_CONF received in wrong state","Connection::disconnect_conf()");
if (plci!=DISCONNECT_CONF_PLCI(&message))
throw CapiError("DISCONNECT_CONF received with wrong PLCI","Connection::disconnect_conf()");
if (DISCONNECT_CONF_INFO(&message))
throw CapiMsgError(DISCONNECT_CONF_INFO(&message),"DISCONNECT_CONF received with Error (Info)","Connection::disconnect_conf()");
}
void
Connection::disconnectCall(disconnect_mode_t disconnect_mode) throw (CapiMsgError)
{
if (debug_level >= 1) {
debug << prefix() << "disconnect initiated" << endl;
}
if ((ncci_state==N1 || ncci_state==N2 || ncci_state==N3 || ncci_state==NACT) && (disconnect_mode==ALL || disconnect_mode==LOGICAL_ONLY) ) { // logical connection up
ncci_state=N4;
capi->disconnect_b3_req(ncci); // can throw CapiMsgError. Fatal here. Propagate
if (disconnect_mode==LOGICAL_ONLY)
keepPhysicalConnection=true;
} else if ((plci_state==PACT || plci_state==P1 || plci_state==P2 || plci_state==P3 || plci_state==P4) && (disconnect_mode==ALL || disconnect_mode==PHYSICAL_ONLY) ) { // physical connection up
plci_state=P5;
capi->disconnect_req(plci); // can throw CapiMsgError. Fatal here. Propagate
}
// otherwise do nothing
}
void
Connection::send_block() throw (CapiWrongState, CapiExternalError, CapiMsgError)
{
if (ncci_state!=NACT)
throw CapiWrongState("unable to send file because connection is not established","Connection::send_block()");
if (!file_to_send)
throw CapiError("unable to play file because no input file is open","Connection::send_block()");
if (buffers_used>=7)
throw CapiError("unable to send file snippet because buffers are full","Connection::send_block()");
bool file_completed=false;
unsigned short buff_num=(buffer_start+buffers_used)%7; // buffer to store the next item
int i=0;
while (i<2048 && !file_completed) {
if (!file_to_send->get(send_buffer[buff_num][i]))
file_completed=true;
else
i++;
}
try {
if (i>0) {
capi->data_b3_req(ncci,send_buffer[buff_num],i,buff_num,0); // can throw CapiMsgError. Propagate.
buffers_used++;
}
}
catch (CapiMsgError e) {
error << prefix() << "WARNING: Can't send data_b3_req. Message was: " << e << endl;
if (file_completed) {
file_to_send->close();
delete file_to_send;
file_to_send=NULL;
}
}
if (file_completed) {
file_to_send->close();
delete file_to_send;
file_to_send=NULL;
if (call_if)
call_if->transmissionComplete();
else
throw CapiExternalError("no call control interface registered!","Connection::send_block()");
}
}
void
Connection::start_file_transmission(string filename) throw (CapiWrongState, CapiExternalError, CapiMsgError)
{
if (debug_level >= 2) {
debug << prefix() << "start_file_transmission " << filename << endl;
}
if (ncci_state!=NACT)
throw CapiWrongState("unable to send file because connection is not established","Connection::start_file_transmission()");
if (file_to_send)
throw CapiExternalError("unable to send file because transmission is already in progress","Connection::start_file_transmission()");
file_to_send=new ifstream(filename.c_str());
if (! (*file_to_send)) { // we can't open the file
delete file_to_send;
file_to_send=NULL;
throw CapiExternalError("unable to open file to send ("+filename+")","Connection::start_file_transmission()");
} else {
pthread_mutex_lock(&send_mutex);
try {
while (file_to_send && buffers_used<conf_send_buffers)
send_block();
}
catch (...) {
pthread_mutex_unlock(&send_mutex);
throw;
}
pthread_mutex_unlock(&send_mutex);
}
}
void
Connection::stop_file_transmission()
{
if (debug_level >= 2) {
debug << prefix() << "stop_file_transmission initiated" << endl;
}
pthread_mutex_lock(&send_mutex);
if (file_to_send) {
file_to_send->close();
delete file_to_send;
file_to_send=NULL;
}
pthread_mutex_unlock(&send_mutex);
timespec delay_time;
delay_time.tv_sec=0; delay_time.tv_nsec=100000000; // 100 msec
while (buffers_used) // wait until all packages are transmitted
nanosleep(&delay_time,NULL);
if (debug_level >= 2) {
debug << prefix() << "stop_file_transmission finished" << endl;
}
}
void
Connection::start_file_reception(string filename) throw (CapiWrongState, CapiExternalError)
{
if (debug_level >= 2) {
debug << prefix() << "start_file_reception " << filename << endl;
}
if (ncci_state!=NACT)
throw CapiWrongState("unable to receive file because connection is not established","Connection::start_file_reception()");
if (file_for_reception)
throw CapiExternalError("file reception is already active","Connection::start_file_reception()");
file_for_reception=new ofstream(filename.c_str());
if (! (*file_for_reception)) { // we can't open the file
delete file_for_reception;
file_for_reception=NULL;
throw CapiExternalError("unable to open file for reception ("+filename+")","Connection::start_file_reception()");
}
}
void
Connection::stop_file_reception()
{
pthread_mutex_lock(&receive_mutex);
if (file_for_reception) {
file_for_reception->close();
delete file_for_reception;
file_for_reception=NULL;
}
pthread_mutex_unlock(&receive_mutex);
if (debug_level >= 2) {
debug << prefix() << "stop_file_reception finished" << endl;
}
}
void
Connection::enableDTMF() throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=PACT)
throw CapiWrongState("unable to enable DTMF because connection is not established","Connection::enableDTMF()");
_cstruct facilityRequestParameter=new unsigned char[1+2+2+2+1+3];
int i=0;
facilityRequestParameter[i++]=2+2+2+1+3; // total length
facilityRequestParameter[i++]=1; facilityRequestParameter[i++]=0; // start DTMF listen
facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for tone-duration
facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for gap-duration
facilityRequestParameter[i++]=0; // we don't want to send DTMF now (=empty struct)
facilityRequestParameter[i++]=2; // now let's start substruct DTMF Characteristics (length)
facilityRequestParameter[i++]=0; facilityRequestParameter[i++]=0; // default value for DTMF Selectivity
try {
capi->facility_req(plci,1,facilityRequestParameter);
}
catch (CapiMsgError) {
delete[] facilityRequestParameter;
throw;
}
delete[] facilityRequestParameter;
}
void
Connection::disableDTMF() throw (CapiWrongState, CapiMsgError)
{
if (plci_state!=PACT)
throw CapiWrongState("unable to disable DTMF because connection is not established","Connection::disableDTMF()");
_cstruct facilityRequestParameter=new unsigned char[1+2+2+2+1+1];
int i=0;
facilityRequestParameter[i++]=2+2+2+1+1; // total length
facilityRequestParameter[i++]=2; facilityRequestParameter[i++]=0; // stop DTMF listen
facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for tone-duration
facilityRequestParameter[i++]=40; facilityRequestParameter[i++]=0; // default value for gap-duration
facilityRequestParameter[i++]=0; // we don't want to send DTMF now (=empty struct)
facilityRequestParameter[i++]=0; // no DTMF Characteristics
try {
capi->facility_req(plci,1,facilityRequestParameter);
}
catch (CapiMsgError) {
delete[] facilityRequestParameter;
throw;
}
delete[] facilityRequestParameter;
}
string
Connection::getDTMF()
{
return received_dtmf;
}
void
Connection::clearDTMF()
{
#ifdef HAVE_STRING_CLEAR
received_dtmf.clear();
#else
received_dtmf="";
#endif
}
string
Connection::getNumber(_cstruct capi_input, bool isCallingNr)
{
// CallingNr: byte 0: length (w/o byte 0), Byte 1+2 see ETS 300 102-1, Chapter 4.5, byte 3-end: number (w/o leading "0" or "00")
// CalledNr: byte 0: length (w/o byte 0), Byte 1 see ETS 300 102-1, Chapter 4, byte 2-end: number w/o leading "0" or "00"
int length=capi_input[0];
if (!length) // no info element given
return "-";
char *nr=new char[length];
memcpy (nr,&capi_input[2],length-1); // copy only number
nr[length-1]='\0'; // add \0
string a(nr);
if (isCallingNr)
a=a.substr(1);
// if we are looking at a CallingPartyNumber and it is an international number or a national number
// (see ETS 300 102-1, chapter 4.5), we'll add the prefix "0" or "+"
if (a.empty()) {
a="-";
} else if (isCallingNr && ((capi_input[1] & 0x70) == 0x20)) { // national number
a='0'+a;
} else if (isCallingNr && ((capi_input[1] & 0x70) == 0x10)) { // international number
a='+'+a;
}
return a;
}
void
Connection::buildBconfiguration(_cdword controller, service_t service, string faxStationID, string faxHeadline, _cword& B1proto, _cword& B2proto, _cword& B3proto, _cstruct& B1config, _cstruct& B2config, _cstruct& B3config) throw (CapiExternalError)
{
switch (service) {
case VOICE:
if (!capi->profiles[controller-1].transp)
throw (CapiExternalError("controller doesn't support voice (transparent) services","Connection::buildBconfiguration()"));
B1proto=1; // bit-transparent
B2proto=1; // Transparent
B3proto=0; // Transparent
B1config=NULL; // no configuration for bit-transparent available
B2config=NULL; // no configuration for transparent available
B3config=NULL; // no configuration for transparent available
break;
case FAXG3: {
B1proto=4; // T.30 modem for Fax G3
B2proto=4; // T.30 for Fax G3
if (capi->profiles[controller-1].faxExt)
B3proto=5; // T.30 for Fax G3 Extended
else if (capi->profiles[controller-1].fax)
B3proto=4; // T.30 for Fax G3
else
throw (CapiExternalError("controller doesn't support fax services","Connection::buildBconfiguration()"));
B1config=NULL; // default configuration (adaptive maximum baud rate, default transmit level)
B2config=NULL; // no configuration available
if (faxStationID.size()>20) // stationID mustn't exceed 20 characters
faxStationID=faxStationID.substr(0,20);
if (faxHeadline.size()>254) // if the string would be longer the struct must be coded different, but I think a header > 254 bytes has no sence
faxHeadline=faxHeadline.substr(0,254);
// convert faxHeadline to CP437 for AVM drivers as they expect the string in this format
if (capi->profiles[controller-1].manufacturer.find("AVM")!=std::string::npos)
convertToCP437(faxHeadline);
B3config=new unsigned char [1+2+2+1+faxStationID.size()+1+faxHeadline.size()]; // length + 1 byte for the length itself
int i=0;
B3config[i++]=2+2+1+faxStationID.size()+1+faxHeadline.size(); // length
B3config[i++]=0; B3config[i++]=4; // resolution = standard; accept color faxes
B3config[i++]=0; B3config[i++]=0; // format: SFF
B3config[i++]=faxStationID.size();
for (unsigned j=0;j<faxStationID.size();j++)
B3config[i++]=faxStationID[j];
B3config[i++]=faxHeadline.size();
for (unsigned j=0;j<faxHeadline.size();j++)
B3config[i++]=faxHeadline[j];
} break;
default:
throw CapiExternalError("unsupported service given by application","Connection::buildBconfiguration()");
break;
}
}
void
Connection::convertToCP437(string &text)
{
size_t from_length=text.size()+1;
size_t to_length=from_length;
char* from_buf=new char[from_length];
char* from_buf_tmp=from_buf; // as pointer is changed by iconv()
char* to_buf = new char[to_length];
char* to_buf_tmp=to_buf; // as pointer is changed by iconv()
strncpy(from_buf,text.c_str(),from_length);
iconv_t conv=iconv_open("CP437","Latin1");
if (conv==(iconv_t)-1) {
error << prefix() << "WARNING: string conversion to CP437 not supported by iconv" << endl;
return;
}
if (iconv(conv,&from_buf_tmp,&from_length,&to_buf_tmp,&to_length)==(size_t)-1) {
char msg[200];
error << prefix() << "WARNING: error during string conversion (iconv): " << strerror_r(errno,msg,200) << endl;
return;
}
if (iconv_close(conv)!=0)
throw CapiExternalError("error during string conversion (iconv_close)","Connection::convertToCP437");
text=to_buf;
delete[] from_buf;
delete[] to_buf;
}
/* History
Old Log (for new changes see ChangeLog):
Revision 1.13 2003/12/21 21:15:10 gernot
* src/backend/connection.cpp (buildBconfiguration): accept
color faxes now by setting bit 10 in B3configuration
Revision 1.12 2003/07/20 19:08:19 gernot
- added missing include of errno.h
Revision 1.11.2.5 2003/11/06 18:32:15 gernot
- implemented DDIStopNumbers
Revision 1.11.2.4 2003/11/02 14:58:16 gernot
- use DDI_base_length instead of DDI_base
- added DDI_stop_numbers option
- use DDI_* options in the Connection class
- call the Python script if number is complete
Revision 1.11.2.3 2003/11/01 22:59:33 gernot
- read CalledPartyNr InfoElements
Revision 1.11.2.2 2003/10/26 16:52:55 gernot
- begin implementation of DDI; get DDI info elements
Revision 1.11.2.1 2003/07/20 19:08:44 gernot
- added missing include of errno.h
Revision 1.11 2003/06/29 06:18:13 gernot
- don't take a wrong character too serious...
Revision 1.10 2003/06/28 12:49:47 gernot
- convert fax headline to CP437, so that german umlauts and other special
characters will work now
Revision 1.9 2003/05/25 13:38:30 gernot
- support reception of color fax documents
Revision 1.8 2003/05/24 13:48:54 gernot
- get fax details (calling station ID, transfer format, ...), handle PLCI
Revision 1.7 2003/04/17 10:39:42 gernot
- support ALERTING notification (to know when it's ringing on the other side)
- cosmetical fixes in capi.cpp
Revision 1.6 2003/04/17 10:36:29 gernot
- fix another typo which could probably lead to errors in sending own number...
Revision 1.5 2003/04/10 21:29:51 gernot
- support empty destination number for incoming calls correctly (austrian
telecom does this (sic))
- core now returns "-" instead of "??" for "no number available" (much nicer
in my eyes)
- new wave file used in remote inquiry for "unknown number"
Revision 1.4 2003/04/04 09:17:59 gernot
- buildBconfiguration() now checks the abilities of the given controller
and throws an error if it doesn't support the service
- it also sets the fax protocol setting now the highest available ability
(fax G3 or fax G3 extended) of the controller, thus preparing fax polling
and *working around a severe bug in the AVM drivers producing a kernel
oops* with some analog fax devices. AVM knows about this and analyzes it.
Revision 1.3 2003/03/21 23:09:59 gernot
- included autoconf tests for gcc-2.95 problems so that it will compile w/o
change for good old gcc-2.95 and gcc3
Revision 1.2 2003/02/28 21:36:51 gernot
- don't allocate new B3config in buildBconfiguration(), fixes bug 532
- limit stationID to 20 characters
Revision 1.1.1.1 2003/02/19 08:19:53 gernot
initial checkin of 0.4
Revision 1.44 2003/02/10 14:20:52 ghillie
merged from NATIVE_PTHREADS to HEAD
Revision 1.43 2003/02/09 15:16:29 ghillie
- fixed some delete calls to delete[]
Revision 1.42.2.1 2003/02/10 14:10:27 ghillie
- use pthread_mutex_* instead of CommonC++ semaphores
Revision 1.42 2003/01/31 16:33:13 ghillie
- callingParty wasn't set
Revision 1.41 2003/01/31 11:27:50 ghillie
- wrong initialization of debug_level for outgoing connections fixed
Revision 1.40 2003/01/19 16:50:27 ghillie
- removed severity in exceptions. No FATAL-automatic-exit any more.
Removed many FATAL conditions, other ones are exiting now by themselves
Revision 1.39 2003/01/13 21:30:23 ghillie
- FIX: removed erroneous checking of connect_ind_msg_nr in rejectWaiting()
and checked for our_call instead (oops, overlooked this one ;-) )
Revision 1.38 2003/01/04 16:08:22 ghillie
- log improvements: log_level, timestamp
- added methods debugMessage(), errorMessage(), removed get*Stream()
- added some additional debug output for connection setup / finish
Revision 1.37 2002/12/18 14:46:07 ghillie
- removed debug output
Revision 1.36 2002/12/18 14:45:13 ghillie
- moved *_state=XY actions direct in front of messages sent to CAPI, so that
parallel executed threads don't see a wrong state (hopefully)
- removed test for connect_ind_msg_nr!=0 in connectWaiting(). Don't know
what I intended with this (sigh)
- added missing "{" in select_b_protocol_conf() :-(
- removed unnecessary plci_state=PACT in select_b_protocol_conf
Revision 1.35 2002/12/16 15:05:47 ghillie
- FIX: corrected disconnect behaviour (physical connection is now disconnected
correctly)
Revision 1.34 2002/12/16 13:13:47 ghillie
- added getCauseB3 to return B3 cause
Revision 1.33 2002/12/13 11:46:19 ghillie
- new attribute our_call to inidicate that we initiated a connection
- send CONNECT_B3_REQ after receiving SELECT_B_PROTOCOL_CONF for outgoing calls
Revision 1.32 2002/12/13 09:57:44 ghillie
- error message formatting done by exception classes now
Revision 1.31 2002/12/11 13:38:43 ghillie
- FIX: added missing init of keepPhysicalConnection in outgoing constructor
- use quick disconnect (PHYSICAL_ONLY) in destructor
- disconnectCall(): added support for PHYSICAL_ONLY disconnect
Revision 1.30 2002/12/10 15:06:15 ghillie
- new methods get*Stream() for use in capisuitemodule
Revision 1.29 2002/12/09 15:42:07 ghillie
- saves debug and error stream in own attributes now
- debug output improvements, error output included
- unregistering at Capi now done as soon as DISCONNECT_IND is received
Revision 1.28 2002/12/06 15:25:39 ghillie
- cleaned up and fixed destructor (wrong order of some calls)
- new return value for getState(): WAITING
Revision 1.27 2002/12/06 13:06:44 ghillie
- added support for saving disconnect cause
- ~Connection does busy wait for disconnect to prevent Connection objects
going away while the corresponding call is still active
- added error checking for connect_ind_msg_nr
- new methods getState() and getCause()
Revision 1.26 2002/12/05 15:04:29 ghillie
- Capi::connect_req() gets this now
- call capi->unregisterConnection(plci) in destructor
- connect_conf() sets plci attribute
- connect_b3_conf() sets ncci attribute
Revision 1.25 2002/12/04 10:43:43 ghillie
- small FIX in getNumber(): added missing parantheses in if condition -> national number & international number work now
Revision 1.24 2002/12/02 12:31:10 ghillie
- renamed Connection::SPEECH to Connection::VOICE
Revision 1.23 2002/11/29 10:25:01 ghillie
- updated comments, use doxygen format now
Revision 1.22 2002/11/27 16:02:54 ghillie
- added missing throw() declaration in changeProtocol()
- added missing state check in acceptWaiting()
- data_b3_ind and disconnect_ind propagate CapiMsgError now
- DTMF handling routines and select_b_protocol_conf test for state of physical connection instead of logical connection now
Revision 1.21 2002/11/25 11:51:45 ghillie
- removed the unhandy CIP parameters from the interface to the application layer, use service type instead
- rejectWaiting() tests against cause!=0 now
- removed isUp() method
Revision 1.20 2002/11/22 15:13:44 ghillie
- new attribute keepPhysicalConnection which prevents disconnect_b3_ind() from sending disconnect_req()
- moved the ugly B*configuration, B*protocol settings from some methods to private method buildBconfiguration
- new methods changeProtocol(), select_b_protocol_conf(), clearDTMF()
- disconnect_b3_ind sets ncci_state to N0 before calling the callbacks
- added parameter disconnect_mode to disconnectCall()
- getDTMF() does non-destructive read now
Revision 1.19 2002/11/21 15:28:12 ghillie
- removed ALERT_REQ sending from constructor - this is now done by the python functions connect_*()
- new method Connection::acceptWaiting() - sends ALERT_REQ for use by the above mentioned python functions
- connectWaiting changes cipValue now
Revision 1.18 2002/11/20 17:24:58 ghillie
- added check if call_if is set in data_b3_ind before it's called (ouch!)
- changed impossible error to ::FATAL in send_block()
Revision 1.17 2002/11/19 15:57:18 ghillie
- Added missing throw() declarations
- phew. Added error handling. All exceptions are caught now.
Revision 1.16 2002/11/18 14:24:09 ghillie
- moved global severity_t to CapiError::severity_t
- added throw() declarations
Revision 1.15 2002/11/18 12:23:17 ghillie
- fix: set buffers_used to 0 in critical section in Connection::disconnect_b3_ind()
- disconnectCall() doesn't throw exception any more (does nothing if we have wrong state),
so we can call it w/o knowledge if connection is still up
Revision 1.14 2002/11/17 14:40:47 ghillie
- improved exception throwing, different exception kinds are used now
- added isUp()
Revision 1.13 2002/11/15 15:25:53 ghillie
added ALERT_REQ so we don't loose a call when we wait before connection establishment
Revision 1.12 2002/11/15 13:49:10 ghillie
fix: callmodule wasn't aborted when call was only connected/disconnected physically
Revision 1.11 2002/11/14 17:05:19 ghillie
major structural changes - much is easier, nicer and better prepared for the future now:
- added DisconnectLogical handler to CallInterface
- DTMF handling moved from CallControl to Connection
- new call module ConnectModule for establishing connection
- python script reduced from 2 functions to one (callWaiting, callConnected
merged to callIncoming)
- call modules implement the CallInterface now, not CallControl any more
=> this freed CallControl from nearly all communication stuff
Revision 1.10 2002/11/13 08:34:54 ghillie
moved history to the bottom
Revision 1.9 2002/11/12 15:51:12 ghillie
minor fixes (avoid deadlock, don't wait for DATA_B3_CONF after DISCONNECT_B3_IND) in file_transmission code
added dataIn handler
minor fixes (and reformatting) in getNumber()
Revision 1.8 2002/11/10 17:05:18 ghillie
changed to support multiple buffers -> deadlock in stop_file_transmission!!
Revision 1.7 2002/11/08 07:57:06 ghillie
added functions to initiate a call
corrected FACILITY calls to use PLCI instead of NCCI in DTMF processing as told by Mr. Ortmann on comp.dcom.isdn.capi
Revision 1.6 2002/10/31 15:39:04 ghillie
added missing FACILITY_RESP message (oops...)
Revision 1.5 2002/10/31 12:40:06 ghillie
added DTMF support
small fixes like making some unnecessary global variables local, removed some unnecessary else cases
Revision 1.4 2002/10/30 14:29:25 ghillie
added getCIPvalue
Revision 1.3 2002/10/30 10:47:13 ghillie
added debug output
Revision 1.2 2002/10/29 14:27:09 ghillie
added stop_file_*, added semaphore calls to guarantee right order of execution (I hope ;-) )
Revision 1.1 2002/10/25 13:29:38 ghillie
grouped files into subdirectories
Revision 1.15 2002/10/24 09:55:52 ghillie
many fixes. Works for one call now
Revision 1.14 2002/10/23 09:43:05 ghillie
small variable name change (stationID->faxStationID)
Revision 1.13 2002/10/10 12:45:40 gernot
added AudioReceive module, some small details changed
Revision 1.12 2002/10/09 14:36:22 gernot
added CallModule base class for all call handling modules
Revision 1.11 2002/10/09 11:18:59 gernot
cosmetic changes (again...) and changed info function of CAPI class
Revision 1.10 2002/10/05 13:53:00 gernot
changed to use thread class of CommonC++ instead of the threads-package
some cosmetic improvements (indentation...)
Revision 1.9 2002/10/04 15:48:03 gernot
structure changes completed & compiles now!
Revision 1.8 2002/10/04 13:27:15 gernot
some restructuring to get it to a working state ;-)
does not do anything useful yet nor does it even compile...
Revision 1.7 2002/10/01 09:02:04 gernot
changes for compilation with gcc3.2
Revision 1.6 2002/09/22 14:22:53 gernot
some cosmetic comment improvements ;-)
Revision 1.5 2002/09/19 12:08:19 gernot
added magic CVS strings
Revision 1.4 2002/09/18 16:59:48 gernot
added version info
* Sun Sep 15 2002 - gernot@hillier.de
- put under CVS, cvs log follows above
* Sun May 20 2002 - gernot@hillier.de
- first version
*/