blob: 98ce6598bf9c7702848ff913b090b9134d2082ac [file] [log] [blame]
/*
* Copyright (c) 2004, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: ircc.c,v 1.9 2005/02/22 22:23:08 adamdunkels Exp $
*/
#include "contiki.h"
#include "ircc.h"
#include "irc-conf.h"
#include "ircc-strings.h"
#include "petsciiconv.h"
#include <string.h>
#define PORT 6667
#define SEND_STRING(s, str) PSOCK_SEND(s, str, strlen(str))
#define ISO_space 0x20
#define ISO_bang 0x21
#define ISO_at 0x40
#define ISO_cr 0x0d
#define ISO_nl 0x0a
#define ISO_colon 0x3a
#define ISO_O 0x4f
enum {
COMMAND_NONE,
COMMAND_JOIN,
COMMAND_PART,
COMMAND_MSG,
COMMAND_ACTIONMSG,
COMMAND_LIST,
COMMAND_QUIT
};
/*---------------------------------------------------------------------------*/
void
ircc_init(void)
{
}
/*---------------------------------------------------------------------------*/
static char *
copystr(char *dest, const char *src, int n)
{
int len;
len = strlen(src);
strncpy(dest, src, n);
if(len > n) {
return dest + n;
} else {
return dest + len;
}
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(setup_connection(struct ircc_state *s))
{
char *ptr;
PSOCK_BEGIN(&s->s);
ptr = s->outputbuf;
ptr = copystr(ptr, ircc_strings_nick, sizeof(s->outputbuf));
ptr = copystr(ptr, s->nick, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_crnl_user, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, s->nick, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_contiki, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, s->server, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_colon_contiki, sizeof(s->outputbuf) - (ptr - s->outputbuf));
SEND_STRING(&s->s, s->outputbuf);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(join_channel(struct ircc_state *s))
{
PSOCK_BEGIN(&s->s);
SEND_STRING(&s->s, ircc_strings_join);
SEND_STRING(&s->s, s->channel);
SEND_STRING(&s->s, ircc_strings_crnl);
ircc_sent(s);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(part_channel(struct ircc_state *s))
{
PSOCK_BEGIN(&s->s);
SEND_STRING(&s->s, ircc_strings_part);
SEND_STRING(&s->s, s->channel);
SEND_STRING(&s->s, ircc_strings_crnl);
ircc_sent(s);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(list_channel(struct ircc_state *s))
{
PSOCK_BEGIN(&s->s);
SEND_STRING(&s->s, ircc_strings_list);
SEND_STRING(&s->s, s->channel);
SEND_STRING(&s->s, ircc_strings_crnl);
ircc_sent(s);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(send_message(struct ircc_state *s))
{
char *ptr;
PSOCK_BEGIN(&s->s);
ptr = s->outputbuf;
ptr = copystr(ptr, ircc_strings_privmsg, sizeof(s->outputbuf));
ptr = copystr(ptr, s->channel, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_colon, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, s->msg, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_crnl, sizeof(s->outputbuf) - (ptr - s->outputbuf));
SEND_STRING(&s->s, s->outputbuf);
ircc_sent(s);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(send_actionmessage(struct ircc_state *s))
{
char *ptr;
PSOCK_BEGIN(&s->s);
ptr = s->outputbuf;
ptr = copystr(ptr, ircc_strings_privmsg, sizeof(s->outputbuf));
ptr = copystr(ptr, s->channel, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_colon, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_action, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, s->msg, sizeof(s->outputbuf) - (ptr - s->outputbuf));
ptr = copystr(ptr, ircc_strings_ctcpcrnl, sizeof(s->outputbuf) - (ptr - s->outputbuf));
SEND_STRING(&s->s, s->outputbuf);
ircc_sent(s);
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
struct parse_result {
char *msg;
char *user;
char *host;
char *name;
char *command;
char *middle;
char *trailing;
};
static struct parse_result r;
static void
parse_whitespace(void)
{
while(*r.msg == ISO_space) ++r.msg;
}
static void
parse_word(void)
{
char *ptr;
ptr = strchr(r.msg, ISO_space);
if(ptr != NULL) {
r.msg = ptr;
}
}
static void
parse_user(void)
{
parse_whitespace();
r.user = r.msg;
parse_word();
*r.msg = 0;
++r.msg;
}
static void
parse_host(void)
{
parse_whitespace();
r.host = r.msg;
parse_word();
*r.msg = 0;
++r.msg;
}
static void
parse_name(void)
{
parse_whitespace();
r.name = r.msg;
parse_word();
*r.msg = 0;
++r.msg;
}
static void
parse_prefix(void)
{
parse_name();
if(*r.msg == ISO_bang) {
++r.msg;
parse_user();
}
if(*r.msg == ISO_at) {
++r.msg;
parse_host();
}
}
static void
parse_command(void)
{
parse_whitespace();
r.command = r.msg;
parse_word();
*r.msg = 0;
++r.msg;
}
/*static void
parse_trailing(void)
{
r.trailing = r.msg;
while(*r.msg != 0 && *r.msg != ISO_cr && *r.msg != ISO_nl) ++r.msg;
*r.msg = 0;
++r.msg;
}*/
static void
parse_params(void)
{
char *ptr;
parse_whitespace();
ptr = strchr(r.msg, ISO_colon);
if(ptr != NULL) {
r.trailing = ptr + 1;
ptr = strchr(ptr, ISO_cr);
if(ptr != NULL) {
*ptr = 0;
}
}
}
static void
parse(char *msg, struct parse_result *dummy)
{
r.msg = msg;
if(*r.msg == ISO_cr || *r.msg == ISO_nl) {
return;
}
if(*r.msg == ISO_colon) {
++r.msg;
parse_prefix();
}
parse_command();
parse_params();
/* printf("user %s host %s name %s command %s middle %s trailing %s\n",
r.user, r.host, r.name, r.command, r.middle, r.trailing);*/
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(handle_input(struct ircc_state *s))
{
char *ptr;
/* struct parse_result r;*/
PSOCK_BEGIN(&s->s);
PSOCK_READTO(&s->s, ISO_nl);
if(PSOCK_DATALEN(&s->s) > 0) {
s->inputbuf[PSOCK_DATALEN(&s->s)] = 0;
if(strncmp(s->inputbuf, ircc_strings_ping, 5) == 0) {
strncpy(s->outputbuf, s->inputbuf, sizeof(s->outputbuf));
/* Turn "PING" into "PONG" */
s->outputbuf[1] = ISO_O;
SEND_STRING(&s->s, s->outputbuf);
} else {
memset(&r, 0, sizeof(r));
parse(s->inputbuf, &r);
if(r.name != NULL) {
ptr = strchr(r.name, ISO_bang);
if(ptr != NULL) {
*ptr = 0;
}
}
if(r.command != NULL && strncmp(r.command, ircc_strings_join, 4) == 0) {
ircc_text_output(s, "Joined channel", r.name);
} else if(r.command != NULL && strncmp(r.command, ircc_strings_part, 4) == 0) {
ircc_text_output(s, "Left channel", r.name);
} else if(r.trailing != NULL) {
if(strncmp(r.trailing, ircc_strings_action,
strlen(ircc_strings_action)) == 0) {
ptr = strchr(&r.trailing[1], 1);
if(ptr != NULL) {
*ptr = 0;
}
ptr = &r.trailing[strlen(ircc_strings_action)];
petsciiconv_topetscii(r.name, strlen(r.name));
petsciiconv_topetscii(ptr, strlen(ptr));
ircc_text_output(s, r.name, ptr);
} else if(strncmp(r.trailing, ircc_strings_version_query,
strlen(ircc_strings_version_query)) == 0) {
if(r.name != NULL) {
strncpy(s->outputbuf, r.name, sizeof(s->outputbuf));
SEND_STRING(&s->s, ircc_strings_notice);
/* user is temporarily stored in outputbuf. */
SEND_STRING(&s->s, s->outputbuf);
SEND_STRING(&s->s, ircc_strings_colon);
SEND_STRING(&s->s, ircc_strings_version);
SEND_STRING(&s->s, ircc_strings_version_string);
SEND_STRING(&s->s, IRC_CONF_SYSTEM_STRING);
SEND_STRING(&s->s, ircc_strings_ctcpcrnl);
}
} else {
petsciiconv_topetscii(r.name, strlen(r.name));
petsciiconv_topetscii(r.trailing, strlen(r.trailing));
ircc_text_output(s, r.name, r.trailing);
}
}
}
}
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(data_or_command(struct ircc_state *s))
{
PSOCK_BEGIN(&s->s);
PSOCK_WAIT_UNTIL(&s->s, PSOCK_NEWDATA(&s->s) ||
(s->command != COMMAND_NONE));
PSOCK_END(&s->s);
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(handle_connection(struct ircc_state *s))
{
PT_BEGIN(&s->pt);
PSOCK_INIT(&s->s, s->inputbuf, sizeof(s->inputbuf) - 1);
PT_WAIT_THREAD(&s->pt, setup_connection(s));
while(1) {
PT_WAIT_UNTIL(&s->pt, data_or_command(s));
if(PSOCK_NEWDATA(&s->s)) {
PT_WAIT_THREAD(&s->pt, handle_input(s));
}
switch(s->command) {
case COMMAND_JOIN:
s->command = COMMAND_NONE;
PT_WAIT_THREAD(&s->pt, join_channel(s));
break;
case COMMAND_PART:
s->command = COMMAND_NONE;
PT_WAIT_THREAD(&s->pt, part_channel(s));
break;
case COMMAND_MSG:
s->command = COMMAND_NONE;
PT_WAIT_THREAD(&s->pt, send_message(s));
break;
case COMMAND_ACTIONMSG:
s->command = COMMAND_NONE;
PT_WAIT_THREAD(&s->pt, send_actionmessage(s));
break;
case COMMAND_LIST:
s->command = COMMAND_NONE;
PT_WAIT_THREAD(&s->pt, list_channel(s));
break;
case COMMAND_QUIT:
s->command = COMMAND_NONE;
tcp_markconn(uip_conn, NULL);
PSOCK_CLOSE(&s->s);
ek_post(EK_PROC_ID(EK_CURRENT()), EK_EVENT_REQUEST_EXIT, NULL);
PT_EXIT(&s->pt);
break;
default:
break;
}
}
PT_END(&s->pt);
}
/*---------------------------------------------------------------------------*/
void
ircc_appcall(void *s)
{
if(uip_closed() || uip_aborted() || uip_timedout()) {
ircc_closed(s);
} else if(uip_connected()) {
ircc_connected(s);
PT_INIT(&((struct ircc_state *)s)->pt);
memset(((struct ircc_state *)s)->channel, 0,
sizeof(((struct ircc_state *)s)->channel));
((struct ircc_state *)s)->command = COMMAND_NONE;
handle_connection(s);
} else if(s != NULL) {
handle_connection(s);
}
}
/*---------------------------------------------------------------------------*/
struct ircc_state *
ircc_connect(struct ircc_state *s, char *servername, u16_t *ipaddr,
char *nick)
{
s->conn = tcp_connect(ipaddr, HTONS(PORT), s);
if(s->conn == NULL) {
return NULL;
}
s->server = servername;
s->nick = nick;
return s;
}
/*---------------------------------------------------------------------------*/
void
ircc_list(struct ircc_state *s)
{
s->command = COMMAND_LIST;
}
/*---------------------------------------------------------------------------*/
void
ircc_join(struct ircc_state *s, char *channel)
{
strncpy(s->channel, channel, sizeof(s->channel));
s->command = COMMAND_JOIN;
}
/*---------------------------------------------------------------------------*/
void
ircc_part(struct ircc_state *s)
{
s->command = COMMAND_PART;
}
/*---------------------------------------------------------------------------*/
void
ircc_quit(struct ircc_state *s)
{
s->command = COMMAND_QUIT;
}
/*---------------------------------------------------------------------------*/
void
ircc_msg(struct ircc_state *s, char *msg)
{
s->msg = msg;
s->command = COMMAND_MSG;
}
/*---------------------------------------------------------------------------*/
void
ircc_actionmsg(struct ircc_state *s, char *msg)
{
s->msg = msg;
s->command = COMMAND_ACTIONMSG;
}
/*---------------------------------------------------------------------------*/