Stop tasting wime by lapping swetters.

This commit is contained in:
zgrep 2018-08-14 03:33:32 -04:00
parent 8b6c9697e0
commit ca232e31ea
10 changed files with 1342 additions and 0 deletions

22
ii/LICENSE Normal file
View File

@ -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.

55
ii/Makefile Normal file
View File

@ -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

27
ii/config.mk Normal file
View File

@ -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}

97
ii/ii.1 Normal file
View File

@ -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 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),

527
ii/ii.c Normal file
View File

@ -0,0 +1,527 @@
/* (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 /* For OS that doesn't includes PIPE_BUF in limits.h, FreeBSD? */
#define PIPE_BUF _POSIX_PIPE_BUF
#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 _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[21];
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 %T", 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 :byeeeee\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;
case 'm':
if(strlen(buf)>=3) {
char newbuf[PIPE_BUF];
snprintf(newbuf, PIPE_BUF, "\01ACTION %s\01", &buf[3]);
proc_channels_privmsg(c->name, newbuf);
}
return;
break;
case 'o':
if(strlen(buf)>=3) {
snprintf(message, PIPE_BUF, "NOTICE %s :%s\r\n", c->name, &buf[3]);
}
break;
case 's':
if(strlen(buf)>=3) {
snprintf(message, PIPE_BUF, "PRIVMSG %s :%s\r\n", c->name, &buf[3]);
}
break;
default:
snprintf(message, PIPE_BUF, "%s\r\n", &buf[1]);
break;
}
} else if (strlen(buf) >= 2 && buf[0] == '/' && buf[1] == '/') {
snprintf(message, PIPE_BUF, "PRIVMSG %s :%s\r\n", c->name, &buf[1]);
} 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 = strstr(cmd, " :"))) {
p++;
*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) && (argv[TOK_CHAN] || argv[TOK_TEXT])) {
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) && argv[TOK_CHAN]) {
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) && argv[TOK_TEXT] && !strcmp(_nick, argv[TOK_TEXT])) {
snprintf(nick, sizeof(nick), "%s", _nick);
snprintf(message, PIPE_BUF, "-!- changed nick to \"%s\"", nick);
print_out(NULL, message);
} else if(!strncmp("NICK", argv[TOK_CMD], 5) && argv[TOK_TEXT])
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) && argv[TOK_ARG])
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] : "");
else
return; /* can't read this message */
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, *n;
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 = n) {
n = 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;
}

1
ii/readme.txt Normal file
View File

@ -0,0 +1 @@
This is https://tools.suckless.org/ii/ but with a few modifications made by me.

6
launch Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env sh
nick="happybot"
if [ -e '/home/zgrep/offtopiabday/hateweekfile' ]; then
nick="hatebot";
fi;
ii/ii -i ~/offtopiabday -s irc.freenode.net -p 6667 -n "$nick" -f 'Happy birthday! By which I mean existential crisis.'

580
sed.py Normal file
View File

@ -0,0 +1,580 @@
#!/usr/bin/env python3
# How to read a line.
def deirc(nick, line):
action = False
if len(line) <= 3:
return action, nick, line
if line[0] == '\u200b':
if line[1] == '<':
try:
close = line.index('>')
assert(line[close+1] == ' ')
assert(not any(map(lambda x: x.isspace(), line[2:close])))
nick = line[2:close]
line = line[close+2:]
except:
pass
elif line[1:3] == '* ':
try:
close = line[3:].index(' ') + 3
assert(not any(map(lambda x: x.isspace(), line[3:close])))
nick = line[3:close]
line = line[close+1:]
action = True
except:
pass
elif line[:8] == '\x01ACTION ' and line[-1] == '\x01':
action = True
line = line[8:-1]
# Redact the nologs.
if line.startswith('[nolog]') or line.startswith('nolog:'):
line = '[REDACTED]'
return action, nick, line
# Set flags and funcs.
from sys import argv
if len(argv) != 4:
print('Usage: [Sspy] in out')
exit(1)
_, flags, fin, fout = argv
methods = []
# Read the outfile.
class ReadOut:
def __init__(self, fout=fout):
self.file = open(fout, 'rb')
self.first = True
def cat(self):
self.first = False
for line in self.file:
yield str(line[:-1], 'utf-8', 'ignore')
def tac(self):
if self.first:
self.first = False
self.file.seek(0, 2)
buffer = b''
if self.file.tell():
self.file.seek(self.file.tell() - 1)
if self.file.read(1) == b'\n':
self.file.seek(self.file.tell() - 1)
while self.file.tell():
self.file.seek(self.file.tell() - 1)
char = self.file.read(1)
if char == b'\n':
yield str(buffer[::-1], 'utf-8', 'ignore')
buffer = b''
else:
buffer += char
self.file.seek(self.file.tell() - 1)
if buffer:
yield str(buffer[::-1], 'utf-8', 'ignore')
def close(self):
self.file.close()
# Define the functions!
def build_replacer(replace):
print('| Building replacement for:', replace)
rep = [] # strings
ref = [] # references
maximum = 0
i = iter(replace)
buf = ''
for c in i:
if c == '\\':
try:
c = next(i)
except:
return None
if c == 'x':
try:
c = next(i) + next(i)
except:
print('| | Illegal "\\xNN", not enough characters?')
return None
try:
buf += chr(int(c, 16))
except:
buf += '\\x' + c
elif c == 'u':
try:
c = next(i) + next(i) + next(i) + next(i)
except:
print('| | Illegal "\\uNNNN", not enough characters?')
return None
try:
buf += chr(int(c, 16))
except:
buf += '\\u' + c
elif c.isdigit():
rep.append(buf)
buf = ''
c = int(c)
ref.append((c,False))
if c > maximum:
maximum = c
elif c == '(':
rep.append(buf)
buf = ''
for c in i:
if c == ')':
try:
buf = int(buf)
if buf < 0:
print('| | Illegal group, less than zero:', buf)
return None
ref.append((buf, False))
if buf > maximum:
maximum = buf
except:
ref.append((buf, False))
buf = ''
else:
buf += c
if len(rep) != len(ref):
print('| | Error, unbalanced parentheses to backreference.')
# Unbalanced parens.
return None
elif c == ')':
rep.append(buf)
buf = ''
for c in i:
if c == '(':
try:
buf = int(buf)
if buf < 0:
print('| | Illegal group, less than zero:', buf)
return None
ref.append((buf, True))
if buf > maximum:
maximum = buf
except:
ref.append((buf, True))
buf = ''
else:
buf += c
if len(rep) != len(ref):
print('| | Error, unbalanced parentheses to reverse of backreference.')
# Unbalanced parens.
return None
elif c in '&\\/':
buf += c
else:
buf += '\\' + c
elif c == '&':
rep.append(buf)
buf = ''
ref.append((0, False))
else:
buf += c
rep.append(buf)
def replacer(zero, numbered, named):
print('| Replacing...')
if maximum > len(numbered):
return None
numbered = [zero] + list(numbered)
result = ''
for i, (code, rev) in enumerate(ref):
result += rep[i] # add non-reference
if isinstance(code, int):
try:
addition = numbered[code]
except:
print('| | Numbered group failed:', code)
return None
else:
try:
addition = named[bytes(code, 'utf-8')]
except:
print('| | Named group failed:', code)
return None
if addition is None:
addition = ''
elif rev:
addition = addition[::-1]
result += addition
result += rep[-1] # add final non-reference
print('| | ...success!')
return result
return replacer
try:
from re2 import compile as regex
except ImportError:
print('Warning: Using re and not re2.')
from re import compile as regex
sed_match = regex(r's/((?:\\.|[^/])*)/((?:\\.|[^/])*)/([^\s~]*)(?:~(\d+))?')
def sed_test(nick, line):
# Is it a command?
match = sed_match.match(line)
if not match:
return None
search, replace, who, back = match.groups()
replace = build_replacer(replace)
if not replace:
return None
return search, replace, nick, who, back
def sed_method(search, replace, nick, who, back):
# Some things to fix.
if not back:
back = 0
else:
back = int(back)
fuzzy = True
if not who:
fuzzy = False
who = nick
elif who == 'g':
who = ''
who = who.lower()
# Turn it into a regex.
try:
search = regex(search)
except:
return None
# Now it is time to try to sed.
log = ReadOut()
for line in log.tac():
_, _, nick, line = line.split(' ', 3)
nick = nick[1:-1]
skip = False
for test, _ in methods:
if test(nick, line):
skip = True
break
if skip:
continue
action, nick, line = deirc(nick, line)
match_nick = nick.lower().replace('*', '')
if fuzzy and not match_nick.startswith(who):
continue
elif not fuzzy and match_nick != who:
continue
if action:
action = False
line = '\x01ACTION ' + line + '\x01'
if not search.search(line):
continue
if back != 0:
back -= 1
continue
log.close()
break
else:
log.close()
return None
result = ''
prev = 0
for match in search.finditer(line):
start, end = match.span()
result += line[prev:start]
prev = end
replacement = replace(line[start:end], match.groups(), match.groupdict())
if replacement is None:
return None
result += replacement
result += line[prev:]
if result[:8] == '\x01ACTION ' and result[-1] == '\x01':
action = True
result = result[8:-1]
log.close()
return action, nick + '*', result
find_match = regex(r'p([\+-]\d+)?/((?:\\.|[^/])*)/([^\s~]*)(?:~(\d+))?')
def find_test(nick, line):
# Is it a command?
match = find_match.match(line)
if not match:
return None
local, search, who, back = match.groups()
return local, search, nick, who, back
def find_method(local, search, nick, who, back):
# Some things to fix.
if not back:
back = 0
else:
back = int(back)
fuzzy = True
if not who:
fuzzy = False
who = nick
elif who == 'g':
who = ''
who = who.lower()
if not local:
local = 0
else:
local = int(local)
# Turn it into a regex.
search = regex(search)
# Now it is time to try to sed.
log = ReadOut()
for line in log.tac():
_, _, nick, line = line.split(' ', 3)
nick = nick[1:-1]
skip = False
for test, _ in methods:
if test(nick, line):
skip = True
break
if skip:
continue
action, nick, line = deirc(nick, line)
match_nick = nick.lower().replace('*', '')
if fuzzy and not match_nick.startswith(who):
continue
elif not fuzzy and match_nick != who:
continue
if action:
match_line = '\x01ACTION ' + line + '\x01'
else:
match_line = line
if not search.search(match_line):
continue
if back != 0:
back -= 1
continue
break
else:
log.close()
return None
if local == 0:
log.close()
return action, nick, line
elif local > 0:
for line in log.cat():
if local == 0:
_, _, nick, line = line.split(' ', 3)
return deirc(nick[1:-1], line)
local -= 1
elif local < 0:
for line in log.tac():
local += 1
if local == 0:
_, _, nick, line = line.split(' ', 3)
return deirc(nick[1:-1], line)
# Should never happen.
return None
def tr_expand(string):
i = iter(string)
result = ''
prev = ''
for char in i:
if prev == '\\' and char in '-\\/':
prev = ''
elif char == '-' and prev:
a = ord(prev)
b = None
try:
b = ord(next(i))
if a < b:
prev = ''.join(map(chr, range(a, b + 1)))
elif a > b:
prev = ''.join(map(chr, reversed(range(b, a + 1))))
char = ''
except:
result += prev
prev = char
if b:
char = chr(b)
else:
char = ''
continue
result += prev
prev = char
result += char
return result
tr_match = regex(r'y/((?:\\.|[^/])*)/((?:\\.|[^/])*)/([^\s~]*)(?:~(\d+))?')
def tr_test(nick, line):
# Is it a command?
match = tr_match.match(line)
if not match:
return None
start, finish, who, back = match.groups()
start = tr_expand(start)
if not start:
return None
finish = tr_expand(finish)
if not finish:
return None
if len(start) != len(finish):
return None
return start, finish, nick, who, back
def tr_method(start, finish, nick, who, back):
# Some things to fix.
translate = {a: b for (a, b) in zip(start, finish)}
if not back:
back = 0
else:
back = int(back)
fuzzy = True
if not who:
fuzzy = False
who = nick
elif who == 'g':
who = ''
who = who.lower()
# Now it is time to try to tr.
log = ReadOut()
for line in log.tac():
_, _, nick, line = line.split(' ', 3)
nick = nick[1:-1]
skip = False
for test, _ in methods:
if test(nick, line):
skip = True
break
if skip:
continue
action, nick, line = deirc(nick, line)
match_nick = nick.lower().replace('*', '')
if fuzzy and not match_nick.startswith(who):
continue
elif not fuzzy and match_nick != who:
continue
result = ''
for c in line:
result += translate.get(c, c)
if result == line:
continue
if back != 0:
back -= 1
continue
log.close()
break
else:
log.close()
return None
return action, nick + '*', result
# Flags! Which do we enable? What do we append?
for flag, test, method in (
('s', sed_test, sed_method),
('p', find_test, find_method),
('y', tr_test, tr_method)
):
if flag.lower() in flags:
methods.append((test, method))
elif flag.upper() in flags:
methods.append((test, lambda *a: None))
# Execute a command.
from subprocess import Popen, PIPE
def cmd(args):
proc = Popen(args, stdout=PIPE)
while True:
line = proc.stdout.readline()
if line:
try:
yield str(line[:-1], 'utf-8', 'ignore')
except:
pass
else:
break
# Do the thing!
from time import sleep
def begin():
for line in cmd(['tail', '-n', '0', '-f', fout]):
_, _, nick, line = line.split(' ', 3)
nick = nick[1:-1]
# Ignore actions.
if line[:8] == '\x01ACTION ' and line[-1] == '\x01':
continue
# Ignore bots and nologs.
if line[0] == '\u200b':
continue
if line.startswith('[nolog]') or line.startswith('nolog:'):
continue
# Try it out.
for test, method in methods:
print('Testing', method.__name__, 'with:', line)
result = test(nick, line)
if not result:
print('| Test complete, yet invalid.')
continue
print('| Test complete and valid.\nProceeding with method', method.__name__ + '.')
result = method(*result)
print('| Method complete.')
if not result:
continue
action, nick, line = result
if action:
reply = '* ' + nick + ' ' + line
else:
reply = '<' + nick + '> ' + line
print('| It is valid! Sending:', reply)
with open(fin, 'w') as fh:
fh.write('\u200b' + reply + '\n')
sleep(0.5)
begin()

3
talk Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
echo -e "$(cat /home/zgrep/zwsp)\x02\x0311,01$@" >> "/home/zgrep/offtopiabday/irc.freenode.net/#offtopia/in";

24
zwspcheck.sh Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env ash
fin="irc.freenode.net/$1/in";
fout="irc.freenode.net/$1/out";
tail -n0 -f "$fout" | while read x; do
y=`echo "$x" | tr 'A-Z' 'a-z' | grep 'zwspcheck'`;
if [ ! -z "$y" ]; then
n=`echo "$x" | sed 's/^[^<]*<//' | sed 's/>.*//'`
m="your";
z=`echo "$y" | grep 'zwspcheck~[0-9][0-9]*'`
nn='';
if [ ! -z "$z" ]; then
z=`echo "$y" | sed 's/.*zwspcheck~//' | sed 's/[^0-9].*//'`
z=`echo "$z+1" | bc`
m="that";
x=`tac "$fout" | grep -E -v '^[0-9\-]+ [0-9:]+ -!-' | grep -F -A "$z" -x -e "$x" | tail -n1`
nn=`echo "$x" | sed 's/^[^<]*<//' | sed 's/>.*//'`
nn=" by $nn";
fi;
z=`echo -n "$x" | sed "s/[^$(cat ~/zwsp)]//g" | sed "s/$(cat ~/zwsp)/z/g" | wc -c`;
s='s'; if [ "$z" -eq "1" ]; then s=''; fi
echo "$(cat ~/zwsp)$n: $z zwsp$s found in $m message$nn." > "$fin";
fi;
done