diff --git a/fscomm/mainwindow.cpp b/fscomm/mainwindow.cpp
index b4720f5034..0cfb076f3e 100644
--- a/fscomm/mainwindow.cpp
+++ b/fscomm/mainwindow.cpp
@@ -179,10 +179,26 @@ void MainWindow::debugConsoleTriggered()
}
+void MainWindow::applyPreprocessors(QStringList cmds)
+{
+ if (g_FSHost.getCurrentActiveCall().isNull()) return;
+ QString uuid = g_FSHost.getCurrentActiveCall().data()->getUuid();
+ foreach(QString cmd, cmds)
+ {
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ switch_api_execute("uuid_preprocess", QString("%1 %2").arg(uuid, cmd).toAscii().data(), NULL, &stream);
+ switch_safe_free(stream.data);
+ }
+}
+
void MainWindow::prefTriggered()
{
if (!preferences)
+ {
preferences = new PrefDialog();
+ connect(preferences, SIGNAL(preprocessorsApplied(QStringList)), this, SLOT(applyPreprocessors(QStringList)));
+ }
preferences->raise();
preferences->show();
diff --git a/fscomm/mainwindow.h b/fscomm/mainwindow.h
index e7348c13d6..3b87da18ed 100644
--- a/fscomm/mainwindow.h
+++ b/fscomm/mainwindow.h
@@ -85,6 +85,7 @@ private slots:
void updateCallTimers();
void debugConsoleTriggered();
void debugEventsTriggered();
+ void applyPreprocessors(QStringList);
private:
Ui::MainWindow *ui;
diff --git a/fscomm/preferences/prefdialog.cpp b/fscomm/preferences/prefdialog.cpp
index c615411591..233de4eb1b 100644
--- a/fscomm/preferences/prefdialog.cpp
+++ b/fscomm/preferences/prefdialog.cpp
@@ -15,6 +15,7 @@ PrefDialog::PrefDialog(QWidget *parent) :
_pref_accounts = new PrefAccounts(ui);
_mod_portaudio = new PrefPortaudio(ui, this);
+ connect(_mod_portaudio, SIGNAL(preprocessorsApplied(QStringList)), this, SIGNAL(preprocessorsApplied(QStringList)));
_mod_sofia = new PrefSofia(ui, this);
readConfig();
}
diff --git a/fscomm/preferences/prefdialog.h b/fscomm/preferences/prefdialog.h
index 7c9907f5c2..2704a501a8 100644
--- a/fscomm/preferences/prefdialog.h
+++ b/fscomm/preferences/prefdialog.h
@@ -26,6 +26,9 @@ protected:
private slots:
void writeConfig();
+signals:
+ void preprocessorsApplied(QStringList);
+
private:
void readConfig();
QSettings *_settings;
diff --git a/fscomm/preferences/prefdialog.ui b/fscomm/preferences/prefdialog.ui
index 1192631e70..fbab506dab 100644
--- a/fscomm/preferences/prefdialog.ui
+++ b/fscomm/preferences/prefdialog.ui
@@ -6,1006 +6,1165 @@
0
0
- 839
- 613
+ 803
+ 667
Preferences
-
- -
-
-
-
- 120
- 0
-
-
-
-
- 120
- 16777215
-
-
-
- false
-
-
- QAbstractItemView::NoDragDrop
-
-
-
- 96
- 84
-
-
-
- QListView::Static
-
-
- QListView::LeftToRight
-
-
- true
-
-
- 12
-
-
- QListView::IconMode
-
+
+
-
+
-
-
- General
-
-
-
- :/images/pref_general.jpg:/images/pref_general.jpg
-
-
- -
-
- Accounts
-
-
-
- :/images/pref_accounts.jpg:/images/pref_accounts.jpg
-
-
- -
-
- Sofia
-
-
-
- :/images/pref_sip.png:/images/pref_sip.png
-
-
- -
-
- PortAudio
-
-
-
- :/images/pref_audio.gif:/images/pref_audio.gif
-
-
-
-
- -
-
-
- 0
-
-
-
+
+
+
+ 120
+ 0
+
+
+
+
+ 120
+ 16777215
+
+
+
+ false
+
+
+ QAbstractItemView::NoDragDrop
+
+
+
+ 96
+ 84
+
+
+
+ QListView::Static
+
+
+ QListView::LeftToRight
+
+
+ true
+
+
+ 12
+
+
+ QListView::IconMode
+
-
-
-
- User Information
-
-
-
-
-
-
- CallerID Name:
-
-
-
- -
-
-
- -
-
-
- CallerID Number:
-
-
-
- -
-
-
-
-
-
-
-
-
-
- -
-
-
- QAbstractItemView::NoEditTriggers
-
-
- QAbstractItemView::SelectRows
-
-
-
- Name
-
-
-
-
- Username
-
-
-
+
+ General
+
+
+
+ :/images/pref_general.jpg:/images/pref_general.jpg
+
-
-
+
+ Accounts
+
+
+
+ :/images/pref_accounts.jpg:/images/pref_accounts.jpg
+
+
+ -
+
+ Sofia
+
+
+
+ :/images/pref_sip.png:/images/pref_sip.png
+
+
+ -
+
+ Audio
+
+
+
+ :/images/pref_audio.gif:/images/pref_audio.gif
+
+
+
+
+ -
+
+
+ 0
+
+
+
-
-
-
- Add
+
+
+ User Information
+
+
-
+
+
+ CallerID Name:
+
+
+
+ -
+
+
+ -
+
+
+ CallerID Number:
+
+
+
+ -
+
+
+
- -
-
-
- Remove
-
-
-
- -
-
-
- true
-
-
- Edit
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
- General
-
-
-
-
-
-
- Global
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
- log-level
-
-
-
- -
-
-
- -
-
-
- auto-restart
-
-
-
- -
-
-
-
-
- true
-
-
- -
-
- false
-
-
-
-
- -
-
-
- debug-presence
-
-
-
- -
-
-
- -
-
-
- rewrite-multicasted-fs-path
-
-
-
- -
-
-
-
-
- false
-
-
- -
-
- true
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Profile
-
-
-
-
-
-
- user-agent-string
-
-
-
- -
-
-
- FreeSWITCH/FSComm
-
-
-
- -
-
-
- hold-music
-
-
-
- -
-
-
- localstream://moh
-
-
-
- -
-
-
- context
-
-
-
- -
-
-
- public
-
-
-
- -
-
-
- dialplan
-
-
-
- -
-
-
- XML
-
-
-
-
-
-
-
-
-
-
- Network
-
-
- -
-
-
- Softphone Profile
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
- sip-port
-
-
-
- -
-
-
- 1
-
-
- 65535
-
-
- 12345
-
-
-
- -
-
-
- use-rtp-timer
-
-
-
- -
-
-
-
-
- true
-
-
- -
-
- false
-
-
-
-
- -
-
-
- rtp-ip
-
-
-
- -
-
-
- auto
-
-
-
- -
-
-
- sip-ip
-
-
-
- -
-
-
- auto
-
-
-
- -
-
-
- apply-nat-acl
-
-
-
- -
-
-
- rfc1918
-
-
-
- -
-
-
- ext-rtp-ip
-
-
-
- -
-
-
- stun:stun.freeswitch.org
-
-
-
- -
-
-
- ext-sip-ip
-
-
-
- -
-
-
- stun:stun.freeswitch.org
-
-
-
-
-
-
-
-
-
-
- Codecs
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
-
-
-
-
- inbound-codec-negotiation
-
-
-
- -
-
-
-
-
- generous
-
-
- -
-
- greedy
-
-
-
-
- -
-
-
- Codecs
-
-
-
- -
-
-
-
-
-
-
- Advanced
-
-
+
+ QAbstractItemView::SelectRows
+
+
+
+ Name
+
+
+
+
+ Username
+
+
+
+
+ -
+
-
-
-
- Profile
+
+
+ Add
-
-
-
-
-
- rfc2833-pt
-
-
-
- -
-
-
- 101
-
-
-
- -
-
-
- debug
-
-
-
- -
-
-
- -
-
-
- sip-trace
-
-
-
- -
-
-
-
-
- false
-
-
- -
-
- true
-
-
-
-
- -
-
-
- dtmf-duration
-
-
-
- -
-
-
- 1000
-
-
- 100
-
-
-
- -
-
-
- rtp-timer-name
-
-
-
- -
-
-
- soft
-
-
-
- -
-
-
- manage-presence
-
-
-
- -
-
-
-
-
- false
-
-
- -
-
- true
-
-
-
-
- -
-
-
- max-proceeding
-
-
-
- -
-
-
- 3
-
-
-
- -
-
-
- nonce-ttl
-
-
-
- -
-
-
- 60
-
-
-
- -
-
-
- rtp-timeout-sec
-
-
-
- -
-
-
- 1000
-
-
- 300
-
-
-
- -
-
-
- rtp-hold-timeout-sec
-
-
-
- -
-
-
- 5000
-
-
- 1800
-
-
-
- -
-
-
- auth-calls
-
-
-
- -
-
-
-
-
- false
-
-
- -
-
- true
-
-
-
-
- -
-
-
- auth-all-packets
-
-
-
- -
-
-
-
-
- false
-
-
- -
-
- true
-
-
-
-
- -
-
-
- disable-register
-
-
-
- -
-
-
-
-
- true
-
-
- -
-
- false
-
-
-
-
- -
-
-
- challenge-realm
-
-
-
- -
-
-
-
-
- auto_from
-
-
- -
-
- auto_to
-
-
-
-
-
+ -
+
+
+ Remove
+
+
+
+ -
+
+
+ true
+
+
+ Edit
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Devices
-
-
-
-
-
-
- indev
-
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ General
+
+
+
-
+
+
+ Global
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ log-level
+
+
+
+ -
+
+
+ -
+
+
+ auto-restart
+
+
+
+ -
+
+
-
+
+ true
+
+
+ -
+
+ false
+
+
+
+
+ -
+
+
+ debug-presence
+
+
+
+ -
+
+
+ -
+
+
+ rewrite-multicasted-fs-path
+
+
+
+ -
+
+
-
+
+ false
+
+
+ -
+
+ true
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Profile
+
+
+
-
+
+
+ user-agent-string
+
+
+
+ -
+
+
+ FreeSWITCH/FSComm
+
+
+
+ -
+
+
+ hold-music
+
+
+
+ -
+
+
+ localstream://moh
+
+
+
+ -
+
+
+ context
+
+
+
+ -
+
+
+ public
+
+
+
+ -
+
+
+ dialplan
+
+
+
+ -
+
+
+ XML
+
+
+
+
+
+
+
-
- -
-
-
- -
-
-
- outdev
-
+
+
+ Network
+
+
+
-
+
+
+ Softphone Profile
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ sip-port
+
+
+
+ -
+
+
+ 1
+
+
+ 65535
+
+
+ 12345
+
+
+
+ -
+
+
+ use-rtp-timer
+
+
+
+ -
+
+
-
+
+ true
+
+
+ -
+
+ false
+
+
+
+
+ -
+
+
+ rtp-ip
+
+
+
+ -
+
+
+ auto
+
+
+
+ -
+
+
+ sip-ip
+
+
+
+ -
+
+
+ auto
+
+
+
+ -
+
+
+ apply-nat-acl
+
+
+
+ -
+
+
+ rfc1918
+
+
+
+ -
+
+
+ ext-rtp-ip
+
+
+
+ -
+
+
+ stun:stun.freeswitch.org
+
+
+
+ -
+
+
+ ext-sip-ip
+
+
+
+ -
+
+
+ stun:stun.freeswitch.org
+
+
+
+
+
+
+
-
- -
-
-
-
-
-
- -
-
-
- false
-
-
-
- 86
- 27
-
-
-
- Test
-
-
-
-
-
- -
-
-
- rindgev
-
+
+
+ Codecs
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ inbound-codec-negotiation
+
+
+
+ -
+
+
-
+
+ generous
+
+
+ -
+
+ greedy
+
+
+
+
+ -
+
+
+ Codecs
+
+
+
+ -
+
+
+
-
- -
-
-
-
-
-
- -
-
-
- true
-
-
-
- 86
- 27
-
-
-
- Test
-
-
-
-
-
- -
-
-
-
-
-
- Loop Test
-
-
-
- -
-
-
- Refresh DevList
-
-
-
-
-
- -
-
-
- Sample Rate
-
+
+
+ Advanced
+
+
+
-
+
+
+ Profile
+
+
+
-
+
+
+ rfc2833-pt
+
+
+
+ -
+
+
+ 101
+
+
+
+ -
+
+
+ debug
+
+
+
+ -
+
+
+ -
+
+
+ sip-trace
+
+
+
+ -
+
+
-
+
+ false
+
+
+ -
+
+ true
+
+
+
+
+ -
+
+
+ dtmf-duration
+
+
+
+ -
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+ rtp-timer-name
+
+
+
+ -
+
+
+ soft
+
+
+
+ -
+
+
+ manage-presence
+
+
+
+ -
+
+
-
+
+ false
+
+
+ -
+
+ true
+
+
+
+
+ -
+
+
+ max-proceeding
+
+
+
+ -
+
+
+ 3
+
+
+
+ -
+
+
+ nonce-ttl
+
+
+
+ -
+
+
+ 60
+
+
+
+ -
+
+
+ rtp-timeout-sec
+
+
+
+ -
+
+
+ 1000
+
+
+ 300
+
+
+
+ -
+
+
+ rtp-hold-timeout-sec
+
+
+
+ -
+
+
+ 5000
+
+
+ 1800
+
+
+
+ -
+
+
+ auth-calls
+
+
+
+ -
+
+
-
+
+ false
+
+
+ -
+
+ true
+
+
+
+
+ -
+
+
+ auth-all-packets
+
+
+
+ -
+
+
-
+
+ false
+
+
+ -
+
+ true
+
+
+
+
+ -
+
+
+ disable-register
+
+
+
+ -
+
+
-
+
+ true
+
+
+ -
+
+ false
+
+
+
+
+ -
+
+
+ challenge-realm
+
+
+
+ -
+
+
-
+
+ auto_from
+
+
+ -
+
+ auto_to
+
+
+
+
+
+
+
+
-
- -
-
-
- 16000
-
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ General
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Devices
+
+
+
-
+
+
+ indev
+
+
+
+ -
+
+
+ -
+
+
+ outdev
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ false
+
+
+
+ 86
+ 27
+
+
+
+ Test
+
+
+
+
+
+ -
+
+
+ rindgev
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ true
+
+
+
+ 86
+ 27
+
+
+
+ Test
+
+
+
+
+
+ -
+
+
-
+
+
+ Loop Test
+
+
+
+ -
+
+
+ Refresh DevList
+
+
+
+
+
+ -
+
+
+ Sample Rate
+
+
+
+ -
+
+
+ 16000
+
+
+
+ -
+
+
+ Codec MS
+
+
+
+ -
+
+
+ 10
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Files
+
+
+
-
+
+
+ ring-file
+
+
+
+ -
+
+
-
+
+
+ tone_stream://%(2000,4000,440.0,480.0);loops=20
+
+
+
+ -
+
+
+ Open
+
+
+
+
+
+ -
+
+
+ ring-interval
+
+
+
+ -
+
+
+ 5
+
+
+
+ -
+
+
+ hold-file
+
+
+
+ -
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+ Open
+
+
+
+
+
+
+
+
+
-
- -
-
-
- Codec MS
-
+
+
+ Preprocessor
+
+
+
-
+
+
+ Echo canceller
+
+
+
+ -
+
+
+ read
+
+
+
+ -
+
+
+ write
+
+
+
+ -
+
+
+ Tail
+
+
+
+ -
+
+
+ -
+
+
+ Echo Suppressor
+
+
+
+ -
+
+
+ read
+
+
+
+ -
+
+
+ write
+
+
+
+ -
+
+
+ Db
+
+
+
+ -
+
+
+ -99
+
+
+ 0
+
+
+
+ -
+
+
+ Noise Supressor
+
+
+
+ -
+
+
+ read
+
+
+
+ -
+
+
+ write
+
+
+
+ -
+
+
+ Db
+
+
+
+ -
+
+
+ -
+
+
+ read
+
+
+
+ -
+
+
+ write
+
+
+
+ -
+
+
+ -
+
+
+ AGC
+
+
+
+ -
+
+
+ Apply
+
+
+ true
+
+
+
+
-
- -
-
-
- 10
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Files
-
-
-
-
-
-
- ring-file
-
-
-
- -
-
-
-
-
-
- tone_stream://%(2000,4000,440.0,480.0);loops=20
-
-
-
- -
-
-
- Open
-
-
-
-
-
- -
-
-
- ring-interval
-
-
-
- -
-
-
- 5
-
-
-
- -
-
-
- hold-file
-
-
-
- -
-
-
-
-
-
- true
-
-
-
- -
-
-
- Open
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- -
+
-
Qt::Horizontal
diff --git a/fscomm/preferences/prefportaudio.cpp b/fscomm/preferences/prefportaudio.cpp
index 14137956f1..8328c500b0 100644
--- a/fscomm/preferences/prefportaudio.cpp
+++ b/fscomm/preferences/prefportaudio.cpp
@@ -15,6 +15,28 @@ PrefPortaudio::PrefPortaudio(Ui::PrefDialog *ui, QObject *parent) :
connect(_ui->PaRingdevTestBtn, SIGNAL(clicked()), this, SLOT(ringdevTest()));
connect(_ui->PaLoopTestBtn, SIGNAL(clicked()), this, SLOT(loopTest()));
connect(_ui->PaRefreshDevListBtn, SIGNAL(clicked()), this, SLOT(refreshDevList()));
+ connect(_ui->btnApplyPreprocessor, SIGNAL(toggled(bool)), this, SLOT(applyPreprocessors(bool)));
+}
+
+void PrefPortaudio::applyPreprocessors(bool state)
+{
+ QStringList cmds;
+ if (!state)
+ {
+ cmds.append("stop");
+ }
+ else
+ {
+ if (_ui->checkAECRead->isChecked()) cmds.append(QString("recho_cancel=%1").arg(_ui->spinAECTail->value()));
+ if (_ui->checkAECWrite->isChecked()) cmds.append(QString("wecho_cancel=%1").arg(_ui->spinAECTail->value()));
+ if (_ui->checkESRead->isChecked()) cmds.append(QString("recho_suppress=%1").arg(_ui->spinESDb->value()));
+ if (_ui->checkESWrite->isChecked()) cmds.append(QString("wecho_suppress=%1").arg(_ui->spinESDb->value()));
+ if (_ui->checkNSRead->isChecked()) cmds.append(QString("rnoise_suppress=%1").arg(_ui->spinNSDb->value()));
+ if (_ui->checkNSWrite->isChecked()) cmds.append(QString("wnoise_suppress=%1").arg(_ui->spinNSDb->value()));
+ if (_ui->checkAGCRead->isChecked()) cmds.append(QString("ragc=%1").arg(_ui->spinAGC->value()));
+ if (_ui->checkAGCWrite->isChecked()) cmds.append(QString("wagc=%1").arg(_ui->spinAGC->value()));
+ }
+ emit preprocessorsApplied(cmds);
}
void PrefPortaudio::ringdevTest()
diff --git a/fscomm/preferences/prefportaudio.h b/fscomm/preferences/prefportaudio.h
index 9ffd285636..d12750ab65 100644
--- a/fscomm/preferences/prefportaudio.h
+++ b/fscomm/preferences/prefportaudio.h
@@ -24,6 +24,11 @@ private slots:
void ringdevTest();
void loopTest();
void refreshDevList();
+ void applyPreprocessors(bool);
+
+signals:
+ void preprocessorsApplied(QStringList);
+
private:
void getPaDevlist(void);
QSettings *_settings;
diff --git a/scripts/setup-git.sh b/scripts/setup-git.sh
new file mode 100755
index 0000000000..1a5e36d339
--- /dev/null
+++ b/scripts/setup-git.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+##### -*- mode:shell-script; indent-tabs-mode:nil; sh-basic-offset:2 -*-
+##### setup git properly for FreeSWITCH
+
+if [ ! -d .git ]; then
+ echo "error: must be run from within the top level of a FreeSWITCH git tree." 1>&2
+ exit 1;
+fi
+
+err () {
+ echo "error: $1" 1>&2
+ exit 1
+}
+
+if ! git config user.name >/dev/null 2>&1; then
+ name=$(git config user.name)
+ [ -z "$name" ] \
+ && [ -n "$NAME" ] && name="$NAME" || name=""
+ echo -n "What is your name? [$name]: "
+ read name_
+ [ -n "$name_" ] && name="$name_"
+ [ -z "$name" ] && err "Your name is required."
+ git config --global user.name "$name"
+fi
+
+if ! git config user.email >/dev/null 2>&1; then
+ email=$(git config user.email)
+ [ -z "$email" ] \
+ && [ -n "$EMAIL" ] && email="$EMAIL" || email=""
+ echo -n "What is your email? [$email]: "
+ read email_
+ [ -n "$email_" ] && email="$email_"
+ [ -z "$email" ] && err "Your email is required."
+ git config --global user.email "$email"
+fi
+
+git config branch.master.rebase true
+
+cat 1>&2 <
+
+ See 'man git-config' for more information.
+EOF
+
+[ -n "$name" ] \
+ && cat 1>&2 <&2 <&2 <private_info;
assert(info != NULL);
-
- switch_core_file_close(info->fh);
- if (unlink(info->file) != 0) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Sound file [%s] delete failed\n", info->file);
+
+ if (info->fh != NULL && info->fh->file_interface != NULL) {
+ switch_core_file_close(info->fh);
+ }
+ if (switch_file_exists(info->file, NULL) == SWITCH_STATUS_SUCCESS) {
+ if (unlink(info->file) != 0) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Sound file [%s] delete failed\n", info->file);
+ }
}
}
diff --git a/src/mod/endpoints/mod_skinny/Net/Skinny.pm b/src/mod/endpoints/mod_skinny/Net/Skinny.pm
index e0ae460eac..c4ee1992ad 100644
--- a/src/mod/endpoints/mod_skinny/Net/Skinny.pm
+++ b/src/mod/endpoints/mod_skinny/Net/Skinny.pm
@@ -6,26 +6,25 @@ package Net::Skinny;
use strict;
use warnings;
-use IO::Socket;
+
+require IO::Socket;
+
use Net::Skinny::Protocol qw/:all/;
-our(@ISA);
-@ISA = qw(IO::Socket::INET);
+our @ISA = qw(IO::Socket::INET);
sub new {
shift->SUPER::new(PeerPort => 2000, @_);
}
-sub send_data
+sub send_raw
{
my $self = shift;
my $type = shift;
- my $data = shift;
- my $len = length($data)+4;
+ my $raw = shift;
+ my $len = length($raw)+4;
printf "Sending message (length=%d, type=%s (%X))", $len, Net::Skinny::Protocol::skinny_message_type2str($type), $type;
- $self->send(
- pack("VVV", $len, 0, $type).
- $data);
+ $self->send(pack("VVV", $len, 0, $type).$raw);
printf ".\n";
}
@@ -33,11 +32,8 @@ sub send_message
{
my $self = shift;
my $type = shift;
- return Net::Skinny::Message->new(
- $self,
- $type,
- @_
- )->send();
+ my $message = Net::Skinny::Message->new($type, @_);
+ return $self->send_raw($message->type(), $message->raw());
}
sub receive_message
@@ -58,20 +54,26 @@ sub receive_message
printf "type=%s (%X))", Net::Skinny::Protocol::skinny_message_type2str($type), $type;
if($len > 4) {
$self->recv($buf, $len-4);
+ } else {
+ $buf = '';
}
printf ".\n";
+ return Net::Skinny::Message->new_raw($type, $buf);
}
sub sleep
{
my $self = shift;
my $t = shift;
-
+ my %args = @_;
+ $args{'quiet'} = 0 if not $args{'quiet'};
printf "Sleeping %d seconds", $t;
while(--$t){
sleep(1);
- printf "." if $t % 10;
- printf "_" unless $t % 10;
+ if(!$args{'quiet'}) {
+ printf "." if $t % 10;
+ printf "_" unless $t % 10;
+ }
}
printf ".\n";
}
diff --git a/src/mod/endpoints/mod_skinny/Net/Skinny/Client.pm b/src/mod/endpoints/mod_skinny/Net/Skinny/Client.pm
new file mode 100644
index 0000000000..ee063cc911
--- /dev/null
+++ b/src/mod/endpoints/mod_skinny/Net/Skinny/Client.pm
@@ -0,0 +1,94 @@
+# Copyright (c) 2010 Mathieu Parent .
+# All rights reserved. This program is free software; you can redistribute it
+# and/or modify it under the same terms as Perl itself.
+
+package Net::Skinny::Client;
+
+use strict;
+use warnings;
+
+use Config;
+use threads;
+use threads::shared;
+use Thread::Queue;
+
+require Net::Skinny;
+use Net::Skinny::Protocol qw/:all/;
+use Net::Skinny::Message;
+
+our(@ISA);
+@ISA = qw(Net::Skinny);
+
+my $keep_alive_thread;
+my $keep_alives :shared;
+our $kept_self;
+my $messages_send_queue;
+my $messages_receive_queue;
+
+$Config{useithreads} or die('Recompile Perl with threads to run this program.');
+
+sub new {
+ $kept_self = shift->SUPER::new(@_);
+ $messages_send_queue = Thread::Queue->new();
+ $messages_receive_queue = Thread::Queue->new();
+ threads->create(\&send_messages_thread_func);
+ threads->create(\&receive_messages_thread_func);
+ return $kept_self;
+}
+
+sub send_message {
+ my $self = shift;
+ $messages_send_queue->enqueue(\@_);
+}
+
+sub receive_message {
+ my $self = shift;
+ my $message = $messages_receive_queue->dequeue();
+ if($message->type() == 0x100) {#keepaliveack
+ if(1) {
+ lock($keep_alives);
+ $keep_alives--;
+ }
+ $message = $messages_receive_queue->dequeue();
+ }
+ return $message;
+}
+
+sub launch_keep_alive_thread
+{
+ if(!$keep_alive_thread) {
+ $keep_alive_thread = threads->create(\&keep_alive_thread_func);
+ } else {
+ print "keep-alive thread is already running\n";
+ }
+ return $keep_alive_thread;
+}
+
+sub keep_alive_thread_func
+{
+ while($kept_self) {
+ if(1) {
+ lock($keep_alives);
+ $keep_alives++;
+ $kept_self->send_message(KEEP_ALIVE_MESSAGE);
+ } #mutex unlocked
+ $kept_self->sleep(30, quiet => 0);
+ }
+}
+
+sub send_messages_thread_func
+{
+ while(my $message = $messages_send_queue->dequeue()) {
+ my $type = shift @$message;
+ $kept_self->SUPER::send_message($type, @$message);
+ }
+}
+
+sub receive_messages_thread_func
+{
+ while(1) {
+ $messages_receive_queue->enqueue($kept_self->SUPER::receive_message());
+ }
+}
+
+1;
diff --git a/src/mod/endpoints/mod_skinny/Net/Skinny/Message.pm b/src/mod/endpoints/mod_skinny/Net/Skinny/Message.pm
index 6a1a0ef1cf..5955216e1c 100644
--- a/src/mod/endpoints/mod_skinny/Net/Skinny/Message.pm
+++ b/src/mod/endpoints/mod_skinny/Net/Skinny/Message.pm
@@ -7,54 +7,96 @@ package Net::Skinny::Message;
use strict;
use warnings;
+use threads;
+use threads::shared;
+
use Net::Skinny::Protocol qw/:all/;
-use Data::Dumper;
-
-require Exporter;
-our @ISA = qw(Exporter);
-our @EXPORT = qw(send);
-
-sub new {
+sub new_empty {
my $class = shift;
my $self = {};
bless $self, $class;
- $self->{'socket'} = shift;
- $self->{'type'} = shift;
- %{$self->{'data'}} = @_;
- return $ self;
+ $self->{'type'} = undef;
+ $self->{'data'} = undef;
+ $self->{'raw'} = undef;
+ return $self;
}
-sub send {
+sub new {
+ my $self = shift->new_empty();
+ $self->type(shift);
+ $self->data(@_) if @_;
+ return $self;
+}
+
+sub new_raw {
+ my $self = shift->new_empty();
+ $self->type(shift);
+ $self->raw(shift);
+ return $self;
+}
+
+sub type
+{
my $self = shift;
- my $struct = Net::Skinny::Protocol::skinny_message_struct($self->{'type'});
- my $raw = '';
- my $parsed_count = 0;
- for my $info ( @$struct) {
- last if !defined($self->{'data'}{@$info[1]});
- if(@$info[0] eq 'char') {
- $raw .= pack("a".@$info[2], $self->{'data'}{@$info[1]});
- } elsif(@$info[0] eq 'uint32_t') {
- $raw .= pack("V".@$info[2], $self->{'data'}{@$info[1]});
- } elsif(@$info[0] eq 'uint16_t') {
- $raw .= pack("n".@$info[2], $self->{'data'}{@$info[1]});
- } elsif(@$info[0] eq 'struct in_addr') {
- $raw .= pack("V".@$info[2], $self->{'data'}{@$info[1]});
- } elsif(@$info[0] eq 'struct station_capabilities') {
- $raw .= $self->{'data'}{@$info[1]};
- } else {
- printf "Unknown type: %s\n", @$info[0];
- return;
- }
- $parsed_count++;
+ my $type = @_ ? shift : undef;
+ if(defined($type)) {
+ $self->{'type'} = $type;
}
- if($parsed_count != scalar(keys %{$self->{'data'}})) {
- printf "Incomplete message (type=%s (%X)) %d out of %d\n", Net::Skinny::Protocol::skinny_message_type2str($self->{'type'}), $self->{'type'},
- $parsed_count, scalar(keys %{$self->{'data'}});
- print Dumper(@$struct);
- return;
+ return $self->{'type'};
+}
+
+sub data
+{
+ my $self = shift;
+ my @data = @_;
+ if(@data) {
+ %{$self->{'data'}} = @data;
+ $self->{'raw'} = undef;
+ } elsif(!defined($self->{'data'})) {
+ printf "Conversion from raw to data not implemented\n";
}
- $self->{'socket'}->send_data($self->{'type'}, $raw);
+ return $self->{'data'};
+}
+
+sub raw
+{
+ my $self = shift;
+ my $raw = shift || undef;
+ if(defined($raw)) {
+ $self->{'raw'} = $raw;
+ $self->{'data'} = undef;
+ }
+ if(!defined($self->{'raw'})) {
+ my $struct = Net::Skinny::Protocol::skinny_message_struct($self->{'type'});
+ my $raw = '';
+ my $parsed_count = 0;
+ for my $info ( @$struct) {
+ last if !defined($self->{'data'}{@$info[1]});
+ if(@$info[0] eq 'char') {
+ $raw .= pack("a".@$info[2], $self->{'data'}{@$info[1]});
+ } elsif(@$info[0] eq 'uint32_t') {
+ $raw .= pack("V".@$info[2], $self->{'data'}{@$info[1]});
+ } elsif(@$info[0] eq 'uint16_t') {
+ $raw .= pack("n".@$info[2], $self->{'data'}{@$info[1]});
+ } elsif(@$info[0] eq 'struct in_addr') {
+ $raw .= pack("V".@$info[2], $self->{'data'}{@$info[1]});
+ } elsif(@$info[0] eq 'struct station_capabilities') {
+ $raw .= $self->{'data'}{@$info[1]};
+ } else {
+ printf "Unknown type: %s\n", @$info[0];
+ return;
+ }
+ $parsed_count++;
+ }
+ if($parsed_count != scalar(keys %{$self->{'data'}})) {
+ printf "Incomplete message (type=%s (%X)) %d out of %d\n", Net::Skinny::Protocol::skinny_message_type2str($self->{'type'}), $self->{'type'},
+ $parsed_count, scalar(keys %{$self->{'data'}});
+ return;
+ }
+ $self->{'raw'} = $raw;
+ }
+ return $self->{'raw'};
}
1;
diff --git a/src/mod/endpoints/mod_skinny/Net/Skinny/Protocol.pm b/src/mod/endpoints/mod_skinny/Net/Skinny/Protocol.pm
index bfe0202787..90de304817 100644
--- a/src/mod/endpoints/mod_skinny/Net/Skinny/Protocol.pm
+++ b/src/mod/endpoints/mod_skinny/Net/Skinny/Protocol.pm
@@ -8,7 +8,6 @@ use strict;
no strict "refs";
use warnings;
use Carp;
-use Data::Dumper;
require Exporter;
our @ISA = qw(Exporter);
@@ -69,7 +68,6 @@ sub _find {
printf "Unparsed line '%s' in %s\n", $_, $struct_name;
}
}
- #print "$name: ".Dumper($struct{$name});
}
}
@sub{@_};
@@ -77,6 +75,7 @@ sub _find {
sub skinny_message_type2str {
my $message_type = shift;
+ return "UndefinedTypeMessage" if !defined($message_type);
keys %const;
while (my ($key, $value) = each %const) {
diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c
index 571223ccac..391923d5e9 100644
--- a/src/mod/endpoints/mod_skinny/mod_skinny.c
+++ b/src/mod/endpoints/mod_skinny/mod_skinny.c
@@ -221,7 +221,7 @@ char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *l
"SELECT channel_uuid, line_instance "
"FROM skinny_active_lines "
"WHERE %s AND %s AND %s "
- "ORDER BY channel_uuid DESC",
+ "ORDER BY call_state, channel_uuid", /* off hook first */
device_condition, line_instance_condition, call_id_condition
))) {
skinny_execute_sql_callback(profile, profile->sql_mutex, sql,
@@ -235,14 +235,22 @@ char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *l
return helper.channel_uuid;
}
+#ifdef SWITCH_DEBUG_RWLOCKS
+switch_core_session_t * skinny_profile_perform_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id, const char *file, const char *func, int line)
+#else
switch_core_session_t * skinny_profile_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id)
+#endif
{
char *uuid;
switch_core_session_t *result = NULL;
uuid = skinny_profile_find_session_uuid(profile, listener, line_instance_p, call_id);
if(!zstr(uuid)) {
+#ifdef SWITCH_DEBUG_RWLOCKS
+ result = switch_core_session_perform_locate(uuid, file, func, line);
+#else
result = switch_core_session_locate(uuid);
+#endif
if(!result) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unable to find session %s on %s:%d, line %d\n",
@@ -357,7 +365,9 @@ struct skinny_line_get_state_helper {
int skinny_line_get_state_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct skinny_line_get_state_helper *helper = pArg;
- helper->call_state = atoi(argv[0]);
+ if (helper->call_state == -1) {
+ helper->call_state = atoi(argv[0]);
+ }
return 0;
}
@@ -383,10 +393,12 @@ uint32_t skinny_line_get_state(listener_t *listener, uint32_t line_instance, uin
}
switch_assert(call_id_condition);
+ helper.call_state = -1;
if ((sql = switch_mprintf(
"SELECT call_state FROM skinny_active_lines "
"WHERE device_name='%s' AND device_instance=%d "
- "AND %s AND %s",
+ "AND %s AND %s "
+ "ORDER BY call_state, channel_uuid", /* off hook first */
listener->device_name, listener->device_instance,
line_instance_condition, call_id_condition
))) {
@@ -531,6 +543,7 @@ void tech_init(private_t *tech_pvt, skinny_profile_t *profile, switch_core_sessi
switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
tech_pvt->call_id = ++profile->next_call_id;
+ tech_pvt->party_id = tech_pvt->call_id;
tech_pvt->profile = profile;
switch_core_session_set_private(session, tech_pvt);
tech_pvt->session = session;
@@ -633,16 +646,7 @@ int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnN
send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_OFF);
send_clear_prompt_status(listener, line_instance, call_id);
if(call_state == SKINNY_CONNECTED) { /* calling parties */
- send_close_receive_channel(listener,
- call_id, /* uint32_t conference_id, */
- helper->tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
- call_id /* uint32_t conference_id2, */
- );
- send_stop_media_transmission(listener,
- call_id, /* uint32_t conference_id, */
- helper->tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
- call_id /* uint32_t conference_id2, */
- );
+ skinny_session_stop_media(helper->tech_pvt->session, listener, line_instance);
}
skinny_line_set_state(listener, line_instance, call_id, SKINNY_ON_HOOK);
@@ -650,7 +654,6 @@ int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnN
/* TODO: DefineTimeDate */
send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, call_id);
-
}
return 0;
}
@@ -1229,6 +1232,9 @@ static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj)
switch_clear_flag_locked(listener, LFLAG_RUNNING);
break;
}
+ if (!listener_is_ready(listener)) {
+ break;
+ }
if (!request) {
continue;
@@ -1473,7 +1479,7 @@ static switch_status_t load_skinny_config(void)
}
profile = switch_core_alloc(profile_pool, sizeof(skinny_profile_t));
profile->pool = profile_pool;
- profile->name = profile_name;
+ profile->name = switch_core_strdup(profile->pool, profile_name);
switch_mutex_init(&profile->listener_mutex, SWITCH_MUTEX_NESTED, profile->pool);
switch_mutex_init(&profile->sql_mutex, SWITCH_MUTEX_NESTED, profile->pool);
switch_mutex_init(&profile->sock_mutex, SWITCH_MUTEX_NESTED, profile->pool);
diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h
index 7bcc40b96c..3443d2f7dd 100644
--- a/src/mod/endpoints/mod_skinny/mod_skinny.h
+++ b/src/mod/endpoints/mod_skinny/mod_skinny.h
@@ -189,7 +189,12 @@ switch_status_t skinny_profile_dump(const skinny_profile_t *profile, switch_stre
switch_status_t skinny_profile_find_listener_by_device_name(skinny_profile_t *profile, const char *device_name, listener_t **listener);
switch_status_t skinny_profile_find_listener_by_device_name_and_instance(skinny_profile_t *profile, const char *device_name, uint32_t device_instance, listener_t **listener);
char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id);
+#ifdef SWITCH_DEBUG_RWLOCKS
+switch_core_session_t * skinny_profile_perform_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id, const char *file, const char *func, int line);
+#define skinny_profile_find_session(profile, listener, line_instance_p, call_id) skinny_profile_perform_find_session(profile, listener, line_instance_p, call_id, __FILE__, __SWITCH_FUNC__, __LINE__)
+#else
switch_core_session_t * skinny_profile_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id);
+#endif
switch_status_t dump_device(skinny_profile_t *profile, const char *device_name, switch_stream_handle_t *stream);
/*****************************************************************************/
diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c
index 533a922f32..94c61eb1dd 100644
--- a/src/mod/endpoints/mod_skinny/skinny_protocol.c
+++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c
@@ -446,19 +446,20 @@ switch_status_t skinny_create_ingoing_session(listener_t *listener, uint32_t *li
line_instance = *line_instance_p;
if((nsession = skinny_profile_find_session(listener->profile, listener, line_instance_p, 0))) {
- switch_core_session_rwunlock(nsession);
if(skinny_line_get_state(listener, *line_instance_p, 0) == SKINNY_OFF_HOOK) {
/* Reuse existing session */
*session = nsession;
return SWITCH_STATUS_SUCCESS;
}
- skinny_session_hold_line(nsession, listener, *line_instance_p);
+ switch_core_session_rwunlock(nsession);
}
*line_instance_p = line_instance;
if(*line_instance_p == 0) {
*line_instance_p = 1;
}
+ skinny_hold_active_calls(listener);
+
skinny_line_get(listener, *line_instance_p, &button);
if (!button || !button->shortname) {
@@ -494,7 +495,11 @@ switch_status_t skinny_create_ingoing_session(listener_t *listener, uint32_t *li
"Error Creating Session thread\n");
goto error;
}
-
+ if (switch_core_session_read_lock(nsession) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT,
+ "Error Locking Session\n");
+ goto error;
+ }
if (!(tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(nsession),
NULL, listener->profile->dialplan,
button->shortname, button->name,
@@ -543,6 +548,49 @@ done:
return SWITCH_STATUS_SUCCESS;
}
+struct skinny_session_process_dest_helper {
+ private_t *tech_pvt;
+ listener_t *listener;
+ uint32_t line_instance;
+};
+
+int skinny_session_process_dest_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+ struct skinny_session_process_dest_helper *helper = pArg;
+ listener_t *listener = NULL;
+
+ char *device_name = argv[0];
+ uint32_t device_instance = atoi(argv[1]);
+ /* uint32_t position = atoi(argv[2]); */
+ uint32_t line_instance = atoi(argv[3]);
+ /* char *label = argv[4]; */
+ /* char *value = argv[5]; */
+ /* char *caller_name = argv[6]; */
+ /* uint32_t ring_on_idle = atoi(argv[7]); */
+ /* uint32_t ring_on_active = atoi(argv[8]); */
+ /* uint32_t busy_trigger = atoi(argv[9]); */
+ /* char *forward_all = argv[10]; */
+ /* char *forward_busy = argv[11]; */
+ /* char *forward_noanswer = argv[12]; */
+ /* uint32_t noanswer_duration = atoi(argv[13]); */
+ /* char *channel_uuid = argv[14]; */
+ /* uint32_t call_id = atoi(argv[15]); */
+ /* uint32_t call_state = atoi(argv[16]); */
+
+ skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
+ if(listener) {
+ if(!strcmp(device_name, helper->listener->device_name)
+ && (device_instance == helper->listener->device_instance)
+ && (line_instance == helper->line_instance)) {/* the calling line */
+ /* nothing */
+ } else {
+ /* TODO: capture and check what should happen here*/
+ skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY);
+ }
+ }
+ return 0;
+}
+
switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace)
{
switch_channel_t *channel = NULL;
@@ -555,31 +603,43 @@ switch_status_t skinny_session_process_dest(switch_core_session_t *session, list
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
- if(!dest) {
- if(append_dest == '\0') {/* no digit yet */
- send_start_tone(listener, SKINNY_TONE_DIALTONE, 0, line_instance, tech_pvt->call_id);
- } else {
- if(strlen(tech_pvt->caller_profile->destination_number) == 0) {/* first digit */
- send_stop_tone(listener, line_instance, tech_pvt->call_id);
- send_select_soft_keys(listener, line_instance, tech_pvt->call_id,
- SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT, 0xffff);
- }
+ if (!dest) {
+ if (backspace) { /* backspace */
+ *tech_pvt->caller_profile->destination_number++ = '\0';
+ }
+ if (append_dest != '\0' && !backspace) {/* append digit */
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
"%s%c", tech_pvt->caller_profile->destination_number, append_dest);
}
+ if (strlen(tech_pvt->caller_profile->destination_number) == 0) {/* no digit yet */
+ send_start_tone(listener, SKINNY_TONE_DIALTONE, 0, line_instance, tech_pvt->call_id);
+ if(backspace) {
+ send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff);
+ /* TODO: How to clear the screen? */
+ }
+ } else if (strlen(tech_pvt->caller_profile->destination_number) == 1) {/* first digit */
+ send_stop_tone(listener, line_instance, tech_pvt->call_id);
+ send_select_soft_keys(listener, line_instance, tech_pvt->call_id,
+ SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT, 0xffff);
+ }
} else {
tech_pvt->caller_profile->destination_number = switch_core_strdup(tech_pvt->caller_profile->pool,
dest);
}
/* TODO Number is complete -> check against dialplan */
- if((strlen(tech_pvt->caller_profile->destination_number) >= 4) || dest) {
+ if ((strlen(tech_pvt->caller_profile->destination_number) >= 4) || dest) {
+ struct skinny_session_process_dest_helper helper = {0};
send_dialed_number(listener, tech_pvt->caller_profile->destination_number, line_instance, tech_pvt->call_id);
skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_PROCEED);
skinny_send_call_info(session, listener, line_instance);
- skinny_session_start_media(session, listener, line_instance);
- }
- switch_core_session_rwunlock(session);
+ skinny_session_start_media(session, listener, line_instance);
+
+ helper.tech_pvt = tech_pvt;
+ helper.listener = listener;
+ helper.line_instance = line_instance;
+ skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_process_dest_callback, &helper);
+ }
return SWITCH_STATUS_SUCCESS;
}
@@ -603,8 +663,6 @@ switch_status_t skinny_session_ring_out(switch_core_session_t *session, listener
line_instance, tech_pvt->call_id);
skinny_send_call_info(session, listener, line_instance);
- switch_core_session_rwunlock(session);
-
return SWITCH_STATUS_SUCCESS;
}
@@ -643,16 +701,14 @@ int skinny_session_answer_callback(void *pArg, int argc, char **argv, char **col
if(!strcmp(device_name, helper->listener->device_name)
&& (device_instance == helper->listener->device_instance)
&& (line_instance == helper->line_instance)) {/* the answering line */
-
-
- send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id);
- send_set_speaker_mode(listener, SKINNY_SPEAKER_ON);
+ /* nothing */
+ } else {
+ send_define_current_time_date(listener);
send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
- skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_OFF_HOOK);
- /* send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, SKINNY_KEY_SET_OFF_HOOK, 0xffff); */
- /* display_prompt_status(listener, 0, "\200\000",
- line_instance, tech_pvt->call_id); */
- send_activate_call_plane(listener, line_instance);
+ skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY);
+ send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0x0002);
+ send_display_prompt_status(listener, 0, "\200\037", line_instance, helper->tech_pvt->call_id);
+ send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, helper->tech_pvt->call_id);
}
}
return 0;
@@ -671,6 +727,12 @@ switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
+ send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id);
+ send_set_speaker_mode(listener, SKINNY_SPEAKER_ON);
+ send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
+ skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_OFF_HOOK);
+ send_activate_call_plane(listener, line_instance);
+
helper.tech_pvt = tech_pvt;
helper.listener = listener;
helper.line_instance = line_instance;
@@ -679,8 +741,6 @@ switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t
skinny_session_start_media(session, listener, line_instance);
- switch_core_session_rwunlock(session);
-
return SWITCH_STATUS_SUCCESS;
}
@@ -717,8 +777,6 @@ switch_status_t skinny_session_start_media(switch_core_session_t *session, liste
tech_pvt->call_id);
skinny_send_call_info(session, listener, line_instance);
- switch_core_session_rwunlock(session);
-
return SWITCH_STATUS_SUCCESS;
}
@@ -734,22 +792,129 @@ switch_status_t skinny_session_hold_line(switch_core_session_t *session, listene
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
- /* TODO */
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Hold is not implemented yet. Hanging up the line.\n");
+ skinny_session_stop_media(session, listener, line_instance);
+ switch_ivr_hold(session, NULL, 1);
- switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
-
- switch_core_session_rwunlock(session);
+ send_define_current_time_date(listener);
+ send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_WINK);
+ skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_HOLD);
+ send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_ON_HOLD, 0xffff);
+ send_display_prompt_status(listener, 0, "\200\003",
+ line_instance, tech_pvt->call_id);
+ skinny_send_call_info(tech_pvt->session, listener, line_instance);
+ send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
+ send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance)
{
- /* TODO */
+ switch_channel_t *channel = NULL;
+ private_t *tech_pvt = NULL;
+
+ switch_assert(session);
+ switch_assert(listener);
+ switch_assert(listener->profile);
+
+ channel = switch_core_session_get_channel(session);
+ tech_pvt = switch_core_session_get_private(session);
+
+ skinny_hold_active_calls(listener);
+ send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, tech_pvt->call_id);
+ send_set_speaker_mode(listener, SKINNY_SPEAKER_ON);
+ send_select_soft_keys(listener, line_instance, tech_pvt->call_id, SKINNY_KEY_SET_RING_OUT, 0xffff);
+ skinny_session_start_media(session, listener, line_instance);
+ switch_ivr_unhold(session);
+ send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
return SWITCH_STATUS_SUCCESS;
}
+switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance)
+{
+ switch_channel_t *channel = NULL;
+ private_t *tech_pvt = NULL;
+
+ switch_assert(session);
+ switch_assert(listener);
+ switch_assert(listener->profile);
+
+ channel = switch_core_session_get_channel(session);
+ tech_pvt = switch_core_session_get_private(session);
+
+ send_close_receive_channel(listener,
+ tech_pvt->call_id, /* uint32_t conference_id, */
+ tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
+ tech_pvt->call_id /* uint32_t conference_id2, */
+ );
+ send_stop_media_transmission(listener,
+ tech_pvt->call_id, /* uint32_t conference_id, */
+ tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
+ tech_pvt->call_id /* uint32_t conference_id2, */
+ );
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+struct skinny_hold_active_calls_helper {
+ listener_t *listener;
+};
+
+int skinny_hold_active_calls_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+ struct skinny_hold_active_calls_helper *helper = pArg;
+ switch_core_session_t *session;
+
+ /* char *device_name = argv[0]; */
+ /* uint32_t device_instance = atoi(argv[1]); */
+ /* uint32_t position = atoi(argv[2]); */
+ uint32_t line_instance = atoi(argv[3]);
+ /* char *label = argv[4]; */
+ /* char *value = argv[5]; */
+ /* char *caller_name = argv[6]; */
+ /* uint32_t ring_on_idle = atoi(argv[7]); */
+ /* uint32_t ring_on_active = atoi(argv[8]); */
+ /* uint32_t busy_trigger = atoi(argv[9]); */
+ /* char *forward_all = argv[10]; */
+ /* char *forward_busy = argv[11]; */
+ /* char *forward_noanswer = argv[12]; */
+ /* uint32_t noanswer_duration = atoi(argv[13]); */
+ /* char *channel_uuid = argv[14]; */
+ uint32_t call_id = atoi(argv[15]);
+ /* uint32_t call_state = atoi(argv[16]); */
+
+ session = skinny_profile_find_session(helper->listener->profile, helper->listener, &line_instance, call_id);
+
+ if(session) {
+ skinny_session_hold_line(session, helper->listener, line_instance);
+ switch_core_session_rwunlock(session);
+ }
+
+ return 0;
+}
+
+switch_status_t skinny_hold_active_calls(listener_t *listener)
+{
+ struct skinny_hold_active_calls_helper helper = {0};
+ char *sql;
+
+ helper.listener = listener;
+
+ if ((sql = switch_mprintf(
+ "SELECT skinny_lines.*, channel_uuid, call_id, call_state "
+ "FROM skinny_active_lines "
+ "INNER JOIN skinny_lines "
+ "ON skinny_active_lines.device_name = skinny_lines.device_name "
+ "AND skinny_active_lines.device_instance = skinny_lines.device_instance "
+ "AND skinny_active_lines.line_instance = skinny_lines.line_instance "
+ "WHERE skinny_lines.device_name='%s' AND skinny_lines.device_instance=%d AND call_state=%d",
+ listener->device_name, listener->device_instance, SKINNY_CONNECTED))) {
+ skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_hold_active_calls_callback, &helper);
+ switch_safe_free(sql);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
/*****************************************************************************/
/* SKINNY BUTTONS */
/*****************************************************************************/
@@ -1480,6 +1645,9 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r
}
}
}
+ if (xroot) {
+ switch_xml_free(xroot);
+ }
status = SWITCH_STATUS_SUCCESS;
@@ -1789,10 +1957,26 @@ switch_status_t skinny_handle_soft_key_set_request(listener_t *listener, skinny_
message->data.soft_key_set.total_soft_key_set_count = 11;
/* TODO fill the set */
- message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[0] = SOFTKEY_REDIAL;
- message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[1] = SOFTKEY_NEWCALL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[0] = SOFTKEY_NEWCALL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL;
+
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[1] = SOFTKEY_REDIAL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_OFF_HOOK].soft_key_template_index[2] = SOFTKEY_ENDCALL;
+
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[0] = SOFTKEY_BACKSPACE;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT].soft_key_template_index[2] = SOFTKEY_ENDCALL;
+
message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[0] = SOFTKEY_ENDCALL;
- message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[0] = SOFTKEY_ENDCALL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[1] = SOFTKEY_HOLD;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_CONNECTED].soft_key_template_index[2] = SOFTKEY_NEWCALL;
+
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[0] = SOFTKEY_ANSWER;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[1] = SOFTKEY_ENDCALL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_RING_IN].soft_key_template_index[2] = SOFTKEY_NEWCALL;
+
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[0] = SOFTKEY_NEWCALL;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[1] = SOFTKEY_RESUME;
+ message->data.soft_key_set.soft_key_set[SKINNY_KEY_SET_ON_HOLD].soft_key_template_index[2] = SOFTKEY_ENDCALL;
skinny_send_reply(listener, message);
@@ -1931,7 +2115,6 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn
case SOFTKEY_NEWCALL:
status = skinny_create_ingoing_session(listener, &line_instance, &session);
tech_pvt = switch_core_session_get_private(session);
- assert(tech_pvt != NULL);
skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0);
break;
@@ -1942,6 +2125,13 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn
status = skinny_session_hold_line(session, listener, line_instance);
}
break;
+ case SOFTKEY_BACKSPACE:
+ session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id);
+
+ if(session) {
+ skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 1);
+ }
+ break;
case SOFTKEY_ENDCALL:
session = skinny_profile_find_session(listener->profile, listener, &line_instance, request->data.soft_key_event.call_id);
@@ -1967,7 +2157,7 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
- "Unknown SoftKeyEvent type while busy: %d.\n", request->data.soft_key_event.event);
+ "Unknown SoftKeyEvent type: %d.\n", request->data.soft_key_event.event);
}
if(session) {
@@ -2002,6 +2192,11 @@ switch_status_t skinny_handle_off_hook_message(listener_t *listener, skinny_mess
skinny_session_process_dest(session, listener, line_instance, NULL, '\0', 0);
}
+
+ if(session) {
+ switch_core_session_rwunlock(session);
+ }
+
return SWITCH_STATUS_SUCCESS;
}
@@ -2037,6 +2232,11 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown Stimulus Type Received [%d]\n", request->data.stimulus.instance_type);
}
+
+ if(session) {
+ switch_core_session_rwunlock(session);
+ }
+
return SWITCH_STATUS_SUCCESS;
}
@@ -2118,9 +2318,13 @@ switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *liste
}
switch_channel_mark_answered(channel);
- switch_core_session_rwunlock(session);
}
end:
+
+ if(session) {
+ switch_core_session_rwunlock(session);
+ }
+
return status;
}
@@ -2171,6 +2375,9 @@ switch_status_t skinny_handle_keypad_button_message(listener_t *listener, skinny
switch_channel_queue_dtmf(channel, &dtmf);
}
}
+ }
+
+ if(session) {
switch_core_session_rwunlock(session);
}
@@ -2195,9 +2402,12 @@ switch_status_t skinny_handle_on_hook_message(listener_t *listener, skinny_messa
channel = switch_core_session_get_channel(session);
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ }
+ if(session) {
switch_core_session_rwunlock(session);
}
+
return status;
}
diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h
index ea004f12fc..3199738856 100644
--- a/src/mod/endpoints/mod_skinny/skinny_protocol.h
+++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h
@@ -630,6 +630,8 @@ switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t
switch_status_t skinny_session_start_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance);
switch_status_t skinny_session_hold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance);
switch_status_t skinny_session_unhold_line(switch_core_session_t *session, listener_t *listener, uint32_t line_instance);
+switch_status_t skinny_session_stop_media(switch_core_session_t *session, listener_t *listener, uint32_t line_instance);
+switch_status_t skinny_hold_active_calls(listener_t *listener);
void skinny_line_get(listener_t *listener, uint32_t instance, struct line_stat_res_message **button);
void skinny_speed_dial_get(listener_t *listener, uint32_t instance, struct speed_dial_stat_res_message **button);
diff --git a/src/mod/endpoints/mod_skinny/test-skinny.pl b/src/mod/endpoints/mod_skinny/test-skinny.pl
index 390466f530..a8c7bb5d68 100644
--- a/src/mod/endpoints/mod_skinny/test-skinny.pl
+++ b/src/mod/endpoints/mod_skinny/test-skinny.pl
@@ -15,6 +15,7 @@ use Sys::Hostname;
use Net::Skinny;
use Net::Skinny::Protocol qw/:all/;
use Net::Skinny::Message;
+use Net::Skinny::Client;
#Config
my $skinny_server = hostname;
@@ -23,13 +24,13 @@ my $device_ip = 10+256*(11+256*(12+256*13)); # 10.11.12.13
#======
$| = 1;
-my $socket = Net::Skinny->new(
+my $socket = Net::Skinny::Client->new(
PeerAddr => $skinny_server,
PeerPort => 2000,
);
if(!$socket) {
- print "Unable to connect to server\n";
+ printf "Unable to connect to server %s\n", $skinny_server;
exit 1;
}
# =============================================================================
@@ -84,11 +85,8 @@ $socket->send_message(
count => 2
);
-for(my $i = 0; $i < 1; $i++) {
- $socket->sleep(5);
- $socket->send_message(KEEP_ALIVE_MESSAGE);
- $socket->receive_message(); # keepaliveack
-}
+$socket->launch_keep_alive_thread();
+
$socket->sleep(5);
#NewCall
diff --git a/src/mod/languages/mod_managed/freeswitch_wrap.cxx b/src/mod/languages/mod_managed/freeswitch_wrap.cxx
index 737940ec6c..49fb617ef6 100644
--- a/src/mod/languages/mod_managed/freeswitch_wrap.cxx
+++ b/src/mod/languages/mod_managed/freeswitch_wrap.cxx
@@ -21403,21 +21403,36 @@ SWIGEXPORT char * SWIGSTDCALL CSharp_switch_channel_get_variable_partner(void *
}
-SWIGEXPORT int SWIGSTDCALL CSharp_switch_channel_export_variable_var_check(void * jarg1, char * jarg2, char * jarg3, int jarg4, int jarg5) {
+SWIGEXPORT int SWIGSTDCALL CSharp_switch_channel_export_variable_var_check(void * jarg1, char * jarg2, char * jarg3, int jarg4) {
int jresult ;
switch_channel_t *arg1 = (switch_channel_t *) 0 ;
char *arg2 = (char *) 0 ;
char *arg3 = (char *) 0 ;
switch_bool_t arg4 ;
- switch_bool_t arg5 ;
switch_status_t result;
arg1 = (switch_channel_t *)jarg1;
arg2 = (char *)jarg2;
arg3 = (char *)jarg3;
arg4 = (switch_bool_t)jarg4;
- arg5 = (switch_bool_t)jarg5;
- result = (switch_status_t)switch_channel_export_variable_var_check(arg1,(char const *)arg2,(char const *)arg3,arg4,arg5);
+ result = (switch_status_t)switch_channel_export_variable_var_check(arg1,(char const *)arg2,(char const *)arg3,arg4);
+ jresult = result;
+ return jresult;
+}
+
+
+SWIGEXPORT int SWIGSTDCALL CSharp_switch_channel_export_variable_printf(void * jarg1, char * jarg2, char * jarg3) {
+ int jresult ;
+ switch_channel_t *arg1 = (switch_channel_t *) 0 ;
+ char *arg2 = (char *) 0 ;
+ char *arg3 = (char *) 0 ;
+ void *arg4 = 0 ;
+ switch_status_t result;
+
+ arg1 = (switch_channel_t *)jarg1;
+ arg2 = (char *)jarg2;
+ arg3 = (char *)jarg3;
+ result = (switch_status_t)switch_channel_export_variable_printf(arg1,(char const *)arg2,(char const *)arg3,arg4);
jresult = result;
return jresult;
}
diff --git a/src/mod/languages/mod_managed/managed/swig.cs b/src/mod/languages/mod_managed/managed/swig.cs
index 13756d32a6..1821bbb088 100644
--- a/src/mod/languages/mod_managed/managed/swig.cs
+++ b/src/mod/languages/mod_managed/managed/swig.cs
@@ -2904,8 +2904,13 @@ public class freeswitch {
return ret;
}
- public static switch_status_t switch_channel_export_variable_var_check(SWIGTYPE_p_switch_channel channel, string varname, string value, switch_bool_t var_check, switch_bool_t nolocal) {
- switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_channel_export_variable_var_check(SWIGTYPE_p_switch_channel.getCPtr(channel), varname, value, (int)var_check, (int)nolocal);
+ public static switch_status_t switch_channel_export_variable_var_check(SWIGTYPE_p_switch_channel channel, string varname, string value, switch_bool_t var_check) {
+ switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_channel_export_variable_var_check(SWIGTYPE_p_switch_channel.getCPtr(channel), varname, value, (int)var_check);
+ return ret;
+ }
+
+ public static switch_status_t switch_channel_export_variable_printf(SWIGTYPE_p_switch_channel channel, string varname, string fmt) {
+ switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_channel_export_variable_printf(SWIGTYPE_p_switch_channel.getCPtr(channel), varname, fmt);
return ret;
}
@@ -10215,7 +10220,10 @@ class freeswitchPINVOKE {
public static extern string switch_channel_get_variable_partner(HandleRef jarg1, string jarg2);
[DllImport("mod_managed", EntryPoint="CSharp_switch_channel_export_variable_var_check")]
- public static extern int switch_channel_export_variable_var_check(HandleRef jarg1, string jarg2, string jarg3, int jarg4, int jarg5);
+ public static extern int switch_channel_export_variable_var_check(HandleRef jarg1, string jarg2, string jarg3, int jarg4);
+
+ [DllImport("mod_managed", EntryPoint="CSharp_switch_channel_export_variable_printf")]
+ public static extern int switch_channel_export_variable_printf(HandleRef jarg1, string jarg2, string jarg3);
[DllImport("mod_managed", EntryPoint="CSharp_switch_channel_get_variable_dup")]
public static extern string switch_channel_get_variable_dup(HandleRef jarg1, string jarg2, int jarg3);