initial commit

base
TiynGER 4 years ago
commit 0b3aa2c842

36
FAQ

@ -0,0 +1,36 @@
FAQ
===
Where is IRC command xy (ignore etc.)?
--------------------------------------
ii is for advanced users, please use standard tools like awk, sed and grep for
this. This can be done easily and will not bloat the code.
Where is a graphical interface?
-------------------------------
Basically ii follows the UNIX philosophy so it is only file based. But it
should be easy to build different interface because they only have to handle
the FIFOs and output files. Feel free to implement or wait until we have done
this. Actually I use ii in combination with vim, multitail and screen and it works
like a charm.
Which commands are supported?
-----------------------------
j (join or msg), t (topic), a (away), n (nick), l (leave). The missing are
obsolete or can be easily used by typing the IRC commands itself (i.e. /WHO
instead of /who).
How can I recognize queries?
----------------------------
ii itself doesn't support this but the queries.sh script is an example
of how to get the new and changed files in your irc directory.
To get an instant notice of a new file other mechanisms like inotify/dnotify
could be used as well but I was too lazy to try it out since the script
is enough for me.
What other fancy stuff can I do with ii?
----------------------------------------
It is very easy to write irc bots in ii:
tail -f \#/out | while read foo; do name=echo $foo | awk '{print $2}' | sed 's,<\\(.*\\)>,\\1,'; if 0 -eq expr $RANDOM % 10 then echo "$name: WHAT??" ; fi; done
This will just spam a channel but think about using nagios2irc or you can
use ii to generate channel stats. Your imagination should be boundless.

@ -0,0 +1,22 @@
MIT/X Consortium License
(C)opyright MMV-MMVI Anselm R. Garbe <garbeam@wmii.de>
(C)opyright MMV-MMVIII Nico Golde <nico at ngolde dot de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

@ -0,0 +1,55 @@
# ii - irc it - simple but flexible IRC client
# (C)opyright MMV-MMVI Anselm R. Garbe
# (C)opyright MMV-MMVII Anselm R. Garbe, Nico Golde
include config.mk
SRC = ii.c
OBJ = ${SRC:.c=.o}
all: options ii
@echo built ii
options:
@echo ii build options:
@echo "LIBS = ${LIBS}"
@echo "INCLUDES = ${INCLUDES}"
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
dist: clean
@mkdir -p ii-${VERSION}
@cp -R query.sh Makefile CHANGES README FAQ LICENSE config.mk ii.c ii.1 ii-${VERSION}
@tar -cf ii-${VERSION}.tar ii-${VERSION}
@gzip ii-${VERSION}.tar
@rm -rf ii-${VERSION}
@echo created distribution ii-${VERSION}.tar.gz
ii: ${OBJ}
@echo LD $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
install: all
@mkdir -p ${DESTDIR}${DOCDIR}
@mkdir -p ${DESTDIR}${BINDIR}
@mkdir -p ${DESTDIR}${MAN1DIR}
@install -d ${DESTDIR}${BINDIR} ${DESTDIR}${MAN1DIR}
@install -m 644 CHANGES README query.sh FAQ LICENSE ${DESTDIR}${DOCDIR}
@install -m 775 ii ${DESTDIR}${BINDIR}
@install -m 444 ii.1 ${DESTDIR}${MAN1DIR}
@echo "installed ii"
uninstall: all
@rm -f ${DESTDIR}${MAN1DIR}/ii.1
@rm -rf ${DESTDIR}${DOCDIR}
@rm -f ${DESTDIR}${BINDIR}/ii
@echo "uninstalled ii"
clean:
rm -f ii *~ *.o *core *.tar.gz

@ -0,0 +1,8 @@
# ii
This is my patched version of ii.
The base version is directly from suckless.org.
This belongs to my larbs installation script, meaning it's supposed to work in the environment of the larbs-base-installation.
## Patches
The list below shows the currently applied patches to this build.
- ii-1.7-ssl.diff

@ -0,0 +1,27 @@
# Customize to fit your system
# paths
PREFIX = /usr/local
BINDIR = ${PREFIX}/bin
MANDIR = ${PREFIX}/share/man
MAN1DIR = ${MANDIR}/man1
DOCDIR = ${PREFIX}/share/doc/ii
# Set the following to install to a different root
DESTDIR =
INCDIR = ${PREFIX}/include
LIBDIR = ${PREFIX}/lib
VERSION = 1.7
# includes and libs
INCLUDES = -I. -I${INCDIR} -I/usr/include
LIBS = -L${LIBDIR} -L/usr/lib -lc
# uncomment and comment other variables for compiling on Solaris
#LIBS = -L${LIBDIR} -L/usr/lib -lc -lsocket -lnsl
#CFLAGS = -g ${INCLUDES} -DVERSION=\"${VERSION}\"
# compiler
CC = cc
CFLAGS = -g -O0 -W -Wall ${INCLUDES} -DVERSION=\"${VERSION}\"
LDFLAGS = ${LIBS}

@ -0,0 +1,225 @@
diff -up a/config.mk b/config.mk
--- a/config.mk 2013-01-05 08:26:47.000000000 -0500
+++ b/config.mk 2013-02-15 15:27:10.183075163 -0500
@@ -16,7 +16,7 @@ VERSION = 1.7
# includes and libs
INCLUDES = -I. -I${INCDIR} -I/usr/include
-LIBS = -L${LIBDIR} -L/usr/lib -lc
+LIBS = -L${LIBDIR} -L/usr/lib -lc -lssl -lcrypto
# uncomment and comment other variables for compiling on Solaris
#LIBS = -L${LIBDIR} -L/usr/lib -lc -lsocket -lnsl
#CFLAGS = -g ${INCLUDES} -DVERSION=\"${VERSION}\"
diff -up a/ii.1 b/ii.1
--- a/ii.1 2013-01-05 08:26:47.000000000 -0500
+++ b/ii.1 2013-02-15 15:28:42.739074771 -0500
@@ -25,6 +25,8 @@ and ii creates a new channel directory w
.IR servername ]
.RB [ \-p
.IR port ]
+.RB [ \-e
+.IR ssl ]
.RB [ \-k
.IR environment variable ]
.RB [ \-i
@@ -42,6 +44,9 @@ lets you override the default servername
.BI \-p " port"
lets you override the default port (6667)
.TP
+.BI \-e " ssl"
+lets you connect using ssl encryption. The default ssl port is 6697.
+.TP
.BI \-k " environment variable"
lets you specify an environment variable that contains your IRC password, e.g. IIPASS="foobar" ii -k IIPASS.
This is done in order to prevent other users from eavesdropping the server password via the process list.
diff -up a/ii.c b/ii.c
--- a/ii.c 2013-01-05 08:26:47.000000000 -0500
+++ b/ii.c 2013-02-15 15:33:39.603075095 -0500
@@ -18,12 +18,23 @@
#include <ctype.h>
#include <time.h>
#include <unistd.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
#ifndef PIPE_BUF /* FreeBSD don't know PIPE_BUF */
#define PIPE_BUF 4096
#endif
#define PING_TIMEOUT 300
#define SERVER_PORT 6667
+#define SSL_SERVER_PORT 6697
+#define WRITE(con, mes, len) (use_ssl ? SSL_write(irc->sslHandle, mes, len) : write(con->irc, mes, len))
+#define READ(fd, buf, size) (from_server && use_ssl ? SSL_read(irc->sslHandle, buf, size) : read(fd, buf, size))
+typedef struct {
+ int irc;
+ SSL *sslHandle;
+ SSL_CTX *sslContext;
+} conn;
enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST };
typedef struct Channel Channel;
@@ -33,7 +44,8 @@ struct Channel {
Channel *next;
};
-static int irc;
+conn *irc;
+static int use_ssl;
static time_t last_response;
static Channel *channels = NULL;
static char *host = "irc.freenode.net";
@@ -45,7 +57,7 @@ static void usage() {
fputs("ii - irc it - " VERSION "\n"
"(C)opyright MMV-MMVI Anselm R. Garbe\n"
"(C)opyright MMV-MMXI Nico Golde\n"
- "usage: ii [-i <irc dir>] [-s <host>] [-p <port>]\n"
+ "usage: ii [-i <irc dir>] [-s <host>] [-p <port>] [-e ssl]\n"
" [-n <nick>] [-k <password>] [-f <fullname>]\n", stderr);
exit(EXIT_FAILURE);
}
@@ -148,11 +160,12 @@ static void login(char *key, char *fulln
nick, nick, host, fullname ? fullname : nick);
else snprintf(message, PIPE_BUF, "NICK %s\r\nUSER %s localhost %s :%s\r\n",
nick, nick, host, fullname ? fullname : nick);
- write(irc, message, strlen(message)); /* login */
+ WRITE(irc, message, strlen(message)); /* login */
}
-static int tcpopen(unsigned short port) {
+conn *tcpopen(unsigned short port) {
int fd;
+ conn *c;
struct sockaddr_in sin;
struct hostent *hp = gethostbyname(host);
@@ -172,7 +185,22 @@ static int tcpopen(unsigned short port)
perror("ii: cannot connect to host");
exit(EXIT_FAILURE);
}
- return fd;
+ c = malloc(sizeof(conn));
+ c->irc = fd;
+ if(use_ssl) {
+ c->sslHandle = NULL;
+ c->sslContext = NULL;
+ SSL_load_error_strings();
+ SSL_library_init();
+ c->sslContext = SSL_CTX_new(SSLv23_client_method());
+ if(c->sslContext == NULL)
+ ERR_print_errors_fp(stderr);
+ c->sslHandle = SSL_new(c->sslContext);
+ if(!SSL_set_fd(c->sslHandle, c->irc)
+ || (SSL_connect(c->sslHandle) != 1))
+ ERR_print_errors_fp(stderr);
+ }
+ return c;
}
static size_t tokenize(char **result, size_t reslen, char *str, char delim) {
@@ -219,7 +247,7 @@ static void proc_channels_privmsg(char *
snprintf(message, PIPE_BUF, "<%s> %s", nick, buf);
print_out(channel, message);
snprintf(message, PIPE_BUF, "PRIVMSG %s :%s\r\n", channel, buf);
- write(irc, message, strlen(message));
+ WRITE(irc, message, strlen(message));
}
static void proc_channels_input(Channel *c, char *buf) {
@@ -273,7 +301,7 @@ static void proc_channels_input(Channel
else
snprintf(message, PIPE_BUF,
"PART %s :ii - 500 SLOC are too much\r\n", c->name);
- write(irc, message, strlen(message));
+ WRITE(irc, message, strlen(message));
close(c->fd);
/*create_filepath(infile, sizeof(infile), c->name, "in");
unlink(infile); */
@@ -288,7 +316,7 @@ static void proc_channels_input(Channel
snprintf(message, PIPE_BUF, "%s\r\n", &buf[1]);
if (message[0] != '\0')
- write(irc, message, strlen(message));
+ WRITE(irc, message, strlen(message));
}
static void proc_server_cmd(char *buf) {
@@ -339,7 +367,7 @@ static void proc_server_cmd(char *buf) {
return;
} else if(!strncmp("PING", argv[TOK_CMD], 5)) {
snprintf(message, PIPE_BUF, "PONG %s\r\n", argv[TOK_TEXT]);
- write(irc, message, strlen(message));
+ WRITE(irc, message, strlen(message));
return;
} else if(!argv[TOK_NICKSRV] || !argv[TOK_USER]) { /* server command */
snprintf(message, PIPE_BUF, "%s%s", argv[TOK_ARG] ? argv[TOK_ARG] : "", argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
@@ -373,11 +401,11 @@ static void proc_server_cmd(char *buf) {
print_out(argv[TOK_CHAN], message);
}
-static int read_line(int fd, size_t res_len, char *buf) {
+static int read_line(int fd, size_t res_len, char *buf, int from_server) {
size_t i = 0;
char c = 0;
do {
- if(read(fd, &c, sizeof(char)) != sizeof(char))
+ if(READ(fd, &c, sizeof(char)) != sizeof(char))
return -1;
buf[i++] = c;
}
@@ -388,7 +416,7 @@ static int read_line(int fd, size_t res_
static void handle_channels_input(Channel *c) {
static char buf[PIPE_BUF];
- if(read_line(c->fd, PIPE_BUF, buf) == -1) {
+ if(read_line(c->fd, PIPE_BUF, buf, 0) == -1) {
close(c->fd);
int fd = open_channel(c->name);
if(fd != -1)
@@ -402,7 +430,7 @@ static void handle_channels_input(Channe
static void handle_server_output() {
static char buf[PIPE_BUF];
- if(read_line(irc, PIPE_BUF, buf) == -1) {
+ if(read_line(irc->irc, PIPE_BUF, buf, 1) == -1) {
perror("ii: remote host closed connection");
exit(EXIT_FAILURE);
}
@@ -419,8 +447,8 @@ static void run() {
snprintf(ping_msg, sizeof(ping_msg), "PING %s\r\n", host);
for(;;) {
FD_ZERO(&rd);
- maxfd = irc;
- FD_SET(irc, &rd);
+ maxfd = irc->irc;
+ FD_SET(irc->irc, &rd);
for(c = channels; c; c = c->next) {
if(maxfd < c->fd)
maxfd = c->fd;
@@ -440,10 +468,10 @@ static void run() {
print_out(NULL, "-!- ii shutting down: ping timeout");
exit(EXIT_FAILURE);
}
- write(irc, ping_msg, strlen(ping_msg));
+ WRITE(irc, ping_msg, strlen(ping_msg));
continue;
}
- if(FD_ISSET(irc, &rd)) {
+ if(FD_ISSET(irc->irc, &rd)) {
handle_server_output();
last_response = time(NULL);
}
@@ -475,10 +503,13 @@ int main(int argc, char *argv[]) {
case 'p': port = strtol(argv[++i], NULL, 10); break;
case 'n': snprintf(nick,sizeof(nick),"%s", argv[++i]); break;
case 'k': key = getenv(argv[++i]); break;
+ case 'e': use_ssl = 1; ++i; break;
case 'f': fullname = argv[++i]; break;
default: usage(); break;
}
}
+ if(use_ssl)
+ port = port == SERVER_PORT ? SSL_SERVER_PORT : port;
irc = tcpopen(port);
if(!snprintf(path, sizeof(path), "%s/%s", prefix, host)) {
fputs("ii: path to irc directory too long\n", stderr);

97
ii.1

@ -0,0 +1,97 @@
.de FN
\fI\|\\$1\|\fP\\$2
..
.TH ii 1
.SH NAME
ii \- irc it or irc improved
.SH DESCRIPTION
.B ii
is a minimalistic FIFO and filesystem based IRC client.
It creates an irc directory tree with server, channel and
nick name directories.
In every directory a FIFO file (in) and and normal file (out)
is placed. This will be for example ~/irc/irc.freenode.net/.
The in file is used to communicate with the servers and the out
files includes the server messages. For every channel and every nick
name there will be new in and out files.
The basic idea of this is to be able to communicate with an IRC
server with basic command line tools.
For example if you will join a channel just do echo "/j #channel" > in
and ii creates a new channel directory with in and out file.
.SH SYNOPSIS
.B ii
.RB [ \-s
.IR servername ]
.RB [ \-p
.IR port ]
.RB [ \-k
.IR environment variable ]
.RB [ \-i
.IR prefix ]
.RB [ \-n
.IR nickname ]
.RB [ \-f
.IR realname ]
.SH OPTIONS
.TP
.BI \-s " servername"
lets you override the default servername (irc.freenode.net)
.TP
.BI \-p " port"
lets you override the default port (6667)
.TP
.BI \-k " environment variable"
lets you specify an environment variable that contains your IRC password, e.g. IIPASS="foobar" ii -k IIPASS.
This is done in order to prevent other users from eavesdropping the server password via the process list.
.TP
.BI \-i " prefix"
lets you override the default irc path (~/irc)
.TP
.BI \-n " nickname"
lets you override the default nick ($USER)
.TP
.BI \-f " realname"
lets you specify your real name associated with your nick
.SH DIRECTORIES
.TP
.FN ~/irc
In this directory the irc tree will be created. In this directory you
will find a directory for your server (default: irc.freenode.net) in
which the FIFO and the output file will be stored.
If you join a channel a new directory with the name of the channel
will be created in the ~/irc/$servername/ directory.
.SH COMMANDS
.TP
.FN /a " [<message>]"
mark yourself as away
.TP
.FN /j " #channel/nickname [<message>]"
join a channel or open private conversation with user
.TP
.FN /l " #channel/nickname"
leave a channel or query
.TP
.FN /n " nick"
change the nick name
.TP
.FN /t " topic"
set the topic of a channel
.TP
Everything which is not a command will simply be posted into the channel or to the server.
So if you need /who just write /WHO as described in the RFC to the server in FIFO.
.TP
.FN "out file usage"
Write wrappers, pagers or use your tools of choice to display the out file contents (loco, multitail, etc.).
.SH CONTACT
.TP
Write to ii (at) modprobe (dot) de for suggestions, fixes, 7|-|>< ;) etc.
.SH AUTHORS
Copyright \(co 2005-2006 by Anselm R. Garbe <garbeam (at) gmail (dot) com> and
Copyright \(co 2005-2008 by Nico Golde <nico (at) ngolde (dot) de>
.SH SEE ALSO
.BR echo (1),
.BR tail (1),

494
ii.c

@ -0,0 +1,494 @@
/* (C)opyright MMV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMV-MMXI Nico Golde <nico at ngolde dot de>
* See LICENSE file for license details. */
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#ifndef PIPE_BUF /* FreeBSD don't know PIPE_BUF */
#define PIPE_BUF 4096
#endif
#define PING_TIMEOUT 300
#define SERVER_PORT 6667
enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST };
typedef struct Channel Channel;
struct Channel {
int fd;
char *name;
Channel *next;
};
static int irc;
static time_t last_response;
static Channel *channels = NULL;
static char *host = "irc.freenode.net";
static char nick[32]; /* might change while running */
static char path[_POSIX_PATH_MAX];
static char message[PIPE_BUF]; /* message buf used for communication */
static void usage() {
fputs("ii - irc it - " VERSION "\n"
"(C)opyright MMV-MMVI Anselm R. Garbe\n"
"(C)opyright MMV-MMXI Nico Golde\n"
"usage: ii [-i <irc dir>] [-s <host>] [-p <port>]\n"
" [-n <nick>] [-k <password>] [-f <fullname>]\n", stderr);
exit(EXIT_FAILURE);
}
static char *striplower(char *s) {
char *p = NULL;
for(p = s; p && *p; p++) {
if(*p == '/') *p = ',';
*p = tolower(*p);
}
return s;
}
/* creates directories top-down, if necessary */
static void create_dirtree(const char *dir) {
char tmp[256];
char *p = NULL;
size_t len;
snprintf(tmp, sizeof(tmp),"%s",dir);
len = strlen(tmp);
if(tmp[len - 1] == '/')
tmp[len - 1] = 0;
for(p = tmp + 1; *p; p++)
if(*p == '/') {
*p = 0;
mkdir(tmp, S_IRWXU);
*p = '/';
}
mkdir(tmp, S_IRWXU);
}
static int get_filepath(char *filepath, size_t len, char *channel, char *file) {
if(channel) {
if(!snprintf(filepath, len, "%s/%s", path, channel))
return 0;
create_dirtree(filepath);
return snprintf(filepath, len, "%s/%s/%s", path, channel, file);
}
return snprintf(filepath, len, "%s/%s", path, file);
}
static void create_filepath(char *filepath, size_t len, char *channel, char *suffix) {
if(!get_filepath(filepath, len, striplower(channel), suffix)) {
fputs("ii: path to irc directory too long\n", stderr);
exit(EXIT_FAILURE);
}
}
static int open_channel(char *name) {
static char infile[256];
create_filepath(infile, sizeof(infile), name, "in");
if(access(infile, F_OK) == -1)
mkfifo(infile, S_IRWXU);
return open(infile, O_RDONLY | O_NONBLOCK, 0);
}
static void add_channel(char *cname) {
Channel *c;
int fd;
char *name = striplower(cname);
for(c = channels; c; c = c->next)
if(!strcmp(name, c->name))
return; /* already handled */
fd = open_channel(name);
if(fd == -1) {
printf("ii: exiting, cannot create in channel: %s\n", name);
exit(EXIT_FAILURE);
}
c = calloc(1, sizeof(Channel));
if(!c) {
perror("ii: cannot allocate memory");
exit(EXIT_FAILURE);
}
if(!channels) channels = c;
else {
c->next = channels;
channels = c;
}
c->fd = fd;
c->name = strdup(name);
}
static void rm_channel(Channel *c) {
Channel *p;
if(channels == c) channels = channels->next;
else {
for(p = channels; p && p->next != c; p = p->next);
if(p->next == c)
p->next = c->next;
}
free(c->name);
free(c);
}
static void login(char *key, char *fullname) {
if(key) snprintf(message, PIPE_BUF,
"PASS %s\r\nNICK %s\r\nUSER %s localhost %s :%s\r\n", key,
nick, nick, host, fullname ? fullname : nick);
else snprintf(message, PIPE_BUF, "NICK %s\r\nUSER %s localhost %s :%s\r\n",
nick, nick, host, fullname ? fullname : nick);
write(irc, message, strlen(message)); /* login */
}
static int tcpopen(unsigned short port) {
int fd;
struct sockaddr_in sin;
struct hostent *hp = gethostbyname(host);
memset(&sin, 0, sizeof(struct sockaddr_in));
if(!hp) {
perror("ii: cannot retrieve host information");
exit(EXIT_FAILURE);
}
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
sin.sin_port = htons(port);
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("ii: cannot create socket");
exit(EXIT_FAILURE);
}
if(connect(fd, (const struct sockaddr *) &sin, sizeof(sin)) < 0) {
perror("ii: cannot connect to host");
exit(EXIT_FAILURE);
}
return fd;
}
static size_t tokenize(char **result, size_t reslen, char *str, char delim) {
char *p = NULL, *n = NULL;
size_t i;
if(!str)
return 0;
for(n = str; *n == ' '; n++);
p = n;
for(i = 0; *n != 0;) {
if(i == reslen)
return 0;
if(i > TOK_CHAN - TOK_CMD && strtol(result[0], NULL, 10) > 0) delim=':'; /* workaround non-RFC compliant messages */
if(*n == delim) {
*n = 0;
result[i++] = p;
p = ++n;
} else
n++;
}
if(i<reslen && p < n && strlen(p))
result[i++] = p;
return i; /* number of tokens */
}
static void print_out(char *channel, char *buf) {
static char outfile[256], server[256], buft[18];
FILE *out = NULL;
time_t t = time(0);
if(channel) snprintf(server, sizeof(server), "-!- %s", channel);
if(strstr(buf, server)) channel="";
create_filepath(outfile, sizeof(outfile), channel, "out");
if(!(out = fopen(outfile, "a"))) return;
if(channel && channel[0]) add_channel(channel);
strftime(buft, sizeof(buft), "%F %R", localtime(&t));
fprintf(out, "%s %s\n", buft, buf);
fclose(out);
}
static void proc_channels_privmsg(char *channel, char *buf) {
snprintf(message, PIPE_BUF, "<%s> %s", nick, buf);
print_out(channel, message);
snprintf(message, PIPE_BUF, "PRIVMSG %s :%s\r\n", channel, buf);
write(irc, message, strlen(message));
}
static void proc_channels_input(Channel *c, char *buf) {
/* static char infile[256]; */
char *p = NULL;
if(buf[0] != '/' && buf[0] != 0) {
proc_channels_privmsg(c->name, buf);
return;
}
message[0] = '\0';
if(buf[2] == ' ' || buf[2] == '\0') switch (buf[1]) {
case 'j':
p = strchr(&buf[3], ' ');
if(p) *p = 0;
if((buf[3]=='#')||(buf[3]=='&')||(buf[3]=='+')||(buf[3]=='!')){
if(p) snprintf(message, PIPE_BUF, "JOIN %s %s\r\n", &buf[3], p + 1); /* password protected channel */
else snprintf(message, PIPE_BUF, "JOIN %s\r\n", &buf[3]);
add_channel(&buf[3]);
}
else if(p){
add_channel(&buf[3]);
proc_channels_privmsg(&buf[3], p + 1);
return;
}
break;
case 't':
if(strlen(buf)>=3) snprintf(message, PIPE_BUF, "TOPIC %s :%s\r\n", c->name, &buf[3]);
break;
case 'a':
if(strlen(buf)>=3){
snprintf(message, PIPE_BUF, "-!- %s is away \"%s\"", nick, &buf[3]);
print_out(c->name, message);
}
if(buf[2] == 0 || strlen(buf)<3) /* or used to make else part safe */
snprintf(message, PIPE_BUF, "AWAY\r\n");
else
snprintf(message, PIPE_BUF, "AWAY :%s\r\n", &buf[3]);
break;
case 'n':
if(strlen(buf)>=3){
snprintf(nick, sizeof(nick),"%s", &buf[3]);
snprintf(message, PIPE_BUF, "NICK %s\r\n", &buf[3]);
}
break;
case 'l':
if(c->name[0] == 0)
return;
if(buf[2] == ' ' && strlen(buf)>=3)
snprintf(message, PIPE_BUF, "PART %s :%s\r\n", c->name, &buf[3]);
else
snprintf(message, PIPE_BUF,
"PART %s :ii - 500 SLOC are too much\r\n", c->name);
write(irc, message, strlen(message));
close(c->fd);
/*create_filepath(infile, sizeof(infile), c->name, "in");
unlink(infile); */
rm_channel(c);
return;
break;
default:
snprintf(message, PIPE_BUF, "%s\r\n", &buf[1]);
break;
}
else
snprintf(message, PIPE_BUF, "%s\r\n", &buf[1]);
if (message[0] != '\0')
write(irc, message, strlen(message));
}
static void proc_server_cmd(char *buf) {
char *argv[TOK_LAST], *cmd = NULL, *p = NULL;
int i;
if(!buf || *buf=='\0')
return;
for(i = 0; i < TOK_LAST; i++)
argv[i] = NULL;
/* <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
<command> ::= <letter> { <letter> } | <number> <number> <number>
<SPACE> ::= ' ' { ' ' }
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
or NUL or CR or LF, the first of which may not be ':'>
<trailing> ::= <Any, possibly *empty*, sequence of octets not including NUL or CR or LF>
<crlf> ::= CR LF */
if(buf[0] == ':') { /* check prefix */
if (!(p = strchr(buf, ' '))) return;
*p = 0;
for(++p; *p == ' '; p++);
cmd = p;
argv[TOK_NICKSRV] = &buf[1];
if((p = strchr(buf, '!'))) {
*p = 0;
argv[TOK_USER] = ++p;
}
} else
cmd = buf;
/* remove CRLFs */
for(p = cmd; p && *p != 0; p++)
if(*p == '\r' || *p == '\n')
*p = 0;
if((p = strchr(cmd, ':'))) {
*p = 0;
argv[TOK_TEXT] = ++p;
}
tokenize(&argv[TOK_CMD], TOK_LAST - TOK_CMD, cmd, ' ');
if(!argv[TOK_CMD] || !strncmp("PONG", argv[TOK_CMD], 5)) {
return;
} else if(!strncmp("PING", argv[TOK_CMD], 5)) {
snprintf(message, PIPE_BUF, "PONG %s\r\n", argv[TOK_TEXT]);
write(irc, message, strlen(message));
return;
} else if(!argv[TOK_NICKSRV] || !argv[TOK_USER]) { /* server command */
snprintf(message, PIPE_BUF, "%s%s", argv[TOK_ARG] ? argv[TOK_ARG] : "", argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
print_out(0, message);
return;
} else if(!strncmp("ERROR", argv[TOK_CMD], 6))
snprintf(message, PIPE_BUF, "-!- error %s", argv[TOK_TEXT] ? argv[TOK_TEXT] : "unknown");
else if(!strncmp("JOIN", argv[TOK_CMD], 5)) {
if (argv[TOK_TEXT] != NULL)
argv[TOK_CHAN] = argv[TOK_TEXT];
snprintf(message, PIPE_BUF, "-!- %s(%s) has joined %s", argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]);
} else if(!strncmp("PART", argv[TOK_CMD], 5)) {
snprintf(message, PIPE_BUF, "-!- %s(%s) has left %s", argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]);
} else if(!strncmp("MODE", argv[TOK_CMD], 5))
snprintf(message, PIPE_BUF, "-!- %s changed mode/%s -> %s %s", argv[TOK_NICKSRV], argv[TOK_CMD + 1] ? argv[TOK_CMD + 1] : "" , argv[TOK_CMD + 2]? argv[TOK_CMD + 2] : "", argv[TOK_CMD + 3] ? argv[TOK_CMD + 3] : "");
else if(!strncmp("QUIT", argv[TOK_CMD], 5))
snprintf(message, PIPE_BUF, "-!- %s(%s) has quit \"%s\"", argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
else if(!strncmp("NICK", argv[TOK_CMD], 5))
snprintf(message, PIPE_BUF, "-!- %s changed nick to %s", argv[TOK_NICKSRV], argv[TOK_TEXT]);
else if(!strncmp("TOPIC", argv[TOK_CMD], 6))
snprintf(message, PIPE_BUF, "-!- %s changed topic to \"%s\"", argv[TOK_NICKSRV], argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
else if(!strncmp("KICK", argv[TOK_CMD], 5))
snprintf(message, PIPE_BUF, "-!- %s kicked %s (\"%s\")", argv[TOK_NICKSRV], argv[TOK_ARG], argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
else if(!strncmp("NOTICE", argv[TOK_CMD], 7))
snprintf(message, PIPE_BUF, "-!- \"%s\")", argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
else if(!strncmp("PRIVMSG", argv[TOK_CMD], 8))
snprintf(message, PIPE_BUF, "<%s> %s", argv[TOK_NICKSRV], argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
if(!argv[TOK_CHAN] || !strncmp(argv[TOK_CHAN], nick, strlen(nick)))
print_out(argv[TOK_NICKSRV], message);
else
print_out(argv[TOK_CHAN], message);
}
static int read_line(int fd, size_t res_len, char *buf) {
size_t i = 0;
char c = 0;
do {
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;
}
static void handle_channels_input(Channel *c) {
static char buf[PIPE_BUF];
if(read_line(c->fd, PIPE_BUF, buf) == -1) {
close(c->fd);
int fd = open_channel(c->name);
if(fd != -1)
c->fd = fd;
else
rm_channel(c);
return;
}
proc_channels_input(c, buf);
}
static void handle_server_output() {
static char buf[PIPE_BUF];
if(read_line(irc, PIPE_BUF, buf) == -1) {
perror("ii: remote host closed connection");
exit(EXIT_FAILURE);
}
proc_server_cmd(buf);
}
static void run() {
Channel *c;
int r, maxfd;
fd_set rd;
struct timeval tv;
char ping_msg[512];
snprintf(ping_msg, sizeof(ping_msg), "PING %s\r\n", host);
for(;;) {
FD_ZERO(&rd);
maxfd = irc;
FD_SET(irc, &rd);
for(c = channels; c; c = c->next) {
if(maxfd < c->fd)
maxfd = c->fd;
FD_SET(c->fd, &rd);
}
tv.tv_sec = 120;
tv.tv_usec = 0;
r = select(maxfd + 1, &rd, 0, 0, &tv);
if(r < 0) {
if(errno == EINTR)
continue;
perror("ii: error on select()");
exit(EXIT_FAILURE);
} else if(r == 0) {
if(time(NULL) - last_response >= PING_TIMEOUT) {
print_out(NULL, "-!- ii shutting down: ping timeout");
exit(EXIT_FAILURE);
}
write(irc, ping_msg, strlen(ping_msg));
continue;
}
if(FD_ISSET(irc, &rd)) {
handle_server_output();
last_response = time(NULL);
}
for(c = channels; c; c = c->next)
if(FD_ISSET(c->fd, &rd))
handle_channels_input(c);
}
}
int main(int argc, char *argv[]) {
int i;
unsigned short port = SERVER_PORT;
struct passwd *spw = getpwuid(getuid());
char *key = NULL, *fullname = NULL;
char prefix[_POSIX_PATH_MAX];
if(!spw) {
fputs("ii: getpwuid() failed\n", stderr);
exit(EXIT_FAILURE);
}
snprintf(nick, sizeof(nick), "%s", spw->pw_name);
snprintf(prefix, sizeof(prefix),"%s/irc", spw->pw_dir);
if (argc <= 1 || (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'h')) usage();
for(i = 1; (i + 1 < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'i': snprintf(prefix,sizeof(prefix),"%s", argv[++i]); break;
case 's': host = argv[++i]; break;
case 'p': port = strtol(argv[++i], NULL, 10); break;
case 'n': snprintf(nick,sizeof(nick),"%s", argv[++i]); break;
case 'k': key = getenv(argv[++i]); break;
case 'f': fullname = argv[++i]; break;
default: usage(); break;
}
}
irc = tcpopen(port);
if(!snprintf(path, sizeof(path), "%s/%s", prefix, host)) {
fputs("ii: path to irc directory too long\n", stderr);
exit(EXIT_FAILURE);
}
create_dirtree(path);
add_channel(""); /* master channel */
login(key, fullname);
run();
return EXIT_SUCCESS;
}

@ -0,0 +1,29 @@
#!/bin/sh
# ----------------------------------------------------
# Nico Golde <nico@ngolde.de>
# License: do whatever you want with this code
# Purpose: locate new queries for the ii irc client
# ----------------------------------------------------
IRCPATH=$HOME/irc
TMPFILE=$IRCPATH/queries.tmp
if [ ! -f $TMPFILE ]; then
touch $TMPFILE
fi
echo "searching new query data"
for i in `find $IRCPATH -newer $TMPFILE -name 'out'`
do
grep -v '\-!\-' $i > /dev/null 2>&1 # if file doesnt just contain server stuff
if [ $? -ne 1 ]; then
# strip server, nickserv and channel out files
echo $i | egrep -v -i "nickserv|#|$IRCPATH/(irc\.freenode\.net|irc\.oftc\.net)/out" > /dev/null 2>&1
if [ $? -ne 1 ]; then
printf "new data in: %s\n========================================================\n" "$i"
tail -5 $i
fi
fi
done
touch $TMPFILE
Loading…
Cancel
Save