Skip to content

Commit

Permalink
mod_asterisk_queues: Add Asterisk queue agent module.
Browse files Browse the repository at this point in the history
This adds several modules related to allowing
Asterisk queue agents to interact with queues
using a BBS module:

* mod_asterisk_ami: Asterisk Manager Interface (AMI)
  integration using CAMI library callbacks. Other modules
  can then receive AMI events, such as mod_asterisk_queues.
* mod_asterisk_queues: Generic queue management system.
  Specific queue functionality needs to be implemented
  in custom user modules that define queue handlers.
  This module handles all the general queue management
  functionality so that handlers only need to define
  queue-specific business logic.
* mod_ncurses: Provides graphical ncurses interface
  that works abstractly within the BBS. Since ncurses
  is generally not safe to use in multithreaded programs,
  this interface forks and run ncurses on a node in a
  separate process and returns the chosen value
  to the calling BBS function. This also abstracts the
  complexity of ncurses away to make simple menus easy
  to create.

This commit does include specific queue handlers.

As part of this change:

* Variables can now be defined per user in variables.conf
  and can be used by modules to restrict or customize
  functionality (such as by mod_asterisk_queues)
  • Loading branch information
InterLinked1 committed Nov 19, 2023
1 parent 9addb13 commit 18e401f
Show file tree
Hide file tree
Showing 25 changed files with 2,065 additions and 25 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Key features and capabilities include:

* Internet Relay Chat client and server (including ChanServ), with native IRC, Slack, and Discord relays

* Queue agent position system for Asterisk

* Emulated slow baud rate support

* TDD/TTY (telecommunications device for the deaf) support
Expand Down
4 changes: 2 additions & 2 deletions bbs/alertpipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ int bbs_alertpipe_close(int alert_pipe[2])
return 0;
}

int bbs_alertpipe_poll(int alert_pipe[2])
int bbs_alertpipe_poll(int alert_pipe[2], int ms)
{
int res;
for (;;) {
struct pollfd pfd = { alert_pipe[0], POLLIN, 0 };
res = poll(&pfd, 1, -1);
res = poll(&pfd, 1, ms);
if (res < 0) {
if (errno != EINTR) {
bbs_warning("poll returned error: %s\n", strerror(errno));
Expand Down
17 changes: 9 additions & 8 deletions bbs/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,13 @@ int bbs_user_authenticate(struct bbs_user *user, const char *username, const cha
return 0;
}

static int post_auth(struct bbs_node *node, struct bbs_user *user)
{
bbs_auth("Node %d now logged in as %s (via %s)\n", node->id, bbs_username(user), node->protname);
bbs_event_dispatch(node, EVENT_USER_LOGIN);
return 0;
}

int bbs_node_attach_user(struct bbs_node *node, struct bbs_user *user)
{
bbs_assert_exists(node);
Expand All @@ -609,7 +616,7 @@ int bbs_node_attach_user(struct bbs_node *node, struct bbs_user *user)
return -1;
}
node->user = user;
bbs_auth("Node %d now logged in as %s (via %s)\n", node->id, bbs_username(user), node->protname);
post_auth(node, user); /* Manually emit event since bbs_authenticate wasn't used */
return 0;
}

Expand Down Expand Up @@ -668,13 +675,7 @@ int bbs_authenticate(struct bbs_node *node, const char *username, const char *pa
bbs_warning("Username '%s' contains space (may not be compatible with all services)\n", username); /* e.g. IRC */
}

/* Do not run any callbacks for user login here, since this function isn't always
* called on authentication (SSH for example could call bbs_user_authenticate
* and then bbs_node_attach_user).
* Any such stuff should be done in node.c after user login */

bbs_auth("Node %d now logged in as %s\n", node->id, bbs_username(node->user));
bbs_event_dispatch(node, EVENT_USER_LOGIN); /* XXX If bbs_user_authenticate is called directly, this event isn't emitted */
post_auth(node, node->user);
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion bbs/bbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ static void *monitor_sig_flags(void *unused)
UNUSED(unused);

for (;;) {
if (bbs_alertpipe_poll(sig_alert_pipe) <= 0) {
if (bbs_alertpipe_poll(sig_alert_pipe, -1) <= 0) {
break;
}
pthread_mutex_lock(&sig_lock);
Expand Down
5 changes: 5 additions & 0 deletions bbs/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,11 @@ static struct bbs_module *unload_resource_nolock(struct bbs_module *mod, int for
if (force > 1) {
bbs_warning("Warning: Forcing removal of module '%s' with use count %d\n", mod->name, mod->usecount);
} else {
if (RWLIST_EMPTY(&mod->refs)) {
/* The integer count is positive, but our list is empty?
* Critical lack of synchronization! (Probably a bug) */
bbs_error("Module '%s' supposedly has use count %d, but refcount list is empty?\n", mod->name, mod->usecount);
}
bbs_warning("Soft unload failed, '%s' has use count %d\n", mod->name, mod->usecount);
return NULL;
}
Expand Down
1 change: 1 addition & 0 deletions bbs/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,7 @@ static int node_intro(struct bbs_node *node)
bbs_node_var_set_fmt(node, "BBS_USERID", "%d", node->user->id);
bbs_node_var_set_fmt(node, "BBS_USERPRIV", "%d", node->user->priv);
bbs_node_var_set(node, "BBS_USERNAME", bbs_username(node->user));
bbs_user_init_vars(node); /* Set any custom variables for this user */

/*! \todo Notify user's friends that s/he's logged on now */
/*! \todo Notify the sysop (sysop console), via BELL, that a new user has logged in, if and only if the sysop console is idle */
Expand Down
2 changes: 1 addition & 1 deletion bbs/pty.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ int bbs_node_spy(int fdin, int fdout, unsigned int nodenum)
* Except, we only get signals from ^C on the foreground console.
*/
if (fgconsole) {
if (bbs_alertpipe_poll(spy_alert_pipe) > 0) {
if (bbs_alertpipe_poll(spy_alert_pipe, -1) > 0) {
bbs_alertpipe_read(spy_alert_pipe);
}
/* Restore the original handler for ^C */
Expand Down
31 changes: 28 additions & 3 deletions bbs/variables.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "include/linkedlists.h"
#include "include/variables.h"
#include "include/node.h"
#include "include/user.h"
#include "include/config.h"
#include "include/utils.h"
#include "include/cli.h"
Expand Down Expand Up @@ -120,16 +121,16 @@ static int load_config(void)

while ((section = bbs_config_walk(cfg, section))) {
if (strcmp(bbs_config_section_name(section), "variables")) {
bbs_warning("Invalid section '%s', ignoring\n", bbs_config_section_name(section));
/* [variables] is the only valid section */
/* [variables] contains global variables
* Don't load anything else into memory directly. */
continue;
}
while ((keyval = bbs_config_section_walk(section, keyval))) {
const char *key = bbs_keyval_key(keyval), *value = bbs_keyval_val(keyval);
bbs_var_set_user(key, value);
}
}
bbs_config_free(cfg);
/* Don't free the config, since we'll reference it whenever users log in. */
return 0;
}

Expand Down Expand Up @@ -192,6 +193,30 @@ int bbs_vars_init(void)
return load_config() || bbs_cli_register_multiple(cli_commands_variables);
}

/*! \note Could use a callback for this (allowing NULL modules in event.c),
* but just make this callable directly from node.c */
int bbs_user_init_vars(struct bbs_node *node)
{
struct bbs_config_section *section = NULL;
struct bbs_keyval *keyval = NULL;
struct bbs_config *cfg = bbs_config_load("variables.conf", 1);

if (!cfg) {
return 0;
}

while ((section = bbs_config_walk(cfg, section))) {
if (strcasecmp(bbs_config_section_name(section), bbs_username(node->user))) {
continue;
}
while ((keyval = bbs_config_section_walk(section, keyval))) {
const char *key = bbs_keyval_key(keyval), *value = bbs_keyval_val(keyval);
bbs_node_var_set(node, key, value);
}
}
return 0;
}

int bbs_varlist_last_var_append(struct bbs_vars *vars, const char *s)
{
struct bbs_var *v;
Expand Down
6 changes: 6 additions & 0 deletions configs/mod_asterisk_ami.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
; mod_asterisk_ami.conf

;[ami] ; Connection information for Asterisk Manager Interface
;hostname=127.0.0.1
;username=amiuser
;password=amipass
19 changes: 19 additions & 0 deletions configs/mod_asterisk_queues.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
; mod_asterisk_queues.conf

[general]
title = OPERATOR SERVICE POSITION SYSTEM
callmenutitle = OPERATOR SERVICE POSITION SYSTEM (CHOOSE CALL)
queueidvar = queueuniq ; Name of Asterisk channel variable on queue calls that will contain a unique, numeric ID for the call

; Agents must have the variable ASTERISK_AGENT_ID defined. Only these users may use the module.
; You can define these variables statically in variables.conf.

;[sales]
;title = SALES ; Queue name that will display to the agent.
;handler = sales ; The name of a queue call handler that will handle this call.
; Currently, these handlers are implemented in custom C handlers that must be written for each module.
; These are generally proprietary business logic - so you will likely have to implement your own.

;[engineering]
;title = ENGINEERING
;handler = engineering
8 changes: 8 additions & 0 deletions configs/variables.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@
; Variable names without lowercase letters should be avoided, since reserved and predefined BBS variables will always be all caps.
; Some variables are defined automatically, run /variables from the sysop console to see available variables.
;foo=bar

; Per-user variables that are automatically defined when a user logs in.
;[sysop] ; Username for which these variables are defined.
;SUPERADMIN=1
;abc=def

;[pat]
;ASTERISK_AGENT_ID=111
3 changes: 2 additions & 1 deletion include/alertpipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ int bbs_alertpipe_close(int alert_pipe[2]);

/*!
* \brief Wait indefinitely for traffic on an alertpipe
* \param ms Same as poll()
* \retval Same as poll()
*/
int bbs_alertpipe_poll(int alert_pipe[2]);
int bbs_alertpipe_poll(int alert_pipe[2], int ms);
34 changes: 34 additions & 0 deletions include/mod_asterisk_ami.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* LBBS -- The Lightweight Bulletin Board System
*
* Copyright (C) 2023, Naveen Albert
*
* Naveen Albert <[email protected]>
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/

/*! \file
*
* \brief Asterisk Manager Interface
*
* \author Naveen Albert <[email protected]>
*/

int __bbs_ami_callback_register(int (*callback)(struct ami_event *event, const char *eventname), void *mod);

/*!
* \brief Register an AMI callback
* \param callback Callback function to execute on AMI events
* \retval 0 on success, -1 on failure
*/
#define bbs_ami_callback_register(callback) __bbs_ami_callback_register(callback, BBS_MODULE_SELF)

/*!
* \brief Unregister an AMI callback previously registered using bbs_ami_callback_register
* \param callback
* \retval 0 on success, -1 on failure
*/
int bbs_ami_callback_unregister(int (*callback)(struct ami_event *event, const char *eventname));
47 changes: 47 additions & 0 deletions include/mod_asterisk_queues.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* LBBS -- The Lightweight Bulletin Board System
*
* Copyright (C) 2023, Naveen Albert
*
* Naveen Albert <[email protected]>
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/

/*! \file
*
* \brief Asterisk Queue Position System
*
* \author Naveen Albert <[email protected]>
*/

struct queue_call_handle {
/* Agent info */
struct bbs_node *node; /*!< Node of agent handling call */
int agentid; /*!< ID of agent handling call */
/* Call info */
int id; /*!< Queue call ID */
const char *channel; /*!< Channel name */
int ani2; /*!< ANI II */
unsigned long ani; /*!< ANI */
unsigned long dnis; /*!< DNIS */
const char *cnam; /*!< CNAM */
};

int __bbs_queue_call_handler_register(const char *name, int (*handler)(struct queue_call_handle *qch), void *mod);

/*!
* \brief Register a queue call handler
* \param name Name of handler to register
* \retval 0 on success, -1 on failure
*/
#define bbs_queue_call_handler_register(name, handler) __bbs_queue_call_handler_register(name, handler, BBS_MODULE_SELF)

/*!
* \brief Unregister a queue call handler
* \param name Name of registered handler
* \retval 0 on success, -1 on failure
*/
int bbs_queue_call_handler_unregister(const char *name);
104 changes: 104 additions & 0 deletions include/mod_ncurses.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* LBBS -- The Lightweight Bulletin Board System
*
* Copyright (C) 2023, Naveen Albert
*
* Naveen Albert <[email protected]>
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/

/*! \file
*
* \brief Graphical text menus
*
* \author Naveen Albert <[email protected]>
*/

#define MAX_NCURSES_MENU_OPTIONS 64

struct bbs_ncurses_menu {
const char *title;
const char *subtitle;
const char *keybindings;
char keybind[MAX_NCURSES_MENU_OPTIONS];
char *options[MAX_NCURSES_MENU_OPTIONS];
char *optvals[MAX_NCURSES_MENU_OPTIONS];
int num_options;
};

/*!
* \brief Initialize a menu. You must call this before using it in any way.
*/
void bbs_ncurses_menu_init(struct bbs_ncurses_menu *menu);

/*! \brief Destroy a menu when done with it */
void bbs_ncurses_menu_destroy(struct bbs_ncurses_menu *menu);

/*!
* \brief Set the title of a menu
* \param menu
* \param title Menu title. Must remain valid throughout the lifetime of menu.
*/
void bbs_ncurses_menu_set_title(struct bbs_ncurses_menu *menu, const char *title);

/*!
* \brief Set the subtitle of a menu
* \param menu
* \param subtitle Menu subtitle. Must remain valid throughout the lifetime of menu.
*/
void bbs_ncurses_menu_set_subtitle(struct bbs_ncurses_menu *menu, const char *subtitle);

/*!
* \brief Disable custom key bindings. A default 'q' option will still be added to quit.
* \param menu
*/
void bbs_ncurses_menu_disable_keybindings(struct bbs_ncurses_menu *menu);

/*!
* \brief Add an option to a menu
* \param menu
* \param key Key binding. Specify 0 for no key binding.
* \param opt Will be duplicated.
* \param value Will be duplicated.
* \retval -1 on failure, 0 on success
*/
int bbs_ncurses_menu_addopt(struct bbs_ncurses_menu *menu, char key, const char *opt, const char *value) __nonnull ((1, 3));

/*!
* \brief Get the option value of a menu item at a particular index
* \param menu
* \param index
* \return NULL on failure or invalid index
* \return Option value
*/
const char *bbs_ncurses_menu_getopt_name(struct bbs_ncurses_menu *menu, int index);

/*!
* \brief Get the keybinding of a menu item at a particular index
* \param menu
* \param index
* \return 0 on failure or invalid index
* \return Key binding
*/
char bbs_ncurses_menu_getopt_key(struct bbs_ncurses_menu *menu, int index);

/*!
* \brief Get an option using a menu (pass to bbs_ncurses_menu_getopt_name for the text value)
* \param node
* \param menu
* \retval -1 on failure, option index otherwise
*/
int bbs_ncurses_menu_getopt(struct bbs_node *node, struct bbs_ncurses_menu *menu);

/*!
* \brief Run a menu and return the keybinding for the chosen option.
* This is a convenience wrapper that calls bbs_ncurses_menu_getopt
* and then calls bbs_ncurses_menu_getopt_key on the result, if there is one.
* \param node
* \param menu
* \return Same as bbs_ncurses_menu_getopt_key
*/
char bbs_ncurses_menu_getopt_selection(struct bbs_node *node, struct bbs_ncurses_menu *menu);
Loading

0 comments on commit 18e401f

Please sign in to comment.