Skip to content

Instantly share code, notes, and snippets.

@aji
Created April 25, 2012 02:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aji/2485532 to your computer and use it in GitHub Desktop.
Save aji/2485532 to your computer and use it in GitHub Desktop.
/*
* libmowgli: A collection of useful routines for programming.
* linetest.c: Testing of the linebuffer
*
* Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>
* Copyright (c) 2012 Elizabeth J. Myers <elizabeth@sporksmoo.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/* CFLAGS = $(pkg-config --cflags libmowgli-2 libunicorn) */
/* LIBS = $(pkg-config --libs libmowgli-2 libunicorn) */
#include <mowgli.h>
#include <unicorn.h>
#include <stdlib.h>
mowgli_eventloop_t *base_eventloop;
char buf[512];
typedef struct {
mowgli_linebuf_t *linebuf;
irc_hook_table_t *events;
irc_hook_table_t *commands;
irc_message_t *msg;
bool prefix;
const char *rpl_dest;
const char *rpl_command;
const char *rpl_issuer;
} client_t;
void eat_line(mowgli_linebuf_t *linebuf, char *line, size_t len, void *userdata);
void write_line(mowgli_linebuf_t *linebuf, char *buf, size_t len)
{
printf("> %s\n", buf);
mowgli_linebuf_write(linebuf, buf, len);
}
void client_raw(client_t *client, char *line)
{
write_line(client->linebuf, line, strlen(line));
}
void client_rawf(client_t *client, const char *fmt, ...)
{
char buf[512];
va_list va;
va_start(va, fmt);
vsprintf(buf, fmt, va);
va_end(va);
client_raw(client, buf);
}
void client_replyf(client_t *client, bool prefix, const char *fmt, ...)
{
const char *p, *q;
char buf[512];
va_list va;
p = q = "";
if (prefix && client->prefix) {
p = client->rpl_issuer;
q = ": ";
}
va_start(va, fmt);
vsprintf(buf, fmt, va);
va_end(va);
client_rawf(client, "%s %s :%s%s%s", client->rpl_command, client->rpl_dest, p, q, buf);
}
void cmd_echo(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
if (parc == 0) {
client_replyf(client, true, "Usage: !echo <text>");
return;
}
client_replyf(client, true, "%s", parv[0]);
}
void cmd_flip(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
if (rand() % 2 == 0) {
client_replyf(client, false, "heads");
} else {
client_replyf(client, false, "tails");
}
}
void cmd_roll(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
int sides;
if (parc == 0) {
client_replyf(client, true, "Usage: !roll <sides>");
return;
}
sides = atoi(parv[0]);
if (sides < 2) {
client_replyf(client, true, "Must roll above 2 sides");
return;
}
client_replyf(client, false, "rolled %d", (rand() % sides) + 1);
}
void cmd_join(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
if (strcmp(client->rpl_issuer, "aji"))
return;
client_rawf(client, "JOIN %s", parv[0]);
}
void cmd_part(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
if (strcmp(client->rpl_issuer, "aji"))
return;
client_rawf(client, "PART %s :byebye", parv[0]);
}
void dispatch_commands(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
char *command, *arg;
char buf[512];
if (parc != 3)
return;
client->prefix = true;
client->rpl_dest = parv[1];
client->rpl_command = "PRIVMSG";
client->rpl_issuer = parv[0];
if (parv[1][0] != '#') {
client->prefix = false;
client->rpl_dest = client->rpl_issuer;
client->rpl_command = "NOTICE";
} else if (parv[2][0] != '!') {
return;
}
strcpy(buf, parv[2]);
command = buf;
if (command[0] == '!')
command++;
arg = strchr(buf, ' ');
if (arg != NULL) {
*arg++ = '\0';
irc_hook_call(client->commands, command, 1, (const char**)&arg);
} else {
irc_hook_call(client->commands, command, 0, (const char**)NULL);
}
}
void event_001(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
}
void event_ping(int parc, const char *parv[], void *priv)
{
client_t *client = priv;
client_rawf(client, "PONG :%s", parv[parc - 1]);
}
client_t * create_client(const char *server, const char *port, const char *nick, const char *user, const char *realname)
{
client_t *client;
struct addrinfo hints, *res;
bool use_ssl = false;
mowgli_vio_sockaddr_t addr;
int ret;
mowgli_linebuf_t *linebuf;
if (*port == '+')
{
port++;
use_ssl = true;
}
client = mowgli_alloc(sizeof(client_t));
linebuf = mowgli_linebuf_create(eat_line, client);
client->linebuf = linebuf;
/* allocate libunicorn structures */
client->events = irc_hook_table_create();
client->commands = irc_hook_table_create();
client->msg = irc_message_create();
/* add events and commands */
irc_hook_add(client->events, "PRIVMSG", dispatch_commands, client);
irc_hook_add(client->events, "001", event_001, client);
irc_hook_add(client->events, "PING", event_ping, client);
irc_hook_add(client->commands, "echo", cmd_echo, client);
irc_hook_add(client->commands, "flip", cmd_flip, client);
irc_hook_add(client->commands, "roll", cmd_roll, client);
irc_hook_add(client->commands, "join", cmd_join, client);
irc_hook_add(client->commands, "part", cmd_part, client);
/* Do name res */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((ret = getaddrinfo(server, port, &hints, &res)) != 0)
{
linebuf->vio->error.op = MOWGLI_VIO_ERR_OP_OTHER;
linebuf->vio->error.type = MOWGLI_VIO_ERR_ERRCODE;
linebuf->vio->error.code = ret;
mowgli_strlcpy(linebuf->vio->error.string, gai_strerror(ret), sizeof(linebuf->vio->error.string));
mowgli_vio_error(linebuf->vio);
return NULL;
}
/* Wrap the VIO object */
if (use_ssl)
{
if (mowgli_vio_openssl_setssl(linebuf->vio, NULL) != 0)
return NULL;
}
/* We have to have a socket before starting the linebuf */
if (mowgli_vio_socket(linebuf->vio, res->ai_family, res->ai_socktype, res->ai_protocol) != 0)
return NULL;
/* Attach the linebuf */
mowgli_linebuf_attach_to_eventloop(linebuf, base_eventloop);
/* Do the connect */
if (mowgli_vio_connect(linebuf->vio, mowgli_vio_sockaddr_from_struct(&addr, res->ai_addr, res->ai_addrlen)) != 0)
return NULL;
/* Write IRC handshake */
snprintf(buf, 512, "USER %s * 8 :%s", user, realname);
write_line(client->linebuf, buf, strlen(buf));
snprintf(buf, 512, "NICK %s", nick);
write_line(client->linebuf, buf, strlen(buf));
return client;
}
void eat_line(mowgli_linebuf_t *linebuf, char *line, size_t len, void *userdata)
{
char str[512];
client_t *client = userdata;
/* Avoid malicious lines -- servers shouldn't send them */
if (linebuf->flags & MOWGLI_LINEBUF_LINE_HASNULLCHAR)
return;
strncpy(str, line, sizeof(str));
str[len + 1] = '\0';
printf("-> %s\n", str);
irc_message_reset(client->msg);
irc_message_parse(client->msg, str);
/* dispatch */
irc_hook_simple_dispatch(client->events, client->msg);
return;
}
int main(int argc, const char *argv[])
{
client_t *client;
const char *serv, *port;
if (argc < 3)
{
fprintf(stderr, "Not enough arguments\n");
fprintf(stderr, "Usage: %s [server] [(+)port]\n", argv[0]);
fprintf(stderr, "For SSL, put a + in front of port\n");
return EXIT_FAILURE;
}
base_eventloop = mowgli_eventloop_create();
serv = argv[1];
port = argv[2];
client = create_client(serv, port, "libunicorn", "horned", "The libunicorn example bot that does nothing useful");
if (client == NULL)
return EXIT_FAILURE;
mowgli_eventloop_run(base_eventloop);
mowgli_free(client);
mowgli_eventloop_destroy(base_eventloop);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment