# the GNU General Public License
#
-APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom.so app_mp3.so
+APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom.so app_mp3.so app_system.so app_echo.so
CFLAGS+=
--- /dev/null
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Echo application -- play back what you hear to evaluate latency
+ *
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+
+
+static char *tdesc = "Simple Echo Application";
+
+static char *app = "Echo";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int skel_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct localuser *u;
+ struct ast_frame *f;
+ LOCAL_USER_ADD(u);
+ /* Do our thing here */
+ while((f = ast_read(chan))) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (ast_write(chan, f))
+ break;
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ if (f->subclass == '#') {
+ res = 0;
+ break;
+ } else
+ if (ast_write(chan, f))
+ break;
+ }
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ STANDARD_HANGUP_LOCALUSERS;
+ return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, skel_exec);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
+#include <asterisk/module.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
-static pthread_mutex_t skellock = PTHREAD_MUTEX_INITIALIZER;
-
-static int usecnt=0;
static char *tdesc = "Trivial skeleton Application";
static char *app = "skel";
-struct skeluser {
- struct ast_channel *chan;
- struct skeluser *next;
-} *users = NULL;
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
static int skel_exec(struct ast_channel *chan, void *data)
{
int res=0;
- struct skeluser *u, *ul=NULL;
+ struct localuser *u;
if (!data) {
ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
return -1;
}
- if (!(u=malloc(sizeof(struct skeluser)))) {
- ast_log(LOG_WARNING, "Out of memory\n");
- return -1;
- }
- pthread_mutex_lock(&skellock);
- u->chan = chan;
- u->next = users;
- users = u;
- usecnt++;
- pthread_mutex_unlock(&skellock);
+ LOCAL_USER_ADD(u);
/* Do our thing here */
- pthread_mutex_lock(&skellock);
- u = users;
- while(u) {
- if (ul)
- ul->next = u->next;
- else
- users = u->next;
- u = u->next;
- }
- usecnt--;
- pthread_mutex_unlock(&skellock);
+ LOCAL_USER_REMOVE(u);
return res;
}
int unload_module(void)
{
- struct skeluser *u;
- pthread_mutex_lock(&skellock);
- u = users;
- while(u) {
- /* Hang up anybody who is using us */
- ast_softhangup(u->chan);
- u = u->next;
- }
- pthread_mutex_unlock(&skellock);
+ STANDARD_HANGUP_LOCALUSERS;
return ast_unregister_application(app);
}
int usecount(void)
{
int res;
- pthread_mutex_lock(&skellock);
- res = usecnt;
- pthread_mutex_unlock(&skellock);
+ STANDARD_USECOUNT(res);
return res;
}
--- /dev/null
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Execute arbitrary system commands
+ *
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+
+
+static char *tdesc = "Generic System() application";
+
+static char *app = "System";
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int skel_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct localuser *u;
+ if (!data) {
+ ast_log(LOG_WARNING, "System requires an argument(command)\n");
+ return -1;
+ }
+ LOCAL_USER_ADD(u);
+ /* Do our thing here */
+ res = system((char *)data);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", data);
+ res = -1;
+ } else if (res == 127) {
+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", data);
+ res = -1;
+ } else {
+ if (res && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101))
+ chan->priority+=100;
+ res = 0;
+ }
+ LOCAL_USER_REMOVE(u);
+ return res;
+}
+
+int unload_module(void)
+{
+ STANDARD_HANGUP_LOCALUSERS;
+ return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+ return ast_register_application(app, skel_exec);
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
struct ast_config *cfg;
char *copy, *name, *passwd, *email, *dir, *fmt, *fmts, *fn=NULL;
char comment[256];
- struct ast_filestream *writer, *others[MAX_OTHER_FORMATS];
+ struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
char *sfmt[MAX_OTHER_FORMATS];
int res = -1, fmtcnt=0, x;
int msgnum;
snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
(chan->callerid ? chan->callerid : "Unknown"),
name, ext, chan->name);
+ if (ast_fileexists(fn, NULL) > 0) {
+ msgnum++;
+ continue;
+ }
writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
- if (!writer && (errno != EEXIST))
+ if (!writer)
break;
msgnum++;
} while(!writer && (msgnum < MAXMSG));
if (f->frametype == AST_FRAME_VOICE) {
/* Write the primary format */
res = ast_writestream(writer, f);
+ if (res) {
+ ast_log(LOG_WARNING, "Error writing primary frame\n");
+ break;
+ }
/* And each of the others */
- for (x=0;x<fmtcnt;x++)
+ for (x=0;x<fmtcnt;x++) {
res |= ast_writestream(others[x], f);
+ }
ast_frfree(f);
/* Exit on any error */
if (res) {
ast_log(LOG_WARNING, "No voicemail configuration\n");
goto out;
}
- if (ast_streamfile(chan, "vm-login"))
+ if (ast_streamfile(chan, "vm-login")) {
+ ast_log(LOG_WARNING, "Couldn't stream login file\n");
goto out;
+ }
do {
/* Prompt for, and read in the username */
- if (ast_readstring(chan, username, sizeof(username), 2000, 5000, "#"))
+ if (ast_readstring(chan, username, sizeof(username), 2000, 5000, "#")) {
+ ast_log(LOG_WARNING, "Couldn't read username\n");
goto out;
+ }
if (!strlen(username)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
res = 0;
goto out;
}
- if (ast_streamfile(chan, "vm-password"))
+ if (ast_streamfile(chan, "vm-password")) {
+ ast_log(LOG_WARNING, "Unable to stream password file\n");
goto out;
- if (ast_readstring(chan, password, sizeof(password), 2000, 5000, "#"))
+ }
+ if (ast_readstring(chan, password, sizeof(password), 2000, 5000, "#")) {
+ ast_log(LOG_WARNING, "Unable to read password\n");
goto out;
+ }
copy = ast_variable_retrieve(cfg, NULL, username);
if (copy) {
copy = strdup(copy);
int option_debug=0;
int option_nofork=0;
int option_quiet=0;
+int option_console=0;
int option_highpriority=0;
+int fully_booted = 0;
#define HIGH_PRIORITY 1
#define HIGH_PRIORITY_SCHED SCHED_RR
system call. We don't actually need to do anything though. */
if (option_debug)
ast_log(LOG_DEBUG, "Urgent handler\n");
+ signal(num, urg_handler);
return;
}
if (!pos)
fprintf(stdout, "\r");
fprintf(stdout, s + pos);
+ fflush(stdout);
if (complete)
/* Wake up a select()ing console */
pthread_kill(consolethread, SIGURG);
/* Called when readline data is available */
if (s && strlen(s))
add_history(s);
- if (s)
+ /* Give the console access to the shell */
+ if (s) {
+ if (s[0] == '!') {
+ if (s[1])
+ system(s+1);
+ else
+ system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ } else
ast_cli_command(STDOUT_FILENO, s);
+ if (!strcasecmp(s, "help"))
+ fprintf(stdout, " !<command> Executes a given shell command\n");
+ } else
+ fprintf(stdout, "\nUse \"quit\" to exit\n");
}
static char quit_help[] =
exit(1);
}
/* Check for options */
- while((c=getopt(argc, argv, "dvqp")) != EOF) {
+ while((c=getopt(argc, argv, "dvqpc")) != EOF) {
switch(c) {
case 'd':
option_debug++;
option_nofork++;
break;
+ case 'c':
+ option_console++;
+ option_nofork++;
case 'p':
option_highpriority++;
break;
case 'v':
option_verbose++;
+ option_nofork++;
break;
case 'q':
option_quiet++;
exit(1);
}
}
+ ast_register_verbose(console_verboser);
/* Print a welcome message if desired */
- if (option_verbose) {
+ if (option_verbose || option_console) {
ast_verbose( "Asterisk, Copyright (C) 1999 Mark Spencer\n");
ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n");
ast_verbose( "=========================================================================\n");
}
+ if (option_console && !option_verbose)
+ ast_verbose("[ Booting...");
signal(SIGURG, urg_handler);
signal(SIGINT, quit_handler);
signal(SIGTERM, quit_handler);
exit(1);
/* We might have the option of showing a console, but for now just
do nothing... */
-
- /* Console stuff now... */
- /* Register our quit function */
- ast_cli_register(&quit);
- consolethread = pthread_self();
- ast_register_verbose(console_verboser);
- if (option_verbose)
+ if (option_console && !option_verbose)
+ ast_verbose(" ]\n");
+ if (option_verbose || option_console)
ast_verbose( "Asterisk Ready.\n");
- if (strlen(filename))
- read_history(filename);
- rl_callback_handler_install(ASTERISK_PROMPT, consolehandler);
- rl_completion_entry_function = (Function *)cli_generator;
- for(;;) {
- FD_ZERO(&rfds);
- FD_SET(STDIN_FILENO, &rfds);
- res = select(STDIN_FILENO + 1, &rfds, NULL, NULL, NULL);
- if (res > 0) {
- rl_callback_read_char();
- } else if (res < 1) {
- rl_forced_update_display();
- }
-
- }
+ fully_booted = 1;
+ if (option_console) {
+ /* Console stuff now... */
+ /* Register our quit function */
+ ast_cli_register(&quit);
+ consolethread = pthread_self();
+ if (strlen(filename))
+ read_history(filename);
+ rl_callback_handler_install(ASTERISK_PROMPT, consolehandler);
+ rl_completion_entry_function = (Function *)cli_generator;
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_SET(STDIN_FILENO, &rfds);
+ res = select(STDIN_FILENO + 1, &rfds, NULL, NULL, NULL);
+ if (res > 0) {
+ rl_callback_read_char();
+ } else if (res < 1) {
+ rl_forced_update_display();
+ }
+
+ }
+ } else {
+ /* Do nothing */
+ select(0,NULL,NULL,NULL,NULL);
+ }
return 0;
}
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
+#include <unistd.h>
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/file.h>
#include <asterisk/translate.h>
+
+
+#ifdef DEBUG_MUTEX
+/* Convenient mutex debugging functions */
+#define PTHREAD_MUTEX_LOCK(a) __PTHREAD_MUTEX_LOCK(__FUNCTION__, a)
+#define PTHREAD_MUTEX_UNLOCK(a) __PTHREAD_MUTEX_UNLOCK(__FUNCTION__, a)
+
+static int __PTHREAD_MUTEX_LOCK(char *f, pthread_mutex_t *a) {
+ ast_log(LOG_DEBUG, "Locking %p (%s)\n", a, f);
+ return pthread_mutex_lock(a);
+}
+
+static int __PTHREAD_MUTEX_UNLOCK(char *f, pthread_mutex_t *a) {
+ ast_log(LOG_DEBUG, "Unlocking %p (%s)\n", a, f);
+ return pthread_mutex_unlock(a);
+}
+#else
+#define PTHREAD_MUTEX_LOCK(a) pthread_mutex_lock(a)
+#define PTHREAD_MUTEX_UNLOCK(a) pthread_mutex_unlock(a)
+#endif
+
struct chanlist {
char type[80];
char description[80];
struct ast_channel * (*requester)(char *type, int format, void *data);
struct chanlist *next;
} *backends = NULL;
-
struct ast_channel *channels = NULL;
/* Protect the channel list (highly unlikely that two things would change
struct ast_channel *(*requester)(char *type, int format, void *data))
{
struct chanlist *chan, *last=NULL;
- if (pthread_mutex_lock(&chlock)) {
+ if (PTHREAD_MUTEX_LOCK(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
while(chan) {
if (!strcasecmp(type, chan->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return -1;
}
last = chan;
chan = malloc(sizeof(struct chanlist));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return -1;
}
strncpy(chan->type, type, sizeof(chan->type));
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return 0;
}
{
struct ast_channel *tmp;
struct ast_channel_pvt *pvt;
- pthread_mutex_lock(&chlock);
+ PTHREAD_MUTEX_LOCK(&chlock);
tmp = malloc(sizeof(struct ast_channel));
memset(tmp, 0, sizeof(struct ast_channel));
if (tmp) {
}
} else
ast_log(LOG_WARNING, "Out of memory\n");
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return tmp;
}
struct ast_channel *ast_channel_walk(struct ast_channel *prev)
{
struct ast_channel *l, *ret=NULL;
- pthread_mutex_lock(&chlock);
+ PTHREAD_MUTEX_LOCK(&chlock);
l = channels;
if (!prev) {
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return l;
}
while(l) {
ret = l->next;
l = l->next;
}
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return ret;
}
void ast_channel_free(struct ast_channel *chan)
{
struct ast_channel *last=NULL, *cur;
- pthread_mutex_lock(&chlock);
+ PTHREAD_MUTEX_LOCK(&chlock);
cur = channels;
while(cur) {
if (cur == chan) {
free(chan->callerid);
pthread_mutex_destroy(&chan->lock);
free(chan);
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
}
int ast_softhangup(struct ast_channel *chan)
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
- if (pthread_mutex_lock(&chlock)) {
+ if (PTHREAD_MUTEX_LOCK(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return;
}
else
backends = backends->next;
free(chan);
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return;
}
last = chan;
chan = chan->next;
}
- pthread_mutex_unlock(&chlock);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
}
int ast_answer(struct ast_channel *chan)
{
struct chanlist *chan;
struct ast_channel *c = NULL;
- if (pthread_mutex_lock(&chlock)) {
+ if (PTHREAD_MUTEX_LOCK(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return NULL;
}
if (!(chan->capabilities & format)) {
format = ast_translator_best_choice(format, chan->capabilities);
}
+ PTHREAD_MUTEX_UNLOCK(&chlock);
if (chan->requester)
c = chan->requester(type, format, data);
- pthread_mutex_unlock(&chlock);
- break;
+ return c;
}
chan = chan->next;
}
if (!chan)
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+ PTHREAD_MUTEX_UNLOCK(&chlock);
return c;
}
if (!len)
return -1;
do {
- if (c->streamid > -1) {
+ if ((c->streamid > -1) || (c->trans && (c->trans->streamid > -1))) {
d = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
+ usleep(1000);
if (!d)
d = ast_waitfordigit(c, to);
} else {
#ifdef ANNEX_B
Decod(&tmp->dec, tmpdata, f->data, 0);
for (x=0;x<Frame;x++)
- (tmp->buf + tmp->tail)[x] = tmpdata[x];
+ (tmp->buf + tmp->tail)[x] = (short)tmpdata[x];
#else
Decod(&tmp->dec, tmp->buf + tmp->tail, f->data, 0);
#endif
[interfaces]
;
; Lines for which we are the user termination. They accept incoming
-; and outgoing calls.
+; and outgoing calls. We use the default context on the first 8 lines
+; used by internal phones.
;
+context=default
;user=voice00
;user=voice01
;user=voice02
;user=voice05
;user=voice06
;user=voice07
-context=default
-user=voice13
-user=voice14
-user=voice15
; Calls on 16 and 17 come from the outside world, so they get
; a little bit special treatment
context=remote
-user=voice16
-user=voice17
+;user=voice16
+;user=voice17
;
; Next we have lines which we only accept calls on, and typically
; do not send outgoing calls on (i.e. these are where we are the
;
; Type of dialing
;
-;dialtype=tone
-dialtype=pulse
+dialtype=tone
+;dialtype=pulse
;
; Mode selection. "Immediate" means that as soon as you dial, you're connected
; and the line is considered up. "Ring" means we wait until the ring cadence
;
; List all devices we can use.
;
-device=/dev/ttyS3
+;device=/dev/ttyS3
;
; Module Loader configuration file
;
+
[modules]
autoload=yes
-;load=pbx_gtkconsole.so
+;
+; If you want, load the GTK console right away.
+; Don't load the KDE console since
+; it's not as sophisticated right now.
+;
noload=pbx_gtkconsole.so
+;load=pbx_gtkconsole.so
noload=pbx_kdeconsole.so
+;
+; Intercom application is obsoleted by
+; chan_oss. Don't load it.
+;
noload=app_intercom.so
-;load=chan_vofr.so
-;load=chan_h323.so
+;
+; Module names listed in "global" section will have symbols globally
+; exported to modules loaded after them.
+;
+[global]
+chan_modem.so=yes
; Voicemail Configuration
;
[general]
-; Default format for writing Voicemail
-; format=g723sf|rawgsm|mp3|wav
-format=g723sf|wav49|wav
+; Default formats for writing Voicemail
+;format=g723sf|wav49|wav
+format=gsm|wav49|wav
+;
+; Each mailbox is listed in the form <mailbox>=<password>,<name>,<email>
+; if the e-mail is specified, a message will be sent when a message is
+; received, to the given mailbox.
+;
[default]
-4200=2345,Mark Spencer,markster@linux-support.net
-4300=2345,Ben Rigas,ben@american-computer.net
-4310=2345,Sales,sales@marko.net
-4069=2345,Matt Brooks,matt@marko.net
-4110=1379,Rob Flynn,rflynn@blueridge.net
+1234=4242,Example Mailbox,root@localhost
+;4200=9855,Mark Spencer,markster@linux-support.net
+;4300=3456,Ben Rigas,ben@american-computer.net
+;4310=5432,Sales,sales@marko.net
+;4069=6522,Matt Brooks,matt@marko.net
+;4110=3443,Rob Flynn,rflynn@blueridge.net
for the first digit */
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders);
#define CHECK_BLOCKING(c) { \
- if ((c)->blocking) \
+ if ((c)->blocking) {\
ast_log(LOG_WARNING, "Blocking '%s', already blocked by thread %ld in procedure %s\n", (c)->name, (c)->blocker, (c)->blockproc); \
- else { \
+ /* *((int *)0)=0; */ \
+ } else { \
(c)->blocker = pthread_self(); \
(c)->blockproc = __PRETTY_FUNCTION__; \
- c->blocking = -1; } }
+ c->blocking = -1; \
+ } }
#if defined(__cplusplus) || defined(c_plusplus)
}
int errors=0;
int res;
struct module *m;
+ int flags=0;
+ char *val;
+ int o;
+ struct ast_config *cfg;
+ /* Keep the module file parsing silent */
+ o = option_verbose;
+ option_verbose = 0;
+ cfg = ast_load(AST_MODULE_CONFIG);
+ option_verbose = o;
+ if (cfg) {
+ if ((val = ast_variable_retrieve(cfg, "global", resource_name))
+ && ast_true(val))
+ flags |= RTLD_GLOBAL;
+ ast_destroy(cfg);
+ }
+
if (pthread_mutex_lock(&modlock))
ast_log(LOG_WARNING, "Failed to lock\n");
m = module_list;
} else {
snprintf(fn, sizeof(fn), "%s/%s", AST_MODULE_DIR, resource_name);
}
- m->lib = dlopen(fn, RTLD_NOW | RTLD_GLOBAL);
+ m->lib = dlopen(fn, RTLD_NOW | flags);
if (!m->lib) {
ast_log(LOG_WARNING, "%s\n", dlerror());
free(m);
pthread_mutex_unlock(&modlock);
return -1;
}
- if (option_verbose)
- ast_verbose( " => (%s)\n", m->description());
+ if (!fully_booted) {
+ if (option_verbose)
+ ast_verbose( " => (%s)\n", m->description());
+ if (option_console && !option_verbose)
+ ast_verbose( ".");
+ } else {
+ if (option_verbose)
+ ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
+ }
+ m->next = module_list;
+ module_list = m;
pthread_mutex_unlock(&modlock);
if ((res = m->load_module())) {
- ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, fn, res);
+ ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
ast_unload_resource(resource_name, 0);
return -1;
}
- m->next = module_list;
- module_list = m;
+ ast_update_use_count();
return 0;
}
#include <asterisk/module.h>
#include <asterisk/logger.h>
#include <asterisk/options.h>
+#include <asterisk/cli.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
+#include <sys/time.h>
+#include <sys/signal.h>
#include <gtk/gtk.h>
#include <glib.h>
static pthread_t console_thread;
static int inuse=0;
+static int clipipe[2];
+static int cleanupid = -1;
+
static char *dtext = "Asterisk PBX Console (GTK Version)";
static GtkWidget *window;
static GtkWidget *verb;
static GtkWidget *modules;
static GtkWidget *statusbar;
+static GtkWidget *cli;
+
+static struct timeval last;
static void update_statusbar(char *msg)
{
gdk_threads_enter();
gtk_widget_destroy(window);
gdk_threads_leave();
+ close(clipipe[0]);
+ close(clipipe[1]);
}
return 0;
}
+static int cleanup(void *useless)
+{
+ gdk_threads_enter();
+ gtk_clist_thaw(GTK_CLIST(verb));
+ gtk_widget_queue_resize(verb->parent);
+ gtk_clist_moveto(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1, 0, 0, 0);
+ cleanupid = -1;
+ gdk_threads_leave();
+ return 0;
+}
+
-static void verboser(char *stuff, int opos, int replacelast, int complete)
+static void __verboser(char *stuff, int opos, int replacelast, int complete)
{
char *s2[2];
- pthread_mutex_lock(&verb_lock);
+ struct timeval tv;
+ int ms;
s2[0] = stuff;
s2[1] = NULL;
- gdk_threads_enter();
+ gtk_clist_freeze(GTK_CLIST(verb));
if (replacelast)
gtk_clist_remove(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1);
gtk_clist_append(GTK_CLIST(verb), s2);
- gtk_clist_moveto(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1, 0, 0, 0);
- gdk_threads_leave();
+ if (last.tv_sec || last.tv_usec) {
+ gdk_threads_leave();
+ gettimeofday(&tv, NULL);
+ if (cleanupid > -1)
+ gtk_timeout_remove(cleanupid);
+ ms = (tv.tv_sec - last.tv_sec) * 1000 + (tv.tv_usec - last.tv_usec) / 1000;
+ if (ms < 100) {
+ /* We just got a message within 100ms, so just schedule an update
+ in the near future */
+ cleanupid = gtk_timeout_add(200, cleanup, NULL);
+ } else {
+ cleanup(&cleanupid);
+ }
+ last = tv;
+ } else {
+ gettimeofday(&last, NULL);
+ }
+}
+
+static void verboser(char *stuff, int opos, int replacelast, int complete)
+{
+ pthread_mutex_lock(&verb_lock);
+ /* Lock appropriately if we're really being called in verbose mode */
+ __verboser(stuff, opos, replacelast, complete);
pthread_mutex_unlock(&verb_lock);
}
+static void cliinput(void *data, int source, GdkInputCondition ic)
+{
+ static char buf[256];
+ static int offset = 0;
+ int res;
+ char *c;
+ char *l;
+ char n;
+ /* Read as much stuff is there */
+ res = read(source, buf + offset, sizeof(buf) - 1 - offset);
+ if (res > -1)
+ buf[res + offset] = '\0';
+ /* make sure we've null terminated whatever we have so far */
+ c = buf;
+ l = buf;
+ while(*c) {
+ if (*c == '\n') {
+ /* Keep the trailing \n */
+ c++;
+ n = *c;
+ *c = '\0';
+ __verboser(l, 0, 0, 1);
+ *(c - 1) = '\0';
+ *c = n;
+ l = c;
+ } else
+ c++;
+ }
+ if (strlen(l)) {
+ /* We have some left over */
+ memmove(buf, l, strlen(l) + 1);
+ offset = strlen(buf);
+ } else {
+ offset = 0;
+ }
+
+}
+
+
static void remove_module()
{
int res;
if (GTK_CLIST(modules)->selection) {
module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (int) GTK_CLIST(modules)->selection->data);
}
- gdk_threads_enter();
gtk_clist_freeze(GTK_CLIST(modules));
gtk_clist_clear(GTK_CLIST(modules));
ast_update_module_list(add_mod);
if (module)
gtk_clist_select_row(GTK_CLIST(modules), gtk_clist_find_row_from_data(GTK_CLIST(modules), module), -1);
gtk_clist_thaw(GTK_CLIST(modules));
- gdk_threads_leave();
return 1;
}
static void exit_completely(GtkWidget *widget, gpointer data)
{
- /* This is the wrong way to do this. We need an ast_clean_exit() routine */
- exit(0);
+#if 0
+ /* Clever... */
+ ast_cli_command(clipipe[1], "quit");
+#else
+ kill(getpid(), SIGTERM);
+#endif
}
static void exit_nicely(GtkWidget *widget, gpointer data)
return NULL;
}
+static int cli_activate()
+{
+ char buf[256];
+ strncpy(buf, gtk_entry_get_text(GTK_ENTRY(cli)), sizeof(buf));
+ gtk_entry_set_text(GTK_ENTRY(cli), "");
+ if (strlen(buf)) {
+ ast_cli_command(clipipe[1], buf);
+ }
+ return TRUE;
+}
+
static int show_console()
{
GtkWidget *hbox;
gtk_container_add(GTK_CONTAINER(sw), verb);
gtk_widget_show(verb);
gtk_widget_show(sw);
- gtk_widget_set_usize(verb, 600, 400);
+ gtk_widget_set_usize(verb, 640, 400);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, gtk_label_new("Verbose Status"));
hbox = gtk_vbox_new(FALSE, 0);
gtk_widget_show(hbox);
+
+ /* Command line */
+ cli = gtk_entry_new();
+ gtk_widget_show(cli);
+
+ gtk_signal_connect(GTK_OBJECT(cli), "activate",
+ GTK_SIGNAL_FUNC (cli_activate), NULL);
gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox), wbox, FALSE, FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(hbox), cli, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), statusbar, FALSE, FALSE, 0);
-
-
gtk_container_add(GTK_CONTAINER(window), hbox);
gtk_window_set_title(GTK_WINDOW(window), "Asterisk Console");
+ gtk_widget_grab_focus(cli);
pthread_create(&console_thread, NULL, consolethread, NULL);
/* XXX Okay, seriously fix me! XXX */
usleep(100000);
gtk_clist_freeze(GTK_CLIST(verb));
ast_loader_register(mod_update);
gtk_clist_thaw(GTK_CLIST(verb));
+ gdk_input_add(clipipe[0], GDK_INPUT_READ, cliinput, NULL);
mod_update();
update_statusbar("Asterisk Console Ready");
return 0;
int load_module(void)
{
+ if (pipe(clipipe)) {
+ ast_log(LOG_WARNING, "Unable to create CLI pipe\n");
+ return -1;
+ }
g_thread_init(NULL);
if (gtk_init_check(NULL, NULL)) {
/* XXX Do we need to call this twice? XXX */
int ast_say_number(struct ast_channel *chan, int num)
{
int res = 0;
+ int playh = 0;
char fn[256] = "";
while(num && !res) {
+ if (playh) {
+ snprintf(fn, sizeof(fn), "digits/hundred");
+ playh = 0;
+ } else
if (num < 20) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
num -= ((num / 10) * 10);
} else {
- ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
- res = -1;
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ } else {
+ ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
}
if (!res) {
res = ast_streamfile(chan, fn);
--- /dev/null
+Ó\17\99ÕS^ÀÍ\15ªU\ee ³M-W\83VÀ¹\98J\92[¬¡j\13m¶éÓè\82\b#àÀÖ\92CK\9cá8ë1·\ 5äÁ8Ô±jMo!¡\eú¿iÒv\9b\1d°sdH\v±ÛebÂ,\9d]¤\19Ö\85´Û\9e%\94^$)\1cn8ÄÒkú¥k¤Å¨æòÓ¢ÈãGâµ \98o\8a6åñ\99\eÚ\84\96Ód4\vÒ5ª!k\8a¦Ûè\91Ød\8cÃÈ\93\95;?\8cä\8b\1d\89×*\8d$&Ür[\84Ò1á¤ë¶\83%\19\99¶\ 3\8fD9^Ô:Ü\91"\91Ú\80Æ]\8b#ÙðV½\14Ò4Ðá³\89"ó"qÉ/\8b!a&\8f\f\80\87â'\14MðÛÆÃZ\ 3i\10\94Ò3èis\85¹ ²U\14\81ãÕ!\8eÇ \7f",Þ\81öÙ}âµ\9d\82W ÒqÑe{{Ç)\1c\8e;o{å\83T²Âw¤\154üE(s*(ÛMLþÓs\93\9eyw\90\ fU\16¨¢rÑI¾-ÄâqPµ\vQúmkMá¤N\96ÁÔ/\94\1f:qÎ/'je*o³§\13×\98¤m2äÙ.'oo®+Ó\18çcÔm\9c[xÛ°¶ä7@³ÙQÄê\9eS\kQ¸ÔjG=ÙP\8a\94P¶ÕÔ,¬_pk¯¥Û磌k\8fbñ\90áCisÇ\ff·ÙÓMTÒp6%Ó¬¬_pkPß\8eUÆÔÓÏVü5w\1dk®Êãxd5n\90\85\9b1´\9bÓë\93×:kp=ÚRÆ¡ÚíX+»è
+ÜJHÉ\85\84æäÌÿJ\ eÇbÓêyÞkî©\97\93¯h<\80Ç9[\85¸×~Èh$\89ÊØ\83\ 6ì\86²9[Ôj\82\16jîÅhÊôfÊ\85#\9b»ñ\8bD\84¥ÈÄi·¬\85\ 6^
+r4ìÓj\83gq\85¨9Rrþ4¼\88Ð\9cR\84\88\89ÉW5AÙ\93\89\b¯\12u0þÓ«d3©\8b¨ò¿\8cÎÔ\89§Ä
+\89=p\89(ÄÜQ½X\93((Í\9eѤÓç\84ghçLK¬h\14\1a\93˧cÔ;"\9d'É\180ÂS¡ )\ééTÓ(\8cç``«9km&\1a£\ 5\avµ\fÉfªYc\8d¢Ä«'±2\9eE\eÓ)u3 ÈC\ 2>UÍ\18¯\ 5\ e\rréqR#¶BV¶Ü¯e¸\14M$îÒå\84ê(´¤dÅr+cºÆ¶ã\127\1cÀ£ûe!¦ë¨ä5"\95\84\82Ò\9fweà°ÄG.ö\19S\9eÄFäÍÙ\ 3²£óZ±ÚÂb\81{\91[¹\8aÓ\1dW\1f
+³b\r\95NÏd±\ 1Ôºî@ò·\ 1\96\19\10Ä£º\81{¢"× Ó\1dN"\99³\ 25,ª\aÔ³"«'\91°«µãFÛ\8d´\9fZÂ\88\96\97ÜâÓ]UÞY`!®ûQÉ\13±A\81\17«;v_AØÜ¹Ò\90³ÁB¦\85ÔjÒ^\86\19áT¢\7fª\8es[¢\81Ûe\ e¨Ó¦@ç±îV\93Ö@¨\8a,S¥Ò^S¥"à ¾õ²ÄÓ¦Á\16äm´ãØ\80Ù\9c\89["À\80£+mGYÓ s\99Z\9e\80¥¬µÉnÖ å£rE3| ¸ÓqÄ¥Ç@öá®Á-ÒÝj\9cÚfÀ¼òØ´ÄàÏrMÌ¢Þ £T1\86\9d\82À¯u\9a76Ô jaX\94 Çl\92i,\98 ¶ÓLÕ\93Õ@¤\99\90Å\1c\8b!E"\9d"îÓßzÙQ\8c ã6\94·òªà·"\88´êÆ Ë!±¥SpÀÆò\8a8ÕÒÝZ\ÙX Æì®M\ 4Zàºã\10\89\l §h*'\1e\82@êÛ\91EÒ\9db\11\19Ô@8ì\97[;ì`µK\19\13VÀ\1abBÃó\98à_S\11}u
\ No newline at end of file
--- /dev/null
+Õ\1d\91-£R¢4t`Ö¡f¢¤¥\81\e* ¶«4;\12p¢Q²I^\9cÖ\9dy`©»"\9f\eIV\9a\98\81\r\81c,ÒÀâU\OÚ« ÁË¥Uòó×èxÝÙ|¢\e(V£\99\ 2\11\15\ 4\1a\91\8cá\99©sS\9f\98¢Î\16Í4¤Ö[xØ\91Ø¡\86¥±FͺÂ\18ÙnD¨ÜÂ6ͦç(\9câ\9b¢5fÈÕ\9c`\98Y»#ÊÕfh ß\ 1øänÍCçb\ 3Ô¢7fY$´§i7&Ô\9dr¡rw%(ª\89Ö\14\83ÈÄãm¸6X\85\ 4T0&Ë\9b+7,¶±%Ô¦d\1aè_Í4ä\96ûÅ\lÿ\e¶6îy\ e[u\eDny13#nJÆÕ&u\1f8\8aîà-`Â]wÐ;ðIÆ\93wQ$\8b\8eÚ+°°²¡\ 5\12\1eÔét¦øëRK\84m8äw1\ 6\94\93q[ul&ä1\85®u0ê,\8dÉLÓê|æ±ç/4¬\9b\v$èÐFÌ\89IotÍ\8a«iÆÚèï9%î\18ãÔ*[ZrîÈÈ42',îÈ\84³>\99[}E¿å\9bÓ&v¦Õ\1da8äÔhd\16ò}äô+if\82}ÈFãyÕâvÃ\88à,\17V{¥C\84qªÅÔ¦cß1{é9$×A%\8eè\13
+D)vw¬KD²'#wì¶í\96\ f\1cÓæcë0qh\86ªE)Ws\f<%m·bçG7-¸.\93æË%ZmË·Òålç(æ©\ 5#hÇ\1aè\86Çc¿\88´ìç`mÉ¿îÅ\8eeq¥iÒ%§\99\19x¦9wÁ³\1a=\ eK¤ñCx49-\15zåÉm¢G#Ò&¦Xá ä\96¥Në\7f{B\80ËÆ4¥~û]ò&㢢(ÉvɧÒ'¥$é\80Â-D\8d°ÓÒat\96ùKà\ 1I\f+5\1fê\81ªõvù¤ÓàL ÚPág\9b\98¤n°¡L\9aój3ê\81Rà)w\e¤¡èä\95Ú\e×b\9a$¢\92¡Û \951%\8fåæ\91\87¸\91h£\1aÞ.ÈĦD¿d\8fX\9cØ¥\91%"ê¤=³\12m²£G\fÚ\rÆËl¤8øµLÓ¹i(ä\11ÆÜØh\89]pj\86\14Ý\15\9d\8c\86ç;\16\8a¾¥\84¨Ø\9d½¹<¡\b«fnÞãØdp\9dq\8c§k\9c}9i´ÆÉ\13S´¦ÀÃ\17ãÀøþdå½NhÂÕ\1dLrè\81h8å\9bûºdǶÁ@¸î\83¯ËBmÆã}-H\93\92È$Õ\9eD÷(\98ªI\b\90\84\f\83hÉò©¦Û\81h\M\857?\85¨bì\91É=Õ\1fE¾Ø\8bG'\1aoÊ\9c¨¨%\92²Ä\12\8f\ 4\937øY\1cÔ¦\19
+h"LÔÝU¾\90\8få²ún<Ñ\91C·\ 2MUm\93\ 3J\17²ÁÔ\93e´ÛqÙwÔ\1dnyIÜ\85\15´° ]\97d´ãIÛX¦¦ZäMÉP\9b¢É#³Ç¤Ó\9ev¹
+\9b\ 3¶Ú\8dÇ\10\9d\ 2dc\9ay\12¡"\1aÄj¶[¡A$þx\17Òߥ¸\9c¡\ 2\ 2Â.HË¡ÂZ\9bR?\95Z£4\81\92$\82§\ 2P\91J}QÔÝMù\19³Bò\91RÝ\18«\83\82ÜnYbRÄ\89%mÉ"¥¤jÁæ°fÓßfY¬¤T\1cq¦¤³"\10\94e\97-¶¤km\ e8ܶâ6âmÂ2Ò$¥ì\99·!\8aæ¾é%la\16µÒ\18ݼÁ¡×ÊÉm¾\82!¤±ÇdÒ\9ffå Ç#·\e\13I\1c½a×H²fÚ»a¹#jE̹AÅs²Q#Ò vd¢àÀÃR¶H\92¹!\87\14NJíT\81"ß©\97"¹A&Øn¨Ü
\ No newline at end of file
--- /dev/null
+Ô c\9c\91\82À¦írXS° ¦ì)Ô\9cºàZ¬Ú\9d_\9cÀÉ&j¦âÒ\1fzXÐh\80r\89ÁEþ\86\80Ôô\8dVæ\92 Ùª\96Y3\94 É#M8[Ôà\8b\18ÙÖàÃ\1cQ¸ãÔ 'esY\9eà ËRFåtÀ\r\fN$\8dÒ]z\9cÙÄ\80+!e#4êàÊÔ¾¹cr\80æã\964Ù\92ÀÄâ\8dÂãÒÝj\99\12RÀ¹\19\918\9b®\8034Q\ 2\85\84àoóÕW\97ðÀ¿\1aq\17%Ò\9ddÜâb@©]n=\1dÂ\80·\eªÉ\9dðà6ß\1f\18\96hÁ¹\1eù¶ÑÒa\87]Ùua\84\93i˶\83CJ\1dV8\9a\87c4Û\93È%¹D&Y1£ÁÔhr§0\83ìÉl\11èÒ\7fª(Ð\11Ù>\83M\8dRvÄÜ\83ÌÅ\1d\89¯\19Õkr_(\81hÔ\19f'v\7fé\ 3r&£}î7$\8eÁÚ\92¬È\98¥8çÕª\82\97p{ÍXÏMÈêy&\9a\99ï\86\8eÌá\18\81\86¯ñ\8eW¹m¶ÜÔè{\1fXwL¤Û\96pÞàé\bÎ(°gë-½DÒ'\eul&ì\9b\bÖÕh[_Ru(\88\15 1}éêô1P¼Ís+8ï¼\1d"á\ e\13lR;gÖ$K_aç\rj\95\ 6´Üo͸ð\9dX¥o+'\1c¾Ñ*Ù\fÜC¶(çÕ_LòØÛj±\14\89\17äÛ'\9a·p!ËÚÈ#\9dwî\8cì)\97\ 2N¹gÒ'¥ÙRÚ\89]Ò)ØÛÚDÆìÜD£ÜCÖÕÊ[pîB\14\1au+ÛÒcdâ\12è\83[e\r¨ÜÚ \9dIéãÊ¡E]¯!¤Ü¢7ô\92ÅTÓb|!RîÁ#Y$ó\13ª@è#G\1aäì\ 14}\92\96Ô\8cÀþ]ÏYjÒ\9ec-Q¡D\8cR\bÌêÁ'#i©!n ¥Ý\919\98r «j\15ØÓ×"r\9d\18qA;\19\ 5J\14ÇéÉ\18Ö¤ÜzãFZq )\88ű\eM),ØëpLêØ\84*\e\920ܤg9´íéfÆÝÛ\9ew\9bZÅ+¨\83©\12Ùê\9a\11)QHF*\85º\9b¶¥<¾\93»\7fRÇEäv;\\9eÆG(}(\8cÙkiÉhÌ\85ú\9d:\19\1ddÅæ-MªãÈÃI\15\96Úü«dz£¶\80*Ö_$2à\98\832\f)Ûv\80ãzÒ)¤¥ë\88¹m\9f\97\1e\97ã\81\ 5̽ýÖ^<öà\83dÌ\1c©3u\8aʰ[M½´\8e\84!\9d¡Y\7f\9dF\8b\1a*íhÕ]E·\10è&\19\e\ 1¦Ò£g©Z\92X\8fì\ 4\8d\9a\ 6&Ê¥%8T\16-¤ÕÝE;\10î\84\15\95\80£$¥ä:\riÁ}Vä\81nnI\9a©ÃË-çTãÖ^<û\18ð¦z\84u\96\94«å¸â\8eË7«Go[®Ç#©ÄE"x8\9cÕ\eM{\18©DGõ\8e´\95\9eBÏYl(R«EÈë\91¾¬¯ÇÇ#r63Ô\1dmþ\98¦\84\94Nµ¡\vR¢\99£\8b\\f°ã\96å»ú\9d°Â\88Lq¶ÐÒ'Ŭ\9aª\83 RjˬÂa\15\bn×[¦á»+u[îî\ 1s:¨8¥Ò#\8e䢧dÉ\ 5ª'$VBÚ\1d±¶í¾!WXMÄ\1c´¡Md\ 2ÇZÒßn]baÃI\12j\v\14P\83'/Ö¦å¯!Å,±\9d\ 6ª¢\1aÜzé\18Ò\9fe]"«b\17Q)¨\98«A)«eÍ\16fáO\1aRT\98«`³\93UªÞÒ\1ft¥"ØÁK"\8c\91e¦A&Í\8e¨ãä\80Ù\9d¹ÉXà\80©\1a\91ªóÑÝK%\19\9aÀ¥*\96+\1fÞ\80Çd²ÌSTÀÆ)²"e¾À¶\9cnC+
\ No newline at end of file
--- /dev/null
+ÓßiÕ\12\86 Ìé·H\9cbà¶ÛvÃ³Ì ÉÓ\8dʤnÀDáX¢oÓ]z\19"´ X$9»S¢ <[s(ÂêÀ©!vEUvÀ\ 4\v}\92!Ó\1fj¡\1a¦@\97\1c\92ÈüR \1dYWú+\\80\16<ËÂp\82À3\13®\88ÚÒ]zY\19À \97*Q·+æ\80Ù,§K\1ezÀÔQ\16äh §\ev,ãÔß\82`ÙÖ\80É£\89Ç$´\80ÖÔ\8cÈ¢fÀ\14ô«¥\96\80à¦ËÒ8ªÕ¡z\1dYÐÀ§aÑÉ"r`ß\9b\97Y\1d¨ÀÄÜ\94í$\9a\80%\13\8aI\8aÖé\9bebæÁFÒQ"\8dñâÉ&·Ë¶_ËÈͶAdRH1«M"\9aÓ1\9b\9e:ZD\90@)5$êCW[\92ý¾YÌG#m¹/aÎÚ$dÔ\95ÓðºÚ=]°9¨\91É$sÎ\b"³÷+q\10¥Yn\v\1cqïC[v%ÈÔ¯ÃÕ½qϸ/\9c9\eqQ8!\8dM \82\90äâ¡0*qñVã¦8¨ÔñÃ\99üoväÊÖ'mP7L))/o\8dQ[ò*\9dmí9'\86¨ÙÔò»Ùûo\10º#V]Emm3\98¦%¯ÛÌÃÝÆ×>mÎÇ\1c\8\9cÔó³Ö:×ñÅ\1cm7ÏÙ\8e\ 6ê\94Û"o©\82ÀÔ¦ØÛQÆäÐHÜÔ±¼\1a;oÌ\8eW\ f*ém\ eÔêf1
+oÌM\ fn·cÝé*4·å/Ôî¼\19ºq\ f
+¤J)/q\8f\ 1zr«#áðÇ#\177#ß3¼*n=¢Õ2³\99»\82ð, ©\ 6ÜãpíÃÍÆés®X£²N[u\rhÆ\b"\9fÕ1´R:u¯22\86«Uu®\99S\8etów,ÁaM#/í1Q]MGEÕ2\9bÚyï©Ê*ã\ 3\92{\8byùr9|{#â\8dÈ¢}Î'^\8d\1e\9bÕq\94\1az¾ÑÖÚ\ 2\ 5'}1oErDì\81O¶¡¯ùDÆÑI[l\10\98Õm\94\1ey\81ÆL¢Öª\87®È4ÍU\13ÂÒqdP¦ë\89ÐH¢\8dÇ\84Õ\83ÞùزÉ#u&Ã\8e\90Á,þ\19"\91N«\9dQ\86\14\90ÐËõ\12G#Õª\84"¸\970FÜc'$\95\r} µÇ#\97o6à\91·%\9b*Îu¬F\93Õèt\1a¸\9b«Èúm\f\83ÜÌñ}*\90f\9fjº¤²]| \8atdhºÌÖ§[Ò¹¡ë3K©6\85æ\8czg\84ÈK£IxsÌ\e\1fê\8co\15\91\85PÕfKÞù§ÌVèÉ\e¦¦}Nµ\ 5¬ ©5u\ 68\1c«H{Zj7\13ÔçBÚ+®ªÈÛlW\1c°Ä\8c";5¦b¯Ý@ä\ eÄc\am¾»\ 2Ô"R\1d£ª£ÚÌ\8dM\18²\81ä}\8bÛìÊ\81êâ\ eÇ ðaÖ%lZÔ\1fk!\19¢ÁX¬Q\15zæàÜåw\95w¸\81,¬-Õ+ðÁD\9c\ 66ÛÖ"z\1d"Tá\q\8d%"|ÁY5\8eË'²¡Y'D\9b\8eTÂI\e±O\1cÕ¤Z\15\1a\96ÂfÓq\87ª\88á,ÛÙ®ÈÁgb¥ÛêÄA\rMS$Ô%QÙá\8c¡\97CmÈ¥Î\80¨\85½ÌÎA[\1cj<Ìî ö\8am\91\vÓ!i\1a\1aàà¤Ó1,\99ê\81f]\8aY[\9c Õ5\8e&Ô´ ¢Ó\88È£Ó\1fj]Ùê \ 6ÊÖÈóÚàÖô\8eYcØàèä\8eW[\8e\80»
+\90»cÒ]z\98âR Ø\93\92ÉT¸\80É${VìÔÀ*«CÌ¡`Ç\rn'\eÒ\9ds\19\19bà·\13i½\14àÀë#\8eG,\94\80lå\95rãÚ\80¦¥\8d\9aã
\ No newline at end of file
--- /dev/null
+Ô\1fEP\99\88áZüSÉ]\9fÁX|ûÔÁ|\81\92Lo39\A\1fT-»VÒfr¡Y\84¡Í;2|V\ 1¶\1cN6ÓÊ\80\96fGH\91ðA(Ý\11i
+Ò§zÙ\1a\97!)a\82]mRàó+u£
+\94Á"e$Kk\94¡Eâ\9beÒé\8b\95Q\84\81\19\8aJÙ¬p¡\87\1cQ4\9b\90ÀÃ\14fÁ%ÐÁ\ÜV\1aëÒh\83\15\1aQ!dmrL¯láj[°Ûu\96¡8S$Z\9dXÁPl)ZûÒ£e¨cQ\ 1\7f\14e+Û·By%j¬Ê[#:Ý\9c\99k\7fâÈ\8aq\1fôÒ¤vlë¯%\12¤N1¢o&×|rÄ¢oC² OÖ\1cjÄ\96l)ÑoÒç\85-ªoÃ÷££K\1cáÊ©\7f\8eT\9ck-µ$vÖ$kÌ:\\8d\87´Ó©lz°iMÑuNÅ\14k-;wU²ãi¬ L\8e££iéÌ\9c\95Y\98Ôh\êøijyCÔ&×ÏìkD¤¸Ùg,\9a\95ñKKÍ\89\96ÂèK*Óèm£(g v\95\fѦeNÑ$jµ\1aË\rƾ\11ÆâËMÔå\97¶ÛÔhTâðË-4,R×z|«(È\ 6\83®gLÌ\em4ãkHù5¿'#Ô¦Ri°Ê)\ 4ÒNÍkæE\88À\12§µÞ\ 6\876MÆäÀ¤=b¿m|Õ\1ei¬¨»CH\1eÄù1\8c¦G\16ñ'nÀ\84§Ü©i¢Ê£½\e.ôkÕ\1eid©®ã!\9b\9ct±¯$» Ù¯%x¥\17
+\94\ 4«xã&\92\b%\8bÖ\1a\80è°Êã\99\8cÆtdâäËAÕ¤"Ö¤2!(ý\99%\aÓvÇkÓãsq¸ge.Uy´oɨ;mwî\9afÌ8\82mI7£)&#\95\15\13Ó%e*øv¬³-Ø9"vêG¡\ e)/}È\ eê,Úä\81\ 5\92ÿ\97äÓÓãL*x¦È\16ã¥\80ö\82¨XàSf\9a\84Æ3CWKiª\87Ðãr¶\92Óßbqû\87\ 5È\ eæ\e\89\aXÃU(\95\8e§Ú¦iÎÔ\8e\85Ï\92.Ô\9dÒb£¬êrD}*2©\15\8cCµH\89Ù,î"¹X\95\16âîâ"\82\85:õÓ\9e«m\9aV»ã)GBR\82U]\ 2r¤ÄAú×\8aÃ\14±".f**ØZ\83iâÜ¢\e\96 û»©#éîÎJßë¬1$m¶Û\84¥É7y¥CÚ\94q&\19°e´\9d\9a|öêF\v\15Ù¡\18ê§§Dm)¥z\85òP\8d(§ÕZ\82r-pÉÙ\1f9+¿IÆÛ\rÆî\8f\bîãZƤ\96ÈÙ\ 3ÍÄïÓ£m.q\99\ 6l-Ñ<ä\92å`j.ýÿP¤\88V·[\97FI\93¶îÌÓ¤mjñhÊHãnDÄ\97%!T¶êÄ\98ÅÅ$ÉÔ\84\97Ä\15\1d¶¬\eÒæ}æ°\9a\86¦£m±\19\9a£#¯;&\9fgÊÛmsZ\9d$ù¬íÆÓÒæefø ¢\1e*\10\82Á\9cÃNüÅúËÝE\15\e\88x\9b ãÍä¨ÜÓ¤c©ù¤Ä:ì\96\a¬¥Å)LòµmP¢â«ÑÁ\1d¨Â\9a\1f²èBÔ_\99i¢Üâ \ 5Qr\98±C½kù¹S¼Â\91EoFêÀ\82\1d\8af¦Á×\15\81éSÒæ\90U´¦èã[4\97ÉfÕ$Tü2\98Ä^¥K\19rF+Õ\98aìár\84$ÁáB¡R¢¶õ\8aüŦ¤MÕmÚßê£h\981DÑÖX\92mazâ¥"mú\ e©#\a·w\86)Tæ1ê\83âÜ\84\84ZÃ\81°Ò×\94zeS¼"¶ÖXû\9aà\ 3M\19\94{\1c°äX5\93Å\80¥Mc\88ÛAÔØ\9a%[Z\ 2³\19\84\8cÓ\8a\82k%tlé®Ã5\z©°°ã'ýy(eÔ"¢a[¦¢Áj\f«\ 4T¡ù]N7ï¤\82PêºidÔ\82Ke\8e
+©Õ\1d\93Y\13ÜÁñ&¯^å\92ú§¬6ÛjáÙ\16Lßf\84\83×\18±6Û
\ No newline at end of file
--- /dev/null
+Òd\8a\94ÛÆÀë\9fB6ëªàËq4T\8eR \96©\r\88\93 ¡ZÝwUMÒ¤\83Ù\e¼ÁE\vü\95k\`§\9e\86êçÂÁ9\94\92)ÔÌÁÄØi©%Ñ,Ö!#kCÛîIÇ\etÂ&YjÚÁ¨ÃØ\85U*ä\82»;ÅÌ¡ÑiÍ¥ªÂ¨\©Íþ\9e¢àät´\f\83\ 3%\rsÀ®lâw\89é£&ÑéÃõ°}"ÔÊd\18\9ayb±\12ÿH^{C%$m¸þy§ºä\8cz\92Ó(\84vøs*6ÜÞ\84ËsÌ\92Ûh»|s¬\1f©7ÆÞql;l\1e¢õÔ(\8c.¸áÌ\954Sk\ 2r¬ÔÐa0\1eo\8cKÞV>ìoªh¦\9c\87\ 3Ó«\84v¹q®W\14eË\98oJtM\89¨\81Ý.]âñ«cmÌ[Ø\86@ÊÓk¤®©ÛLD\8cE¡îmÌI6vþöÛ.7í=Y#Û\8cú¥\8d\18 Óª\9cn¹Ü°\ 6\92P4_o\rÜ~³wvq\8cÛ÷7\14á\fåt%\85öÓé£îzs\fäÃI¼ÓåO·½V¶äw«nÙ\81$Ñë0FÔr8\12Ó+¬®iñ\f\83¼15\1d}lË\16zyQ\94\90ÔÔrHØ\81\r¨k±\86ÛÓ,\94n±\84«õLHH\9e\85\vß\97½ÉU\8cÍÈ,f[PÚɪin\ 2 Ò줪r\8f\v\ 6B··ìâKÉ¥\95¡\13\91 ¯m¸×Tä©}K\f \89Òp«n¢\93&}ëª'5\8e§Ã\15è¥\96§T=\80ªÙæÉ4ÄqËýÒn²®ª\95d\8cé˺\1f\94èDÔvûW°¦F\v\99\90È\95E\96*Ï©,Ònâê#\95#G-¿3\18\95%´åú\86²\93\ 4âéa\84\8a\95\ 5;;¢¸\8dÒpÓi¬\8cÄ÷Zm\975\93¥éæn¶\9a\8fH0ÒJ(ì\91$Pr\8a8\8fÒj³àì\90æ4ä¾]\16®£¨\8b\96\80[\8e\87Æä\92Q\sc!bD\19\10Ò/kR*\88£é»\ eF©\8c¡SS§\85&ì\82&å\12(\91ð"»$·ÒåÒe\9c\9d\1a\86áĤp\13,\8c¡ÀñI¬ä\82ÁYúêùs\9a!i\93M6\11Ò%\93X\ª¡\94ÃFÒÍî¡M\15·4Þ|ÀïYñ'=à`\92S°¦\99Ò!¢\99\92ê\816ÞE·ZjÁWl½Ç5\áÇ\1amÀ\9bRÁY)a5\1dÒf¢\95\91z¡N¦Ö%]j¡)_Õ\99Ô®ÁE\1e\91=\f\88á+q.K\vÑb\9bY\e³Aº£\91·Ý ¡5²UÆÜ\90`à \b¦ÜÔ\80Õ¬zØMÑâ\92\99Z\83as-Ë8Û\8e\81x]®fÛ|Á´#L¥\1aªÁI\ e±Û,Ñ"\92\99\9a¤\81)4uØ,ÌÁ$ÜQ\89\e\9a Æår=òÐ\81i²È¹,Ѥ\82ØÒðá £\85ÆdcA5\1a/ZSÈ Êô\92=[¦ái*±ÆìÑe\8a\90Ûð!HÚ\8aÇ\10ºàºYJV\8e §\16iæÝ¬ÁH¯Î«ZÑer\91"pÁ<Ø\8cÅK¸Á'\ 4ÉÃ#RÁLæ\89i5àÀù\9f±¬áÒ©r\10\93ÜáY\11aJá`¡3c*\14\8b\88ÁÞÝS/vdÁ$´É\r'Ô*«YRÖ¡¥znKÄpÁ\86"\10@\9dVâ\fÓ°¶ÁpÁ±/E\18ó
\ No newline at end of file