mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-05 20:20:07 +00:00
Merge sms stuff and move to "utils" subdir (bug #2973)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4429 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
2
Makefile
2
Makefile
@@ -187,7 +187,7 @@ CFLAGS+= $(MALLOC_DEBUG)
|
||||
CFLAGS+= $(BUSYDETECT)
|
||||
CFLAGS+= $(OPTIONS)
|
||||
CFLAGS+=# -fomit-frame-pointer
|
||||
SUBDIRS=res channels pbx apps codecs formats agi cdr astman stdtime
|
||||
SUBDIRS=res channels pbx apps codecs formats agi cdr utils stdtime
|
||||
ifeq (${OSARCH},Linux)
|
||||
LIBS=-ldl -lpthread
|
||||
endif
|
||||
|
2346
apps/app_sms.c
2346
apps/app_sms.c
File diff suppressed because it is too large
Load Diff
683
astman/astman.c
683
astman/astman.c
@@ -1,683 +0,0 @@
|
||||
/*
|
||||
* ASTerisk MANager
|
||||
* Copyright (C) 2002, Linux Support Services, Inc.
|
||||
*
|
||||
* Distributed under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <newt.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <asterisk/md5.h>
|
||||
#include <asterisk/manager.h>
|
||||
|
||||
#undef gethostbyname
|
||||
|
||||
#define MAX_HEADERS 80
|
||||
#define MAX_LEN 256
|
||||
|
||||
static struct ast_mansession {
|
||||
struct sockaddr_in sin;
|
||||
int fd;
|
||||
char inbuf[MAX_LEN];
|
||||
int inlen;
|
||||
} session;
|
||||
|
||||
static struct ast_chan {
|
||||
char name[80];
|
||||
char exten[20];
|
||||
char context[20];
|
||||
char priority[20];
|
||||
char callerid[40];
|
||||
char state[10];
|
||||
struct ast_chan *next;
|
||||
} *chans;
|
||||
|
||||
static struct ast_chan *find_chan(char *name)
|
||||
{
|
||||
struct ast_chan *prev = NULL, *chan = chans;
|
||||
while(chan) {
|
||||
if (!strcmp(name, chan->name))
|
||||
return chan;
|
||||
prev = chan;
|
||||
chan = chan->next;
|
||||
}
|
||||
chan = malloc(sizeof(struct ast_chan));
|
||||
if (chan) {
|
||||
memset(chan, 0, sizeof(struct ast_chan));
|
||||
strncpy(chan->name, name, sizeof(chan->name) - 1);
|
||||
if (prev)
|
||||
prev->next = chan;
|
||||
else
|
||||
chans = chan;
|
||||
}
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void del_chan(char *name)
|
||||
{
|
||||
struct ast_chan *prev = NULL, *chan = chans;
|
||||
while(chan) {
|
||||
if (!strcmp(name, chan->name)) {
|
||||
if (prev)
|
||||
prev->next = chan->next;
|
||||
else
|
||||
chans = chan->next;
|
||||
return;
|
||||
}
|
||||
prev = chan;
|
||||
chan = chan->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void fdprintf(int fd, char *fmt, ...)
|
||||
{
|
||||
char stuff[4096];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(stuff, sizeof(stuff), fmt, ap);
|
||||
va_end(ap);
|
||||
write(fd, stuff, strlen(stuff));
|
||||
}
|
||||
|
||||
static char *get_header(struct message *m, char *var)
|
||||
{
|
||||
char cmp[80];
|
||||
int x;
|
||||
snprintf(cmp, sizeof(cmp), "%s: ", var);
|
||||
for (x=0;x<m->hdrcount;x++)
|
||||
if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
|
||||
return m->headers[x] + strlen(cmp);
|
||||
return "";
|
||||
}
|
||||
|
||||
static int event_newstate(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
chan = find_chan(get_header(m, "Channel"));
|
||||
strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_newexten(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
chan = find_chan(get_header(m, "Channel"));
|
||||
strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
|
||||
strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
|
||||
strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_newchannel(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
chan = find_chan(get_header(m, "Channel"));
|
||||
strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
|
||||
strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_status(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
chan = find_chan(get_header(m, "Channel"));
|
||||
strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
|
||||
strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
|
||||
strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
|
||||
strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
|
||||
strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_hangup(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
del_chan(get_header(m, "Channel"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_ignore(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_rename(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
chan = find_chan(get_header(m, "Oldname"));
|
||||
strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
|
||||
return 0;
|
||||
}
|
||||
static struct event {
|
||||
char *event;
|
||||
int (*func)(struct ast_mansession *s, struct message *m);
|
||||
} events[] = {
|
||||
{ "Newstate", event_newstate },
|
||||
{ "Newchannel", event_newchannel },
|
||||
{ "Newexten", event_newexten },
|
||||
{ "Hangup", event_hangup },
|
||||
{ "Rename", event_rename },
|
||||
{ "Status", event_status },
|
||||
{ "Link", event_ignore },
|
||||
{ "Unlink", event_ignore },
|
||||
{ "StatusComplete", event_ignore }
|
||||
};
|
||||
|
||||
static int process_message(struct ast_mansession *s, struct message *m)
|
||||
{
|
||||
int x;
|
||||
char event[80] = "";
|
||||
strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
|
||||
if (!strlen(event)) {
|
||||
fprintf(stderr, "Missing event in request");
|
||||
return 0;
|
||||
}
|
||||
for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
|
||||
if (!strcasecmp(event, events[x].event)) {
|
||||
if (events[x].func(s, m))
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (x >= sizeof(events) / sizeof(events[0]))
|
||||
fprintf(stderr, "Ignoring unknown event '%s'", event);
|
||||
#if 0
|
||||
for (x=0;x<m->hdrcount;x++) {
|
||||
printf("Header: %s\n", m->headers[x]);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rebuild_channels(newtComponent c)
|
||||
{
|
||||
void *prev = NULL;
|
||||
struct ast_chan *chan;
|
||||
char tmpn[42];
|
||||
char tmp[256];
|
||||
int x=0;
|
||||
prev = newtListboxGetCurrent(c);
|
||||
newtListboxClear(c);
|
||||
chan = chans;
|
||||
while(chan) {
|
||||
snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
|
||||
if (strlen(chan->exten))
|
||||
snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
|
||||
tmpn, chan->state,
|
||||
chan->exten, chan->context, chan->priority);
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%-30s %8s",
|
||||
tmpn, chan->state);
|
||||
newtListboxAppendEntry(c, tmp, chan);
|
||||
x++;
|
||||
chan = chan->next;
|
||||
}
|
||||
if (!x)
|
||||
newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
|
||||
newtListboxSetCurrentByKey(c, prev);
|
||||
}
|
||||
|
||||
static int has_input(struct ast_mansession *s)
|
||||
{
|
||||
int x;
|
||||
for (x=1;x<s->inlen;x++)
|
||||
if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_input(struct ast_mansession *s, char *output)
|
||||
{
|
||||
/* output must have at least sizeof(s->inbuf) space */
|
||||
int res;
|
||||
int x;
|
||||
struct timeval tv = {0, 0};
|
||||
fd_set fds;
|
||||
for (x=1;x<s->inlen;x++) {
|
||||
if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
|
||||
/* Copy output data up to and including \r\n */
|
||||
memcpy(output, s->inbuf, x + 1);
|
||||
/* Add trailing \0 */
|
||||
output[x+1] = '\0';
|
||||
/* Move remaining data back to the front */
|
||||
memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
|
||||
s->inlen -= (x + 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (s->inlen >= sizeof(s->inbuf) - 1) {
|
||||
fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
|
||||
s->inlen = 0;
|
||||
}
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(s->fd, &fds);
|
||||
res = select(s->fd + 1, &fds, NULL, NULL, &tv);
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "Select returned error: %s\n", strerror(errno));
|
||||
} else if (res > 0) {
|
||||
res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
|
||||
if (res < 1)
|
||||
return -1;
|
||||
s->inlen += res;
|
||||
s->inbuf[s->inlen] = '\0';
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_check(struct ast_mansession *s, struct message **mout)
|
||||
{
|
||||
static struct message m;
|
||||
int res;
|
||||
|
||||
if (mout)
|
||||
*mout = NULL;
|
||||
|
||||
for(;;) {
|
||||
res = get_input(s, m.headers[m.hdrcount]);
|
||||
if (res == 1) {
|
||||
#if 0
|
||||
fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
|
||||
fgetc(stdin);
|
||||
#endif
|
||||
/* Strip trailing \r\n */
|
||||
if (strlen(m.headers[m.hdrcount]) < 2)
|
||||
continue;
|
||||
m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
|
||||
if (!strlen(m.headers[m.hdrcount])) {
|
||||
if (mout && strlen(get_header(&m, "Response"))) {
|
||||
*mout = &m;
|
||||
return 0;
|
||||
}
|
||||
if (process_message(s, &m))
|
||||
break;
|
||||
memset(&m, 0, sizeof(&m));
|
||||
} else if (m.hdrcount < MAX_HEADERS - 1)
|
||||
m.hdrcount++;
|
||||
} else if (res < 0) {
|
||||
return -1;
|
||||
} else if (res == 2)
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct message *wait_for_response(int timeout)
|
||||
{
|
||||
struct message *m;
|
||||
struct timeval tv;
|
||||
int res;
|
||||
fd_set fds;
|
||||
for (;;) {
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
FD_SET(session.fd, &fds);
|
||||
res = select(session.fd + 1, &fds, NULL, NULL, &tv);
|
||||
if (res < 1)
|
||||
break;
|
||||
if (input_check(&session, &m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (m)
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int manager_action(char *action, char *fmt, ...)
|
||||
{
|
||||
struct ast_mansession *s;
|
||||
char tmp[4096];
|
||||
va_list ap;
|
||||
|
||||
s = &session;
|
||||
fdprintf(s->fd, "Action: %s\r\n", action);
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
||||
va_end(ap);
|
||||
write(s->fd, tmp, strlen(tmp));
|
||||
fdprintf(s->fd, "\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_message(char *title, char *msg)
|
||||
{
|
||||
newtComponent form;
|
||||
newtComponent label;
|
||||
newtComponent ok;
|
||||
struct newtExitStruct es;
|
||||
|
||||
newtCenteredWindow(60,7, title);
|
||||
|
||||
label = newtLabel(4,1,msg);
|
||||
ok = newtButton(27, 3, "OK");
|
||||
form = newtForm(NULL, NULL, 0);
|
||||
newtFormAddComponents(form, label, ok, NULL);
|
||||
newtFormRun(form, &es);
|
||||
newtPopWindow();
|
||||
newtFormDestroy(form);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static newtComponent showform;
|
||||
static int show_doing(char *title, char *tmp)
|
||||
{
|
||||
struct newtExitStruct es;
|
||||
newtComponent label;
|
||||
showform = newtForm(NULL, NULL, 0);
|
||||
newtCenteredWindow(70,4, title);
|
||||
label = newtLabel(3,1,tmp);
|
||||
newtFormAddComponents(showform,label, NULL);
|
||||
newtFormSetTimer(showform, 200);
|
||||
newtFormRun(showform, &es);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hide_doing(void)
|
||||
{
|
||||
newtPopWindow();
|
||||
newtFormDestroy(showform);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void try_status(void)
|
||||
{
|
||||
struct message *m;
|
||||
manager_action("Status", "");
|
||||
m = wait_for_response(10000);
|
||||
if (!m) {
|
||||
show_message("Status Failed", "Timeout waiting for response");
|
||||
} else if (strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
show_message("Status Failed Failed", get_header(m, "Message"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void try_hangup(newtComponent c)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
struct message *m;
|
||||
|
||||
chan = newtListboxGetCurrent(c);
|
||||
if (chan) {
|
||||
manager_action("Hangup", "Channel: %s\r\n", chan->name);
|
||||
m = wait_for_response(10000);
|
||||
if (!m) {
|
||||
show_message("Hangup Failed", "Timeout waiting for response");
|
||||
} else if (strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
show_message("Hangup Failed", get_header(m, "Message"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int get_user_input(char *msg, char *buf, int buflen)
|
||||
{
|
||||
newtComponent form;
|
||||
newtComponent ok;
|
||||
newtComponent cancel;
|
||||
newtComponent inpfield;
|
||||
const char *input;
|
||||
int res = -1;
|
||||
struct newtExitStruct es;
|
||||
|
||||
newtCenteredWindow(60,7, msg);
|
||||
|
||||
inpfield = newtEntry(5, 2, "", 50, &input, 0);
|
||||
ok = newtButton(22, 3, "OK");
|
||||
cancel = newtButton(32, 3, "Cancel");
|
||||
form = newtForm(NULL, NULL, 0);
|
||||
newtFormAddComponents(form, inpfield, ok, cancel, NULL);
|
||||
newtFormRun(form, &es);
|
||||
strncpy(buf, input, buflen - 1);
|
||||
if (es.u.co == ok)
|
||||
res = 0;
|
||||
else
|
||||
res = -1;
|
||||
newtPopWindow();
|
||||
newtFormDestroy(form);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void try_redirect(newtComponent c)
|
||||
{
|
||||
struct ast_chan *chan;
|
||||
char dest[256];
|
||||
struct message *m;
|
||||
char channame[256];
|
||||
char tmp[80];
|
||||
char *context;
|
||||
|
||||
chan = newtListboxGetCurrent(c);
|
||||
if (chan) {
|
||||
strncpy(channame, chan->name, sizeof(channame) - 1);
|
||||
snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
|
||||
if (get_user_input(tmp, dest, sizeof(dest)))
|
||||
return;
|
||||
if ((context = strchr(dest, '@'))) {
|
||||
*context = '\0';
|
||||
context++;
|
||||
manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
|
||||
} else {
|
||||
manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
|
||||
}
|
||||
m = wait_for_response(10000);
|
||||
if (!m) {
|
||||
show_message("Hangup Failed", "Timeout waiting for response");
|
||||
} else if (strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
show_message("Hangup Failed", get_header(m, "Message"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int manage_calls(char *host)
|
||||
{
|
||||
newtComponent form;
|
||||
newtComponent quit;
|
||||
newtComponent hangup;
|
||||
newtComponent redirect;
|
||||
newtComponent channels;
|
||||
struct newtExitStruct es;
|
||||
char tmp[80];
|
||||
|
||||
/* If there's one thing you learn from this code, it is this...
|
||||
Never, ever fly Air France. Their customer service is absolutely
|
||||
the worst. I've never heard the words "That's not my problem" as
|
||||
many times as I have from their staff -- It should, without doubt
|
||||
be their corporate motto if it isn't already. Don't bother giving
|
||||
them business because you're just a pain in their side and they
|
||||
will be sure to let you know the first time you speak to them.
|
||||
|
||||
If you ever want to make me happy just tell me that you, too, will
|
||||
never fly Air France again either (in spite of their excellent
|
||||
cuisine). */
|
||||
snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
|
||||
newtCenteredWindow(74, 20, tmp);
|
||||
form = newtForm(NULL, NULL, 0);
|
||||
newtFormWatchFd(form, session.fd, NEWT_FD_READ);
|
||||
newtFormSetTimer(form, 100);
|
||||
quit = newtButton(62, 16, "Quit");
|
||||
redirect = newtButton(35, 16, "Redirect");
|
||||
hangup = newtButton(50, 16, "Hangup");
|
||||
channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
|
||||
newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
|
||||
newtListboxSetWidth(channels, 72);
|
||||
|
||||
show_doing("Getting Status", "Retrieving system status...");
|
||||
try_status();
|
||||
hide_doing();
|
||||
|
||||
for(;;) {
|
||||
newtFormRun(form, &es);
|
||||
if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
|
||||
if (input_check(&session, NULL)) {
|
||||
show_message("Disconnected", "Disconnected from remote host");
|
||||
break;
|
||||
}
|
||||
} else if (es.reason == NEWT_EXIT_COMPONENT) {
|
||||
if (es.u.co == quit)
|
||||
break;
|
||||
if (es.u.co == hangup) {
|
||||
try_hangup(channels);
|
||||
} else if (es.u.co == redirect) {
|
||||
try_redirect(channels);
|
||||
}
|
||||
}
|
||||
rebuild_channels(channels);
|
||||
}
|
||||
newtFormDestroy(form);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int login(char *hostname)
|
||||
{
|
||||
newtComponent form;
|
||||
newtComponent cancel;
|
||||
newtComponent login;
|
||||
newtComponent username;
|
||||
newtComponent password;
|
||||
newtComponent label;
|
||||
newtComponent ulabel;
|
||||
newtComponent plabel;
|
||||
const char *user;
|
||||
const char *pass;
|
||||
struct message *m;
|
||||
struct newtExitStruct es;
|
||||
char tmp[55];
|
||||
struct hostent *hp;
|
||||
int res = -1;
|
||||
|
||||
session.fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (session.fd < 0) {
|
||||
snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
|
||||
show_message("Socket failed", tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
|
||||
show_doing("Connecting....", tmp);
|
||||
|
||||
|
||||
hp = gethostbyname(hostname);
|
||||
if (!hp) {
|
||||
snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
|
||||
show_message("Host lookup failed", tmp);
|
||||
return -1;
|
||||
}
|
||||
hide_doing();
|
||||
snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
|
||||
show_doing("Connecting...", tmp);
|
||||
|
||||
session.sin.sin_family = AF_INET;
|
||||
session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
|
||||
memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
|
||||
|
||||
if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
|
||||
snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
|
||||
show_message("Connect Failed", tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hide_doing();
|
||||
|
||||
login = newtButton(5, 6, "Login");
|
||||
cancel = newtButton(25, 6, "Cancel");
|
||||
newtCenteredWindow(40, 10, "Asterisk Manager Login");
|
||||
snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
|
||||
label = newtLabel(4,1, tmp);
|
||||
|
||||
ulabel = newtLabel(4,2,"Username:");
|
||||
plabel = newtLabel(4,3,"Password:");
|
||||
|
||||
username = newtEntry(14, 2, "", 20, &user, 0);
|
||||
password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN);
|
||||
|
||||
form = newtForm(NULL, NULL, 0);
|
||||
newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
|
||||
newtFormRun(form, &es);
|
||||
if (es.reason == NEWT_EXIT_COMPONENT) {
|
||||
if (es.u.co == login) {
|
||||
snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
|
||||
show_doing("Logging in", tmp);
|
||||
/* Check to see if the remote host supports MD5 Authentication */
|
||||
manager_action("Challenge", "AuthType: MD5\r\n");
|
||||
m = wait_for_response(10000);
|
||||
if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
char *challenge = get_header(m, "Challenge");
|
||||
int x;
|
||||
int len = 0;
|
||||
char md5key[256] = "";
|
||||
struct MD5Context md5;
|
||||
unsigned char digest[16];
|
||||
MD5Init(&md5);
|
||||
MD5Update(&md5, challenge, strlen(challenge));
|
||||
MD5Update(&md5, pass, strlen(pass));
|
||||
MD5Final(digest, &md5);
|
||||
for (x=0; x<16; x++)
|
||||
len += sprintf(md5key + len, "%2.2x", digest[x]);
|
||||
manager_action("Login",
|
||||
"AuthType: MD5\r\n"
|
||||
"Username: %s\r\n"
|
||||
"Key: %s\r\n",
|
||||
user, md5key);
|
||||
m = wait_for_response(10000);
|
||||
hide_doing();
|
||||
if (!strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
res = 0;
|
||||
} else {
|
||||
show_message("Login Failed", get_header(m, "Message"));
|
||||
}
|
||||
} else {
|
||||
memset(m, 0, sizeof(m));
|
||||
manager_action("Login",
|
||||
"Username: %s\r\n"
|
||||
"Secret: %s\r\n",
|
||||
user, pass);
|
||||
m = wait_for_response(10000);
|
||||
hide_doing();
|
||||
if (m) {
|
||||
if (!strcasecmp(get_header(m, "Response"), "Success")) {
|
||||
res = 0;
|
||||
} else {
|
||||
show_message("Login Failed", get_header(m, "Message"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newtFormDestroy(form);
|
||||
return res;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: astman <host>\n");
|
||||
exit(1);
|
||||
}
|
||||
newtInit();
|
||||
newtCls();
|
||||
newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
|
||||
newtPushHelpLine("Welcome to the Asterisk Manager!");
|
||||
if (login(argv[1])) {
|
||||
newtFinished();
|
||||
exit(1);
|
||||
}
|
||||
manage_calls(argv[1]);
|
||||
newtFinished();
|
||||
return 0;
|
||||
}
|
834
doc/app_sms.html
Executable file
834
doc/app_sms.html
Executable file
@@ -0,0 +1,834 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h1>* Application SMS</h1>
|
||||
The SMS module for asterisk was developed by Adrian Kennard, and is an
|
||||
implementation of the ETSI specification for landline SMS, ETSI ES 201
|
||||
912, which is available from www.etsi.org. Landline SMS is starting to
|
||||
be available in various parts of Europe, and is available from BT in
|
||||
the UK. However, asterisk would allow gateways to be created in other
|
||||
locations such as the US, and use of SMS capable phones such as the
|
||||
Magic Messenger. SMS works using analogue or ISDN lines.<br>
|
||||
<h2>Background</h2>
|
||||
Short Message Service (SMS), or <span style="font-style: italic;">texting</span>
|
||||
is very popular between mobile phones. A message can be sent between
|
||||
two phones, and normally contains 160 characters. There are ways in
|
||||
which various types of data can be encoded in a text message such as
|
||||
ring tones, and small graphic, etc. Text messaging is being used for
|
||||
voting and competitions, and also SPAM...<br>
|
||||
<br>
|
||||
Sending a message involves the mobile phone contacting a message centre
|
||||
(SMSC) and passing the message to it. The message centre then contacts
|
||||
the destination mobile to deliver the message. The SMSC is responsible
|
||||
for storing the message and trying to send it until the destination
|
||||
mobile is available, or a timeout.<br>
|
||||
<br>
|
||||
Landline SMS works in basically the same way. You would normally have a
|
||||
suitable text capable landline phone, or a separate texting box such as
|
||||
a Magic Messenger on your phone line. This sends a message to a message
|
||||
centre your telco provides by making a normal call and sending the data
|
||||
using 1200 Baud FSK signaling according to the ETSI spec. To receive a
|
||||
message the message centre calls the line with a specific calling
|
||||
number, and the text capable phone answers the call and receives the
|
||||
data using 1200 Baud FSK signaling. This works particularly well in the
|
||||
UK as the calling line identity is sent before the first ring, so no
|
||||
phones in the house would ring when a message arrives.<br>
|
||||
<h2>Typical use with asterisk</h2>
|
||||
Sending messages from an asterisk box can be used for a variety of
|
||||
reasons, including notification from any monitoring systems, email
|
||||
subject lines, etc.<br>
|
||||
Receiving messages to an asterisk box is typically used just to email
|
||||
the messages to someone appropriate - we email and texts that are
|
||||
received to our direct numbers to the appropriate person. Received
|
||||
messages could also be used to control applications, manage
|
||||
competitions, votes, post items to IRC, anything.<br>
|
||||
Using a terminal such as a magic messenger, an asterisk box could ask
|
||||
as a message centre sending messages to the terminal, which will beep
|
||||
and pop up the message (and remember 100 or so messages in its memory).<br>
|
||||
<h2>Terminology</h2>
|
||||
<table style="text-align: left;" border="1" cellpadding="2"
|
||||
cellspacing="2">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">SMS<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Short Message Service<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">i.e. text messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">SMSC<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Short Message Service Centre<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">The system responsible for
|
||||
storing and forwarding messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">MO<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Mobile Originated<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">A message on its way from a
|
||||
mobile or landline device to the SMSC<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">MT<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Mobile Terminated<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">A message on its way from the
|
||||
SMSC to the mobile or landline device<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">RX<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Receive<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">A message coming in to the
|
||||
asterisk box<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">TX<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Transmit<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">A message going out of the
|
||||
asterisk box<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Sub address</h2>
|
||||
When sending a message to a landline, you simply send to the landline
|
||||
number. In the UK, all of the mobile operators (bar one) understand
|
||||
sending messages to landlines and pass the messages to the BTText
|
||||
system for delivery to the landline.<br>
|
||||
<br>
|
||||
The specification for landline SMS allows for the possibility of more
|
||||
than one device on a single landline. These can be configured with <span
|
||||
style="font-style: italic;">Sub addresses</span> which are a single
|
||||
digit. To send a message to a specific device the message is sent to
|
||||
the landline number with an extra digit appended to the end. The telco
|
||||
can define a default sub address (9 in the UK) which is used when the
|
||||
extra digit is not appended to the end. When the call comes in, part of
|
||||
the calling line ID is the sub address, so that only one device on the
|
||||
line answers the call and receives the message.<br>
|
||||
<br>
|
||||
Sub addresses also work for outgoing messages. Part of the number
|
||||
called by the device to send a message is its sub address. Sending from
|
||||
the default sub address (9 in the UK) means the message is delivered
|
||||
with the <span style="font-style: italic;">sender </span>being the
|
||||
normal landline number. Sending from any other sub address makes the <span
|
||||
style="font-style: italic;">sender</span> the landline number with an
|
||||
extra digit on the end.<br>
|
||||
<br>
|
||||
Using asterisk, you can make use of the sub addresses for sending and
|
||||
receiving messages. Using DDI (DID, i.e. multiple numbers on the line
|
||||
on ISDN) you can also make use of many different numbers for SMS.<br>
|
||||
<h2>Build / installation</h2>
|
||||
<span style="font-weight: bold;">app_sms.c</span> is included in the
|
||||
latest cvs. It lives in the asterisk source <span
|
||||
style="font-weight: bold;">apps</span> directory and is included in
|
||||
the object list (<span style="font-weight: bold;">app_sms.so</span>) in
|
||||
<span style="font-weight: bold;">apps/Makefile</span>.<br>
|
||||
<span style="font-weight: bold;">smsq.c</span> is a stand alone helper
|
||||
application which is used to send SMSs from the command line. It uses
|
||||
the <span style="font-weight: bold;">popt</span> library. A line for
|
||||
your make file is:-<br>
|
||||
<pre>smsq: smsq.c<br> cc -O -o smsq smsq.c -lpopt<br></pre>
|
||||
<span style="font-family: monospace;"></span>
|
||||
<h2>extensions.conf</h2>
|
||||
The following contexts are recommended.<br>
|
||||
<pre>; Mobile Terminated, RX. This is used when an incoming call from the SMS arrives, with the queue (called number and sub address) in ${EXTEN}<br>; Running an app after receipt of the text allows the app to find all messages in the queue and handle them, e.g. email them.<br>; The app may be something like smsq --process=somecommand --queue=${EXTEN} to run a command for each received message<br>; See below for usage<br>[smsmtrx]<br>exten = _X.,1, SMS(${EXTEN}|a)<br>exten = _X.,2,System("someapptohandleincomingsms ${EXTEN}")<br>exten = _X.,3,Hangup<br><br>; Mobile originated, RX. This is receiving a message from a device, e.g. a Magic Messenger on a sip extension<br>; Running an app after receipt of the text allows the app to find all messages in the queue and handle then, e.g. sending them to the public SMSC<br>; The app may be something like smsq --process=somecommand --queue=${EXTEN} to run a command for each received message<br>; See below for example usage<br>[smsmorx]<br>exten = _X.,1, SMS(${EXTEN}|sa)<br>exten = _X.,2,System("someapptohandlelocalsms ${EXTEN}")<br>exten = _X.,3,Hangup<span
|
||||
style="font-family: sans-serif;"></span><span
|
||||
style="font-family: sans-serif;"></span></pre>
|
||||
<span style="font-family: sans-serif;"></span><span
|
||||
style="font-weight: bold;">smsmtrx</span> is normally accessed by an
|
||||
incoming call from the SMSC. In the UK this call is from a CLI of
|
||||
080058752X0 where X is the sub address. As such a typical usage in the
|
||||
extensions.conf at the point of handling an incoming call is:-<br>
|
||||
<pre>exten = _X./8005875290,1,Goto(smsmtrx,${EXTEN},1)<br>exten = _X./_80058752[0-8]0,1,Goto(smsmtrx,${EXTEN}-${CALLERIDNUM:8:1},1)<br></pre>
|
||||
Alternatively, if you have the correct national prefix on incoming CLI,
|
||||
e.g. using zaphfc, you might use:-<br>
|
||||
<pre>exten = _X./08005875290,1,Goto(smsmtrx,${EXTEN},1)<br>exten = _X./_080058752[0-8]0,1,Goto(smsmtrx,${EXTEN}-${CALLERIDNUM:9:1},1)</pre>
|
||||
<span style="font-weight: bold;">smsmorx</span> is normally accessed by
|
||||
a call from a local sip device connected to a Magic Messenger. It could
|
||||
however by that you are operating asterisk as a message centre for
|
||||
calls from outside. Either way, you look at the called number and goto
|
||||
smsmorx. In the UK, the SMSC number that would be dialed is 1709400X
|
||||
where X is the caller sub address. As such typical usage in
|
||||
extension.config at the point of handling a call from a sip phone is:-<br>
|
||||
<pre>exten = 17094009,1,Goto(smsmorx,${CALLERIDNUM},1)<br>exten = _1709400[0-8],1,Goto(smsmorx,${CALLERIDNUM}-{EXTEN:7:1},1)<br></pre>
|
||||
<h2>Using smsq</h2>
|
||||
<span style="font-weight: bold;">smsq</span> is a simple helper
|
||||
application designed to make it easy to send messages from a command
|
||||
line. it is intended to run on the asterisk box and have direct access
|
||||
to the queue directories for SMS and for asterisk.<br>
|
||||
<br>
|
||||
In its simplest form you can send an SMS by a command such as <br>
|
||||
<br>
|
||||
smsq 0123456789 This is a test to 0123456789<br>
|
||||
<br>
|
||||
This would create a queue file for a mobile originated TX message in
|
||||
queue 0 to send the text "This is a test to 0123456789" to 0123456789.
|
||||
It would then place a file in the /var/spool/asterisk/outgoing
|
||||
directory to initiate a call to 17094009 (the default message centre in
|
||||
smsq) attached to application SMS with argument of the queue name (0).<br>
|
||||
<br>
|
||||
Normally smsq will queue a message ready to send, and will then create
|
||||
a file in the asterisk outgoing directory causing asterisk to actually
|
||||
connect to the message centre or device and actually send the pending
|
||||
message(s).<br>
|
||||
<br>
|
||||
Using --process, smsq can however be used on received queues to run a
|
||||
command for each file (matching the queue if specified) with various
|
||||
environment variables set based on the message (see below);<br>
|
||||
<br>
|
||||
smsq options:-<br>
|
||||
<br>
|
||||
<table style="text-align: left;" border="1" cellpadding="2"
|
||||
cellspacing="2">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--help</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Show help text<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--usage<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Show usage<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--queue<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-q<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify a specific queue<br>
|
||||
In no specified, messages are queued under queue "0"<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--da<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-d<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify destination address<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--oa<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-o<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify originating address<br>
|
||||
This also implies that we are generating a mobile terminated message<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--ud<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-m<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify the actual message<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--ud-file<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-f<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify a file to be read for
|
||||
the context of the message<br>
|
||||
A blank filename (e.g. --ud-file= on its own) means read stdin. Very
|
||||
useful when using via ssh where command line parsing could mess up the
|
||||
message.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mt<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-t<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Mobile terminated message to be
|
||||
generated<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mo<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Mobile originated message to be
|
||||
generated<br>
|
||||
Default<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--tx<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Transmit message<br>
|
||||
Default<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--rx<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-r<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Generate a message in the
|
||||
receive queue<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--UTF-8<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Treat the file as UTF-8 encoded
|
||||
(default) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--UCS-1<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Treat the file as raw 8 bit
|
||||
UCS-1 data, not UTF-8 encoded<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--UCS-2<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Treat the file as raw 16 bit
|
||||
bigendian USC-2 data<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--process<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specific a command to process
|
||||
for each file in the queue<br>
|
||||
Implies --rx and --mt if not otherwise specified.<br>
|
||||
Sets environment variables for every possible variable,
|
||||
and also ud, ud8 (USC-1 hex), and ud16 (USC-2 hex) for each call.
|
||||
Removes files.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--motx-channel<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify the channel for motx
|
||||
calls<br>
|
||||
May contain X to use sub address based on queue name or may be full
|
||||
number<br>
|
||||
Default is Local/1709400X<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--motx-callerid<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify the caller ID for motx
|
||||
calls<br>
|
||||
The default is the queue name without -X suffix<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--motx-wait<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Wait time for motx call<br>
|
||||
Default 10<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--motx-delay<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Retry time for motx call<br>
|
||||
Default 1<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--motx-retries<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Retries for motx call<br>
|
||||
Default 10<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mttx-channel<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify the channel for mttx
|
||||
calls<br>
|
||||
Default is Local/ and the queue name without -X suffix<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mtttx-callerid<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify the callerid for mttx
|
||||
calls<br>
|
||||
May include X to use sub address based on queue name or may be full
|
||||
number<br>
|
||||
Default is 080058752X0<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mttx-wait<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Wait time for mttx call<br>
|
||||
Default 10<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mttx-delay<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Retry time for mttx call<br>
|
||||
Default 30<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mttx-retries<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Retries for mttx call<br>
|
||||
Default 100<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--default-sub-address<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">The default sub address assumed
|
||||
(e.g. for X in CLI and dialled numbers as above) when none added (-X)
|
||||
to queue<br>
|
||||
Default 9<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--no-dial<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-x<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Create queue, but do not dial to
|
||||
send message<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--no-wait<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Do not wait if a call appears to
|
||||
be in progress<br>
|
||||
This could have a small window where a mesdsage is queued but not sent,
|
||||
so regular calls to smsq should be done to pick up any missed messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--concurrent<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">How many concurrent calls to
|
||||
allow (per queue), default 1<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--mr<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-n<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Message reference<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--pid<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">-p<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Protocol ID<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--dcs<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Data coding scheme<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--udh<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specific hex string of user data
|
||||
header specified (not including the initial length byte)<br>
|
||||
May be a blank string to indicate header is included in the user data
|
||||
already but user data header indication to be set.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--srr<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Status report requested<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--rp<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Return path requested<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--vp<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify validity period (seconds)<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--scts<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Specify timestamp
|
||||
(YYYY-MM-DDTHH:MM:SS)<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">--spool-dir<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;"><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Spool dir (in which sms and
|
||||
outgoing are found)<br>
|
||||
Default /var/spool/asterisk<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Other arguments starting '-' or '--' are invalid and will cause an
|
||||
error. Any trailing arguments are processed as follows:-<br>
|
||||
</p>
|
||||
<ul>
|
||||
<li>If the message is mobile originating and no destination address
|
||||
has been specified, then the first argument is assumed to be a
|
||||
destination address</li>
|
||||
<li>If the message is mobile terminating and no destination address
|
||||
has been specified, then the first argument is assumed to be the queue
|
||||
name</li>
|
||||
<li>If there is no user data, or user data file specified, then any
|
||||
following arguments are assumed to be the message, which are
|
||||
concatenated.</li>
|
||||
<li>If no user data is specified, then no message is sent. However,
|
||||
unless --no-dial is specified, smsq checks for pending messages and
|
||||
generates an outgoing anyway</li>
|
||||
</ul>
|
||||
Note that when smsq attempts to make a file in
|
||||
/var/spool/asterisk/outgoing, it checks if there is already a call
|
||||
queued for that queue. It will try several filenames, up to the
|
||||
--concorrent setting. If these files
|
||||
exists, then this means asterisk is already queued to send all messages
|
||||
for that queue, and so asterisk should pick up the message just queued.
|
||||
However, this alone could create a race condition, so if the files
|
||||
exist then smsq will wait up to 3 seconds to confirm it still exists or
|
||||
if the queued messages have been sent already.
|
||||
The --no-wait turns off this behaviour. Basically, this means that if
|
||||
you have a lot of messages to send all at
|
||||
once, asterisk will not make unlimited concurrent calls to the same
|
||||
message centre or device for the same queue. This is because it is
|
||||
generally more efficient to make one call and send all of the messages
|
||||
one after the other.<br>
|
||||
<br>
|
||||
smsq can be used with no arguments, or with a queue name only, and it
|
||||
will check for any pending messages and cause an outgoing if there are
|
||||
any. It only sets up one outgoing call at a time based on the first
|
||||
queued message it finds. A outgoing call will normally send all queued
|
||||
messages for that queue. One way to use smsq would be to run with no
|
||||
queue name (so any queue) every minute or every few seconds to send
|
||||
pending message. This is not normally necessary unless --no-dial is
|
||||
selected. Note that smsq does only check motx or mttx depending on the
|
||||
options selected, so it would need to be called twice as a general
|
||||
check.<br>
|
||||
<br>
|
||||
UTF-8 is used to parse command line arguments for user data, and is the
|
||||
default when reading a file. If an invalid UTF-8 sequence is found, it
|
||||
is treated as UCS-1 data (i.e, as is).<br>
|
||||
<br>
|
||||
The --process option causes smsq to scan the specified queue (default
|
||||
is mtrx) for messages (matching the queue specified, or any if queue
|
||||
not specified) and run a command and delete the file. The command is
|
||||
run with a number of environment variables set as follows. Note that
|
||||
these are unset if not needed and not just taken from the calling
|
||||
environment. This allows simple processing of incoming messages<br>
|
||||
<br>
|
||||
<table style="text-align: left;" border="1" cellpadding="2"
|
||||
cellspacing="2">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$queue<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Set if a queue specified<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$?srr<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">srr is set (to blank) if srr
|
||||
defined and has value 1.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$?rp<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">rp is set (to blank) if rp
|
||||
defined and has value 1.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$ud<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">User data, UTF-8 encoding,
|
||||
including any control characters, but with nulls stripped out<br>
|
||||
Useful for the content of emails, for example, as it includes any
|
||||
newlines, etc.<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$ude<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">User data, escaped UTF-8,
|
||||
including all characters, but control characters \n, \r, \t, \f, \xxx
|
||||
and \ is escaped as \\<br>
|
||||
Useful fGuaranteed one line printable text, so useful in Subject lines
|
||||
of emails, etc<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$ud8<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Hex UCS-1 coding of user data (2
|
||||
hex digits per character)<br>
|
||||
Present only if all user data is in range U+0000 to U+00FF<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">$ud16<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Hex UCS-2 coding of user data (4
|
||||
hex digits per chartacter)<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;"><span style="font-style: italic;">other</span><br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Other fields set using their
|
||||
field name, e.g. mr, pid, dcs, etc. udh is a hex byte string<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>File formats</h2>
|
||||
By default all queues are held in a director /var/spool/asterisk/sms.
|
||||
Within this directory are sub directories mtrx, mttx, morx, motx which
|
||||
hold the received messages and the messages ready to send. Also,
|
||||
/var/log/asterisk/sms is a log file of all messages handled.<br>
|
||||
<br>
|
||||
The file name in each queue directory starts with the queue parameter
|
||||
to SMS which is normally the CLI used for an outgoing message or the
|
||||
called number on an incoming message, and may have -X (X being sub
|
||||
address) appended. If no queue ID is known, then 0 is used by smsq by
|
||||
default. After this is a dot, and then any text. Files are scanned for
|
||||
matching queue ID and a dot at the start. This means temporary files
|
||||
being created can be given a different name not starting with a queue
|
||||
(we recommend a . on the start of the file name for temp files).<br>
|
||||
<br>
|
||||
Files in these queues are in the form of a simple text file where each
|
||||
line starts with a keyword and an = and then data. udh and ud have
|
||||
options for hex encoding, see below.<br>
|
||||
<br>
|
||||
UTF-8. The user data (ud) field is treated as being UTF-8 encoded
|
||||
unless the DCS is specified indicating 8 bit formart. If 8 bit format
|
||||
is specified then the user data is sent as is.<br>
|
||||
<br>
|
||||
The keywords are as
|
||||
follows:-<br>
|
||||
<table style="text-align: left;" border="1" cellpadding="2"
|
||||
cellspacing="2">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">oa</td>
|
||||
<td style="vertical-align: top;">Originating address<br>
|
||||
The phone number from which the message came<br>
|
||||
Present on mobile terminated messages and is the CLI for morx messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">da<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Destination Address<br>
|
||||
The phone number to which the message is sent<br>
|
||||
Present on mobile originated messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">scts<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">The service centre time stamp<br>
|
||||
Format YYYY-MM-DDTHH:MM:SS<br>
|
||||
Present on mobile terminated messages<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">pid<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">One byte decimal protocol ID<br>
|
||||
See GSM specs for more details<br>
|
||||
Normally 0 or absent<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">dcs<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">One byte decimal data coding
|
||||
scheme<br>
|
||||
If omitted, a sensible default is used (see below)<br>
|
||||
See GSM specs for more details<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">mr<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">One byte decimal message
|
||||
reference<br>
|
||||
Present on mobile originated messages, added by default if absent<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">srr<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">0 or 1 for status report request<br>
|
||||
Does not work in UK yet, not implemented in app_sms yet<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">rp<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">0 or 1 return path<br>
|
||||
See GSM specs for details<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">vp<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Validity period in seconds<br>
|
||||
Does not work in UK yet<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">udh<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">Hex string of user data header
|
||||
prepended to the SMS contents, excluding initial length byte.<br>
|
||||
Consistent with ud, this is specified as udh# rather than udh=<br>
|
||||
If blank, this means that the udhi flag will be set but any user data
|
||||
header must be in the ud field<br>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top;">ud<br>
|
||||
</td>
|
||||
<td style="vertical-align: top;">User data, may be text, or hex,
|
||||
see below<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
udh is specified as as udh# followed by hex (2 hex digits per byte). If
|
||||
present, then the user data header indicator bit is set, and the length
|
||||
plus the user data header is added to the start of the user data, with
|
||||
padding if necessary (to septet boundary in 7 bit format).<br>
|
||||
<br>
|
||||
User data can hold an USC character codes U+0000 to U+FFFF. Any other
|
||||
characters are coded as U+FEFF<br>
|
||||
ud can be specified as ud= followed by UTF-8 encoded text if it
|
||||
contains no control characters, i.e. only (U+0020 to U+FFFF). Any
|
||||
invalid UTF-8 sequences are treated as is (U+0080-U+00FF).<br>
|
||||
ud can also be specified as ud# followed by hex (2 hex digits per byte)
|
||||
containing characters U+0000 to U+00FF only.<br>
|
||||
ud can also be specified as ud## followed by hex (4 hex digits per
|
||||
byte) containing UCS-2 characters.<br>
|
||||
When written by app_sms (e.g. incoming messages), the file is written
|
||||
with ud= if it can be (no control characters). If it cannot, the a
|
||||
comment line ;ud= is used to show the user data for human readability
|
||||
and ud# or ud## is used.<br>
|
||||
<h2>Delivery reports</h2>
|
||||
The SMS specification allows for delivery reports. These are requested
|
||||
using the srr bit. However, as these do not work in the UK yet they are
|
||||
not fully implemented in this application. If anyone has a telco that
|
||||
does implement these, please let me know. BT in the UK have a non
|
||||
standard way to do this by starting the message with *0#, and so this
|
||||
application may have a UK specific bodge in the near future to handle
|
||||
these.<br>
|
||||
<br>
|
||||
The main changes that are proposed for delivery report handling are :-<br>
|
||||
<ul>
|
||||
<li>New queues for sent messages, one file for each destination
|
||||
address and message reference.</li>
|
||||
<li>New field in message format, user reference, allowing
|
||||
applications to tie up their original message with a report.</li>
|
||||
<li>Handling of the delivery confirmation/rejection and connecting to
|
||||
the outgoing message - the received message file would then have fields
|
||||
for the original outgoing message and user reference allowing
|
||||
applications to handle confirmations better.<br>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
</body>
|
||||
</html>
|
@@ -1,2 +1,3 @@
|
||||
astman
|
||||
.depend
|
||||
astman
|
||||
smsq
|
@@ -8,7 +8,9 @@ ifeq ($(findstring BSD,${OSARCH}),BSD)
|
||||
CFLAGS+=-I/usr/local/include -L/usr/local/lib
|
||||
endif
|
||||
|
||||
TARGET=$(shell if [ -f /usr/include/newt.h ]; then echo "astman"; else if [ -f /usr/local/include/newt.h ]; then echo "astman"; else echo "none" ; fi ; fi)
|
||||
TARGET+=$(shell if [ -f /usr/include/popt.h ]; then echo "smsq"; else if [ -f /usr/local/include/popt.h ]; then echo "smsq"; fi ; fi)
|
||||
TARGET+=$(shell if [ -f /usr/include/newt.h ]; then echo "astman"; else if [ -f /usr/local/include/newt.h ]; then echo "astman"; fi ; fi)
|
||||
|
||||
all: depend $(TARGET)
|
||||
|
||||
install:
|
||||
@@ -27,6 +29,9 @@ clean:
|
||||
astman: astman.o ../md5.o
|
||||
$(CC) $(CFLAGS) -o astman astman.o ../md5.o -lnewt
|
||||
|
||||
smsq: smsq.o
|
||||
$(CC) $(CFLAGS) -o smsq smsq.o -lpopt
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
740
utils/smsq.c
Executable file
740
utils/smsq.c
Executable file
@@ -0,0 +1,740 @@
|
||||
#include <stdio.h>
|
||||
#include <popt.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
// SMS queuing application for use with asterisk app_sms
|
||||
// by Adrian Kennard, 2004
|
||||
|
||||
/* reads next USC character from null terminated UTF-8 string and advanced pointer */
|
||||
/* for non valid UTF-8 sequences, returns character as is */
|
||||
/* Does not advance pointer for null termination */
|
||||
static int utf8decode (unsigned char **pp)
|
||||
{
|
||||
unsigned char *p = *pp;
|
||||
if (!*p)
|
||||
return 0; /* null termination of string */
|
||||
(*pp)++;
|
||||
if (*p < 0xC0)
|
||||
return *p; /* ascii or continuation character */
|
||||
if (*p < 0xE0)
|
||||
{
|
||||
if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
|
||||
return *p; /* not valid UTF-8 */
|
||||
(*pp)++;
|
||||
return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
|
||||
}
|
||||
if (*p < 0xF0)
|
||||
{
|
||||
if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
|
||||
return *p; /* not valid UTF-8 */
|
||||
(*pp) += 2;
|
||||
return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
|
||||
}
|
||||
if (*p < 0xF8)
|
||||
{
|
||||
if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
|
||||
return *p; /* not valid UTF-8 */
|
||||
(*pp) += 3;
|
||||
return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
|
||||
}
|
||||
if (*p < 0xFC)
|
||||
{
|
||||
if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
|
||||
|| (p[4] & 0xC0) != 0x80)
|
||||
return *p; /* not valid UTF-8 */
|
||||
(*pp) += 4;
|
||||
return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
|
||||
}
|
||||
if (*p < 0xFE)
|
||||
{
|
||||
if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
|
||||
|| (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
|
||||
return *p; /* not valid UTF-8 */
|
||||
(*pp) += 5;
|
||||
return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) +
|
||||
(p[5] & 0x3F);
|
||||
}
|
||||
return *p; /* not sensible */
|
||||
}
|
||||
|
||||
// check for any queued messages in specific queue (queue="" means any queue)
|
||||
// returns 0 if nothing queued, 1 if queued and outgoing set up OK, 2 of outgoing exists
|
||||
static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent)
|
||||
{
|
||||
char ogname[100],
|
||||
temp[100],
|
||||
dirname[100],
|
||||
*p=NULL;
|
||||
FILE *f;
|
||||
DIR *d;
|
||||
int ql = strlen (queue);
|
||||
struct dirent *fn;
|
||||
sprintf (dirname, "sms/%s", dir);
|
||||
d = opendir (dirname);
|
||||
if (!d)
|
||||
return 0;
|
||||
while ((fn = readdir (d))
|
||||
&& !(*fn->d_name != '.'
|
||||
&& ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))));
|
||||
if (!fn)
|
||||
{
|
||||
closedir (d);
|
||||
return 0;
|
||||
}
|
||||
if (!ql)
|
||||
{ // not searching any specific queue, so use whatr we found as the queue
|
||||
queue = fn->d_name;
|
||||
ql = p - queue;
|
||||
}
|
||||
p = strchr (queue, '-');
|
||||
if (p && p < queue + ql)
|
||||
{
|
||||
ql = p - queue;
|
||||
subaddress = p[1];
|
||||
}
|
||||
sprintf (temp, "sms/.smsq-%d", getpid ());
|
||||
f = fopen (temp, "w");
|
||||
if (!f)
|
||||
{
|
||||
perror (temp);
|
||||
closedir (d);
|
||||
return 0;
|
||||
}
|
||||
fprintf (f, "Channel: ");
|
||||
if (!channel)
|
||||
fprintf (f, "Local/%.*s\n", ql, queue);
|
||||
else
|
||||
{
|
||||
p = strchr (channel, '/');
|
||||
if (!p)
|
||||
p = channel;
|
||||
p = strchr (p, 'X');
|
||||
if (p)
|
||||
fprintf (f, "%.*s%c%s\n", p - channel, channel, subaddress, p + 1);
|
||||
else
|
||||
fprintf (f, "%s\n", channel);
|
||||
}
|
||||
fprintf (f, "Callerid: SMS <");
|
||||
if (!callerid)
|
||||
fprintf (f, "%.*s", ql, queue);
|
||||
else
|
||||
{
|
||||
p = strchr (callerid, 'X');
|
||||
if (p)
|
||||
fprintf (f, "%.*s%c%s", p - callerid, callerid, subaddress, p + 1);
|
||||
else
|
||||
fprintf (f, "%s", callerid);
|
||||
}
|
||||
fprintf (f, ">\n");
|
||||
fprintf (f, "Application: SMS\n");
|
||||
fprintf (f, "Data: %.*s", ql, queue);
|
||||
if (dir[1] == 't')
|
||||
fprintf (f, "|s");
|
||||
fprintf (f, "\nMaxRetries: %d\n", retries);
|
||||
fprintf (f, "RetryTime: %d\n", delay);
|
||||
fprintf (f, "WaitTime: %d\n", wait);
|
||||
fclose (f);
|
||||
closedir (d);
|
||||
{
|
||||
int try = 0;
|
||||
while (try < concurrent)
|
||||
{
|
||||
try++;
|
||||
sprintf (ogname, "outgoing/smsq.%s.%s.%d", dir, queue, try);
|
||||
if (!link (temp, ogname))
|
||||
{ // queued OK
|
||||
unlink (temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// failed to create call queue
|
||||
unlink (temp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Process received queue entries and run through a process, setting environment variables
|
||||
static void rxqcheck (char *dir, char *queue, char *process)
|
||||
{
|
||||
unsigned char *p;
|
||||
char dirname[100],
|
||||
temp[100];
|
||||
DIR *d;
|
||||
int ql = strlen (queue);
|
||||
struct dirent *fn;
|
||||
sprintf (temp, "sms/.smsq-%d", getpid ());
|
||||
sprintf (dirname, "sms/%s", dir);
|
||||
d = opendir (dirname);
|
||||
if (!d)
|
||||
return;
|
||||
while ((fn = readdir (d)))
|
||||
if ((*fn->d_name != '.'
|
||||
&& ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.'))))
|
||||
{ /* process file */
|
||||
char filename[NAME_MAX + 10];
|
||||
char line[1000];
|
||||
unsigned short ud[160];
|
||||
unsigned char udl = 0;
|
||||
FILE *f;
|
||||
sprintf (filename, "sms/%s/%s", dir, fn->d_name);
|
||||
if (rename (filename, temp))
|
||||
continue; // cannot access file
|
||||
f = fopen (temp, "r");
|
||||
unlink (temp);
|
||||
if (!f)
|
||||
{
|
||||
perror (temp);
|
||||
continue;
|
||||
}
|
||||
unsetenv ("oa");
|
||||
unsetenv ("da");
|
||||
unsetenv ("scts");
|
||||
unsetenv ("pid");
|
||||
unsetenv ("dcs");
|
||||
unsetenv ("mr");
|
||||
unsetenv ("srr");
|
||||
unsetenv ("rp");
|
||||
unsetenv ("vp");
|
||||
unsetenv ("udh");
|
||||
unsetenv ("ud");
|
||||
unsetenv ("ude");
|
||||
unsetenv ("ud8");
|
||||
unsetenv ("ud16");
|
||||
unsetenv ("morx");
|
||||
unsetenv ("motx");
|
||||
unsetenv ("queue");
|
||||
if (*queue)
|
||||
setenv ("queue", queue, 1);
|
||||
setenv (dir, "", 1);
|
||||
while (fgets (line, sizeof (line), f))
|
||||
{
|
||||
for (p = line; *p && *p != '\n' && *p != '\r'; p++);
|
||||
*p = 0; /* strip eoln */
|
||||
p = line;
|
||||
if (!*p || *p == ';')
|
||||
continue; /* blank line or comment, ignore */
|
||||
while (isalnum (*p))
|
||||
{
|
||||
*p = tolower (*p);
|
||||
p++;
|
||||
}
|
||||
while (isspace (*p))
|
||||
*p++ = 0;
|
||||
if (*p == '=')
|
||||
{ // =
|
||||
*p++ = 0;
|
||||
if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid")
|
||||
|| !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp"))
|
||||
setenv (line, p, 1);
|
||||
else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p))
|
||||
setenv (line, "", 1);
|
||||
else if (!strcmp (line, "ud"))
|
||||
{ // read the user data as UTF-8
|
||||
long v;
|
||||
udl = 0;
|
||||
while ((v = utf8decode (&p)) && udl < 160)
|
||||
if (v && v <= 0xFFFF)
|
||||
ud[udl++] = v;
|
||||
}
|
||||
} else if (*p == '#')
|
||||
{
|
||||
*p++ = 0;
|
||||
if (*p == '#')
|
||||
{ // ##
|
||||
p++;
|
||||
if (!strcmp (line, "udh"))
|
||||
setenv (line, p, 1);
|
||||
else if (!strcmp (line, "ud"))
|
||||
{ // read user data UCS-2
|
||||
udl = 0;
|
||||
while (*p && udl < 160)
|
||||
{
|
||||
if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
|
||||
{
|
||||
ud[udl++] =
|
||||
(((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
|
||||
(((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
|
||||
(((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
|
||||
p += 4;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{ // #
|
||||
if (!strcmp (line, "ud"))
|
||||
{ // read user data UCS-1
|
||||
udl = 0;
|
||||
while (*p && udl < 160)
|
||||
{
|
||||
if (isxdigit (*p) && isxdigit (p[1]))
|
||||
{
|
||||
ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
|
||||
p += 2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose (f);
|
||||
// set up user data variables
|
||||
{
|
||||
char temp[481];
|
||||
int n,
|
||||
p;
|
||||
for (n = 0, p = 0; p < udl; p++)
|
||||
{
|
||||
unsigned short v = ud[p];
|
||||
if (v)
|
||||
{
|
||||
if (v < 0x80)
|
||||
temp[n++] = v;
|
||||
else if (v < 0x800)
|
||||
{
|
||||
temp[n++] = (0xC0 + (v >> 6));
|
||||
temp[n++] = (0x80 + (v & 0x3F));
|
||||
} else
|
||||
{
|
||||
temp[n++] = (0xE0 + (v >> 12));
|
||||
temp[n++] = (0x80 + ((v >> 6) & 0x3F));
|
||||
temp[n++] = (0x80 + (v & 0x3F));
|
||||
}
|
||||
}
|
||||
}
|
||||
temp[n] = 0;
|
||||
setenv ("ud", temp, 1);
|
||||
for (n = 0, p = 0; p < udl; p++)
|
||||
{
|
||||
unsigned short v = ud[p];
|
||||
if (v < ' ' || v == '\\')
|
||||
{
|
||||
temp[n++] = '\\';
|
||||
if (v == '\\')
|
||||
temp[n++] = '\\';
|
||||
else if (v == '\n')
|
||||
temp[n++] = 'n';
|
||||
else if (v == '\r')
|
||||
temp[n++] = 'r';
|
||||
else if (v == '\t')
|
||||
temp[n++] = 't';
|
||||
else if (v == '\f')
|
||||
temp[n++] = 'f';
|
||||
else
|
||||
{
|
||||
temp[n++] = '0' + (v >> 6);
|
||||
temp[n++] = '0' + ((v >> 3) & 7);
|
||||
temp[n++] = '0' + (v & 7);
|
||||
}
|
||||
} else if (v < 0x80)
|
||||
temp[n++] = v;
|
||||
else if (v < 0x800)
|
||||
{
|
||||
temp[n++] = (0xC0 + (v >> 6));
|
||||
temp[n++] = (0x80 + (v & 0x3F));
|
||||
} else
|
||||
{
|
||||
temp[n++] = (0xE0 + (v >> 12));
|
||||
temp[n++] = (0x80 + ((v >> 6) & 0x3F));
|
||||
temp[n++] = (0x80 + (v & 0x3F));
|
||||
}
|
||||
}
|
||||
temp[n] = 0;
|
||||
setenv ("ude", temp, 1);
|
||||
for (p = 0; p < udl && ud[p] < 0x100; p++);
|
||||
if (p == udl)
|
||||
{
|
||||
for (n = 0, p = 0; p < udl; p++)
|
||||
{
|
||||
sprintf (temp + n, "%02X", ud[p]);
|
||||
n += 2;
|
||||
}
|
||||
setenv ("ud8", temp, 1);
|
||||
}
|
||||
for (n = 0, p = 0; p < udl; p++)
|
||||
{
|
||||
sprintf (temp + n, "%04X", ud[p]);
|
||||
n += 4;
|
||||
}
|
||||
setenv ("ud16", temp, 1);
|
||||
}
|
||||
// run the command
|
||||
system (process);
|
||||
}
|
||||
closedir (d);
|
||||
}
|
||||
|
||||
// Main app
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
char c;
|
||||
int mt = 0,
|
||||
mo = 0,
|
||||
tx = 0,
|
||||
rx = 0,
|
||||
nodial = 0,
|
||||
nowait = 0,
|
||||
concurrent = 1,
|
||||
motxwait = 10,
|
||||
motxdelay = 1,
|
||||
motxretries = 10,
|
||||
mttxwait = 10,
|
||||
mttxdelay = 30,
|
||||
mttxretries = 100,
|
||||
mr = -1,
|
||||
pid = -1,
|
||||
dcs = -1,
|
||||
srr = 0,
|
||||
rp = 0,
|
||||
vp = 0,
|
||||
udl = 0,
|
||||
utf8 = 0,
|
||||
ucs1 = 0,
|
||||
ucs2 = 0;
|
||||
unsigned short ud[160];
|
||||
unsigned char *uds = 0,
|
||||
*udh = 0;
|
||||
char *da = 0,
|
||||
*oa = 0,
|
||||
*queue = "",
|
||||
*udfile = 0,
|
||||
*process = 0,
|
||||
*spooldir = "/var/spool/asterisk",
|
||||
*motxchannel = "Local/1709400X",
|
||||
*motxcallerid = 0,
|
||||
*mttxchannel = 0,
|
||||
*mttxcallerid = "080058752X0",
|
||||
*defaultsubaddress = "9",
|
||||
subaddress = 0,
|
||||
*scts = 0;
|
||||
poptContext optCon; // context for parsing command-line options
|
||||
const struct poptOption optionsTable[] = {
|
||||
{"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"},
|
||||
{"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"},
|
||||
{"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"},
|
||||
{"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"},
|
||||
{"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"},
|
||||
{"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0},
|
||||
{"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0},
|
||||
{"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0},
|
||||
{"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0},
|
||||
{"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0},
|
||||
{"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0},
|
||||
{"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0},
|
||||
{"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"},
|
||||
{"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0},
|
||||
{"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0},
|
||||
{"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"},
|
||||
{"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"},
|
||||
{"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0,
|
||||
"Caller ID for motx calls (default is queue name without sub address)", "number"},
|
||||
{"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer",
|
||||
"seconds"},
|
||||
{"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"},
|
||||
{"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"},
|
||||
{"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0,
|
||||
"Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"},
|
||||
{"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0,
|
||||
"Caller ID for mttx calls (default is queue name without sub address)", "number"},
|
||||
{"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer",
|
||||
"seconds"},
|
||||
{"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"},
|
||||
{"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"},
|
||||
{"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"},
|
||||
{"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"},
|
||||
{"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"},
|
||||
{"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"},
|
||||
{"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0},
|
||||
{"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0},
|
||||
{"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"},
|
||||
{"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"},
|
||||
{"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"},
|
||||
{"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"},
|
||||
POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
|
||||
};
|
||||
|
||||
optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
|
||||
poptSetOtherOptionHelp (optCon, "<oa/da> <message>");
|
||||
|
||||
/* Now do options processing, get portname */
|
||||
if ((c = poptGetNextOpt (optCon)) < -1)
|
||||
{
|
||||
/* an error occurred during option processing */
|
||||
fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c));
|
||||
return 1;
|
||||
}
|
||||
if (!ucs1 && !ucs2)
|
||||
utf8 = 1;
|
||||
if (utf8 + ucs1 + ucs2 > 1)
|
||||
{
|
||||
fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n");
|
||||
return 1;
|
||||
}
|
||||
if (!udfile && (ucs1 || ucs2))
|
||||
{
|
||||
fprintf (stderr, "Command line arguments always treated as UTF-8\n");
|
||||
return 1;
|
||||
}
|
||||
// if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon);
|
||||
if (!mt && !mo && process)
|
||||
mt = 1;
|
||||
if (!mt && !mo && oa)
|
||||
mt = 1;
|
||||
if (!mt)
|
||||
mo = 1;
|
||||
if (mt && mo)
|
||||
{
|
||||
fprintf (stderr, "Cannot be --mt and --mo\n");
|
||||
return 1;
|
||||
}
|
||||
if (!rx && !tx && process)
|
||||
rx = 1;
|
||||
if (!rx)
|
||||
tx = 1;
|
||||
if (tx && rx)
|
||||
{
|
||||
fprintf (stderr, "Cannot be --tx and --rx\n");
|
||||
return 1;
|
||||
}
|
||||
if (rx)
|
||||
nodial = 1;
|
||||
if (uds && udfile)
|
||||
{
|
||||
fprintf (stderr, "Cannot have --ud and --ud-file\n");
|
||||
return 1;
|
||||
}
|
||||
if (mo && !da && poptPeekArg (optCon))
|
||||
da = (char *) poptGetArg (optCon);
|
||||
if (mt && !oa && poptPeekArg (optCon))
|
||||
oa = (char *) poptGetArg (optCon);
|
||||
if (tx && oa && mo)
|
||||
{
|
||||
fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n");
|
||||
return 1;
|
||||
}
|
||||
if (tx && da && mt)
|
||||
{
|
||||
fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n");
|
||||
return 1;
|
||||
}
|
||||
if (da && strlen (da) > 20)
|
||||
{
|
||||
fprintf (stderr, "--da too long\n");
|
||||
return 1;
|
||||
}
|
||||
if (oa && strlen (oa) > 20)
|
||||
{
|
||||
fprintf (stderr, "--oa too long\n");
|
||||
return 1;
|
||||
}
|
||||
if (queue && strlen (queue) > 20)
|
||||
{
|
||||
fprintf (stderr, "--queue name too long\n");
|
||||
return 1;
|
||||
}
|
||||
if (mo && scts)
|
||||
{
|
||||
fprintf (stderr, "scts is set my service centre\n");
|
||||
return 1;
|
||||
}
|
||||
if (uds)
|
||||
{ /* simple user data command line option in \UTF-8 */
|
||||
while (udl < 160 && *uds)
|
||||
{
|
||||
int v = utf8decode (&uds);
|
||||
if (v > 0xFFFF)
|
||||
{
|
||||
fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
|
||||
return 1;
|
||||
}
|
||||
ud[udl++] = v;
|
||||
}
|
||||
}
|
||||
if (!uds && !udfile && poptPeekArg (optCon))
|
||||
{ /* multiple command line arguments in UTF-8 */
|
||||
while (poptPeekArg (optCon) && udl < 160)
|
||||
{
|
||||
unsigned char *a = (char *) poptGetArg (optCon);
|
||||
if (udl && udl < 160)
|
||||
ud[udl++] = ' '; /* space between arguments */
|
||||
while (udl < 160 && *a)
|
||||
{
|
||||
int v = utf8decode (&a);
|
||||
if (v > 0xFFFF)
|
||||
{
|
||||
fprintf (stderr, "Invalid character U+%X at %d\n", v, udl);
|
||||
return 1;
|
||||
}
|
||||
ud[udl++] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (poptPeekArg (optCon))
|
||||
{
|
||||
fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon));
|
||||
return 1;
|
||||
}
|
||||
if (udfile)
|
||||
{ // get message from file
|
||||
unsigned char dat[1204],
|
||||
*p = dat,
|
||||
*e;
|
||||
int f,
|
||||
n;
|
||||
if (*udfile)
|
||||
f = open (udfile, O_RDONLY);
|
||||
else
|
||||
f = fileno (stdin);
|
||||
if (f < 0)
|
||||
{
|
||||
perror (udfile);
|
||||
return 1;
|
||||
}
|
||||
n = read (f, dat, sizeof (dat));
|
||||
if (n < 0)
|
||||
{
|
||||
perror (udfile);
|
||||
return 1;
|
||||
}
|
||||
if (*udfile)
|
||||
close (f);
|
||||
e = dat + n;
|
||||
if (utf8)
|
||||
{ /* UTF-8 */
|
||||
while (p < e && udl < 160 && *p)
|
||||
ud[udl++] = utf8decode (&p);
|
||||
} else if (ucs1)
|
||||
{ /* UCS-1 */
|
||||
while (p < e && udl < 160)
|
||||
ud[udl++] = *p++;
|
||||
} else
|
||||
{ /* UCS-2 */
|
||||
while (p + 1 < e && udl < 160)
|
||||
{
|
||||
ud[udl++] = (*p << 8) + p[1];
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queue)
|
||||
{
|
||||
char *d = strrchr (queue, '-');
|
||||
if (d && d[1])
|
||||
subaddress = d[1];
|
||||
else
|
||||
subaddress = *defaultsubaddress;
|
||||
}
|
||||
|
||||
if (chdir (spooldir))
|
||||
{
|
||||
perror (spooldir);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (oa || da)
|
||||
{ // send message
|
||||
char temp[100],
|
||||
queuename[100],
|
||||
*dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx");
|
||||
FILE *f;
|
||||
sprintf (temp, "sms/.smsq-%d", getpid ());
|
||||
mkdir ("sms", 0777); // ensure directory exists
|
||||
mkdir (dir, 0777); // ensure directory exists
|
||||
sprintf (queuename, "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), getpid ());
|
||||
f = fopen (temp, "w");
|
||||
if (!f)
|
||||
{
|
||||
perror (temp);
|
||||
return 1;
|
||||
}
|
||||
if (oa)
|
||||
fprintf (f, "oa=%s\n", oa);
|
||||
if (da)
|
||||
fprintf (f, "da=%s\n", da);
|
||||
if (scts)
|
||||
fprintf (f, "scts=%s\n", scts);
|
||||
if (pid >= 0)
|
||||
fprintf (f, "pid=%d\n", pid);
|
||||
if (dcs >= 0)
|
||||
fprintf (f, "dcs=%d\n", dcs);
|
||||
if (mr >= 0)
|
||||
fprintf (f, "mr=%d\n", mr);
|
||||
if (srr)
|
||||
fprintf (f, "srr=1\n");
|
||||
if (rp)
|
||||
fprintf (f, "rp=1\n");
|
||||
if (udh)
|
||||
fprintf (f, "udh#%s\n", udh);
|
||||
if (vp > 0)
|
||||
fprintf (f, "vp=%d\n", vp);
|
||||
if (udl)
|
||||
{
|
||||
int p;
|
||||
for (p = 0; p < udl && ud[p] < 0x100; p++);
|
||||
if (p == udl)
|
||||
{
|
||||
for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++);
|
||||
if (p == udl)
|
||||
{ /* use text */
|
||||
fprintf (f, "ud=");
|
||||
for (p = 0; p < udl; p++)
|
||||
fputc (ud[p], f);
|
||||
} else
|
||||
{ /* use one byte hex */
|
||||
fprintf (f, "ud#");
|
||||
for (p = 0; p < udl; p++)
|
||||
fprintf (f, "%02X", ud[p]);
|
||||
}
|
||||
} else
|
||||
{ /* use two byte hex */
|
||||
fprintf (f, "ud##");
|
||||
for (p = 0; p < udl; p++)
|
||||
fprintf (f, "%04X", ud[p]);
|
||||
}
|
||||
fprintf (f, "\n");
|
||||
}
|
||||
fclose (f);
|
||||
if (rename (temp, queuename))
|
||||
{
|
||||
perror (queuename);
|
||||
unlink (temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodial && tx && !process)
|
||||
{ // dial to send messages
|
||||
char ret=0,
|
||||
try = 3;
|
||||
if (nowait)
|
||||
try = 1;
|
||||
while (try--)
|
||||
{
|
||||
if (mo)
|
||||
ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent);
|
||||
else
|
||||
ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent);
|
||||
if (ret < 2)
|
||||
break; // sent, or queued OK
|
||||
if (try)
|
||||
sleep (1);
|
||||
}
|
||||
if (ret == 2 && !nowait)
|
||||
fprintf (stderr, "No call scheduled as already sending\n");
|
||||
}
|
||||
if (process)
|
||||
rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user