734 lines
18 KiB
C
734 lines
18 KiB
C
/* blccc - Blinkenlights Chaos Control Center
|
|
*
|
|
* Copyright (c) 2001-2002 Sven Neumann <sven@gimp.org>
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <blib/blib.h>
|
|
|
|
#include "bltypes.h"
|
|
#include "blconfig.h"
|
|
#include "blmarshal.h"
|
|
#include "blisdn.h"
|
|
|
|
|
|
/* #define ISDN_VERBOSE 1 */
|
|
|
|
|
|
#define POLL_TIMEOUT 120 /* ms */
|
|
#define RESET_TIMEOUT 60 /* sec */
|
|
|
|
|
|
static void bl_isdn_class_init (BlIsdnClass *class);
|
|
static void bl_isdn_init (BlIsdn *isdn);
|
|
static void bl_isdn_finalize (GObject *object);
|
|
|
|
static gboolean bl_isdn_setup (BlIsdn *isdn,
|
|
GError **error);
|
|
static void bl_isdn_teardown (BlIsdn *isdn);
|
|
|
|
static void bl_isdn_hangup_all (BlIsdn *isdn);
|
|
|
|
static void bl_isdn_heartbeat_start (BlIsdn *isdn);
|
|
static void bl_isdn_heartbeat_stop (BlIsdn *isdn);
|
|
|
|
static void bl_isdn_handle_message (BlIsdn *isdn,
|
|
guchar *msg,
|
|
gint len);
|
|
|
|
static gboolean udp_prepare (GSource *source,
|
|
gint *timeout);
|
|
static gboolean udp_check (GSource *source);
|
|
static gboolean udp_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data);
|
|
|
|
|
|
typedef struct _BlIsdnSource BlIsdnSource;
|
|
struct _BlIsdnSource
|
|
{
|
|
GSource source;
|
|
BlIsdn *isdn;
|
|
};
|
|
|
|
static GSourceFuncs udp_funcs =
|
|
{
|
|
udp_prepare,
|
|
udp_check,
|
|
udp_dispatch,
|
|
NULL
|
|
};
|
|
|
|
enum
|
|
{
|
|
INCOMING_CALL,
|
|
STATE_CHANGED,
|
|
KEY_PRESSED,
|
|
LAST_SIGNAL
|
|
};
|
|
static guint bl_isdn_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
|
|
/* BlIsdn source function */
|
|
|
|
static gboolean
|
|
udp_prepare (GSource *source,
|
|
gint *timeout)
|
|
{
|
|
BlIsdn *isdn = ((BlIsdnSource *) source)->isdn;
|
|
fd_set set;
|
|
struct timeval tv;
|
|
|
|
FD_ZERO (&set);
|
|
FD_SET (isdn->listen_sock, &set);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
isdn->packet_pending = (select (isdn->listen_sock + 1,
|
|
&set, NULL, NULL, &tv) > 0);
|
|
|
|
if (timeout)
|
|
*timeout = POLL_TIMEOUT;
|
|
|
|
if (isdn->packet_pending)
|
|
isdn->no_packets = 0;
|
|
else
|
|
isdn->no_packets++;
|
|
|
|
if (isdn->no_packets == (RESET_TIMEOUT * 1000 / POLL_TIMEOUT))
|
|
{
|
|
g_printerr ("BlIsdn: %d seconds have gone by without any sign of life\n"
|
|
" from the ISDN system. I'll try to reconnect ...\n",
|
|
RESET_TIMEOUT);
|
|
|
|
bl_isdn_teardown (isdn);
|
|
bl_isdn_setup (isdn, NULL);
|
|
}
|
|
|
|
return isdn->packet_pending;
|
|
}
|
|
|
|
static gboolean
|
|
udp_check (GSource *source)
|
|
{
|
|
BlIsdn *isdn = ((BlIsdnSource *) source)->isdn;
|
|
|
|
return isdn->packet_pending;
|
|
}
|
|
|
|
static gboolean
|
|
udp_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
BlIsdn *isdn = ((BlIsdnSource *) source)->isdn;
|
|
guchar buffer[2048];
|
|
fd_set set;
|
|
struct timeval tv;
|
|
|
|
isdn->packet_pending = FALSE;
|
|
|
|
FD_ZERO (&set);
|
|
FD_SET (isdn->listen_sock, &set);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
/* on first call empty the queue */
|
|
if (!isdn->sock_initialized)
|
|
{
|
|
while (select (isdn->listen_sock + 1, &set, NULL, NULL, &tv) > 0)
|
|
{
|
|
FD_ZERO (&set);
|
|
FD_SET (isdn->listen_sock, &set);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
if (FD_ISSET (isdn->listen_sock, &set))
|
|
read (isdn->listen_sock, buffer, sizeof (buffer));
|
|
}
|
|
isdn->sock_initialized = TRUE;
|
|
}
|
|
|
|
while (select (isdn->listen_sock + 1, &set, NULL, NULL, &tv) > 0)
|
|
{
|
|
FD_ZERO (&set);
|
|
FD_SET (isdn->listen_sock, &set);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
if (FD_ISSET (isdn->listen_sock, &set))
|
|
{
|
|
gint bytes;
|
|
|
|
bytes = read (isdn->listen_sock, buffer, sizeof (buffer) - 1);
|
|
|
|
if (bytes > 0)
|
|
{
|
|
buffer[bytes] = '\0';
|
|
|
|
if (!isdn->blocked)
|
|
bl_isdn_handle_message (isdn, buffer, bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* BlIsdn GObject framework */
|
|
|
|
GType
|
|
bl_isdn_get_type (void)
|
|
{
|
|
static GType isdn_type = 0;
|
|
|
|
if (!isdn_type)
|
|
{
|
|
static const GTypeInfo isdn_info =
|
|
{
|
|
sizeof (BlIsdnClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) bl_isdn_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (BlIsdn),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) bl_isdn_init,
|
|
};
|
|
|
|
isdn_type = g_type_register_static (G_TYPE_OBJECT,
|
|
"BlIsdn",
|
|
&isdn_info, 0);
|
|
}
|
|
|
|
return isdn_type;
|
|
}
|
|
|
|
static void
|
|
bl_isdn_class_init (BlIsdnClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
parent_class = g_type_class_peek_parent (class);
|
|
|
|
object_class = G_OBJECT_CLASS (class);
|
|
|
|
bl_isdn_signals[INCOMING_CALL] =
|
|
g_signal_new ("incoming_call",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (BlIsdnClass, incoming_call),
|
|
NULL, NULL,
|
|
bl_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
|
bl_isdn_signals[STATE_CHANGED] =
|
|
g_signal_new ("state_changed",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (BlIsdnClass, state_changed),
|
|
NULL, NULL,
|
|
bl_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
|
bl_isdn_signals[KEY_PRESSED] =
|
|
g_signal_new ("key_pressed",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (BlIsdnClass, key_pressed),
|
|
NULL, NULL,
|
|
bl_marshal_VOID__POINTER_INT,
|
|
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_INT);
|
|
|
|
class->incoming_call = NULL;
|
|
class->state_changed = NULL;
|
|
class->key_pressed = NULL;
|
|
|
|
object_class->finalize = bl_isdn_finalize;
|
|
}
|
|
|
|
static void
|
|
bl_isdn_init (BlIsdn *isdn)
|
|
{
|
|
isdn->listen_sock = -1;
|
|
isdn->send_sock = -1;
|
|
isdn->heartbeat = 0;
|
|
isdn->sock_initialized = FALSE;
|
|
isdn->state_initialized = FALSE;
|
|
isdn->packet_pending = FALSE;
|
|
isdn->no_packets = 0;
|
|
isdn->blocked = FALSE;
|
|
isdn->lines = NULL;
|
|
}
|
|
|
|
static void
|
|
bl_isdn_finalize (GObject *object)
|
|
{
|
|
BlIsdn *isdn = BL_ISDN (object);
|
|
|
|
bl_isdn_teardown (isdn);
|
|
|
|
if (isdn->source)
|
|
{
|
|
g_source_destroy (isdn->source);
|
|
isdn->source = NULL;
|
|
}
|
|
if (isdn->lines)
|
|
{
|
|
g_free (isdn->lines);
|
|
isdn->lines = NULL;
|
|
}
|
|
if (isdn->config)
|
|
{
|
|
g_object_unref (isdn->config);
|
|
isdn->config = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
bl_isdn_setup (BlIsdn *isdn,
|
|
GError **error)
|
|
{
|
|
BlConfig *config;
|
|
struct sockaddr_in addr;
|
|
struct hostent *dest;
|
|
gchar *cmd;
|
|
gint port;
|
|
gint i;
|
|
|
|
config = isdn->config;
|
|
|
|
dest = gethostbyname (config->isdn_host);
|
|
if (dest == NULL)
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Unable to resolve host '%s'", config->isdn_host);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((isdn->listen_sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1)
|
|
{
|
|
memset (&addr, 0, sizeof (addr));
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons (config->isdn_listen);
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if ((bind (isdn->listen_sock,
|
|
(struct sockaddr *) &addr, sizeof (addr))) == -1)
|
|
{
|
|
g_set_error (error, 0, 0, "Failed to bind listening socket: %s",
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Failed to open listening socket: %s", g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
port = ntohs (addr.sin_port);
|
|
|
|
memset (&addr, 0, sizeof (addr));
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons (config->isdn_port);
|
|
memcpy (&addr.sin_addr.s_addr, dest->h_addr_list[0], dest->h_length);
|
|
|
|
if ((isdn->send_sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1)
|
|
{
|
|
i = 1;
|
|
if ((setsockopt (isdn->send_sock,
|
|
SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i))) == -1)
|
|
{
|
|
g_set_error (error, 0, 0, "Failed to configure send socket: %s",
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if ((connect (isdn->send_sock,
|
|
(struct sockaddr *) &addr, sizeof (addr))) == -1)
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Failed to bind send socket: %s", g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Failed to open send socket: %s", g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
cmd = g_strdup_printf ("0:register:%d", port);
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn > %s\n", cmd);
|
|
#endif
|
|
if (send (isdn->send_sock, cmd, strlen (cmd), 0) == -1)
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Failed to send register message: %s", g_strerror (errno));
|
|
g_free (cmd);
|
|
return FALSE;
|
|
}
|
|
g_free (cmd);
|
|
|
|
g_printerr ("Connected to ISDN system (%s:%d), listening on port %d\n",
|
|
config->isdn_host, config->isdn_port, port);
|
|
|
|
bl_isdn_heartbeat_start (isdn);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
bl_isdn_teardown (BlIsdn *isdn)
|
|
{
|
|
bl_isdn_heartbeat_stop (isdn);
|
|
|
|
if (isdn->listen_sock > -1)
|
|
{
|
|
close (isdn->listen_sock);
|
|
isdn->listen_sock = -1;
|
|
isdn->sock_initialized = FALSE;
|
|
}
|
|
|
|
isdn->no_packets = 0;
|
|
|
|
bl_isdn_hangup_all (isdn);
|
|
|
|
if (isdn->send_sock > -1)
|
|
{
|
|
close (isdn->send_sock);
|
|
isdn->send_sock = -1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
bl_isdn_hangup_all (BlIsdn *isdn)
|
|
{
|
|
BlIsdnLine *line;
|
|
gint i;
|
|
|
|
for (i = 0, line = isdn->lines; i < isdn->num_lines; i++, line++)
|
|
{
|
|
if (line->offhook)
|
|
{
|
|
gchar *msg = g_strdup_printf ("%d:hangup", i + 1);
|
|
|
|
send (isdn->send_sock, msg, strlen (msg), 0);
|
|
|
|
line->offhook = FALSE;
|
|
if (!isdn->blocked)
|
|
g_signal_emit (G_OBJECT (isdn),
|
|
bl_isdn_signals[STATE_CHANGED], 0, line, NULL);
|
|
|
|
g_free (line->called_number);
|
|
g_free (line->calling_number);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
bl_isdn_heartbeat (BlIsdn *isdn)
|
|
{
|
|
const gchar *msg = "0:heartbeat";
|
|
|
|
send (isdn->send_sock, msg, strlen (msg), 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
bl_isdn_heartbeat_start (BlIsdn *isdn)
|
|
{
|
|
g_return_if_fail (isdn->heartbeat == 0);
|
|
|
|
isdn->heartbeat = g_timeout_add (10000,
|
|
(GSourceFunc) bl_isdn_heartbeat, isdn);
|
|
}
|
|
|
|
static void
|
|
bl_isdn_heartbeat_stop (BlIsdn *isdn)
|
|
{
|
|
if (isdn->heartbeat)
|
|
{
|
|
g_source_remove (isdn->heartbeat);
|
|
isdn->heartbeat = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
bl_isdn_handle_message (BlIsdn *isdn,
|
|
guchar *msg,
|
|
gint len)
|
|
{
|
|
gint channel;
|
|
|
|
if (len < 2 || msg[1] != ':')
|
|
return;
|
|
|
|
channel = msg[0] - '0';
|
|
msg += 2;
|
|
|
|
if (channel > 0 && channel <= isdn->num_lines)
|
|
{
|
|
BlIsdnLine *line = isdn->lines + channel - 1;
|
|
|
|
g_return_if_fail (line->channel == channel);
|
|
|
|
if (line->offhook)
|
|
{
|
|
if (strncmp (msg, "onhook", 6) == 0)
|
|
{
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn: Line %d hung up\n", channel);
|
|
#endif
|
|
line->offhook = FALSE;
|
|
g_signal_emit (G_OBJECT (isdn),
|
|
bl_isdn_signals[STATE_CHANGED], 0, line);
|
|
|
|
g_free (line->calling_number);
|
|
line->calling_number = NULL;
|
|
|
|
g_free (line->called_number);
|
|
line->called_number = NULL;
|
|
|
|
return;
|
|
}
|
|
else if (strncmp (msg, "dtmf:", 5) == 0)
|
|
{
|
|
BModuleKey key;
|
|
|
|
switch (msg[5])
|
|
{
|
|
case '0'...'9':
|
|
key = msg[5] - '0';
|
|
break;
|
|
case '#':
|
|
key = B_KEY_HASH;
|
|
break;
|
|
case '*':
|
|
key = B_KEY_ASTERISK;
|
|
break;
|
|
default:
|
|
g_printerr ("BlIsdn: Unknown DTMF key '%s'\n", msg + 5);
|
|
return;
|
|
}
|
|
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn: Key %c pressed on line %d\n",
|
|
msg[5], channel);
|
|
#endif
|
|
g_signal_emit (G_OBJECT (isdn),
|
|
bl_isdn_signals[KEY_PRESSED], 0, line, key);
|
|
return;
|
|
}
|
|
}
|
|
else /* onhook */
|
|
{
|
|
if (strncmp (msg, "setup:", 6) == 0)
|
|
{
|
|
gchar *caller = msg + 6;
|
|
gchar *called;
|
|
|
|
if ((called = strchr (caller, ':')))
|
|
*called++ = '\0';
|
|
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn: incoming call on line %d from %s to %s\n",
|
|
channel, caller, called);
|
|
#endif
|
|
g_free (line->called_number);
|
|
line->called_number = g_strdup (called);
|
|
|
|
g_free (line->calling_number);
|
|
line->calling_number = g_strdup (caller);
|
|
|
|
g_signal_emit (G_OBJECT (isdn),
|
|
bl_isdn_signals[INCOMING_CALL], 0, line);
|
|
return;
|
|
}
|
|
else if (strncmp (msg, "connected", 9) == 0)
|
|
{
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn: line %d connected\n", channel);
|
|
#endif
|
|
line->offhook = TRUE;
|
|
g_signal_emit (G_OBJECT (isdn),
|
|
bl_isdn_signals[STATE_CHANGED], 0, line);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (channel != 0)
|
|
{
|
|
g_printerr ("BlIsdn: line %d is out of range (%d lines configured)\n",
|
|
channel, isdn->num_lines);
|
|
return;
|
|
}
|
|
|
|
if (strncmp (msg, "error:", 6) == 0)
|
|
{
|
|
if (channel == 0)
|
|
g_printerr ("BlIsdn: General error signalled: %s\n", msg + 6);
|
|
else
|
|
g_printerr ("BlIsdn: Error signalled on line %d: %s\n",
|
|
channel, msg + 6);
|
|
}
|
|
}
|
|
|
|
BlIsdn *
|
|
bl_isdn_new (BlConfig *config,
|
|
GError **error)
|
|
{
|
|
BlIsdn *isdn;
|
|
struct hostent *dest;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (BL_IS_CONFIG (config), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
g_return_val_if_fail (config->isdn_host != NULL, NULL);
|
|
g_return_val_if_fail (config->isdn_port > 0, NULL);
|
|
g_return_val_if_fail (config->isdn_listen > 0, NULL);
|
|
g_return_val_if_fail (config->isdn_lines < 9, NULL);
|
|
|
|
dest = gethostbyname (config->isdn_host);
|
|
if (dest == NULL)
|
|
{
|
|
g_set_error (error, 0, 0,
|
|
"Unable to resolve host '%s'", config->isdn_host);
|
|
return FALSE;
|
|
}
|
|
|
|
isdn = BL_ISDN (g_object_new (BL_TYPE_ISDN, NULL));
|
|
|
|
isdn->config = g_object_ref (config);
|
|
|
|
if (! bl_isdn_setup (isdn, error))
|
|
{
|
|
bl_isdn_teardown (isdn);
|
|
g_object_unref (isdn);
|
|
return NULL;
|
|
}
|
|
|
|
isdn->num_lines = config->isdn_lines;
|
|
isdn->lines = g_new0 (BlIsdnLine, isdn->num_lines);
|
|
|
|
for (i = 0; i < isdn->num_lines; i++)
|
|
isdn->lines[i].channel = i + 1;
|
|
|
|
isdn->source = g_source_new (&udp_funcs, sizeof (BlIsdnSource));
|
|
g_source_set_priority (isdn->source, G_PRIORITY_DEFAULT);
|
|
g_source_set_can_recurse (isdn->source, FALSE);
|
|
|
|
((BlIsdnSource *) isdn->source)->isdn = isdn;
|
|
g_source_attach (isdn->source, NULL);
|
|
|
|
return isdn;
|
|
}
|
|
|
|
void
|
|
bl_isdn_call_accept (BlIsdn *isdn,
|
|
BlIsdnLine *line,
|
|
const gchar *wamp)
|
|
{
|
|
gchar *cmd;
|
|
|
|
g_return_if_fail (BL_IS_ISDN (isdn));
|
|
g_return_if_fail (line != NULL);
|
|
|
|
if (isdn->blocked)
|
|
return;
|
|
|
|
cmd = g_strdup_printf ("%d:accept", line->channel);
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn > %s\n", cmd);
|
|
#endif
|
|
send (isdn->send_sock, cmd, strlen (cmd), 0);
|
|
g_free (cmd);
|
|
|
|
if (wamp)
|
|
{
|
|
cmd = g_strdup_printf ("%d:playbackground:%s", line->channel, wamp);
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn > %s\n", cmd);
|
|
#endif
|
|
send (isdn->send_sock, cmd, strlen (cmd), 0);
|
|
g_free (cmd);
|
|
}
|
|
}
|
|
|
|
void
|
|
bl_isdn_call_hangup (BlIsdn *isdn,
|
|
BlIsdnLine *line,
|
|
BlIsdnReason reason)
|
|
{
|
|
gchar *cmd;
|
|
|
|
g_return_if_fail (BL_IS_ISDN (isdn));
|
|
g_return_if_fail (line != NULL);
|
|
|
|
if (reason)
|
|
cmd = g_strdup_printf ("%d:hangup:%d", line->channel, reason);
|
|
else
|
|
cmd = g_strdup_printf ("%d:hangup", line->channel);
|
|
|
|
#if ISDN_VERBOSE
|
|
g_printerr ("BlIsdn > %s\n", cmd);
|
|
#endif
|
|
send (isdn->send_sock, cmd, strlen (cmd), 0);
|
|
g_free (cmd);
|
|
}
|
|
|
|
void
|
|
bl_isdn_block (BlIsdn *isdn)
|
|
{
|
|
g_return_if_fail (BL_IS_ISDN (isdn));
|
|
|
|
bl_isdn_hangup_all (isdn);
|
|
|
|
isdn->blocked = TRUE;
|
|
|
|
g_printerr ("BlIsdn: Thrown out all callers, ISDN is now blocked!\n");
|
|
}
|
|
|
|
void
|
|
bl_isdn_unblock (BlIsdn *isdn)
|
|
{
|
|
g_return_if_fail (BL_IS_ISDN (isdn));
|
|
|
|
isdn->blocked = FALSE;
|
|
|
|
g_printerr ("BlIsdn: Unblocked ISDN!\n");
|
|
}
|