Commit ee54ea37 authored by stettberger's avatar stettberger

* Added MIME support (thanks to Pekka Riikonen)

parent f6491ff3
......@@ -2,9 +2,9 @@ CC=gcc
prefix=/usr/local
DESTDIR=
TARGET=si
SILCINCLUDE=/usr/local/silc/include
SILCLIB=/usr/local/silc/lib
FILES=si.c mit.c
SILCINCLUDE=/usr/include/silc
SILCLIB=/usr/lib/silc
FILES=si.c mit.c mm.c
CFLAGS=-I${SILCINCLUDE} -Wall -ggdb
LDFLAGS=-L${SILCLIB} -lsilc -lsilcclient -lpthread -ldl
......@@ -18,4 +18,4 @@ clean:
rm -f ${TARGET} *~ *.o *core
mit: mit.c
install:
install ${TARGET} ${DESTDIR}/${prefix}/bin
\ No newline at end of file
install ${TARGET} ${DESTDIR}/${prefix}/bin
......@@ -25,7 +25,7 @@
static char *lower(char *s) {
char *p;
char *p;
for(p = s; p && *p; p++) *p = tolower(*p);
return s;
}
......@@ -75,42 +75,6 @@ static int open_channel(char *name)
if(access(infile, F_OK) == -1)
mkfifo(infile, S_IRWXU);
return open(infile, O_RDONLY | O_NONBLOCK, 0);
}
static void add_channel(SilcChannelEntry channel) {
Channel *c;
int fd;
for(c = channels; c; c = c->next)
if(!strcmp(channel->channel_name, c->silc->channel_name))
return; /* already handled */
fd = open_channel(channel->channel_name);
if(fd == -1) {
perror("si: cannot create in channel");
return;
}
c = calloc(1, sizeof(Channel));
NULL_TEST_ARG(c, exit(1));
if(!channels)
channels = c;
else {
c->next = channels;
channels = c;
}
c->fd = fd;
c->silc=channel;
}
static void rm_channel(Channel *c) {
Channel *p;
if(channels == c)
channels = channels->next;
else
for(p = channels; p; p = p->next)
if(p->next == c)
p->next = c->next;
free(c);
}
static int read_line(int fd, size_t res_len, char *buf)
......@@ -118,35 +82,112 @@ static int read_line(int fd, size_t res_len, char *buf)
size_t i = 0;
char c;
do {
if(read(fd, &c, sizeof(char)) != sizeof(char))
return -1;
buf[i++] = c;
if(read(fd, &c, sizeof(char)) != sizeof(char))
return -1;
buf[i++] = c;
}
while(c != '\n' && i < res_len);
buf[i - 1] = 0; /* eliminates '\n' */
return 0;
}
void handle_channels_input(Channel *c) {
static char buf[PIPE_BUF];
if(read_line(c->fd, PIPE_BUF, buf) == -1) {
int fd = open_channel(c->silc->channel_name);
if(fd != -1)
c->fd = fd;
else
rm_channel(c);
return;
}
proc_channels_input(c, buf);
static void rm_channel(Channel *c)
{
Channel *p;
if(channels == c) {
c->silc->context = NULL;
channels = channels->next;
} else
for(p = channels; p; p = p->next)
if(p->next == c) {
p->silc->context = NULL;
p->next = c->next;
}
free(c);
}
static void handle_channels_input(Channel *c)
{
static char buf[PIPE_BUF];
if (read_line(c->fd, PIPE_BUF, buf) == -1) {
int fd = open_channel(c->silc->channel_name);
if(fd != -1)
c->fd = fd;
else
rm_channel(c);
return;
}
proc_channels_input(c, buf);
}
static void add_channel(SilcChannelEntry channel)
{
Channel *c;
int fd;
if (channel->context)
return; /* already handled */
fd = open_channel(channel->channel_name);
if(fd == -1) {
perror("si: cannot create in channel");
return;
}
c = calloc(1, sizeof(*c));
NULL_TEST_ARG(c, exit(1));
if(!channels)
channels = c;
else {
c->next = channels;
channels = c;
}
c->fd = fd;
c->silc=channel;
c->silc->context = c;
}
void rm_query(Query *peer) {
Query *q;
if (peer==queries) {
peer->silc->context = NULL;
queries=queries->next;
}
else
for (q=queries; q; q=q->next)
if (SILC_ID_CLIENT_COMPARE(peer->silc->id, q->next->silc->id)) {
q->silc->context = NULL;
q->next=peer->next;
}
free(peer);
}
static void handle_query_input(Query *peer)
{
static char buf[PIPE_BUF];
char *path;
if(read_line(peer->fd, PIPE_BUF, buf) == -1) {
path=malloc(strlen(peer->silc->nickname)+8);
NULL_TEST(path);
int fd = open_channel(path);
if(fd != -1)
peer->fd = fd;
else
rm_query(peer);
return;
}
proc_queries_input(peer, buf);
}
void add_query(SilcClientEntry peer) {
Query *q;
int fd;
char *path;
for ( q=queries; q; q=q->next)
if (SILC_ID_CLIENT_COMPARE(peer->id, q->silc->id))
return; /* Already handled */
if (peer->context)
return; /* Already handled */
path=malloc(strlen(peer->nickname)+8);
NULL_TEST_ARG(path,exit(1));
sprintf(path, "query/%s", peer->nickname);
......@@ -168,18 +209,7 @@ void add_query(SilcClientEntry peer) {
}
q->silc=peer;
q->fd=fd;
}
void rm_query(Query *peer) {
Query *q;
if (peer==queries) {
queries=queries->next;
}
else
for (q=queries; q; q=q->next)
if (SILC_ID_CLIENT_COMPARE(peer->silc->id, q->next->silc->id))
q->next=peer->next;
free(peer);
q->silc->context = q;
}
void add_transfer(int num, SilcUInt32 id, SilcClientEntry sender, int type) {
......@@ -206,26 +236,9 @@ void rm_transfer(Transfer *t) {
if (t==transfers) {
transfers=transfers->next;
}
else
for (tmp=transfers; tmp; tmp=tmp->next)
else
for (tmp=transfers; tmp; tmp=tmp->next)
if(tmp->next->id==t->id)
tmp->next=t->next;
free(t);
}
void handle_query_input ( Query *peer ){
static char buf[PIPE_BUF];
char *path;
if(read_line(peer->fd, PIPE_BUF, buf) == -1) {
path=malloc(strlen(peer->silc->nickname)+8);
NULL_TEST(path);
int fd = open_channel(path);
if(fd != -1)
peer->fd = fd;
else
rm_query(peer);
return;
}
proc_queries_input(peer, buf);
}
/*
mm.c
Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 2006 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
/* Multimedia routines */
SILC_TASK_CALLBACK(si_handle_data_input);
/* MIME fragment assembler */
SilcMimeAssembler mimeass = NULL;
/* Process incoming MIME message */
static void
si_process_mime(SilcClient client, SilcClientConnection conn,
SilcClientEntry sender, SilcChannelEntry channel,
SilcMessagePayload payload, SilcChannelPrivateKey key,
SilcMessageFlags flags, SilcMime mime, bool recursive)
{
const char *type;
const unsigned char *data;
SilcUInt32 data_len;
SIMime simime = NULL;
Channel *c;
Query *q;
static char outfile[256], tmp[256];
int ret;
if (!mime)
return;
/* Check for fragmented MIME message */
if (silc_mime_is_partial(mime)) {
if (!mimeass)
mimeass = silc_mime_assembler_alloc();
/* Defragment */
mime = silc_mime_assemble(mimeass, mime);
if (!mime)
/* More fragments to come */
return;
/* Process the complete message */
si_process_mime(client, conn, sender, channel, payload, key,
flags, mime, FALSE);
return;
}
/* Check for multipart message */
if (silc_mime_is_multipart(mime)) {
SilcMime p;
const char *mtype;
SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
/* Only "mixed" type supported */
if (strcmp(mtype, "mixed"))
goto out;
silc_dlist_start(parts);
while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
/* Recursively process parts */
si_process_mime(client, conn, sender, channel, payload,
key, flags, p, TRUE);
}
goto out;
}
/* Get content type and MIME data */
type = silc_mime_get_field(mime, "Content-Type");
if (!type)
goto out;
data = silc_mime_get_data(mime, &data_len);
if (!data)
goto out;
/* Process according to content type */
/* Plain text */
if (strstr(type, "text/plain")) {
/* Default is UTF-8, don't check for other charsets */
if (!strstr(type, "utf-8"))
goto out;
if (channel)
silc_channel_message(client, conn, sender, channel, payload, key,
SILC_MESSAGE_FLAG_UTF8, data, data_len);
else
silc_private_message(client, conn, sender, payload,
SILC_MESSAGE_FLAG_UTF8, data, data_len);
goto out;
}
/* Process supported types */
if (channel) {
c = channel->context;
/* Check if we support this MIME type */
for (simime = c->mimes; simime; simime = simime->next) {
const char *mtype = silc_mime_get_field(simime->mime, "Content-Type");
if (mtype && !strcmp(mtype, type))
break;
}
if (!simime)
goto out; /* Unsupported MIME type */
if (simime->out_fd < 0) {
memset(tmp, 0, sizeof(tmp));
snprintf(tmp, sizeof(tmp) - 1, "out-%s", type);
if (strrchr(tmp, '/'))
*strrchr(tmp, '/') = '-';
create_filepath(outfile, sizeof(outfile), channel->channel_name, tmp);
}
} else {
q = sender->context;
/* Check if we support this MIME type */
for (simime = q->mimes; simime; simime = simime->next) {
const char *mtype = silc_mime_get_field(simime->mime, "Content-Type");
if (mtype && !strcmp(mtype, type))
break;
}
if (!simime)
goto out; /* Unsupported MIME type */
if (simime->out_fd < 0) {
memset(tmp, 0, sizeof(tmp));
snprintf(tmp, sizeof(tmp) - 1, "%s/out-%s", sender->nickname, type);
if (strrchr(tmp, '/'))
*strrchr(tmp, '/') = '-';
create_filepath(outfile, sizeof(outfile), "query", tmp);
}
}
if (simime->out_fd < 0) {
/* Open FIFO for writing */
if (access(outfile, F_OK) == -1)
mkfifo(outfile, S_IRWXU);
simime->out_fd = open(outfile, O_WRONLY | O_NONBLOCK, 0);
if (simime->out_fd < 0)
goto out;
}
/* Write to FIFO */
while (data_len > 0) {
ret = write(simime->out_fd, data, data_len);
if (ret <= 0)
break;
data += ret;
data_len -= ret;
}
out:
if (!recursive)
silc_mime_free(mime);
}
/* Creates a data input FIFO */
static int si_open_data_fifo(char *path, char *suffix, void *context)
{
char infile[256], tmp[32];
int fd;
memset(infile, 0, sizeof(infile));
memset(tmp, 0, sizeof(tmp));
snprintf(tmp, sizeof(tmp) - 1, "in-%s", suffix);
if (strrchr(tmp, '/'))
*strrchr(tmp, '/') = '-';
create_filepath(infile, sizeof(infile), path, tmp);
/* Open FIFO */
if (access(infile, F_OK) == -1)
mkfifo(infile, S_IRWXU);
fd = open(infile, O_RDONLY | O_NONBLOCK, 0);
/* Add to scheduler */
if (fd != -1)
silc_schedule_task_add(silc_client->client->schedule, fd,
si_handle_data_input, context, 0, 0,
SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
return fd;
}
/* Task callback to handle data input from FIFO */
SILC_TASK_CALLBACK(si_handle_data_input)
{
SIMime simime = context;
Channel *c;
Query *q;
unsigned char buf[8192];
unsigned char *data;
SilcUInt32 data_len;
int ret;
DEBUG("Start");
ret = read(fd, buf, sizeof(buf));
if (ret <= 0) {
/* Writer went away, close and reopen FIFO */
close(fd);
silc_schedule_unset_listen_fd(silc_client->client->schedule, fd);
silc_schedule_task_del_by_fd(silc_client->client->schedule, fd);
if (simime->type == CHANNEL) {
c = simime->context;
si_open_data_fifo(c->silc->channel_name,
(char *)silc_mime_get_field(simime->mime,
"Content-Type"),
context);
} else {
q = simime->context;
snprintf((char *)buf, sizeof(buf) - 1, "query/%s", q->silc->nickname);
si_open_data_fifo((char *)buf, (char *)silc_mime_get_field(simime->mime, "Content-Type"),
context);
}
return;
}
/* Encode the data */
silc_mime_add_data(simime->mime, buf, ret);
data = silc_mime_encode(simime->mime, &data_len);
if (!data)
return;
/* Send the MIME data */
if (simime->type == CHANNEL) {
c = simime->context;
silc_client_send_channel_message(silc_client->client,
silc_client->conn, c->silc,
c->silc->curr_key,
SILC_MESSAGE_FLAG_DATA,
data, data_len, TRUE);
} else {
q = simime->context;
silc_client_send_private_message(silc_client->client,
silc_client->conn, q->silc,
SILC_MESSAGE_FLAG_DATA,
data, data_len, TRUE);
}
silc_free(data);
}
/* Open data FIFO for input */
static void si_open_mime(void *context, ContextType type, char *mimetype)
{
SIMime simime, mimes;
Channel *c;
Query *q;
int fd;
char path[256];
memset(path, 0, sizeof(path));
if (type == CHANNEL) {
c = context;
mimes = c->mimes;
snprintf(path, sizeof(path) - 1, "%s", c->silc->channel_name);
} else {
q = context;
mimes = q->mimes;
snprintf(path, sizeof(path) - 1, "query/%s", q->silc->nickname);
}
for (simime = mimes; simime; simime = simime->next) {
const char *mtype = silc_mime_get_field(simime->mime, "Content-Type");
if (mtype && !strcmp(mtype, mimetype))
return; /* already handled */
}
DEBUG("Input: MIME %s", mimetype);
simime = silc_calloc(1, sizeof(*simime));
if (!simime) {
perror(("si: cannot open FIFO, out of memory"));
return;
}
simime->out_fd = -1;
simime->type = type;
simime->context = context;
simime->mime = silc_mime_alloc();
if (!simime->mime) {
perror(("si: cannot open FIFO, out of memory"));
silc_free(simime);
return;
}
silc_mime_add_field(simime->mime, "MIME-Version", "1.0");
silc_mime_add_field(simime->mime, "Content-Type", mimetype);
silc_mime_add_field(simime->mime, "Content-Transfer-Encoding", "binary");
fd = si_open_data_fifo(path, mimetype, simime);
if (fd < 0) {
perror(("si: cannot open FIFO"));
silc_mime_free(simime->mime);
silc_free(simime);
return;
}
if (type == CHANNEL) {
c = context;
if (!c->mimes)
c->mimes = simime;
else {
simime->next = c->mimes;
c->mimes = simime;
}
} else {
q = context;
if (!q->mimes)
q->mimes = simime;
else {
simime->next = q->mimes;
q->mimes = simime;
}
}
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment