#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <eps.h>
#define DEBUG
#define MAX_FAX_VARS 10
#define MAX_VAR_LEN 255
#define DEFAULT_IMAGE_SIZE 10240
#define TMPDIR "/var/tmp"
#define OUTDIR "/var/spool/asterisk/outgoing"
typedef struct __fvar_ {
char name[MAX_VAR_LEN],
data[MAX_VAR_LEN];
} fvar_t;
static fvar_t f_vars[MAX_FAX_VARS];
static int f_vars_num = 0;
static line_t *image = NULL;
static char p_number[30] = { 0 },
p_file[255] = { 0 },
pc_file[255] = { 0 },
*c_template = NULL;
static int process_recipient(const char *);
static int user_is_number(const char *);
static int process_image(eps_t *);
static int send_fax(void);
static const char *send_fax_variable(const char *);
static int set_fax_variable(const char *, const char *);
int main(int argc, char *argv[])
{
char *l = NULL;
eps_t *eps = NULL;
header_t *h = NULL;
int fd = 0, ret = 0, df = 0, i = 0;
if (argc > 1)
c_template = argv[1];
else
c_template = "/usr/local/share/astfax/ast_fax.call";
f_vars_num = 0;
for (i = 0; i < MAX_FAX_VARS; i++)
memset(&f_vars[i], 0, sizeof(fvar_t));
/*
Stream from stdin
*/
fd = 0;
eps = eps_begin(INTERFACE_STREAM, &fd);
if (eps == NULL) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: eps_begin(INTERFACE_STREAM, 0) failed\n");
#endif
return 1;
}
/*
Run through headers
*/
memset(p_number, 0, sizeof(p_number));
for (h = eps_next_header(eps); h; h = eps_next_header(eps)) {
if ((!(strcasecmp(h->name, "To"))) || (!(strcasecmp(h->name, "Cc")))) {
ret = process_recipient(h->data);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: process_to(%s) failed\n", h->data);
#endif
eps_end(eps);
return 1;
}
}
}
if (!(*p_number)) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: no numbers found\n");
#endif
eps_end(eps);
return 1;
}
ret = set_fax_variable("PHONE", p_number);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: set_fax_variable(\"PHONE\", %s) failed\n", p_number);
#endif
eps_end(eps);
return 1;
}
/*
Make sure we're multipart
*/
if (!(eps->content_type & CON_MULTI)) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: message is not multipart\n");
#endif
eps_end(eps);
return 1;
}
/*
Skip main body
*/
for (l = eps_next_line(eps); l; l = eps_next_line(eps));
/*
Look for attachments
*/
while(!(eps_is_eof(eps))) {
df = 0;
ret = mime_init_stream(eps);
if (!ret)
break;
/*
Make sure we've got a TIFF image
*/
for (h = mime_next_header(eps); h; h = mime_next_header(eps)) {
if (!(strcasecmp(h->name, "Content-type"))) {
if ((h->atoms) && (h->atoms->next->data)) {
if (!(strcasecmp(h->atoms->next->data, "image/tiff")))
df = 1;
}
}
}
if (df) {
if (eps->m->encoding == ENC_BASE64) {
ret = process_image(eps);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: process_image failed\n");
#endif
eps_end(eps);
return 1;
}
}
#ifdef DEBUG
else {
fprintf(stderr, "ast_fax: unsupported encoding scheme\n");
}
#endif
}
/*
Skip the attachment body
*/
else {
for (l = mime_next_line(eps); l; l = mime_next_line(eps));
}
}
/*
Done with EPS
*/
eps_end(eps);
if (!(*p_file)) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: no image could be processed\n");
#endif
return 1;
}
/*
Pass the fax to Asterisk
*/
ret = send_fax();
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "ast_fax: send_fax failed\n");
#endif
return 1;
}
fprintf(stderr, "ast_fax: fax sent\n");
fflush(stderr);
return 0;
}
/*
Determine if the To header contains
a phone number. Check if it's valid,
and save it.
*/
static int process_recipient(const char *data)
{
int len = 0;
group_t *g = NULL;
address_t *a = NULL;
/*
Parse addresses looking for a phone number
*/
g = address_evaluate(data);
if (g == NULL) {
#ifdef DEBUG
fprintf(stderr, "process_recipient: address_evaluate(%s) failed\n", data);
#endif
return 0;
}
if ((!(g->nmembers)) || (g->members == NULL)) {
#ifdef DEBUG
fprintf(stderr, "process_recipient: address_evaluate(%s): no addresses found\n", data);
#endif
address_kill(g);
return 1;
}
/*
Look for a phone number
*/
for (a = g->members; a->next; a = a->next) {
if (!(a->next->user))
continue;
if (user_is_number(a->next->user)) {
if (*p_number) {
#ifdef DEBUG
fprintf(stderr, "process_recipient: found more than one number, ignoring %s\n", a->next->user);
#endif
continue;
}
len = strlen(a->next->user);
if (len >= (sizeof(p_number) - 1))
len = (sizeof(p_number) - 1);
memcpy(p_number, a->next->user, len);
}
}
address_kill(g);
return 1;
}
/*
Validate username is entirely made up of numbers
*/
static int user_is_number(const char *user)
{
int i = 0;
const char *p = NULL;
const char vn[] = "0123456789";
for (p = user; *p; p++) {
for (i = 0; vn[i]; i++) {
if (*p == vn[i])
break;
}
if (!(vn[i]))
return 0;
}
return 1;
}
/*
Decode a base64 encoded image
*/
static int process_image(eps_t *eps)
{
base64_t b;
char *l = NULL;
int ret = 0, fd = 0, i = 0, num = 0;
base64_init(&b);
image = line_alloc();
if (image == NULL) {
#ifdef DEBUG
fprintf(stderr, "process_image: line_alloc failed\n");
#endif
return 0;
}
ret = line_init(image, NULL, DEFAULT_IMAGE_SIZE);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "process_image: line_init(%d) failed\n", DEFAULT_IMAGE_SIZE);
#endif
return 0;
}
for (l = mime_next_line(eps); l; l = mime_next_line(eps)) {
ret = base64_decode(&b, image, l);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "process_image: base64_decode(%p, %p, ...)\n", &b, image);
#endif
return 0;
}
}
/*
Create a temporary file
*/
fd = -1;
num = rand();
for (i = 0; i < 10; i++) {
memset(p_file, 0, sizeof(p_file));
snprintf(p_file, sizeof(p_file), "%s/ast_fax-%d%c%lu%c%d%c%d", TMPDIR, (int)time(NULL), '.',
image->size, '.', num, '.', i);
fd = open(p_file, O_WRONLY|O_CREAT|O_EXCL, 0644);
#ifdef DEBUG
if (fd == -1)
fprintf(stderr, "process_image: open(%s, O_WRONLY|O_CREAT|O_EXCL, 0644) failed\n", p_file);
#endif
else
break;
}
if (fd == -1) {
#ifdef DEBUG
fprintf(stderr, "process_image: couldnt create temporary storage\n");
#endif
memset(p_file, 0, sizeof(p_file));
line_kill(image);
return 0;
}
ret = write(fd, image->data, image->bytes);
if (ret != image->bytes) {
#ifdef DEBUG
fprintf(stderr, "process_image: write(%d, ..., %lu) failed\n", fd, image->bytes);
#endif
line_kill(image);
return 0;
}
close(fd);
line_kill(image);
ret = set_fax_variable("FILE", p_file);
if (!ret) {
#ifdef DEBUG
fprintf(stderr, "process_image: set_fax_variable(\"FILE\", %s) failed\n",
p_file);
#endif
return 0;
}
return 1;
}
/*
Hand over the Fax information to Asterisk
*/
static int send_fax(void)
{
int ret = 0;
const char *data = NULL;
FILE *stream = NULL, *out = NULL;
char b[255] = { 0 }, *h = NULL, *t = NULL, ob[255] = { 0 }, *p = NULL;
/*
Generate our call file from template
*/
stream = fopen(c_template, "r");
if (stream == NULL) {
#ifdef DEBUG
fprintf(stderr, "send_fax: fopen(%s,\"r\") failed\n", c_template);
#endif
return 0;
}
memset(pc_file, 0, sizeof(pc_file));
snprintf(pc_file, sizeof(pc_file), "%s.call", p_file);
out = fopen(pc_file, "w+");
if (out == NULL) {
#ifdef DEBUG
fprintf(stderr, "send_fax: fopen(%s, \"w+\") failed\n", pc_file);
#endif
fclose(stream);
return 0;
}
while(!(feof(stream))) {
memset(b, 0, sizeof(b));
fgets(b, sizeof(b), stream);
if ((*b == '#') || (*b == ';') || (*b == '\0'))
continue;
p = ob;
memset(ob, 0, sizeof(ob));
/*
Generate call file from our very simple template
*/
for (h = b, t = NULL; ((*h) && (p < (ob + (sizeof(ob) - 1)))); h++) {
if ((*h == '$') && (*(h + 1) == '[') && (t == NULL)) {
t = (h + 2);
h++;
}
else if ((*h == ']') && (t != NULL)) {
*h = '\0';
data = send_fax_variable(t);
if (data == NULL) {
#ifdef DEBUG
fprintf(stderr, "send_fax: template contains unknown variable '%s'\n", t);
#endif
fclose(stream);
fclose(out);
return 0;
}
for (t = (char *)data; ((*t) && (p < (ob + (sizeof(ob) - 1)))); t++)
*p++ = *t;
t = NULL;
}
else if (t == NULL)
*p++ = *h;
}
/*
Syntax error occured
*/
if (t) {
#ifdef DEBUG
fprintf(stderr, "send_fax: template contains unterminated variable '%s'\n", t);
#endif
fclose(stream);
fclose(out);
return 0;
}
/*
Write template-generated line to call file
*/
ret = fwrite(ob, (p - ob), 1, out);
if (ret != 1) {
#ifdef DEBUG
fprintf(stderr, "send_fax: fwrite(...) failed; wrote %d/%d bytes\n",
ret, (p - ob));
#endif
fclose(stream);
fclose(out);
return 0;
}
}
fclose(stream);
fclose(out);
/*
Call file has been generated; hand it over to Asterisk
*/
for (p = (pc_file + strlen(pc_file)); ((*p != '/') && (p > pc_file)); p--)
if (p == pc_file) {
#ifdef DEBUG
fprintf(stderr, "send_fax: unexpected syntax for call file path\n");
#endif
return 0;
}
memset(b, 0, sizeof(b));
snprintf(b, sizeof(b), "%s/%s", OUTDIR, p);
ret = rename(pc_file, b);
if (ret == -1) {
#ifdef DEBUG
fprintf(stderr, "send_fax: rename(%s,%s) failed; errno=%d\n",
pc_file, b, errno);
#endif
return 0;
}
return 1;
}
/*
Returns a set variable
*/
static const char *send_fax_variable(const char *name)
{
int i = 0;
if (f_vars_num == 0)
return NULL;
for (i = 0; i < f_vars_num; i++) {
if (!(strcasecmp(f_vars[i].name, name)))
return f_vars[i].data;
}
return NULL;
}
/*
Sets a variable
*/
static int set_fax_variable(const char *name, const char *data)
{
int len = 0;
if ((name == NULL) || (data == NULL) || (!(*name)) || (!(*data)))
return 0;
if (f_vars_num >= MAX_FAX_VARS)
return 0;
len = strlen(name);
if (len >= MAX_VAR_LEN)
len = (MAX_VAR_LEN - 1);
memcpy(f_vars[f_vars_num].name, name, len);
len = strlen(data);
if (len >= MAX_VAR_LEN)
len = (MAX_VAR_LEN - 1);
memcpy(f_vars[f_vars_num].data, data, len);
f_vars_num++;
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1