Compare commits

...

79 Commits
0.5 ... master

Author SHA1 Message Date
Alexander Popov a665c369e3
change default server 2022-09-24 14:27:42 +03:00
Alexander Popov f7b307bb34
update (c) 2022-09-24 13:49:53 +03:00
Hiltjo Posthuma 058547e707 util: trim() fix for UB on pointer arithmetic
Follow-up fix on commit df4c061136

"
While it is true reversing the condition solves a single-byte read at
one before s, there is a second instance of UB.

Having a pointer to one before an object is in of itself UB in C, it's
on the side of language lawyering, but it's UB.
I add here a quote from a C standard draft:
> When an expression that has integer type is added to or subtracted
> from a pointer, the result has the type of the pointer operand.
> If both the pointer operand and the result point to elements of the
> same array object, or one past the last element of the array object,
> the evaluation shall not produce an overflow; otherwise, the
> behavior is undefined.
Taken from: http://www.iso-9899.info/n1570.html#6.5.6p8
"

Thanks Guilherme Janczak <guilherme.janczak@yandex.com>
2021-05-06 12:20:30 +02:00
Hiltjo Posthuma 68c1c4e511 README: fix a minor typo and make a small rewording 2021-05-06 01:47:08 +02:00
Hiltjo Posthuma f2c5daa9fe Makefile improvements
- Respect system/port flags by default.
- Be verbose and do not hide output. This makes it easier to debug a build.
- Remove the "options" target.
- Remove config.mk: just edit the Makefile or override flags if needed.
- dist: no need to clean before packaging files.
- dist: pipe directly to gzip without an intermediate tarball file.
- Define and add a POSIX marker to the Makefile.
2021-05-06 01:45:51 +02:00
Hiltjo Posthuma 81533f966e util: dial: no need for a static struct hints 2021-05-06 01:16:13 +02:00
Hiltjo Posthuma b188c78432 util: fix a shadowed variable name `srv` 2021-05-06 01:14:21 +02:00
Hiltjo Posthuma d9bda20849 fix undefined behaviour of using isspace ctype function
cast all ctype(3) functions argument to (unsigned char) to avoid UB

POSIX says:
"The c argument is an int, the value of which the application shall ensure is a
character representable as an unsigned char or equal to the value of the macro
EOF. If the argument has any other value, the behavior is undefined."

Many libc cast implicitly the value, but NetBSD for example does not, which is
probably the correct thing to interpret it.
2021-05-06 01:09:21 +02:00
Hiltjo Posthuma df4c061136 fix an out-of-bounds read if the input is ""
Notified by Guilherme Janczak <guilherme.janczak@yandex.com>, thanks!
2021-05-06 01:04:35 +02:00
Hiltjo Posthuma ec293427a0 fix include: include sys/select.h for select(2) and FD_(ZERO|SET)
Reported by nonterminal on IRC, thanks!
2020-10-11 14:51:35 +02:00
Anselm R Garbe 9bb34de449 added Joerg Jung's pledge patch 2017-03-18 19:24:55 +01:00
Anselm R Garbe 7f0141bbe9 applied Troels unbuffer stdin patch, thanks 2014-12-22 11:34:35 +01:00
Anselm R Garbe 6ae3c37e30 fix dist target, thanks Dimitris! 2014-11-19 14:28:49 +01:00
Anselm R Garbe 6d4fd01fc1 missed to remove old .hgtags 2014-11-14 16:07:17 +01:00
Anselm R Garbe 6703fe4592 applied 3 additional patches by Dimitris 2014-11-14 16:06:28 +01:00
Anselm R Garbe 5eaa0fff0d applied Eric's follow up patch 2014-11-12 11:18:50 +01:00
Anselm R Garbe f87d3f5bef applied Eric's config.def.h patch 2014-11-02 09:23:48 +01:00
Anselm R Garbe 7cfa52d6eb version bump 2013-08-02 22:45:08 +02:00
Anselm R Garbe 16c1c2df60 fixed a silly bug, reported by several people, including Mark Edgar 2013-08-02 22:20:48 +02:00
Anselm R Garbe fe525b3209 prepared a new release 2013-05-05 17:42:52 +02:00
Connor Lane Smith a28f8b49c8 a few corrections to manpage 2012-04-20 00:15:32 +01:00
anselm@garbe.us bade9ccb6c reversed the -u switch 2012-02-05 16:47:38 +01:00
garbeam@gmail.com 2831ca36c3 applied Quentin Carbonneaux's user patch 2012-01-22 18:27:17 +01:00
Anselm R Garbe 3b5d848cd4 applied Jeroen's manpage patch, thanks! 2011-04-08 12:51:16 +00:00
Anselm R Garbe efb5435694 applied Dimitris patches, thanks 2011-03-06 07:37:21 +00:00
anselm@garbe.us fdaf2adbc9 fixed issue reported by serge on irc, util.c was excluded in dist target 2010-08-06 09:52:12 +01:00
Kris Maglione a4ba8e3b08 Added tag 1.1 for changeset d6140e3685b8 2010-05-25 12:03:25 -04:00
Anselm R Garbe 6af1b812ae cleanup 2010-05-14 18:01:20 +01:00
Anselm R Garbe 75d42255f2 applied anonymous cleanup patch, thanks anonymous! 2010-04-17 11:12:54 +01:00
Anselm R Garbe dad75aa6ba removed debug line 2010-03-23 18:00:37 +00:00
anselm@garbe.us d78d10d1a1 bugfix 2010-03-22 13:29:33 +00:00
Anselm R Garbe 1a4256ef71 merged kris' changes into mainstream sic 2009-11-28 12:16:35 +00:00
Anselm R Garbe 9c7027fcbf applied clamiax' patch for null messages 2009-10-30 09:34:59 +00:00
Kris Maglione 7f1961d4c6 Update sic/util.c to match sic.c tip. 2009-09-23 12:52:13 -04:00
Kris Maglione 2b85380411 Update kris/util.c strlcpy 2009-09-23 11:26:48 -04:00
Anselm R Garbe 14e430ac5b added kris' sic.c and util.c temporarily, will need some time to see what will and won't be integrated into mainstream sic.c 2009-09-23 14:32:20 +01:00
Anselm R Garbe 261dc71e58 applied portions of Mark Edgars patch 2009-09-23 14:21:03 +01:00
Anselm R Garbe 4a4f924fee applied Martin Kopta's splint patch 2009-08-07 08:37:09 +01:00
Anselm R Garbe b3ed5f414f thanks to Matthias-Christian Ott for this hint 2009-02-21 19:14:13 +00:00
a@null ca6ba9a64a applied Jeroen Schot's patch 2009-02-14 11:53:47 +00:00
a@null f3827eec2a applied Matthias-Christian Ott's remark about trailing newlines 2009-02-14 11:42:45 +00:00
Anselm R Garbe 8b2eeee541 Added tag 1.0 for changeset b8794f3ed15e 2008-07-29 19:22:08 +01:00
Anselm R Garbe 2a372e2df1 made Makefile consistent 2008-07-29 19:18:03 +01:00
Anselm R Garbe 888ab22725 updated LICENSE 2008-07-29 19:11:07 +01:00
anselm@aab 8a58b25cb7 updated sic 2008-07-06 13:29:19 +01:00
Anselm R Garbe 589cce35c5 minor update 2008-07-02 21:29:57 +01:00
Anselm R. Garbe 5463bed7ed updating copyright notice in sic as well 2007-04-13 11:50:51 +02:00
Anselm R. Garbe aa40ba1f3d applied a fix inspired by recent wmii commit 2007-02-21 11:00:12 +01:00
Anselm R. Garbe ef99e720ff removed LD as well, this is misleading 2007-02-14 10:05:41 +01:00
Anselm R. Garbe f9f6f8a19c Added tag 0.9 for changeset 96eb1bfede5b 2007-02-13 17:02:42 +01:00
Anselm R. Garbe 82946e102d setting the topic must not be supported, its a trivial IRC command (*and rarely used btw*) 2007-02-13 16:14:52 +01:00
arg@suckless.org 7ef7529687 I believe I have fixed the issue, why sic didn't handled PINGs correctly 2007-02-13 16:06:28 +01:00
Anselm R. Garbe bcda402396 fixed sic man page 2007-02-09 17:07:29 +01:00
Anselm R. Garbe 1160be7ffa removed fullname, added support for tracking NICK changes 2007-02-09 16:16:06 +01:00
Anselm R. Garbe 75f0af7c42 added eprint() 2007-02-09 16:04:49 +01:00
Anselm R. Garbe 81238e16fe consistency patch 2007-02-09 15:40:58 +01:00
Anselm R. Garbe 60fc462aa9 changing the special command operator 2007-02-09 15:18:08 +01:00
Anselm R. Garbe 9b093ac5c6 removed debug output 2007-02-09 15:04:57 +01:00
Anselm R. Garbe 264f8e9422 changed output 2007-02-09 15:02:56 +01:00
Anselm R. Garbe fe1e3560be small fix 2007-02-09 14:42:40 +01:00
Anselm R. Garbe 1edd94af86 some changes 2007-02-09 14:40:11 +01:00
Anselm R. Garbe a3abf3b3ef Don't use sic's hg tip, it is broken atm... 2007-02-08 20:42:29 +01:00
Anselm R. Garbe e3cbbe71c2 adding debug support 2007-02-08 15:32:51 +01:00
Anselm R. Garbe 93842a5fe0 fixed part message 2007-02-08 15:10:47 +01:00
Anselm R. Garbe 1f1228547c small fix 2007-02-08 15:10:10 +01:00
Anselm R. Garbe ea71b6e88e small fix, don't write message to server if channel is not given 2007-02-08 15:03:42 +01:00
Anselm R. Garbe cb68ce9e2c some more simplifications 2007-02-08 14:51:04 +01:00
Anselm R. Garbe 5569a14e8e testing some simplifications 2007-02-08 14:27:39 +01:00
Anselm R. Garbe 2e945e48fb small changes, bigger simplifications are on its way 2007-02-06 16:42:17 +01:00
Anselm R. Garbe a63ccb4600 Added tag 0.8 for changeset 07fb3efaa2e9 2007-02-01 08:22:31 +01:00
Anselm R. Garbe 0a903a0c79 using Tcmd successors more carefully 2007-01-31 11:01:34 +01:00
Anselm R. Garbe 8edb0e23f4 Added tag 0.7 for changeset 643a6e8b8634 2007-01-11 11:44:41 +01:00
Anselm R. Garbe 44e0417e14 applied some segfault patches of Adrien Krunch Kunysz (thanks to Adrien!) 2007-01-11 11:35:02 +01:00
arg@mig29 148029905b V7 2006-12-14 16:52:12 +01:00
arg@mig29 f8d2e7f236 fixed sizeof stuff 2006-11-26 15:53:33 +01:00
arg@mig29 dc15c0d639 Added tag 0.6 for changeset d7923d9e717c1c6f1ed3b17ec90bfdd7e7bfcca0 2006-11-09 17:38:01 +01:00
arg@mig29 73e181ce5b fixed a small grammar typo 2006-11-09 17:37:57 +01:00
arg@mig29 9197fee8a9 new time info 2006-11-03 14:32:05 +01:00
arg@mig29 45ff5ccf31 Added tag 0.5 for changeset 70d49a37b35695f2f771bddaf309f05ea60af8bc 2006-10-26 12:16:42 +02:00
13 changed files with 471 additions and 385 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{*.c,*.h}]
indent_style = tab
indent_size = 4
[*.md]
trim_trailing_whitespace = false
indent_style = tab
[Makefile]
indent_style = tab
indent_size = 4

View File

@ -1,4 +0,0 @@
de32c537aaf66554894712563ffba8d9bc4c2714 0.1
56350a01f27753cfbdbb3dbc25f2a53dd4c2ac45 0.2
d77f00af559258679a0fad5d264685d663e6975a 0.3
a3549fb4c72ff0edb816c8c29be7ff289db5b003 0.4

View File

@ -1,7 +1,10 @@
MIT/X Consortium License
(C)opyright MMV-MMVI Anselm R. Garbe <garbeam@gmail.com>
(C)opyright MMV Nico Golde <nico at ngolde dot de>
© 2022 Alexander Popov <iiiypuk@fastmail.fm>
© 2005-2017 Anselm R Garbe <anselm@garbe.us>
© 2008-2009 Jeroen Schot <schot@a-eskwadraat.nl>
© 2007-2009 Kris Maglione <maglione.k@gmail.com>
© 2005 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"),

View File

@ -1,57 +1,59 @@
# sic - simple irc client
# (C)opyright MMVI Anselm R. Garbe
.POSIX:
include config.mk
NAME = sic
VERSION = 1.3
SRC = sic.c
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
# use system flags.
SIC_CFLAGS = ${CFLAGS}
SIC_LDFLAGS = ${LDFLAGS}
SIC_CPPFLAGS = ${LDFLAGS} -DVERSION=\"${VERSION}\" -D_GNU_SOURCE
BIN = sic
SRC = ${BIN:=.c}
OBJ = ${SRC:.c=.o}
MAN1 = ${BIN:=.1}
all: options sic
all: ${BIN}
options:
@echo sic build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@echo "LD = ${LD}"
${BIN}: ${@:=.o}
${OBJ}: config.h strlcpy.c util.c
.o:
${CC} -o $@ $< ${SIC_LDFLAGS}
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${CC} -c ${SIC_CFLAGS} ${SIC_CPPFLAGS} -o $@ -c $<
${OBJ}: config.mk
sic: ${OBJ}
@echo LD $@
@${LD} -o $@ ${OBJ} ${LDFLAGS}
@strip $@
config.h:
cp config.def.h $@
clean:
@echo cleaning
@rm -f sic ${OBJ} sic-${VERSION}.tar.gz
rm -f ${BIN} ${OBJ} "${NAME}-${VERSION}.tar.gz"
dist: clean
@echo creating dist tarball
@mkdir -p sic-${VERSION}
@cp -R LICENSE Makefile README config.mk sic.1 ${SRC} sic-${VERSION}
@tar -cf sic-${VERSION}.tar sic-${VERSION}
@gzip sic-${VERSION}.tar
@rm -rf sic-${VERSION}
dist:
mkdir -p "${NAME}-${VERSION}"
cp -fR LICENSE Makefile README arg.h config.def.h \
${MAN1} ${SRC} util.c strlcpy.c "${NAME}-${VERSION}"
tar -cf - "${NAME}-${VERSION}" | \
gzip -c > "${NAME}-${VERSION}.tar.gz"
rm -rf "${NAME}-${VERSION}"
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f sic ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/sic
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed 's/VERSION/${VERSION}/g' < sic.1 > ${DESTDIR}${MANPREFIX}/man1/sic.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/sic.1
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f ${BIN} "${DESTDIR}${PREFIX}/bin"
chmod 755 "${DESTDIR}${PREFIX}/bin/${BIN}"
mkdir -p "${DESTDIR}${MANPREFIX}/man1"
sed "s/VERSION/${VERSION}/g" < ${MAN1} > "${DESTDIR}${MANPREFIX}/man1/${MAN1}"
chmod 644 "${DESTDIR}${MANPREFIX}/man1/${MAN1}"
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/sic
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/sic.1
rm -f \
"${DESTDIR}${PREFIX}/bin/${BIN}"\
"${DESTDIR}${MANPREFIX}/man1/${MAN1}"
.PHONY: all options clean dist install uninstall
.PHONY: all clean dist install uninstall

22
README
View File

@ -1,22 +0,0 @@
sic - simple irc client
=======================
sic is an extremly fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It multiplexes
also all channel traffic into one output, that you don't have to switch
different channel buffers, that's actually a feature.
Installation
------------
Edit config.mk to match your local setup. sic is installed into
/usr/local by default.
Afterwards enter the following command to build and install sic
(if necessary as root):
$ make clean install
Running sic
-----------
Simply invoke the 'sic' command with the required arguments.

23
README.md Normal file
View File

@ -0,0 +1,23 @@
sic - simple irc client
=======================
sic is an extremely fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It multiplexes
also all channel traffic into one output so that you don't have to switch
different channel buffers: that's actually a feature.
Installation
------------
Edit the Makefile or override the flags to match your local setup. sic is
installed into /usr/local by default.
Afterwards enter the following command to build and install sic
(if necessary as root):
$ make
# make install
Running sic
-----------
Simply invoke the `sic` command with the required arguments.

63
arg.h Normal file
View File

@ -0,0 +1,63 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][1]\
&& argv[0][0] == '-';\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
/* Handles obsolete -NUM syntax */
#define ARGNUM case '0':\
case '1':\
case '2':\
case '3':\
case '4':\
case '5':\
case '6':\
case '7':\
case '8':\
case '9'
#define ARGEND }\
}
#define ARGC() argc_
#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base)))
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#endif

14
config.def.h Normal file
View File

@ -0,0 +1,14 @@
/* Host used when "-h" is not given */
#define DEFAULT_HOST "iiiypuk.me"
/* Port used when "-p" is not given */
#define DEFAULT_PORT "6667"
/* Timestamp format; see strftime(3). */
#define TIMESTAMP_FORMAT "%Y-%m-%d %R"
/* Command prefix character. In most IRC clients this is '/'. */
#define COMMAND_PREFIX_CHARACTER ':'
/* Parting message used when none is specified with ":l ..." command. */
#define DEFAULT_PARTING_MESSAGE "sic - 250 LOC are too much!"

View File

@ -1,22 +0,0 @@
# sic version
VERSION = 0.5
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
# includes and libs
INCS = -I. -I/usr/include
LIBS = -L/usr/lib -lc
# flags
CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
LDFLAGS = ${LIBS}
#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
#LDFLAGS = -g ${LIBS}
# compiler and linker
CC = cc
LD = ${CC}

61
sic.1
View File

@ -3,53 +3,52 @@
sic \- simple irc client
.SH SYNOPSIS
.B sic
.RB [ \-s " <server>"]
.RB [ \-p " <port>"]
.RB [ \-n " <nick>"]
.RB [ \-k " <keyword>"]
.RB [ \-f " <fullname>"]
.RB [ \-h
.IR host ]
.RB [ \-p
.IR port ]
.RB [ \-n
.IR nick ]
.RB [ \-k
.IR pass ]
.RB [ \-v ]
.SH DESCRIPTION
.B sic
is an extremly fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It multiplexes
also all channel traffic into one output, that you don't have to switch
different channel buffers, that's actually a feature.
is an extremely fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It also
multiplexes all channel traffic into one output. That way you don't have to
switch different channel buffers. So that's actually a feature.
.SH OPTIONS
.TP
.B \-s <server>
Overrides the default server (irc.oftc.net)
.BI \-h " host"
Overrides the default host (irc.oftc.net)
.TP
.B \-p <port>
.BI \-p " port"
Overrides the default port (6667)
.TP
.B \-n <nickname>
.BI \-n " nick"
Override the default nick ($USER)
.TP
.B \-k <keyword>
Specifies the keyword to authenticate your nick on the server
.BI \-k " pass"
Specifies the PASS connection password
.TP
.B \-f <fullname>
Specify the real name (default is $USER)
.TP
.BI \-v
Prints version information to standard output, then exits.
.B \-v
Prints version information to stderr, then exits
.SH COMMANDS
.TP
.B /j #channel
.BI :j " #channel"
Join a channel
.TP
.B /l #channel
.BI :l " #channel"
Leave a channel
.TP
.B /m #channel/user msg
Write a message to #channel/user
.BI :m " #channel/user message"
Send a message to channel or user
.TP
.B /s #channel/user
Set default channel/user
.BI :s " #channel/user"
Set default channel or user
.TP
.B /t topic
Set the channel topic
.TP
Everything which is not a command will simply be posted into the channel or to
the server.
.BI : command
Any line beginning with
.B :
is sent as a command

423
sic.c
View File

@ -1,325 +1,222 @@
/* (C)opyright MMV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMV-MMVI Nico Golde <nico at ngolde dot de>
* See LICENSE file for license details.
*/
/* See LICENSE file for license details. */
#include <sys/select.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#define PINGTIMEOUT 300
#define MAXMSG 4096
#include "arg.h"
#include "config.h"
enum { Tnick, Tuser, Tcmd, Tchan, Targ, Ttext, Tlast };
static char *server = "irc.oftc.net";
static unsigned short port = 6667;
static char *nick = NULL;
static char *fullname = NULL;
static char *password = NULL;
static char bufin[MAXMSG], bufout[MAXMSG];
char *argv0;
static char *host = DEFAULT_HOST;
static char *port = DEFAULT_PORT;
static char *password;
static char nick[32];
static char bufin[4096];
static char bufout[4096];
static char channel[256];
static int srv;
static time_t trespond;
static FILE *srv;
static int
getline(int fd, unsigned int len, char *buf) {
unsigned int i = 0;
char c;
#undef strlcpy
#include "strlcpy.c"
#include "util.c"
do {
if(read(fd, &c, sizeof(char)) != sizeof(char))
return -1;
buf[i++] = c;
}
while(c != '\n' && i < len);
buf[i - 1] = 0;
return 0;
static void
pout(char *channel, char *fmt, ...) {
static char timestr[80];
time_t t;
va_list ap;
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
t = time(NULL);
strftime(timestr, sizeof timestr, TIMESTAMP_FORMAT, localtime(&t));
fprintf(stdout, "%-12s: %s %s\n", channel, timestr, bufout);
}
static void
pout(char *channel, char *msg) {
static char timestr[18];
time_t t = time(0);
sout(char *fmt, ...) {
va_list ap;
strftime(timestr, sizeof(timestr), "%a %R", localtime(&t));
fprintf(stdout, "%-8.8s: %s %s\n", channel, timestr, msg);
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
fprintf(srv, "%s\r\n", bufout);
}
static void
privmsg(char *channel, char *msg) {
snprintf(bufout, sizeof(bufout), "<%s> %s", nick, msg);
pout(channel, bufout);
snprintf(bufout, sizeof(bufout), "PRIVMSG %s :%s\r\n", channel, msg);
write(srv, bufout, strlen(bufout));
if(channel[0] == '\0') {
pout("", "No channel to send to");
return;
}
pout(channel, "<%s> %s", nick, msg);
sout("PRIVMSG %s :%s", channel, msg);
}
static void
parsein(char *msg) {
char *p;
parsein(char *s) {
char c, *p;
if(msg[0] == 0)
if(s[0] == '\0')
return;
if(msg[0] != '/') {
privmsg(channel, msg);
skip(s, '\n');
if(s[0] != COMMAND_PREFIX_CHARACTER) {
privmsg(channel, s);
return;
}
if(!strncmp(msg + 1, "j ", 2) && (msg[3] == '#'))
snprintf(bufout, sizeof(bufout), "JOIN %s\r\n", &msg[3]);
else if(!strncmp(msg + 1, "l ", 2))
snprintf(bufout, sizeof(bufout), "PART %s :sic\r\n", &msg[3]);
else if(!strncmp(msg + 1, "m ", 2)) {
if((p = strchr(&msg[3], ' ')))
*(p++) = 0;
privmsg(&msg[3], p);
return;
c = *++s;
if(c != '\0' && isspace((unsigned char)s[1])) {
p = s + 2;
switch(c) {
case 'j':
sout("JOIN %s", p);
if(channel[0] == '\0')
strlcpy(channel, p, sizeof channel);
return;
case 'l':
s = eat(p, isspace, 1);
p = eat(s, isspace, 0);
if(!*s)
s = channel;
if(*p)
*p++ = '\0';
if(!*p)
p = DEFAULT_PARTING_MESSAGE;
sout("PART %s :%s", s, p);
return;
case 'm':
s = eat(p, isspace, 1);
p = eat(s, isspace, 0);
if(*p)
*p++ = '\0';
privmsg(s, p);
return;
case 's':
strlcpy(channel, p, sizeof channel);
return;
}
}
else if(!strncmp(msg + 1, "s ", 2)) {
strncpy(channel, &msg[3], sizeof(channel));
return;
}
else if(!strncmp(msg + 1, "t ", 2)) {
if((p = strchr(&msg[3], ' ')))
*(p++) = 0;
snprintf(bufout, sizeof(bufout), "TOPIC %s :%s\r\n", &msg[3], p);
}
else
snprintf(bufout, sizeof(bufout), "%s\r\n", &msg[1]);
write(srv, bufout, strlen(bufout));
}
static unsigned int
tokenize(char **result, unsigned int reslen, char *str, char delim) {
char *p, *n;
unsigned int i = 0;
if(!str)
return 0;
for(n = str; *n == delim; n++);
p = n;
for(i = 0; *n != 0;) {
if(i == reslen)
return i;
if(*n == delim) {
*n = 0;
if(strlen(p))
result[i++] = p;
p = ++n;
} else
n++;
}
if((i < reslen) && (p < n) && strlen(p))
result[i++] = p;
return i; /* number of tokens */
sout("%s", s);
}
static void
parsesrv(char *msg) {
char *argv[Tlast], *cmd, *p;
int i;
parsesrv(char *cmd) {
char *usr, *par, *txt;
if(!msg || !(*msg))
usr = host;
if(!cmd || !*cmd)
return;
if(cmd[0] == ':') {
usr = cmd + 1;
cmd = skip(usr, ' ');
if(cmd[0] == '\0')
return;
skip(usr, '!');
}
skip(cmd, '\r');
par = skip(cmd, ' ');
txt = skip(par, ':');
trim(par);
if(!strcmp("PONG", cmd))
return;
if(!strcmp("PRIVMSG", cmd))
pout(par, "<%s> %s", usr, txt);
else if(!strcmp("PING", cmd))
sout("PONG %s", txt);
else {
pout(usr, ">< %s (%s): %s", cmd, par, txt);
if(!strcmp("NICK", cmd) && !strcmp(usr, nick))
strlcpy(nick, txt, sizeof nick);
}
}
for(i = 0; i < Tlast; i++)
argv[i] = NULL;
/* <bufout> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
* <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <server> ]
* <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(msg[0] == ':') { /* check prefix */
p = strchr(msg, ' ');
*p = 0;
for(++p; *p == ' '; p++);
cmd = p;
argv[Tnick] = &msg[1];
if((p = strchr(msg, '!'))) {
*p = 0;
argv[Tuser] = ++p;
}
} else
cmd = msg;
/* remove CRLFs */
for(p = cmd; p && *p != 0; p++)
if(*p == '\r' || *p == '\n')
*p = 0;
if((p = strchr(cmd, ':'))) {
*p = 0;
argv[Ttext] = ++p;
}
tokenize(&argv[Tcmd], Tlast - Tcmd, cmd, ' ');
if(!strncmp("PONG", argv[Tcmd], 5))
return;
else if(!strncmp("PING", argv[Tcmd], 5)) {
snprintf(bufout, sizeof(bufout), "PONG %s\r\n", argv[Ttext]);
write(srv, bufout, strlen(bufout));
return;
}
else if(!argv[Tnick] || !argv[Tuser]) { /* server command */
snprintf(bufout, sizeof(bufout), "%s", argv[Ttext] ? argv[Ttext] : "");
pout(server, bufout);
return;
}
else if(!strncmp("ERROR", argv[Tcmd], 6))
snprintf(bufout, sizeof(bufout), "-!- error %s",
argv[Ttext] ? argv[Ttext] : "unknown");
else if(!strncmp("JOIN", argv[Tcmd], 5)) {
if(argv[Ttext]!=NULL){
p = strchr(argv[Ttext], ' ');
if(p)
*p = 0;
}
argv[Tchan] = argv[Ttext];
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has joined %s",
argv[Tnick], argv[Tuser], argv[Ttext]);
}
else if(!strncmp("PART", argv[Tcmd], 5)) {
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has left %s",
argv[Tnick], argv[Tuser], argv[Tchan]);
}
else if(!strncmp("MODE", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s changed mode/%s -> %s %s",
argv[Tnick], argv[Tcmd + 1],
argv[Tcmd + 2], argv[Tcmd + 3]);
else if(!strncmp("QUIT", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has quit \"%s\"",
argv[Tnick], argv[Tuser],
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("NICK", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s changed nick to %s",
argv[Tnick], argv[Ttext]);
else if(!strncmp("TOPIC", argv[Tcmd], 6))
snprintf(bufout, sizeof(bufout), "-!- %s changed topic to \"%s\"",
argv[Tnick], argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("KICK", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s kicked %s (\"%s\")",
argv[Tnick], argv[Targ],
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("NOTICE", argv[Tcmd], 7))
snprintf(bufout, sizeof(bufout), "-!- \"%s\")",
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("PRIVMSG", argv[Tcmd], 8))
snprintf(bufout, sizeof(bufout), "<%s> %s",
argv[Tnick], argv[Ttext] ? argv[Ttext] : "");
if(!argv[Tchan] || !strncmp(argv[Tchan], nick, strlen(nick)))
pout(argv[Tnick], bufout);
else
pout(argv[Tchan], bufout);
static void
usage(void) {
eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n", argv0);
}
int
main(int argc, char *argv[]) {
int i;
struct timeval tv;
struct hostent *hp;
static struct sockaddr_in addr; /* initially filled with 0's */
char ping[256];
const char *user = getenv("USER");
int n;
fd_set rd;
nick = fullname = getenv("USER");
for(i = 1; i < argc; i++)
if(!strncmp(argv[i], "-s", 3)) {
if(++i < argc) server = argv[i];
}
else if(!strncmp(argv[i], "-p", 3)) {
if(++i < argc) port = (unsigned short)atoi(argv[i]);
}
else if(!strncmp(argv[i], "-n", 3)) {
if(++i < argc) nick = argv[i];
}
else if(!strncmp(argv[i], "-k", 3)) {
if(++i < argc) password = argv[i];
}
else if(!strncmp(argv[i], "-f", 3)) {
if(++i < argc) fullname = argv[i];
}
else if(!strncmp(argv[i], "-v", 3)) {
fputs("sic-"VERSION", (C)opyright MMVI Anselm R. Garbe\n", stdout);
exit(EXIT_SUCCESS);
}
else {
fputs("usage: sic [-s server] [-p port] [-n nick]"
" [-k keyword] [-f fullname] [-v]\n", stderr);
exit(EXIT_FAILURE);
}
strlcpy(nick, user ? user : "unknown", sizeof nick);
ARGBEGIN {
case 'h':
host = EARGF(usage());
break;
case 'p':
port = EARGF(usage());
break;
case 'n':
strlcpy(nick, EARGF(usage()), sizeof nick);
break;
case 'k':
password = EARGF(usage());
break;
case 'v':
eprint("sic-"VERSION", © 2005-2017 Anselm R Garbe, Jeroen Schot, Kris Maglione, Nico Golde\n");
break;
default:
usage();
} ARGEND;
/* init */
if((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "sic: cannot connect server '%s'\n", server);
exit(EXIT_FAILURE);
}
if (NULL == (hp = gethostbyname(server))) {
fprintf(stderr, "sic: cannot resolve hostname '%s'\n", server);
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
if(connect(srv, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
close(srv);
fprintf(stderr, "sic: cannot connect server '%s'\n", server);
exit(EXIT_FAILURE);
}
srv = fdopen(dial(host, port), "r+");
if (!srv)
eprint("fdopen:");
/* login */
if(password)
snprintf(bufout, sizeof(bufout),
"PASS %s\r\nNICK %s\r\nUSER %s localhost %s :%s\r\n",
password, nick, nick, server, fullname);
else
snprintf(bufout, sizeof(bufout), "NICK %s\r\nUSER %s localhost %s :%s\r\n",
nick, nick, server, fullname);
write(srv, bufout, strlen(bufout));
snprintf(ping, sizeof(ping), "PING %s\r\n", server);
channel[0] = 0;
setbuf(stdout, NULL); /* unbuffered stdout */
sout("PASS %s", password);
sout("NICK %s", nick);
sout("USER %s localhost %s :%s", nick, host, nick);
fflush(srv);
setbuf(stdout, NULL);
setbuf(srv, NULL);
setbuf(stdin, NULL);
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1)
eprint("error: pledge:");
#endif
for(;;) { /* main loop */
FD_ZERO(&rd);
FD_SET(0, &rd);
FD_SET(srv, &rd);
FD_SET(fileno(srv), &rd);
tv.tv_sec = 120;
tv.tv_usec = 0;
i = select(srv + 1, &rd, 0, 0, &tv);
if(i < 0) {
n = select(fileno(srv) + 1, &rd, 0, 0, &tv);
if(n < 0) {
if(errno == EINTR)
continue;
perror("sic: error on select()");
exit(EXIT_FAILURE);
} else if(i == 0) {
if(time(NULL) - trespond >= PINGTIMEOUT) {
pout(server, "-!- sic shutting down: parseing timeout");
exit(EXIT_FAILURE);
}
write(srv, ping, strlen(ping));
eprint("sic: error on select():");
}
else if(n == 0) {
if(time(NULL) - trespond >= 300)
eprint("sic shutting down: parse timeout\n");
sout("PING %s", host);
continue;
}
if(FD_ISSET(srv, &rd)) {
if(getline(srv, sizeof(bufin), bufin) == -1) {
perror("sic: remote server closed connection");
exit(EXIT_FAILURE);
}
if(FD_ISSET(fileno(srv), &rd)) {
if(fgets(bufin, sizeof bufin, srv) == NULL)
eprint("sic: remote host closed connection\n");
parsesrv(bufin);
trespond = time(NULL);
}
if(FD_ISSET(0, &rd)) {
if(getline(0, sizeof(bufin), bufin) == -1) {
perror("sic: broken pipe");
exit(EXIT_FAILURE);
}
if(fgets(bufin, sizeof bufin, stdin) == NULL)
eprint("sic: broken pipe\n");
parsein(bufin);
}
}

46
strlcpy.c Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
#include <sys/types.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}

66
util.c Normal file
View File

@ -0,0 +1,66 @@
/* See LICENSE file for license details. */
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
static void
eprint(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
fprintf(stderr, "%s", bufout);
if(fmt[0] && fmt[strlen(fmt) - 1] == ':')
fprintf(stderr, " %s\n", strerror(errno));
exit(1);
}
static int
dial(char *host, char *port) {
struct addrinfo hints;
struct addrinfo *res, *r;
int fd;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo(host, port, &hints, &res) != 0)
eprint("error: cannot resolve hostname '%s':", host);
for(r = res; r; r = r->ai_next) {
if((fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) == -1)
continue;
if(connect(fd, r->ai_addr, r->ai_addrlen) == 0)
break;
close(fd);
}
freeaddrinfo(res);
if(!r)
eprint("error: cannot connect to host '%s'\n", host);
return fd;
}
static char *
eat(char *s, int (*p)(int), int r) {
while(*s != '\0' && p((unsigned char)*s) == r)
s++;
return s;
}
static char*
skip(char *s, char c) {
while(*s != c && *s != '\0')
s++;
if(*s != '\0')
*s++ = '\0';
return s;
}
static void
trim(char *s) {
char *e;
for (e = s + strlen(s); e > s && isspace((unsigned char)*(e - 1)); e--)
;
*e = '\0';
}